c76a366c52f7420c5b6e047e5a1f66ef38105dae
[profile/ivi/pulseaudio-panda.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 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 }