Add "Rear Mic" to alsa mixer paths.
[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 #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 }