]> code.delx.au - pulseaudio/blob - src/modules/module-hal-detect.c
dbus: split dbus-util into dbus-shared
[pulseaudio] / 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 }