Sending translation for Finnish
[profile/ivi/pulseaudio.git] / src / modules / module-hal-detect.c
1 /***
2     This file is part of PulseAudio.
3
4     Copyright 2006 Lennart Poettering
5     Copyright 2006 Shams E. King
6
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.
11
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.
16
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 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
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>
35
36 #include <pulse/xmalloc.h>
37 #include <pulse/timeval.h>
38
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>
49
50 #include <hal/libhal.h>
51
52 #include "module-hal-detect-symdef.h"
53
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)
59 PA_MODULE_USAGE("api=<alsa or oss> "
60                 "tsched=<enable system timer based scheduling mode?>");
61 #elif defined(HAVE_ALSA)
62 PA_MODULE_USAGE("api=<alsa> "
63                 "tsched=<enable system timer based scheduling mode?>");
64 #elif defined(HAVE_OSS)
65 PA_MODULE_USAGE("api=<oss>");
66 #endif
67
68 struct device {
69     char *udi, *originating_udi;
70     char *card_name, *sink_name, *source_name;
71     uint32_t module;
72     pa_bool_t acl_race_fix;
73 };
74
75 struct userdata {
76     pa_core *core;
77     LibHalContext *context;
78     pa_dbus_connection *connection;
79     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 */
80     const char *capability;
81 #ifdef HAVE_ALSA
82     pa_bool_t use_tsched;
83 #endif
84 };
85
86 #define CAPABILITY_ALSA "alsa"
87 #define CAPABILITY_OSS "oss"
88
89 static const char* const valid_modargs[] = {
90     "api",
91 #ifdef HAVE_ALSA
92     "tsched",
93 #endif
94     NULL
95 };
96
97 static void device_free(struct device* d) {
98     pa_assert(d);
99
100     pa_xfree(d->udi);
101     pa_xfree(d->originating_udi);
102     pa_xfree(d->sink_name);
103     pa_xfree(d->source_name);
104     pa_xfree(d->card_name);
105     pa_xfree(d);
106 }
107
108 static const char *strip_udi(const char *udi) {
109     const char *slash;
110
111     pa_assert(udi);
112
113     if ((slash = strrchr(udi, '/')))
114         return slash+1;
115
116     return udi;
117 }
118
119 #ifdef HAVE_ALSA
120
121 enum alsa_type {
122     ALSA_TYPE_PLAYBACK,
123     ALSA_TYPE_CAPTURE,
124     ALSA_TYPE_OTHER
125 };
126
127 static enum alsa_type hal_alsa_device_get_type(LibHalContext *context, const char *udi) {
128     char *type;
129     enum alsa_type t = ALSA_TYPE_OTHER;
130     DBusError error;
131
132     dbus_error_init(&error);
133
134     pa_assert(context);
135     pa_assert(udi);
136
137     if (!(type = libhal_device_get_property_string(context, udi, "alsa.type", &error)))
138         goto finish;
139
140     if (pa_streq(type, "playback"))
141         t = ALSA_TYPE_PLAYBACK;
142     else if (pa_streq(type, "capture"))
143         t = ALSA_TYPE_CAPTURE;
144
145     libhal_free_string(type);
146
147 finish:
148     if (dbus_error_is_set(&error)) {
149         pa_log_error("D-Bus error while parsing HAL ALSA data: %s: %s", error.name, error.message);
150         dbus_error_free(&error);
151     }
152
153     return t;
154 }
155
156 static pa_bool_t hal_alsa_device_is_modem(LibHalContext *context, const char *udi) {
157     char *class;
158     pa_bool_t r = FALSE;
159     DBusError error;
160
161     dbus_error_init(&error);
162
163     pa_assert(context);
164     pa_assert(udi);
165
166     if (!(class = libhal_device_get_property_string(context, udi, "alsa.pcm_class", &error)))
167         goto finish;
168
169     r = pa_streq(class, "modem");
170     libhal_free_string(class);
171
172 finish:
173     if (dbus_error_is_set(&error)) {
174         pa_log_error("D-Bus error while parsing HAL ALSA data: %s: %s", error.name, error.message);
175         dbus_error_free(&error);
176     }
177
178     return r;
179 }
180
181 static int hal_device_load_alsa(struct userdata *u, const char *udi, struct device *d) {
182     enum alsa_type type;
183     int card;
184     DBusError error;
185     pa_module *m;
186     char *args, *originating_udi = NULL, *card_name = NULL;
187
188     dbus_error_init(&error);
189
190     pa_assert(u);
191     pa_assert(udi);
192     pa_assert(d);
193
194     /* We only care for PCM devices */
195     type = hal_alsa_device_get_type(u->context, udi);
196     if (type == ALSA_TYPE_OTHER)
197         goto fail;
198
199     /* We don't care for modems */
200     if (hal_alsa_device_is_modem(u->context, udi))
201         goto fail;
202
203     /* We store only one entry per card, hence we look for the originating device */
204     originating_udi = libhal_device_get_property_string(u->context, udi, "alsa.originating_device", &error);
205     if (dbus_error_is_set(&error) || !originating_udi)
206         goto fail;
207
208     /* Make sure we only load one module per card */
209     if (pa_hashmap_get(u->devices, originating_udi))
210         goto fail;
211
212     /* We need the identifier */
213     card = libhal_device_get_property_int(u->context, udi, "alsa.card", &error);
214     if (dbus_error_is_set(&error))
215         goto fail;
216
217     card_name = pa_sprintf_malloc("alsa_card.%s", strip_udi(originating_udi));
218     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);
219
220     pa_log_debug("Loading module-alsa-card with arguments '%s'", args);
221     m = pa_module_load(u->core, "module-alsa-card", args);
222     pa_xfree(args);
223
224     if (!m)
225         goto fail;
226
227     d->originating_udi = originating_udi;
228     d->module = m->index;
229     d->card_name = card_name;
230
231     return 0;
232
233 fail:
234     if (dbus_error_is_set(&error)) {
235         pa_log_error("D-Bus error while parsing HAL ALSA data: %s: %s", error.name, error.message);
236         dbus_error_free(&error);
237     }
238
239     pa_xfree(originating_udi);
240     pa_xfree(card_name);
241
242     return -1;
243 }
244
245 #endif
246
247 #ifdef HAVE_OSS
248
249 static pa_bool_t hal_oss_device_is_pcm(LibHalContext *context, const char *udi) {
250     char *class = NULL, *dev = NULL, *e;
251     int device;
252     pa_bool_t r = FALSE;
253     DBusError error;
254
255     dbus_error_init(&error);
256
257     pa_assert(context);
258     pa_assert(udi);
259
260     /* We only care for PCM devices */
261     class = libhal_device_get_property_string(context, udi, "oss.type", &error);
262     if (dbus_error_is_set(&error) || !class)
263         goto finish;
264
265     if (!pa_streq(class, "pcm"))
266         goto finish;
267
268     /* We don't like /dev/audio */
269     dev = libhal_device_get_property_string(context, udi, "oss.device_file", &error);
270     if (dbus_error_is_set(&error) || !dev)
271         goto finish;
272
273     if ((e = strrchr(dev, '/')))
274         if (pa_startswith(e + 1, "audio"))
275             goto finish;
276
277     /* We only care for the main device */
278     device = libhal_device_get_property_int(context, udi, "oss.device", &error);
279     if (dbus_error_is_set(&error) || device != 0)
280         goto finish;
281
282     r = TRUE;
283
284 finish:
285
286     if (dbus_error_is_set(&error)) {
287         pa_log_error("D-Bus error while parsing HAL OSS data: %s: %s", error.name, error.message);
288         dbus_error_free(&error);
289     }
290
291     libhal_free_string(class);
292     libhal_free_string(dev);
293
294     return r;
295 }
296
297 static int hal_device_load_oss(struct userdata *u, const char *udi, struct device *d) {
298     DBusError error;
299     pa_module *m;
300     char *args, *originating_udi = NULL, *device, *sink_name = NULL, *source_name = NULL;
301
302     dbus_error_init(&error);
303
304     pa_assert(u);
305     pa_assert(udi);
306     pa_assert(d);
307
308     /* We only care for OSS PCM devices */
309     if (!hal_oss_device_is_pcm(u->context, udi))
310         goto fail;
311
312     /* We store only one entry per card, hence we look for the originating device */
313     originating_udi = libhal_device_get_property_string(u->context, udi, "oss.originating_device", &error);
314     if (dbus_error_is_set(&error) || !originating_udi)
315         goto fail;
316
317     /* Make sure we only load one module per card */
318     if (pa_hashmap_get(u->devices, originating_udi))
319         goto fail;
320
321     /* We need the device file */
322     device = libhal_device_get_property_string(u->context, udi, "oss.device_file", &error);
323     if (!device || dbus_error_is_set(&error))
324         goto fail;
325
326     sink_name = pa_sprintf_malloc("oss_output.%s", strip_udi(udi));
327     source_name = pa_sprintf_malloc("oss_input.%s", strip_udi(udi));
328     args = pa_sprintf_malloc("device=%s sink_name=%s source_name=%s", device, sink_name, source_name);
329
330     libhal_free_string(device);
331
332     pa_log_debug("Loading module-oss with arguments '%s'", args);
333     m = pa_module_load(u->core, "module-oss", args);
334     pa_xfree(args);
335
336     if (!m)
337         goto fail;
338
339     d->originating_udi = originating_udi;
340     d->module = m->index;
341     d->sink_name = sink_name;
342     d->source_name = source_name;
343
344     return 0;
345
346 fail:
347     if (dbus_error_is_set(&error)) {
348         pa_log_error("D-Bus error while parsing OSS HAL data: %s: %s", error.name, error.message);
349         dbus_error_free(&error);
350     }
351
352     pa_xfree(originating_udi);
353     pa_xfree(source_name);
354     pa_xfree(sink_name);
355
356     return -1;
357 }
358 #endif
359
360 static struct device* hal_device_add(struct userdata *u, const char *udi) {
361     struct device *d;
362     int r;
363
364     pa_assert(u);
365     pa_assert(u->capability);
366
367     d = pa_xnew(struct device, 1);
368     d->acl_race_fix = FALSE;
369     d->udi = pa_xstrdup(udi);
370     d->originating_udi = NULL;
371     d->module = PA_INVALID_INDEX;
372     d->sink_name = d->source_name = d->card_name = NULL;
373     r = -1;
374
375 #ifdef HAVE_ALSA
376     if (pa_streq(u->capability, CAPABILITY_ALSA))
377         r = hal_device_load_alsa(u, udi,  d);
378 #endif
379 #ifdef HAVE_OSS
380     if (pa_streq(u->capability, CAPABILITY_OSS))
381         r = hal_device_load_oss(u, udi, d);
382 #endif
383
384     if (r < 0) {
385         device_free(d);
386         return NULL;
387     }
388
389     pa_hashmap_put(u->devices, d->udi, d);
390     pa_hashmap_put(u->devices, d->originating_udi, d);
391
392     return d;
393 }
394
395 static int hal_device_add_all(struct userdata *u) {
396     int n, count = 0;
397     char** udis;
398     DBusError error;
399
400     dbus_error_init(&error);
401
402     pa_assert(u);
403
404     udis = libhal_find_device_by_capability(u->context, u->capability, &n, &error);
405     if (dbus_error_is_set(&error) || !udis)
406         goto fail;
407
408     if (n > 0) {
409         int i;
410
411         for (i = 0; i < n; i++) {
412             struct device *d;
413
414             if ((d = hal_device_add(u, udis[i])))
415                 count++;
416             else
417                 pa_log_debug("Not loaded device %s", udis[i]);
418         }
419     }
420
421     libhal_free_string_array(udis);
422
423     return count;
424
425 fail:
426     if (dbus_error_is_set(&error)) {
427         pa_log_error("D-Bus error while parsing HAL data: %s: %s", error.name, error.message);
428         dbus_error_free(&error);
429     }
430
431     return -1;
432 }
433
434 static void device_added_cb(LibHalContext *context, const char *udi) {
435     DBusError error;
436     struct userdata *u;
437     pa_bool_t good = FALSE;
438
439     dbus_error_init(&error);
440
441     pa_assert(context);
442     pa_assert(udi);
443
444     pa_assert_se(u = libhal_ctx_get_user_data(context));
445
446     good = libhal_device_query_capability(context, udi, u->capability, &error);
447     if (dbus_error_is_set(&error) || !good)
448         goto finish;
449
450     if (!hal_device_add(u, udi))
451         pa_log_debug("Not loaded device %s", udi);
452
453 finish:
454     if (dbus_error_is_set(&error)) {
455         if (!dbus_error_has_name(&error, "org.freedesktop.Hal.NoSuchProperty"))
456             pa_log_error("D-Bus error while parsing HAL data: %s: %s", error.name, error.message);
457         dbus_error_free(&error);
458     }
459 }
460
461 static void device_removed_cb(LibHalContext* context, const char *udi) {
462     struct device *d;
463     struct userdata *u;
464
465     pa_assert(context);
466     pa_assert(udi);
467
468     pa_assert_se(u = libhal_ctx_get_user_data(context));
469
470     if (!(d = pa_hashmap_get(u->devices, udi)))
471         return;
472
473     pa_hashmap_remove(u->devices, d->originating_udi);
474     pa_hashmap_remove(u->devices, d->udi);
475
476     pa_log_debug("Removing HAL device: %s", d->originating_udi);
477
478     pa_module_unload_request_by_index(u->core, d->module, TRUE);
479     device_free(d);
480 }
481
482 static void new_capability_cb(LibHalContext *context, const char *udi, const char* capability) {
483     struct userdata *u;
484
485     pa_assert(context);
486     pa_assert(udi);
487     pa_assert(capability);
488
489     pa_assert_se(u = libhal_ctx_get_user_data(context));
490
491     if (pa_streq(u->capability, capability))
492         /* capability we care about, pretend it's a new device */
493         device_added_cb(context, udi);
494 }
495
496 static void lost_capability_cb(LibHalContext *context, const char *udi, const char* capability) {
497     struct userdata *u;
498
499     pa_assert(context);
500     pa_assert(udi);
501     pa_assert(capability);
502
503     pa_assert_se(u = libhal_ctx_get_user_data(context));
504
505     if (pa_streq(u->capability, capability))
506         /* capability we care about, pretend it was removed */
507         device_removed_cb(context, udi);
508 }
509
510 static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, void *userdata) {
511     struct userdata*u;
512     DBusError error;
513
514     pa_assert(bus);
515     pa_assert(message);
516     pa_assert_se(u = userdata);
517
518     dbus_error_init(&error);
519
520     pa_log_debug("dbus: interface=%s, path=%s, member=%s\n",
521                  dbus_message_get_interface(message),
522                  dbus_message_get_path(message),
523                  dbus_message_get_member(message));
524
525     if (dbus_message_is_signal(message, "org.freedesktop.Hal.Device.AccessControl", "ACLAdded") ||
526         dbus_message_is_signal(message, "org.freedesktop.Hal.Device.AccessControl", "ACLRemoved")) {
527         uint32_t uid;
528         pa_bool_t suspend = strcmp(dbus_message_get_member(message), "ACLRemoved") == 0;
529
530         if (!dbus_message_get_args(message, &error, DBUS_TYPE_UINT32, &uid, DBUS_TYPE_INVALID) || dbus_error_is_set(&error)) {
531             pa_log_error("Failed to parse ACL message: %s: %s", error.name, error.message);
532             goto finish;
533         }
534
535         /* Check if this is about us? */
536         if (uid == getuid() || uid == geteuid()) {
537             struct device *d;
538             const char *udi;
539
540             udi = dbus_message_get_path(message);
541
542             if ((d = pa_hashmap_get(u->devices, udi))) {
543                 pa_bool_t send_acl_race_fix_message = FALSE;
544                 d->acl_race_fix = FALSE;
545
546                 if (d->sink_name) {
547                     pa_sink *sink;
548
549                     if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK))) {
550                         pa_bool_t success = pa_sink_suspend(sink, suspend) >= 0;
551
552                         if (!success && !suspend)
553                             d->acl_race_fix = TRUE; /* resume failed, let's try again */
554                         else if (suspend)
555                             send_acl_race_fix_message = TRUE; /* suspend finished, let's tell everyone to try again */
556                     }
557                 }
558
559                 if (d->source_name) {
560                     pa_source *source;
561
562                     if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE))) {
563                         pa_bool_t success = pa_source_suspend(source, suspend) >= 0;
564
565                         if (!success && !suspend)
566                             d->acl_race_fix = TRUE; /* resume failed, let's try again */
567                         else if (suspend)
568                             send_acl_race_fix_message = TRUE; /* suspend finished, let's tell everyone to try again */
569                     }
570                 }
571
572                 if (d->card_name) {
573                     pa_card *card;
574
575                     if ((card = pa_namereg_get(u->core, d->card_name, PA_NAMEREG_CARD))) {
576                         pa_bool_t success = pa_card_suspend(card, suspend) >= 0;
577
578                         if (!success && !suspend)
579                             d->acl_race_fix = TRUE; /* resume failed, let's try again */
580                         else if (suspend)
581                             send_acl_race_fix_message = TRUE; /* suspend finished, let's tell everyone to try again */
582                     }
583                 }
584
585                 if (send_acl_race_fix_message) {
586                     DBusMessage *msg;
587                     msg = dbus_message_new_signal(udi, "org.pulseaudio.Server", "DirtyGiveUpMessage");
588                     dbus_connection_send(pa_dbus_connection_get(u->connection), msg, NULL);
589                     dbus_message_unref(msg);
590                 }
591
592             } else if (!suspend)
593                 device_added_cb(u->context, udi);
594
595         }
596
597         return DBUS_HANDLER_RESULT_HANDLED;
598
599     } else if (dbus_message_is_signal(message, "org.pulseaudio.Server", "DirtyGiveUpMessage")) {
600         /* We use this message to avoid a dirty race condition when we
601            get an ACLAdded message before the previously owning PA
602            sever has closed the device. We can remove this as
603            soon as HAL learns frevoke() */
604
605         struct device *d;
606         const char *udi;
607
608         udi = dbus_message_get_path(message);
609
610         if ((d = pa_hashmap_get(u->devices, udi))) {
611
612             if (d->acl_race_fix) {
613                 d->acl_race_fix = FALSE;
614                 pa_log_debug("Got dirty give up message for '%s', trying resume ...", udi);
615
616                 if (d->sink_name) {
617                     pa_sink *sink;
618
619                     if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK)))
620                         pa_sink_suspend(sink, FALSE);
621                 }
622
623                 if (d->source_name) {
624                     pa_source *source;
625
626                     if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE)))
627                         pa_source_suspend(source, FALSE);
628                 }
629
630                 if (d->card_name) {
631                     pa_card *card;
632
633                     if ((card = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_CARD)))
634                         pa_card_suspend(card, FALSE);
635                 }
636             }
637
638         } else
639             /* Yes, we don't check the UDI for validity, but hopefully HAL will */
640             device_added_cb(u->context, udi);
641
642         return DBUS_HANDLER_RESULT_HANDLED;
643     }
644
645 finish:
646     dbus_error_free(&error);
647
648     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
649 }
650
651 static void hal_context_free(LibHalContext* hal_context) {
652     DBusError error;
653
654     dbus_error_init(&error);
655
656     libhal_ctx_shutdown(hal_context, &error);
657     libhal_ctx_free(hal_context);
658
659     dbus_error_free(&error);
660 }
661
662 static LibHalContext* hal_context_new(DBusConnection *connection) {
663     DBusError error;
664     LibHalContext *hal_context = NULL;
665
666     dbus_error_init(&error);
667
668     pa_assert(connection);
669
670     if (!(hal_context = libhal_ctx_new())) {
671         pa_log_error("libhal_ctx_new() failed");
672         goto fail;
673     }
674
675     if (!libhal_ctx_set_dbus_connection(hal_context, connection)) {
676         pa_log_error("Error establishing DBUS connection: %s: %s", error.name, error.message);
677         goto fail;
678     }
679
680     if (!libhal_ctx_init(hal_context, &error)) {
681         pa_log_error("Couldn't connect to hald: %s: %s", error.name, error.message);
682         goto fail;
683     }
684
685     return hal_context;
686
687 fail:
688     if (hal_context)
689         hal_context_free(hal_context);
690
691     dbus_error_free(&error);
692
693     return NULL;
694 }
695
696 int pa__init(pa_module*m) {
697     DBusError error;
698     struct userdata *u = NULL;
699     int n = 0;
700     pa_modargs *ma;
701     const char *api;
702
703     pa_assert(m);
704
705     dbus_error_init(&error);
706
707     if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
708         pa_log("Failed to parse module arguments");
709         goto fail;
710     }
711
712     m->userdata = u = pa_xnew(struct userdata, 1);
713     u->core = m->core;
714     u->context = NULL;
715     u->connection = NULL;
716     u->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
717     u->capability = NULL;
718
719 #ifdef HAVE_ALSA
720     u->use_tsched = TRUE;
721
722     if (pa_modargs_get_value_boolean(ma, "tsched", &u->use_tsched) < 0) {
723         pa_log("Failed to parse tsched argument.");
724         goto fail;
725     }
726
727     api = pa_modargs_get_value(ma, "api", "alsa");
728
729     if (pa_streq(api, "alsa"))
730         u->capability = CAPABILITY_ALSA;
731 #else
732     api = pa_modargs_get_value(ma, "api", "oss");
733 #endif
734
735 #ifdef HAVE_OSS
736     if (pa_streq(api, "oss"))
737         u->capability = CAPABILITY_OSS;
738 #endif
739
740     if (!u->capability) {
741         pa_log_error("Invalid API specification.");
742         goto fail;
743     }
744
745     if (!(u->connection = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &error)) || dbus_error_is_set(&error)) {
746         pa_log_error("Unable to contact DBUS system bus: %s: %s", error.name, error.message);
747         goto fail;
748     }
749
750     if (!(u->context = hal_context_new(pa_dbus_connection_get(u->connection)))) {
751         /* pa_hal_context_new() logs appropriate errors */
752         goto fail;
753     }
754
755     n = hal_device_add_all(u);
756
757     libhal_ctx_set_user_data(u->context, u);
758     libhal_ctx_set_device_added(u->context, device_added_cb);
759     libhal_ctx_set_device_removed(u->context, device_removed_cb);
760     libhal_ctx_set_device_new_capability(u->context, new_capability_cb);
761     libhal_ctx_set_device_lost_capability(u->context, lost_capability_cb);
762
763     if (!libhal_device_property_watch_all(u->context, &error)) {
764         pa_log_error("Error monitoring device list: %s: %s", error.name, error.message);
765         goto fail;
766     }
767
768     if (!dbus_connection_add_filter(pa_dbus_connection_get(u->connection), filter_cb, u, NULL)) {
769         pa_log_error("Failed to add filter function");
770         goto fail;
771     }
772
773     if (pa_dbus_add_matches(
774                 pa_dbus_connection_get(u->connection), &error,
775                 "type='signal',sender='org.freedesktop.Hal',interface='org.freedesktop.Hal.Device.AccessControl',member='ACLAdded'",
776                 "type='signal',sender='org.freedesktop.Hal',interface='org.freedesktop.Hal.Device.AccessControl',member='ACLRemoved'",
777                 "type='signal',interface='org.pulseaudio.Server',member='DirtyGiveUpMessage'", NULL) < 0) {
778         pa_log_error("Unable to subscribe to HAL ACL signals: %s: %s", error.name, error.message);
779         goto fail;
780     }
781
782     pa_log_info("Loaded %i modules.", n);
783
784     pa_modargs_free(ma);
785
786     return 0;
787
788 fail:
789     if (ma)
790         pa_modargs_free(ma);
791
792     dbus_error_free(&error);
793     pa__done(m);
794
795     return -1;
796 }
797
798 void pa__done(pa_module *m) {
799     struct userdata *u;
800
801     pa_assert(m);
802
803     if (!(u = m->userdata))
804         return;
805
806     if (u->context)
807         hal_context_free(u->context);
808
809     if (u->devices) {
810         struct device *d;
811
812         while ((d = pa_hashmap_first(u->devices))) {
813             pa_hashmap_remove(u->devices, d->udi);
814             pa_hashmap_remove(u->devices, d->originating_udi);
815             device_free(d);
816         }
817
818         pa_hashmap_free(u->devices, NULL, NULL);
819     }
820
821     if (u->connection) {
822         pa_dbus_remove_matches(
823                 pa_dbus_connection_get(u->connection),
824                 "type='signal',sender='org.freedesktop.Hal',interface='org.freedesktop.Hal.Device.AccessControl',member='ACLAdded'",
825                 "type='signal',sender='org.freedesktop.Hal',interface='org.freedesktop.Hal.Device.AccessControl',member='ACLRemoved'",
826                 "type='signal',interface='org.pulseaudio.Server',member='DirtyGiveUpMessage'", NULL);
827
828         dbus_connection_remove_filter(pa_dbus_connection_get(u->connection), filter_cb, u);
829         pa_dbus_connection_unref(u->connection);
830     }
831
832     pa_xfree(u);
833 }