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