]> code.delx.au - pulseaudio/blob - src/modules/module-hal-detect.c
Merge commit 'origin/master-tx'
[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
49 #include <hal/libhal.h>
50
51 #include "dbus-util.h"
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 pa_log_error("D-Bus error while parsing HAL data: %s: %s", error.name, error.message);
456 dbus_error_free(&error);
457 }
458 }
459
460 static void device_removed_cb(LibHalContext* context, const char *udi) {
461 struct device *d;
462 struct userdata *u;
463
464 pa_assert(context);
465 pa_assert(udi);
466
467 pa_assert_se(u = libhal_ctx_get_user_data(context));
468
469 if (!(d = pa_hashmap_get(u->devices, udi)))
470 return;
471
472 pa_hashmap_remove(u->devices, d->originating_udi);
473 pa_hashmap_remove(u->devices, d->udi);
474
475 pa_log_debug("Removing HAL device: %s", d->originating_udi);
476
477 pa_module_unload_request_by_index(u->core, d->module, TRUE);
478 device_free(d);
479 }
480
481 static void new_capability_cb(LibHalContext *context, const char *udi, const char* capability) {
482 struct userdata *u;
483
484 pa_assert(context);
485 pa_assert(udi);
486 pa_assert(capability);
487
488 pa_assert_se(u = libhal_ctx_get_user_data(context));
489
490 if (pa_streq(u->capability, capability))
491 /* capability we care about, pretend it's a new device */
492 device_added_cb(context, udi);
493 }
494
495 static void lost_capability_cb(LibHalContext *context, const char *udi, const char* capability) {
496 struct userdata *u;
497
498 pa_assert(context);
499 pa_assert(udi);
500 pa_assert(capability);
501
502 pa_assert_se(u = libhal_ctx_get_user_data(context));
503
504 if (pa_streq(u->capability, capability))
505 /* capability we care about, pretend it was removed */
506 device_removed_cb(context, udi);
507 }
508
509 static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, void *userdata) {
510 struct userdata*u;
511 DBusError error;
512
513 pa_assert(bus);
514 pa_assert(message);
515 pa_assert_se(u = userdata);
516
517 dbus_error_init(&error);
518
519 pa_log_debug("dbus: interface=%s, path=%s, member=%s\n",
520 dbus_message_get_interface(message),
521 dbus_message_get_path(message),
522 dbus_message_get_member(message));
523
524 if (dbus_message_is_signal(message, "org.freedesktop.Hal.Device.AccessControl", "ACLAdded") ||
525 dbus_message_is_signal(message, "org.freedesktop.Hal.Device.AccessControl", "ACLRemoved")) {
526 uint32_t uid;
527 pa_bool_t suspend = strcmp(dbus_message_get_member(message), "ACLRemoved") == 0;
528
529 if (!dbus_message_get_args(message, &error, DBUS_TYPE_UINT32, &uid, DBUS_TYPE_INVALID) || dbus_error_is_set(&error)) {
530 pa_log_error("Failed to parse ACL message: %s: %s", error.name, error.message);
531 goto finish;
532 }
533
534 /* Check if this is about us? */
535 if (uid == getuid() || uid == geteuid()) {
536 struct device *d;
537 const char *udi;
538
539 udi = dbus_message_get_path(message);
540
541 if ((d = pa_hashmap_get(u->devices, udi))) {
542 pa_bool_t send_acl_race_fix_message = FALSE;
543 d->acl_race_fix = FALSE;
544
545 if (d->sink_name) {
546 pa_sink *sink;
547
548 if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK))) {
549 pa_bool_t success = pa_sink_suspend(sink, suspend) >= 0;
550
551 if (!success && !suspend)
552 d->acl_race_fix = TRUE; /* resume failed, let's try again */
553 else if (suspend)
554 send_acl_race_fix_message = TRUE; /* suspend finished, let's tell everyone to try again */
555 }
556 }
557
558 if (d->source_name) {
559 pa_source *source;
560
561 if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE))) {
562 pa_bool_t success = pa_source_suspend(source, suspend) >= 0;
563
564 if (!success && !suspend)
565 d->acl_race_fix = TRUE; /* resume failed, let's try again */
566 else if (suspend)
567 send_acl_race_fix_message = TRUE; /* suspend finished, let's tell everyone to try again */
568 }
569 }
570
571 if (d->card_name) {
572 pa_card *card;
573
574 if ((card = pa_namereg_get(u->core, d->card_name, PA_NAMEREG_CARD))) {
575 pa_bool_t success = pa_card_suspend(card, suspend) >= 0;
576
577 if (!success && !suspend)
578 d->acl_race_fix = TRUE; /* resume failed, let's try again */
579 else if (suspend)
580 send_acl_race_fix_message = TRUE; /* suspend finished, let's tell everyone to try again */
581 }
582 }
583
584 if (send_acl_race_fix_message) {
585 DBusMessage *msg;
586 msg = dbus_message_new_signal(udi, "org.pulseaudio.Server", "DirtyGiveUpMessage");
587 dbus_connection_send(pa_dbus_connection_get(u->connection), msg, NULL);
588 dbus_message_unref(msg);
589 }
590
591 } else if (!suspend)
592 device_added_cb(u->context, udi);
593
594 }
595
596 return DBUS_HANDLER_RESULT_HANDLED;
597
598 } else if (dbus_message_is_signal(message, "org.pulseaudio.Server", "DirtyGiveUpMessage")) {
599 /* We use this message to avoid a dirty race condition when we
600 get an ACLAdded message before the previously owning PA
601 sever has closed the device. We can remove this as
602 soon as HAL learns frevoke() */
603
604 struct device *d;
605 const char *udi;
606
607 udi = dbus_message_get_path(message);
608
609 if ((d = pa_hashmap_get(u->devices, udi))) {
610
611 if (d->acl_race_fix) {
612 d->acl_race_fix = FALSE;
613 pa_log_debug("Got dirty give up message for '%s', trying resume ...", udi);
614
615 if (d->sink_name) {
616 pa_sink *sink;
617
618 if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK)))
619 pa_sink_suspend(sink, FALSE);
620 }
621
622 if (d->source_name) {
623 pa_source *source;
624
625 if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE)))
626 pa_source_suspend(source, FALSE);
627 }
628
629 if (d->card_name) {
630 pa_card *card;
631
632 if ((card = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_CARD)))
633 pa_card_suspend(card, FALSE);
634 }
635 }
636
637 } else
638 /* Yes, we don't check the UDI for validity, but hopefully HAL will */
639 device_added_cb(u->context, udi);
640
641 return DBUS_HANDLER_RESULT_HANDLED;
642 }
643
644 finish:
645 dbus_error_free(&error);
646
647 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
648 }
649
650 static void hal_context_free(LibHalContext* hal_context) {
651 DBusError error;
652
653 dbus_error_init(&error);
654
655 libhal_ctx_shutdown(hal_context, &error);
656 libhal_ctx_free(hal_context);
657
658 dbus_error_free(&error);
659 }
660
661 static LibHalContext* hal_context_new(DBusConnection *connection) {
662 DBusError error;
663 LibHalContext *hal_context = NULL;
664
665 dbus_error_init(&error);
666
667 pa_assert(connection);
668
669 if (!(hal_context = libhal_ctx_new())) {
670 pa_log_error("libhal_ctx_new() failed");
671 goto fail;
672 }
673
674 if (!libhal_ctx_set_dbus_connection(hal_context, connection)) {
675 pa_log_error("Error establishing DBUS connection: %s: %s", error.name, error.message);
676 goto fail;
677 }
678
679 if (!libhal_ctx_init(hal_context, &error)) {
680 pa_log_error("Couldn't connect to hald: %s: %s", error.name, error.message);
681 goto fail;
682 }
683
684 return hal_context;
685
686 fail:
687 if (hal_context)
688 hal_context_free(hal_context);
689
690 dbus_error_free(&error);
691
692 return NULL;
693 }
694
695 int pa__init(pa_module*m) {
696 DBusError error;
697 struct userdata *u = NULL;
698 int n = 0;
699 pa_modargs *ma;
700 const char *api;
701
702 pa_assert(m);
703
704 dbus_error_init(&error);
705
706 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
707 pa_log("Failed to parse module arguments");
708 goto fail;
709 }
710
711 m->userdata = u = pa_xnew(struct userdata, 1);
712 u->core = m->core;
713 u->context = NULL;
714 u->connection = NULL;
715 u->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
716 u->capability = NULL;
717
718 #ifdef HAVE_ALSA
719 u->use_tsched = TRUE;
720
721 if (pa_modargs_get_value_boolean(ma, "tsched", &u->use_tsched) < 0) {
722 pa_log("Failed to parse tsched argument.");
723 goto fail;
724 }
725
726 api = pa_modargs_get_value(ma, "api", "alsa");
727
728 if (pa_streq(api, "alsa"))
729 u->capability = CAPABILITY_ALSA;
730 #else
731 api = pa_modargs_get_value(ma, "api", "oss");
732 #endif
733
734 #ifdef HAVE_OSS
735 if (pa_streq(api, "oss"))
736 u->capability = CAPABILITY_OSS;
737 #endif
738
739 if (!u->capability) {
740 pa_log_error("Invalid API specification.");
741 goto fail;
742 }
743
744 if (!(u->connection = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &error)) || dbus_error_is_set(&error)) {
745 pa_log_error("Unable to contact DBUS system bus: %s: %s", error.name, error.message);
746 goto fail;
747 }
748
749 if (!(u->context = hal_context_new(pa_dbus_connection_get(u->connection)))) {
750 /* pa_hal_context_new() logs appropriate errors */
751 goto fail;
752 }
753
754 n = hal_device_add_all(u);
755
756 libhal_ctx_set_user_data(u->context, u);
757 libhal_ctx_set_device_added(u->context, device_added_cb);
758 libhal_ctx_set_device_removed(u->context, device_removed_cb);
759 libhal_ctx_set_device_new_capability(u->context, new_capability_cb);
760 libhal_ctx_set_device_lost_capability(u->context, lost_capability_cb);
761
762 if (!libhal_device_property_watch_all(u->context, &error)) {
763 pa_log_error("Error monitoring device list: %s: %s", error.name, error.message);
764 goto fail;
765 }
766
767 if (!dbus_connection_add_filter(pa_dbus_connection_get(u->connection), filter_cb, u, NULL)) {
768 pa_log_error("Failed to add filter function");
769 goto fail;
770 }
771
772 if (pa_dbus_add_matches(
773 pa_dbus_connection_get(u->connection), &error,
774 "type='signal',sender='org.freedesktop.Hal',interface='org.freedesktop.Hal.Device.AccessControl',member='ACLAdded'",
775 "type='signal',sender='org.freedesktop.Hal',interface='org.freedesktop.Hal.Device.AccessControl',member='ACLRemoved'",
776 "type='signal',interface='org.pulseaudio.Server',member='DirtyGiveUpMessage'", NULL) < 0) {
777 pa_log_error("Unable to subscribe to HAL ACL signals: %s: %s", error.name, error.message);
778 goto fail;
779 }
780
781 pa_log_info("Loaded %i modules.", n);
782
783 pa_modargs_free(ma);
784
785 return 0;
786
787 fail:
788 if (ma)
789 pa_modargs_free(ma);
790
791 dbus_error_free(&error);
792 pa__done(m);
793
794 return -1;
795 }
796
797 void pa__done(pa_module *m) {
798 struct userdata *u;
799
800 pa_assert(m);
801
802 if (!(u = m->userdata))
803 return;
804
805 if (u->context)
806 hal_context_free(u->context);
807
808 if (u->devices) {
809 struct device *d;
810
811 while ((d = pa_hashmap_first(u->devices))) {
812 pa_hashmap_remove(u->devices, d->udi);
813 pa_hashmap_remove(u->devices, d->originating_udi);
814 device_free(d);
815 }
816
817 pa_hashmap_free(u->devices, NULL, NULL);
818 }
819
820 if (u->connection) {
821 pa_dbus_remove_matches(
822 pa_dbus_connection_get(u->connection),
823 "type='signal',sender='org.freedesktop.Hal',interface='org.freedesktop.Hal.Device.AccessControl',member='ACLAdded'",
824 "type='signal',sender='org.freedesktop.Hal',interface='org.freedesktop.Hal.Device.AccessControl',member='ACLRemoved'",
825 "type='signal',interface='org.pulseaudio.Server',member='DirtyGiveUpMessage'", NULL);
826
827 dbus_connection_remove_filter(pa_dbus_connection_get(u->connection), filter_cb, u);
828 pa_dbus_connection_unref(u->connection);
829 }
830
831 pa_xfree(u);
832 }