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