]> code.delx.au - pulseaudio/blob - src/modules/module-hal-detect.c
ed21f4defb1d1a92cc82ecc3ba35ad07fb44b71e
[pulseaudio] / src / modules / module-hal-detect.c
1 /* $Id$ */
2
3 /***
4 This file is part of PulseAudio.
5
6 Copyright 2006 Lennart Poettering
7 Copyright 2006 Shams E. King
8
9 PulseAudio is free software; you can redistribute it and/or modify
10 it under the terms of the GNU Lesser General Public License as published
11 by the Free Software Foundation; either version 2 of the License,
12 or (at your option) any later version.
13
14 PulseAudio is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public License
20 along with PulseAudio; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22 USA.
23 ***/
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <stdio.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <stdlib.h>
33 #include <errno.h>
34 #include <stdlib.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37
38 #include <pulse/xmalloc.h>
39 #include <pulse/timeval.h>
40
41 #include <pulsecore/core-error.h>
42 #include <pulsecore/module.h>
43 #include <pulsecore/log.h>
44 #include <pulsecore/hashmap.h>
45 #include <pulsecore/idxset.h>
46 #include <pulsecore/core-util.h>
47 #include <pulsecore/namereg.h>
48 #include <pulsecore/core-scache.h>
49
50 #include <hal/libhal.h>
51
52 #include "dbus-util.h"
53 #include "module-hal-detect-symdef.h"
54
55 PA_MODULE_AUTHOR("Shahms King")
56 PA_MODULE_DESCRIPTION("Detect available audio hardware and load matching drivers")
57 PA_MODULE_VERSION(PACKAGE_VERSION)
58
59 struct device {
60 uint32_t index;
61 char *udi;
62 char *sink_name, *source_name;
63 };
64
65 struct userdata {
66 pa_core *core;
67 LibHalContext *context;
68 pa_dbus_connection *connection;
69 pa_hashmap *devices;
70 const char *capability;
71 };
72
73 struct timerdata {
74 struct userdata *u;
75 char *udi;
76 };
77
78 #define CAPABILITY_ALSA "alsa"
79 #define CAPABILITY_OSS "oss"
80
81 static void hal_device_free(struct device* d) {
82 pa_assert(d);
83
84 pa_xfree(d->udi);
85 pa_xfree(d->sink_name);
86 pa_xfree(d->source_name);
87 pa_xfree(d);
88 }
89
90 static void hal_device_free_cb(void *d, PA_GCC_UNUSED void *data) {
91 hal_device_free(d);
92 }
93
94 static const char *strip_udi(const char *udi) {
95 const char *slash;
96
97 if ((slash = strrchr(udi, '/')))
98 return slash+1;
99
100 return udi;
101 }
102
103 #ifdef HAVE_ALSA
104
105 typedef enum {
106 ALSA_TYPE_SINK,
107 ALSA_TYPE_SOURCE,
108 ALSA_TYPE_OTHER,
109 ALSA_TYPE_MAX
110 } alsa_type_t;
111
112 static alsa_type_t hal_alsa_device_get_type(LibHalContext *context, const char *udi, DBusError *error) {
113 char *type;
114 alsa_type_t t;
115
116 if (!(type = libhal_device_get_property_string(context, udi, "alsa.type", error)))
117 return ALSA_TYPE_OTHER;
118
119 if (!strcmp(type, "playback"))
120 t = ALSA_TYPE_SINK;
121 else if (!strcmp(type, "capture"))
122 t = ALSA_TYPE_SOURCE;
123 else
124 t = ALSA_TYPE_OTHER;
125
126 libhal_free_string(type);
127
128 return t;
129 }
130
131 static int hal_alsa_device_is_modem(LibHalContext *context, const char *udi, DBusError *error) {
132 char *class;
133 int r;
134
135 if (!(class = libhal_device_get_property_string(context, udi, "alsa.pcm_class", error)))
136 return 0;
137
138 r = strcmp(class, "modem") == 0;
139 pa_xfree(class);
140
141 return r;
142 }
143
144 static pa_module* hal_device_load_alsa(struct userdata *u, const char *udi, char **sink_name, char **source_name) {
145 char args[128];
146 alsa_type_t type;
147 int device, card;
148 const char *module_name;
149 DBusError error;
150 pa_module *m;
151
152 dbus_error_init(&error);
153
154 pa_assert(u);
155 pa_assert(sink_name);
156 pa_assert(source_name);
157
158 *sink_name = *source_name = NULL;
159
160 type = hal_alsa_device_get_type(u->context, udi, &error);
161 if (dbus_error_is_set(&error) || type == ALSA_TYPE_OTHER)
162 goto fail;
163
164 device = libhal_device_get_property_int(u->context, udi, "alsa.device", &error);
165 if (dbus_error_is_set(&error) || device != 0)
166 goto fail;
167
168 card = libhal_device_get_property_int(u->context, udi, "alsa.card", &error);
169 if (dbus_error_is_set(&error))
170 goto fail;
171
172 if (hal_alsa_device_is_modem(u->context, udi, &error))
173 goto fail;
174
175 if (type == ALSA_TYPE_SINK) {
176 *sink_name = pa_sprintf_malloc("alsa_output.%s", strip_udi(udi));
177
178 module_name = "module-alsa-sink";
179 pa_snprintf(args, sizeof(args), "device=hw:%u sink_name=%s", card, *sink_name);
180 } else {
181 *source_name = pa_sprintf_malloc("alsa_output.%s", strip_udi(udi));
182
183 module_name = "module-alsa-source";
184 pa_snprintf(args, sizeof(args), "device=hw:%u source_name=%s", card, *source_name);
185 }
186
187 pa_log_debug("Loading %s with arguments '%s'", module_name, args);
188
189 m = pa_module_load(u->core, module_name, args);
190
191 if (!m) {
192 pa_xfree(*sink_name);
193 pa_xfree(*source_name);
194 *sink_name = *source_name = NULL;
195 }
196
197 return m;
198
199 fail:
200 if (dbus_error_is_set(&error)) {
201 pa_log_error("D-Bus error while parsing ALSA data: %s: %s", error.name, error.message);
202 dbus_error_free(&error);
203 }
204
205 return NULL;
206 }
207
208 #endif
209
210 #ifdef HAVE_OSS
211
212 static int hal_oss_device_is_pcm(LibHalContext *context, const char *udi, DBusError *error) {
213 char *class = NULL, *dev = NULL, *e;
214 int device;
215 int r = 0;
216
217 class = libhal_device_get_property_string(context, udi, "oss.type", error);
218 if (dbus_error_is_set(error) || !class)
219 goto finish;
220
221 if (strcmp(class, "pcm"))
222 goto finish;
223
224 dev = libhal_device_get_property_string(context, udi, "oss.device_file", error);
225 if (dbus_error_is_set(error) || !dev)
226 goto finish;
227
228 if ((e = strrchr(dev, '/')))
229 if (pa_startswith(e + 1, "audio"))
230 goto finish;
231
232 device = libhal_device_get_property_int(context, udi, "oss.device", error);
233 if (dbus_error_is_set(error) || device != 0)
234 goto finish;
235
236 r = 1;
237
238 finish:
239
240 libhal_free_string(class);
241 libhal_free_string(dev);
242
243 return r;
244 }
245
246 static pa_module* hal_device_load_oss(struct userdata *u, const char *udi, char **sink_name, char **source_name) {
247 char args[256];
248 char* device;
249 DBusError error;
250 pa_module *m;
251
252 dbus_error_init(&error);
253
254 pa_assert(u);
255 pa_assert(sink_name);
256 pa_assert(source_name);
257
258 *sink_name = *source_name = NULL;
259
260 if (!hal_oss_device_is_pcm(u->context, udi, &error) || dbus_error_is_set(&error))
261 goto fail;
262
263 device = libhal_device_get_property_string(u->context, udi, "oss.device_file", &error);
264 if (!device || dbus_error_is_set(&error))
265 goto fail;
266
267 *sink_name = pa_sprintf_malloc("alsa_output.%s", strip_udi(udi));
268 *source_name = pa_sprintf_malloc("alsa_output.%s", strip_udi(udi));
269
270 pa_snprintf(args, sizeof(args), "device=%s sink_name=%s source_name=%s", device, sink_name, source_name);
271 libhal_free_string(device);
272
273 pa_log_debug("Loading module-oss with arguments '%s'", args);
274
275 m = pa_module_load(u->core, "module-oss", args);
276
277 if (!m) {
278 pa_xfree(*sink_name);
279 pa_xfree(*source_name);
280 *sink_name = *source_name = NULL;
281 }
282
283 return m;
284
285 fail:
286 if (dbus_error_is_set(&error)) {
287 pa_log_error("D-Bus error while parsing OSS data: %s: %s", error.name, error.message);
288 dbus_error_free(&error);
289 }
290
291 return NULL;
292 }
293 #endif
294
295 static struct device* hal_device_add(struct userdata *u, const char *udi) {
296 pa_module* m = NULL;
297 struct device *d;
298 char *sink_name = NULL, *source_name = NULL;
299
300 pa_assert(u);
301 pa_assert(u->capability);
302
303 #ifdef HAVE_ALSA
304 if (strcmp(u->capability, CAPABILITY_ALSA) == 0)
305 m = hal_device_load_alsa(u, udi, &sink_name, &source_name);
306 #endif
307 #ifdef HAVE_OSS
308 if (strcmp(u->capability, CAPABILITY_OSS) == 0)
309 m = hal_device_load_oss(u, udi, &sink_name, &source_name);
310 #endif
311
312 if (!m)
313 return NULL;
314
315 d = pa_xnew(struct device, 1);
316 d->udi = pa_xstrdup(udi);
317 d->index = m->index;
318 d->sink_name = sink_name;
319 d->source_name = source_name;
320 pa_hashmap_put(u->devices, d->udi, d);
321
322 return d;
323 }
324
325 static int hal_device_add_all(struct userdata *u, const char *capability) {
326 DBusError error;
327 int i, n, count = 0;
328 char** udis;
329
330 pa_assert(u);
331 pa_assert(!u->capability);
332
333 dbus_error_init(&error);
334
335 pa_log_info("Trying capability %s", capability);
336
337 udis = libhal_find_device_by_capability(u->context, capability, &n, &error);
338 if (dbus_error_is_set(&error)) {
339 pa_log_error("Error finding devices: %s: %s", error.name, error.message);
340 dbus_error_free(&error);
341 return -1;
342 }
343
344 if (n > 0) {
345 u->capability = capability;
346
347 for (i = 0; i < n; i++) {
348 struct device *d;
349
350 if (!(d = hal_device_add(u, udis[i])))
351 pa_log_debug("Not loaded device %s", udis[i]);
352 else {
353 if (d->sink_name)
354 pa_scache_play_item_by_name(u->core, "pulse-coldplug", d->sink_name, PA_VOLUME_NORM, 0);
355 count++;
356 }
357 }
358 }
359
360 libhal_free_string_array(udis);
361 return count;
362 }
363
364 static dbus_bool_t device_has_capability(LibHalContext *context, const char *udi, const char* cap, DBusError *error){
365 dbus_bool_t has_prop;
366
367 has_prop = libhal_device_property_exists(context, udi, "info.capabilities", error);
368 if (!has_prop || dbus_error_is_set(error))
369 return FALSE;
370
371 return libhal_device_query_capability(context, udi, cap, error);
372 }
373
374 static void device_added_time_cb(pa_mainloop_api *ea, pa_time_event *ev, const struct timeval *tv, void *userdata) {
375 DBusError error;
376 struct timerdata *td = userdata;
377 int b;
378 struct device *d;
379
380 dbus_error_init(&error);
381
382 b = libhal_device_exists(td->u->context, td->udi, &error);
383
384 if (dbus_error_is_set(&error)) {
385 pa_log_error("Error adding device: %s: %s", error.name, error.message);
386 dbus_error_free(&error);
387 } else if (b) {
388 if (!(d = hal_device_add(td->u, td->udi)))
389 pa_log_debug("Not loaded device %s", td->udi);
390 else {
391 if (d->sink_name)
392 pa_scache_play_item_by_name(td->u->core, "pulse-hotplug", d->sink_name, PA_VOLUME_NORM, 0);
393 }
394 }
395
396 pa_xfree(td->udi);
397 pa_xfree(td);
398 ea->time_free(ev);
399 }
400
401 static void device_added_cb(LibHalContext *context, const char *udi) {
402 DBusError error;
403 struct timeval tv;
404 struct timerdata *t;
405 struct userdata *u;
406 int good = 0;
407
408 pa_assert_se(u = libhal_ctx_get_user_data(context));
409
410 pa_log_debug("HAL Device added: %s", udi);
411
412 dbus_error_init(&error);
413
414 if (u->capability) {
415
416 good = device_has_capability(context, udi, u->capability, &error);
417
418 if (dbus_error_is_set(&error)) {
419 pa_log_error("Error getting capability: %s: %s", error.name, error.message);
420 dbus_error_free(&error);
421 return;
422 }
423
424 } else {
425
426 #ifdef HAVE_ALSA
427 good = device_has_capability(context, udi, CAPABILITY_ALSA, &error);
428
429 if (dbus_error_is_set(&error)) {
430 pa_log_error("Error getting capability: %s: %s", error.name, error.message);
431 dbus_error_free(&error);
432 return;
433 }
434
435 if (good)
436 u->capability = CAPABILITY_ALSA;
437 #endif
438 #if defined(HAVE_OSS) && defined(HAVE_ALSA)
439 if (!good) {
440 #endif
441 #ifdef HAS_OSS
442 good = device_has_capability(context, udi, CAPABILITY_OSS, &error);
443
444 if (dbus_error_is_set(&error)) {
445 pa_log_error("Error getting capability: %s: %s", error.name, error.message);
446 dbus_error_free(&error);
447 return;
448 }
449
450 if (good)
451 u->capability = CAPABILITY_OSS;
452
453 #endif
454 #if defined(HAVE_OSS) && defined(HAVE_ALSA)
455 }
456 #endif
457 }
458
459 if (!good)
460 return;
461
462 /* actually add the device 1/2 second later */
463 t = pa_xnew(struct timerdata, 1);
464 t->u = u;
465 t->udi = pa_xstrdup(udi);
466
467 pa_gettimeofday(&tv);
468 pa_timeval_add(&tv, 500000);
469 u->core->mainloop->time_new(u->core->mainloop, &tv, device_added_time_cb, t);
470 }
471
472 static void device_removed_cb(LibHalContext* context, const char *udi) {
473 struct device *d;
474 struct userdata *u;
475
476 pa_assert_se(u = libhal_ctx_get_user_data(context));
477
478 pa_log_debug("Device removed: %s", udi);
479
480 if ((d = pa_hashmap_remove(u->devices, udi))) {
481 pa_module_unload_by_index(u->core, d->index);
482 hal_device_free(d);
483 }
484 }
485
486 static void new_capability_cb(LibHalContext *context, const char *udi, const char* capability) {
487 struct userdata *u;
488
489 pa_assert_se(u = libhal_ctx_get_user_data(context));
490
491 if (!u->capability || strcmp(u->capability, capability) == 0)
492 /* capability we care about, pretend it's a new device */
493 device_added_cb(context, udi);
494 }
495
496 static void lost_capability_cb(LibHalContext *context, const char *udi, const char* capability) {
497 struct userdata *u;
498
499 pa_assert_se(u = libhal_ctx_get_user_data(context));
500
501 if (u->capability && strcmp(u->capability, capability) == 0)
502 /* capability we care about, pretend it was removed */
503 device_removed_cb(context, udi);
504 }
505
506 static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, void *userdata) {
507 struct userdata*u = userdata;
508 DBusError error;
509
510 pa_assert(bus);
511 pa_assert(message);
512 pa_assert(u);
513
514 dbus_error_init(&error);
515
516 pa_log_debug("dbus: interface=%s, path=%s, member=%s\n",
517 dbus_message_get_interface(message),
518 dbus_message_get_path(message),
519 dbus_message_get_member(message));
520
521 if (dbus_message_is_signal(message, "org.freedesktop.Hal.Device.AccessControl", "ACLAdded") ||
522 dbus_message_is_signal(message, "org.freedesktop.Hal.Device.AccessControl", "ACLRemoved")) {
523 uint32_t uid;
524 int suspend = strcmp(dbus_message_get_member(message), "ACLRemoved") == 0;
525
526 if (!dbus_message_get_args(message, &error, DBUS_TYPE_UINT32, &uid, DBUS_TYPE_INVALID) || dbus_error_is_set(&error)) {
527 pa_log_error("Failed to parse ACL message: %s: %s", error.name, error.message);
528 goto finish;
529 }
530
531 if (uid == getuid() || uid == geteuid()) {
532 struct device *d;
533 const char *udi;
534
535 udi = dbus_message_get_path(message);
536
537 if ((d = pa_hashmap_get(u->devices, udi))) {
538
539 if (d->sink_name) {
540 pa_sink *sink;
541
542 if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK, 0))) {
543
544 int prev_suspended = pa_sink_get_state(sink) == PA_SINK_SUSPENDED;
545
546 if (pa_sink_suspend(sink, suspend) >= 0) {
547 if (!suspend && prev_suspended)
548 pa_scache_play_item_by_name(u->core, "pulse-access", d->sink_name, PA_VOLUME_NORM, 0);
549 else if (suspend && !prev_suspended) {
550 DBusMessage *msg;
551 msg = dbus_message_new_signal(udi, "org.pulseaudio.Server", "DirtyGiveUpMessage");
552 dbus_connection_send(pa_dbus_connection_get(u->connection), msg, NULL);
553 dbus_message_unref(msg);
554 }
555 }
556 }
557 }
558
559 if (d->source_name) {
560 pa_source *source;
561
562 if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE, 0)))
563 pa_source_suspend(source, suspend);
564 }
565
566 } else if (!suspend)
567 device_added_cb(u->context, udi);
568 }
569
570 } else if (dbus_message_is_signal(message, "org.pulseaudio.Server", "DirtyGiveUpMessage")) {
571 /* We use this message to avoid a dirty race condition when we
572 get an ACLAdded message before the previously owning PA
573 sever has closed the device. We can remove this as
574 soon as HAL learns frevoke() */
575
576 const char *udi;
577 struct device *d;
578
579 pa_log_debug("Got dirty give up message, trying resume ...");
580
581 udi = dbus_message_get_path(message);
582
583 if ((d = pa_hashmap_get(u->devices, udi))) {
584
585 if (d->sink_name) {
586 pa_sink *sink;
587
588 if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK, 0))) {
589
590 int prev_suspended = pa_sink_get_state(sink) == PA_SINK_SUSPENDED;
591
592 if (pa_sink_suspend(sink, 0) >= 0)
593 if (prev_suspended && !prev_suspended)
594 pa_scache_play_item_by_name(u->core, "pulse-access", d->sink_name, PA_VOLUME_NORM, 0);
595 }
596 }
597
598 if (d->source_name) {
599 pa_source *source;
600
601 if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE, 0)))
602 pa_source_suspend(source, 0);
603 }
604
605 } else
606 /* Yes, we don't check the UDI for validity, but hopefully HAL will */
607 device_added_cb(u->context, udi);
608 }
609
610 finish:
611 dbus_error_free(&error);
612
613 return DBUS_HANDLER_RESULT_HANDLED;
614 }
615
616 static void hal_context_free(LibHalContext* hal_context) {
617 DBusError error;
618
619 dbus_error_init(&error);
620
621 libhal_ctx_shutdown(hal_context, &error);
622 libhal_ctx_free(hal_context);
623
624 dbus_error_free(&error);
625 }
626
627 static LibHalContext* hal_context_new(pa_core* c, DBusConnection *conn) {
628 DBusError error;
629 LibHalContext *hal_context = NULL;
630
631 dbus_error_init(&error);
632
633 if (!(hal_context = libhal_ctx_new())) {
634 pa_log_error("libhal_ctx_new() failed");
635 goto fail;
636 }
637
638 if (!libhal_ctx_set_dbus_connection(hal_context, conn)) {
639 pa_log_error("Error establishing DBUS connection: %s: %s", error.name, error.message);
640 goto fail;
641 }
642
643 if (!libhal_ctx_init(hal_context, &error)) {
644 pa_log_error("Couldn't connect to hald: %s: %s", error.name, error.message);
645 goto fail;
646 }
647
648 return hal_context;
649
650 fail:
651 if (hal_context)
652 hal_context_free(hal_context);
653
654 dbus_error_free(&error);
655
656 return NULL;
657 }
658
659 int pa__init(pa_core *c, pa_module*m) {
660 DBusError error;
661 pa_dbus_connection *conn;
662 struct userdata *u = NULL;
663 LibHalContext *hal_context = NULL;
664 int n = 0;
665
666 pa_assert(c);
667 pa_assert(m);
668
669 dbus_error_init(&error);
670
671 if (!(conn = pa_dbus_bus_get(c, DBUS_BUS_SYSTEM, &error)) || dbus_error_is_set(&error)) {
672 if (conn)
673 pa_dbus_connection_unref(conn);
674 pa_log_error("Unable to contact DBUS system bus: %s: %s", error.name, error.message);
675 goto fail;
676 }
677
678 if (!(hal_context = hal_context_new(c, pa_dbus_connection_get(conn)))) {
679 /* pa_hal_context_new() logs appropriate errors */
680 pa_dbus_connection_unref(conn);
681 goto fail;
682 }
683
684 u = pa_xnew(struct userdata, 1);
685 u->core = c;
686 u->context = hal_context;
687 u->connection = conn;
688 u->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
689 u->capability = NULL;
690 m->userdata = u;
691
692 #ifdef HAVE_ALSA
693 n = hal_device_add_all(u, CAPABILITY_ALSA);
694 #endif
695 #if defined(HAVE_ALSA) && defined(HAVE_OSS)
696 if (!u->capability)
697 #endif
698 #ifdef HAVE_OSS
699 n += hal_device_add_all(u, CAPABILITY_OSS);
700 #endif
701
702 libhal_ctx_set_user_data(hal_context, u);
703 libhal_ctx_set_device_added(hal_context, device_added_cb);
704 libhal_ctx_set_device_removed(hal_context, device_removed_cb);
705 libhal_ctx_set_device_new_capability(hal_context, new_capability_cb);
706 libhal_ctx_set_device_lost_capability(hal_context, lost_capability_cb);
707
708 if (!libhal_device_property_watch_all(hal_context, &error)) {
709 pa_log_error("Error monitoring device list: %s: %s", error.name, error.message);
710 goto fail;
711 }
712
713 if (!dbus_connection_add_filter(pa_dbus_connection_get(conn), filter_cb, u, NULL)) {
714 pa_log_error("Failed to add filter function");
715 goto fail;
716 }
717
718 dbus_bus_add_match(pa_dbus_connection_get(conn), "type='signal',sender='org.freedesktop.Hal', interface='org.freedesktop.Hal.Device.AccessControl'", &error);
719 if (dbus_error_is_set(&error)) {
720 pa_log_error("Unable to subscribe to HAL ACL signals: %s: %s", error.name, error.message);
721 goto fail;
722 }
723
724 dbus_bus_add_match(pa_dbus_connection_get(conn), "type='signal',interface='org.pulseaudio.Server'", &error);
725 if (dbus_error_is_set(&error)) {
726 pa_log_error("Unable to subscribe to PulseAudio signals: %s: %s", error.name, error.message);
727 goto fail;
728 }
729
730 pa_log_info("Loaded %i modules.", n);
731
732 return 0;
733
734 fail:
735 dbus_error_free(&error);
736 pa__done(c, m);
737 return -1;
738 }
739
740
741 void pa__done(PA_GCC_UNUSED pa_core *c, pa_module *m) {
742 struct userdata *u;
743
744 pa_assert(c);
745 pa_assert(m);
746
747 if (!(u = m->userdata))
748 return;
749
750 if (u->context)
751 hal_context_free(u->context);
752
753 if (u->devices)
754 pa_hashmap_free(u->devices, hal_device_free_cb, NULL);
755
756 if (u->connection)
757 pa_dbus_connection_unref(u->connection);
758
759 pa_xfree(u);
760 }