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