bluetooth: Support port availability flag
[profile/ivi/pulseaudio.git] / 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 }