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