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