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