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