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