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