follow PropertyChanged signals from BlueZ
[profile/ivi/pulseaudio-panda.git] / src / modules / bluetooth / module-bluetooth-discover.c
1 /***
2     This file is part of PulseAudio.
3
4     Copyright 2008 Joao Paulo Rechi Vita
5
6     PulseAudio is free software; you can redistribute it and/or modify
7     it under the terms of the GNU Lesser General Public License as published
8     by the Free Software Foundation; either version 2 of the License,
9     or (at your option) any later version.
10
11     PulseAudio is distributed in the hope that it will be useful, but
12     WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14     General Public License for more details.
15
16     You should have received a copy of the GNU Lesser General Public License
17     along with PulseAudio; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19     USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include <pulse/xmalloc.h>
31 #include <pulsecore/module.h>
32 #include <pulsecore/modargs.h>
33 #include <pulsecore/macro.h>
34 #include <pulsecore/llist.h>
35 #include <pulsecore/core-util.h>
36
37 #include "dbus-util.h"
38 #include "module-bluetooth-discover-symdef.h"
39
40 PA_MODULE_AUTHOR("Joao Paulo Rechi Vita");
41 PA_MODULE_DESCRIPTION("Detect available bluetooth audio devices and load bluetooth audio drivers");
42 PA_MODULE_VERSION(PACKAGE_VERSION);
43 PA_MODULE_USAGE("");
44
45 #define HSP_HS_UUID             "00001108-0000-1000-8000-00805F9B34FB"
46 #define HFP_HS_UUID             "0000111E-0000-1000-8000-00805F9B34FB"
47 #define A2DP_SOURCE_UUID        "0000110A-0000-1000-8000-00805F9B34FB"
48 #define A2DP_SINK_UUID          "0000110B-0000-1000-8000-00805F9B34FB"
49
50 struct uuid {
51     char *uuid;
52     PA_LLIST_FIELDS(struct uuid);
53 };
54
55 struct device {
56     char *name;
57     char *object_path;
58     int paired;
59     struct adapter *adapter;
60     char *alias;
61     int connected;
62     PA_LLIST_HEAD(struct uuid, uuid_list);
63     char *address;
64     int class;
65     int trusted;
66     const char *audio_profile;
67     uint32_t module_index;
68     PA_LLIST_FIELDS(struct device);
69 };
70
71 struct adapter {
72     char *object_path;
73     char *name;
74     char *mode;
75     char *address;
76     PA_LLIST_HEAD(struct device, device_list);
77     PA_LLIST_FIELDS(struct adapter);
78 };
79
80 struct userdata {
81     pa_module *module;
82     pa_dbus_connection *conn;
83     PA_LLIST_HEAD(struct adapter, adapter_list);
84 };
85
86 static struct uuid *uuid_new(const char *uuid) {
87     struct uuid *node;
88
89     node = pa_xnew(struct uuid, 1);
90     node->uuid = pa_xstrdup(uuid);
91     PA_LLIST_INIT(struct uuid, node);
92
93     return node;
94 }
95
96 static void uuid_free(struct uuid *uuid) {
97     pa_assert(uuid);
98
99     pa_xfree(uuid->uuid);
100     pa_xfree(uuid);
101 }
102
103 static struct device *device_new(struct adapter *adapter, const char *object_path) {
104     struct device *node;
105
106     node = pa_xnew(struct device, 1);
107     node->name = NULL;
108     node->object_path = pa_xstrdup(object_path);
109     node->paired = -1;
110     node->adapter = adapter;
111     node->alias = NULL;
112     node->connected = -1;
113     PA_LLIST_HEAD_INIT(struct uuid, node->uuid_list);
114     node->address = NULL;
115     node->class = -1;
116     node->trusted = -1;
117     node->audio_profile = NULL;
118     node->module_index = PA_INVALID_INDEX;
119     PA_LLIST_INIT(struct device, node);
120
121     return node;
122 }
123
124 static void device_free(struct device *device) {
125     struct uuid *i;
126
127     pa_assert(device);
128
129     while ((i = device->uuid_list)) {
130         PA_LLIST_REMOVE(struct uuid, device->uuid_list, i);
131         uuid_free(i);
132     }
133
134     pa_xfree(device->name);
135     pa_xfree(device->object_path);
136     pa_xfree(device->alias);
137     pa_xfree(device->address);
138     pa_xfree(device);
139 }
140
141 static struct adapter *adapter_new(const char *object_path) {
142     struct adapter *node;
143
144     node = pa_xnew(struct adapter, 1);
145     node->object_path = pa_xstrdup(object_path);
146     node->mode = NULL;
147     node->address = NULL;
148     node->name = NULL;
149
150     PA_LLIST_HEAD_INIT(struct device, node->device_list);
151     PA_LLIST_INIT(struct adapter, node);
152
153     return node;
154 }
155
156 static void adapter_free(struct adapter *adapter) {
157     struct device *i;
158
159     pa_assert(adapter);
160
161     while ((i = adapter->device_list)) {
162         PA_LLIST_REMOVE(struct device, adapter->device_list, i);
163         device_free(i);
164     }
165
166     pa_xfree(adapter->object_path);
167     pa_xfree(adapter->mode);
168     pa_xfree(adapter->address);
169     pa_xfree(adapter->name);
170     pa_xfree(adapter);
171 }
172
173 static struct adapter* adapter_find(struct userdata *u, const char *path) {
174     struct adapter *i;
175
176     for (i = u->adapter_list; i; i = i->next)
177         if (pa_streq(i->object_path, path))
178             return i;
179
180     return NULL;
181 }
182
183 static struct device* device_find(struct userdata *u, const char *path) {
184     struct adapter *j;
185     struct device *i;
186
187     for (j = u->adapter_list; j; j = j->next)
188         for (i = j->device_list; i; i = i->next)
189             if (pa_streq(i->object_path, path))
190                 return i;
191
192     return NULL;
193 }
194
195 static const char *yes_no_na(int b) {
196     if (b < 0)
197         return "n/a";
198
199     return pa_yes_no(b);
200 }
201
202 static void print_devices(struct adapter *a) {
203     struct device *i;
204
205     pa_assert(a);
206
207     for (i = a->device_list; i; i = i->next) {
208         struct uuid *j;
209
210         if (pa_streq(i->object_path, "/DEVICE_HEAD"))
211             continue;
212
213         pa_log_debug("\t[ %s ]\n"
214                      "\t\tName = %s\n"
215                      "\t\tPaired = %s\n"
216                      "\t\tAdapter = %s\n"
217                      "\t\tAlias = %s\n"
218                      "\t\tConnected = %s\n"
219                      "\t\tAudio = %s\n",
220                      i->object_path,
221                      pa_strnull(i->name),
222                      yes_no_na(i->paired),
223                      i->adapter->object_path,
224                      pa_strnull(i->alias),
225                      yes_no_na(i->connected),
226                      pa_strnull(i->audio_profile));
227
228         pa_log_debug("\t\tUUIDs = ");
229         for (j = i->uuid_list; j; j = j->next) {
230
231             if (pa_streq(j->uuid, "UUID_HEAD"))
232                 continue;
233
234             pa_log_debug("\t\t         %s", j->uuid);
235         }
236
237         pa_log_debug("\t\tAddress = %s\n"
238                      "\t\tClass = 0x%x\n"
239                      "\t\tTrusted = %s",
240                      i->address,
241                      i->class,
242                      yes_no_na(i->trusted));
243     }
244 }
245
246 static void print_adapters(struct userdata *u) {
247     struct adapter *i;
248
249     pa_assert(u);
250
251     for (i = u->adapter_list; i; i = i->next) {
252
253         if (pa_streq(i->object_path, "/ADAPTER_HEAD"))
254             continue;
255
256         pa_log_debug(
257                 "[ %s ]\n"
258                 "\tName = %s\n"
259                 "\tMode = %s\n"
260                 "\tAddress = %s\n",
261                 i->object_path,
262                 pa_strnull(i->name),
263                 pa_strnull(i->mode),
264                 pa_strnull(i->address));
265
266         print_devices(i);
267     }
268 }
269
270 static const char *strip_object_path(const char *op) {
271     const char *slash;
272
273     if ((slash = strrchr(op, '/')))
274         return slash+1;
275
276     return op;
277 }
278
279 static void load_module_for_device(struct userdata *u, struct device *d) {
280     char *args;
281     pa_module *m;
282
283     pa_assert(u);
284     pa_assert(d);
285
286     /* Check whether we already loaded a module for this device */
287     if (d->module_index != PA_INVALID_INDEX &&
288         pa_idxset_get_by_index(u->module->core->modules, d->module_index))
289         return;
290
291     /* Check whether this is an audio device */
292     if (!d->audio_profile) {
293         pa_log_debug("Ignoring %s since it is not an audio device.", d->object_path);
294         return;
295     }
296
297     args = pa_sprintf_malloc("sink_name=%s address=%s profile=%s", strip_object_path(d->object_path), d->address, d->audio_profile);
298     m = pa_module_load(u->module->core, "module-bluetooth-device", args);
299     pa_xfree(args);
300
301     if (!m) {
302         pa_log_debug("Failed to load module for device %s", d->object_path);
303         return;
304     }
305
306     d->module_index = m->index;
307 }
308
309 static void load_modules(struct userdata *u) {
310     struct device *d;
311     struct adapter *a;
312
313     pa_assert(u);
314
315     for (a = u->adapter_list; a; a = a->next)
316         for (d = a->device_list; d; d = d->next)
317             load_module_for_device(u, d);
318 }
319
320 static int parse_adapter_property(struct userdata *u, struct adapter *a, DBusMessageIter *i) {
321     const char *key;
322     DBusMessageIter variant_i;
323
324     pa_assert(u);
325     pa_assert(a);
326     pa_assert(i);
327
328     if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) {
329         pa_log("Property name not a string.");
330         return -1;
331     }
332
333     dbus_message_iter_get_basic(i, &key);
334
335     if (!dbus_message_iter_next(i)) {
336         pa_log("Property value missing");
337         return -1;
338     }
339
340     if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_VARIANT) {
341         pa_log("Property value not a variant.");
342         return -1;
343     }
344
345     dbus_message_iter_recurse(i, &variant_i);
346
347     if (dbus_message_iter_get_arg_type(&variant_i) == DBUS_TYPE_STRING) {
348         const char *value;
349         dbus_message_iter_get_basic(&variant_i, &value);
350
351         if (pa_streq(key, "Mode")) {
352             pa_xfree(a->mode);
353             a->mode = pa_xstrdup(value);
354         } else if (pa_streq(key, "Address")) {
355             pa_xstrdup(a->address);
356             a->address = pa_xstrdup(value);
357         } else if (pa_streq(key, "Name")) {
358             pa_xfree(a->name);
359             a->name = pa_xstrdup(value);
360         }
361     }
362
363     return 0;
364 }
365
366 static int get_adapter_properties(struct userdata *u, struct adapter *a) {
367     DBusError e;
368     DBusMessage *m = NULL, *r = NULL;
369     DBusMessageIter arg_i, element_i;
370     int ret = -1;
371
372     pa_assert(u);
373     pa_assert(a);
374     dbus_error_init(&e);
375
376     pa_assert_se(m = dbus_message_new_method_call("org.bluez", a->object_path, "org.bluez.Adapter", "GetProperties"));
377
378     r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->conn), m, -1, &e);
379
380     if (!r) {
381         pa_log("org.bluez.Adapter.GetProperties failed: %s", e.message);
382         goto finish;
383     }
384
385     if (!dbus_message_iter_init(r, &arg_i)) {
386         pa_log("org.bluez.Adapter.GetProperties reply has no arguments");
387         goto finish;
388     }
389
390     if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) {
391         pa_log("org.bluez.Adapter.GetProperties argument is not an array");
392         goto finish;
393     }
394
395     dbus_message_iter_recurse(&arg_i, &element_i);
396     while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
397
398         if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
399             DBusMessageIter dict_i;
400
401             dbus_message_iter_recurse(&element_i, &dict_i);
402
403             if (parse_adapter_property(u, a, &dict_i) < 0)
404                 goto finish;
405         }
406
407         if (!dbus_message_iter_next(&element_i))
408             break;
409     }
410
411     ret = 0;
412
413 finish:
414     if (m)
415         dbus_message_unref(m);
416     if (r)
417         dbus_message_unref(r);
418
419     dbus_error_free(&e);
420
421     return ret;
422 }
423
424 static int detect_adapters(struct userdata *u) {
425     DBusError e;
426     DBusMessage *m = NULL, *r = NULL;
427     DBusMessageIter arg_i, element_i;
428     struct adapter *adapter_list_i;
429     int ret = -1;
430
431     pa_assert(u);
432     dbus_error_init(&e);
433
434     /* get adapters */
435     pa_assert_se(m = dbus_message_new_method_call("org.bluez", "/", "org.bluez.Manager", "ListAdapters"));
436     r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->conn), m, -1, &e);
437
438     if (!r) {
439         pa_log("org.bluez.Manager.ListAdapters failed: %s", e.message);
440         goto finish;
441     }
442
443     if (!dbus_message_iter_init(r, &arg_i)) {
444         pa_log("org.bluez.Manager.ListAdapters reply has no arguments");
445         goto finish;
446     }
447
448     if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) {
449         pa_log("org.bluez.Manager.ListAdapters argument is not an array");
450         goto finish;
451     }
452
453     dbus_message_iter_recurse(&arg_i, &element_i);
454     while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
455         if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_OBJECT_PATH) {
456
457             struct adapter *node;
458             const char *value;
459
460             dbus_message_iter_get_basic(&element_i, &value);
461             node = adapter_new(value);
462             PA_LLIST_PREPEND(struct adapter, u->adapter_list, node);
463         }
464
465         if (!dbus_message_iter_next(&element_i))
466             break;
467     }
468
469     ret = 0;
470
471     /* get adapter properties */
472     for (adapter_list_i = u->adapter_list; adapter_list_i; adapter_list_i = adapter_list_i->next)
473         get_adapter_properties(u, adapter_list_i);
474
475 finish:
476     if (m)
477         dbus_message_unref(m);
478     if (r)
479         dbus_message_unref(r);
480
481     dbus_error_free(&e);
482     return ret;
483 }
484
485 static int parse_device_property(struct userdata *u, struct device *d, DBusMessageIter *i) {
486     const char *key;
487     DBusMessageIter variant_i;
488
489     pa_assert(u);
490     pa_assert(d);
491     pa_assert(i);
492
493     if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) {
494         pa_log("Property name not a string.");
495         return -1;
496     }
497
498     dbus_message_iter_get_basic(i, &key);
499
500     if (!dbus_message_iter_next(i))  {
501         pa_log("Property value missing");
502         return -1;
503     }
504
505     if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_VARIANT) {
506         pa_log("Property value not a variant.");
507         return -1;
508     }
509
510     dbus_message_iter_recurse(i, &variant_i);
511
512     pa_log_debug("Parsing device property %s", key);
513
514     switch (dbus_message_iter_get_arg_type(&variant_i)) {
515
516         case DBUS_TYPE_STRING: {
517
518             const char *value;
519             dbus_message_iter_get_basic(&variant_i, &value);
520
521             if (pa_streq(key, "Name")) {
522                 pa_xfree(d->name);
523                 d->name = pa_xstrdup(value);
524             } else if (pa_streq(key, "Alias")) {
525                 pa_xfree(d->alias);
526                 d->alias = pa_xstrdup(value);
527             } else if (pa_streq(key, "Address")) {
528                 pa_xfree(d->address);
529                 d->address = pa_xstrdup(value);
530             }
531
532             break;
533         }
534
535         case DBUS_TYPE_BOOLEAN: {
536
537             dbus_bool_t value;
538             dbus_message_iter_get_basic(&variant_i, &value);
539
540             if (pa_streq(key, "Paired"))
541                 d->paired = !!value;
542             else if (pa_streq(key, "Connected"))
543                 d->connected = !!value;
544             else if (pa_streq(key, "Trusted"))
545                 d->trusted = !!value;
546
547             break;
548         }
549
550         case DBUS_TYPE_UINT32: {
551
552             uint32_t value;
553             dbus_message_iter_get_basic(&variant_i, &value);
554
555             if (pa_streq(key, "Class"))
556                 d->class = (int) value;
557
558             break;
559         }
560
561         case DBUS_TYPE_ARRAY: {
562
563             DBusMessageIter ai;
564             dbus_message_iter_recurse(&variant_i, &ai);
565
566             if (dbus_message_iter_get_arg_type(&ai) == DBUS_TYPE_STRING &&
567                 pa_streq(key, "UUIDs")) {
568
569                 d->audio_profile = NULL;
570
571                 while (dbus_message_iter_get_arg_type(&ai) != DBUS_TYPE_INVALID) {
572                     struct uuid *node;
573                     const char *value;
574
575                     dbus_message_iter_get_basic(&ai, &value);
576                     node = uuid_new(value);
577                     PA_LLIST_PREPEND(struct uuid, d->uuid_list, node);
578
579                     if ((strcasecmp(value, A2DP_SOURCE_UUID) == 0) ||
580                         (strcasecmp(value, A2DP_SINK_UUID) == 0))
581                         d->audio_profile = "a2dp";
582                     else if (((strcasecmp(value, HSP_HS_UUID) == 0) ||
583                               (strcasecmp(value, HFP_HS_UUID) == 0)) &&
584                              !d->audio_profile)
585                         d->audio_profile = "hsp";
586
587                     if (!dbus_message_iter_next(&ai))
588                         break;
589                 }
590             }
591
592             break;
593         }
594     }
595
596     return 0;
597 }
598
599 static int get_device_properties(struct userdata *u, struct device *d) {
600     DBusError e;
601     DBusMessage *m = NULL, *r = NULL;
602     DBusMessageIter arg_i, element_i;
603     int ret = -1;
604
605     pa_assert(u);
606     pa_assert(d);
607
608     dbus_error_init(&e);
609
610     pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->object_path, "org.bluez.Device", "GetProperties"));
611
612     r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->conn), m, -1, &e);
613
614     if (!r) {
615         pa_log("org.bluez.Device.GetProperties failed: %s", e.message);
616         goto finish;
617     }
618
619     if (!dbus_message_iter_init(r, &arg_i)) {
620         pa_log("org.bluez.Device.GetProperties reply has no arguments");
621         goto finish;
622     }
623
624     if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) {
625         pa_log("org.bluez.Device.GetProperties argument is not an array");
626         goto finish;
627     }
628
629     dbus_message_iter_recurse(&arg_i, &element_i);
630     while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
631
632         if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
633             DBusMessageIter dict_i;
634
635             dbus_message_iter_recurse(&element_i, &dict_i);
636
637             if (parse_device_property(u, d, &dict_i) < 0)
638                 goto finish;
639         }
640
641         if (!dbus_message_iter_next(&element_i))
642             break;
643     }
644
645     ret = 0;
646
647 finish:
648     if (m)
649         dbus_message_unref(m);
650     if (r)
651         dbus_message_unref(r);
652
653     dbus_error_free(&e);
654
655     return ret;
656 }
657
658 static int detect_devices(struct userdata *u) {
659     DBusError e;
660     DBusMessage *m = NULL, *r = NULL;
661     DBusMessageIter arg_i, element_i;
662     struct adapter *adapter_list_i;
663     struct device *device_list_i;
664     const char *value;
665     int ret = -1;
666
667     pa_assert(u);
668     dbus_error_init(&e);
669
670     /* get devices of each adapter */
671     for (adapter_list_i = u->adapter_list; adapter_list_i; adapter_list_i = adapter_list_i->next) {
672
673         pa_assert_se(m = dbus_message_new_method_call("org.bluez", adapter_list_i->object_path, "org.bluez.Adapter", "ListDevices"));
674
675         r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->conn), m, -1, &e);
676
677         if (!r) {
678             pa_log("org.bluez.Adapter.ListDevices failed: %s", e.message);
679             goto finish;
680         }
681
682         if (!dbus_message_iter_init(r, &arg_i)) {
683             pa_log("org.bluez.Adapter.ListDevices reply has no arguments");
684             goto finish;
685         }
686
687         if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) {
688             pa_log("org.bluez.Adapter.ListDevices argument is not an array");
689             goto finish;
690         }
691
692         dbus_message_iter_recurse(&arg_i, &element_i);
693         while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
694             if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_OBJECT_PATH) {
695                 struct device *node;
696                 dbus_message_iter_get_basic(&element_i, &value);
697                 node = device_new(adapter_list_i, value);
698                 PA_LLIST_PREPEND(struct device, adapter_list_i->device_list, node);
699             }
700
701             if (!dbus_message_iter_next(&element_i))
702                 break;
703         }
704     }
705
706     /* get device properties */
707     for (adapter_list_i = u->adapter_list; adapter_list_i; adapter_list_i = adapter_list_i->next)
708         for (device_list_i = adapter_list_i->device_list; device_list_i; device_list_i = device_list_i->next)
709             get_device_properties(u, device_list_i);
710
711     ret = 0;
712
713 finish:
714     if (m)
715         dbus_message_unref(m);
716     if (r)
717         dbus_message_unref(r);
718
719     dbus_error_free(&e);
720
721     return ret;
722 }
723
724 static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *msg, void *userdata) {
725     DBusMessageIter arg_i;
726     DBusError err;
727     const char *value;
728     struct userdata *u;
729
730     pa_assert(bus);
731     pa_assert(msg);
732     pa_assert(userdata);
733     u = userdata;
734
735     dbus_error_init(&err);
736
737     pa_log_debug("dbus: interface=%s, path=%s, member=%s\n",
738             dbus_message_get_interface(msg),
739             dbus_message_get_path(msg),
740             dbus_message_get_member(msg));
741
742     if (dbus_message_is_signal(msg, "org.bluez.Manager", "AdapterAdded")) {
743
744         if (!dbus_message_iter_init(msg, &arg_i))
745             pa_log("dbus: message has no parameters");
746         else if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_OBJECT_PATH)
747             pa_log("dbus: argument is not object path");
748         else {
749             struct adapter *node;
750
751             dbus_message_iter_get_basic(&arg_i, &value);
752             pa_log_debug("hcid: adapter %s added", value);
753
754             node = adapter_new(value);
755             PA_LLIST_PREPEND(struct adapter, u->adapter_list, node);
756
757             get_adapter_properties(u, node);
758         }
759
760     } else if (dbus_message_is_signal(msg, "org.bluez.Manager", "AdapterRemoved")) {
761         if (!dbus_message_iter_init(msg, &arg_i))
762             pa_log("dbus: message has no parameters");
763         else if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_OBJECT_PATH)
764             pa_log("dbus: argument is not object path");
765         else {
766             struct adapter *a;
767
768             dbus_message_iter_get_basic(&arg_i, &value);
769             pa_log_debug("hcid: adapter %s removed", value);
770
771             if ((a = adapter_find(u, value))) {
772                 PA_LLIST_REMOVE(struct adapter, u->adapter_list, a);
773                 adapter_free(a);
774             }
775         }
776
777     } else if (dbus_message_is_signal(msg, "org.bluez.Adapter", "PropertyChanged")) {
778
779         if (!dbus_message_iter_init(msg, &arg_i))
780             pa_log("dbus: message has no parameters");
781         else {
782             struct adapter *a;
783
784             if ((a = adapter_find(u, dbus_message_get_path(msg))))
785                 parse_adapter_property(u, a, &arg_i);
786         }
787
788     } else if (dbus_message_is_signal(msg, "org.bluez.Adapter", "DeviceCreated")) {
789
790         if (!dbus_message_iter_init(msg, &arg_i))
791             pa_log("dbus: message has no parameters");
792         else if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_OBJECT_PATH)
793             pa_log("dbus: argument is not object path");
794         else {
795             struct adapter *adapter;
796
797             if (!(adapter = adapter_find(u, dbus_message_get_path(msg))))
798                 pa_log("dbus: failed to find adapter for object path");
799             else {
800                 struct device *node;
801
802                 dbus_message_iter_get_basic(&arg_i, &value);
803                 pa_log_debug("hcid: device %s created", value);
804
805                 node = device_new(adapter, value);
806                 PA_LLIST_PREPEND(struct device, adapter->device_list, node);
807
808                 get_device_properties(u, node);
809                 load_module_for_device(u, node);
810             }
811         }
812
813     } else if (dbus_message_is_signal(msg, "org.bluez.Adapter", "DeviceRemoved")) {
814
815         if (!dbus_message_iter_init(msg, &arg_i))
816             pa_log("dbus: message has no parameters");
817         else if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_OBJECT_PATH)
818             pa_log("dbus: argument is not object path");
819         else {
820             struct device *d;
821
822             dbus_message_iter_get_basic(&arg_i, &value);
823             pa_log_debug("hcid: device %s removed", value);
824
825             if ((d = device_find(u, value))) {
826                 PA_LLIST_REMOVE(struct device, d->adapter->device_list, d);
827                 device_free(d);
828             }
829         }
830
831     } else if (dbus_message_is_signal(msg, "org.bluez.Device", "PropertyChanged")) {
832
833         if (!dbus_message_iter_init(msg, &arg_i))
834             pa_log("dbus: message has no parameters");
835         else {
836             struct device *d;
837
838             if ((d = device_find(u, dbus_message_get_path(msg)))) {
839                 parse_device_property(u, d, &arg_i);
840
841                 /* Hmm, something changed, let's try to reconnect if we
842                  * aren't connected yet */
843                 load_module_for_device(u, d);
844             }
845         }
846     }
847
848     dbus_error_free(&err);
849     return DBUS_HANDLER_RESULT_HANDLED;
850 }
851
852 void pa__done(pa_module* m) {
853     struct userdata *u;
854     struct adapter *i;
855
856     pa_assert(m);
857
858     if (!(u = m->userdata))
859         return;
860
861     while ((i = u->adapter_list)) {
862         PA_LLIST_REMOVE(struct adapter, u->adapter_list, i);
863         adapter_free(i);
864     }
865
866     if (u->conn)
867         pa_dbus_connection_unref(u->conn);
868
869     pa_xfree(u);
870 }
871
872 int pa__init(pa_module* m) {
873     DBusError err;
874     struct userdata *u;
875
876     pa_assert(m);
877     dbus_error_init(&err);
878
879     m->userdata = u = pa_xnew(struct userdata, 1);
880     u->module = m;
881     PA_LLIST_HEAD_INIT(struct adapter, u->adapter_list);
882
883     /* connect to the bus */
884     u->conn = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &err);
885     if (dbus_error_is_set(&err) || (u->conn == NULL) ) {
886         pa_log("Failed to get D-Bus connection: %s", err.message);
887         goto fail;
888     }
889
890     /* static detection of bluetooth audio devices */
891     detect_adapters(u);
892     detect_devices(u);
893
894     print_adapters(u);
895     load_modules(u);
896
897     /* dynamic detection of bluetooth audio devices */
898     if (!dbus_connection_add_filter(pa_dbus_connection_get(u->conn), filter_cb, u, NULL)) {
899         pa_log_error("Failed to add filter function");
900         goto fail;
901     }
902
903     dbus_bus_add_match(pa_dbus_connection_get(u->conn), "type='signal',sender='org.bluez',interface='org.bluez.Manager'", &err);
904     if (dbus_error_is_set(&err)) {
905         pa_log_error("Unable to subscribe to org.bluez.Manager signals: %s: %s", err.name, err.message);
906         goto fail;
907     }
908
909     dbus_bus_add_match(pa_dbus_connection_get(u->conn), "type='signal',sender='org.bluez',interface='org.bluez.Adapter'", &err);
910     if (dbus_error_is_set(&err)) {
911         pa_log_error("Unable to subscribe to org.bluez.Adapter signals: %s: %s", err.name, err.message);
912         goto fail;
913     }
914
915     dbus_bus_add_match(pa_dbus_connection_get(u->conn), "type='signal',sender='org.bluez',interface='org.bluez.Device'", &err);
916     if (dbus_error_is_set(&err)) {
917         pa_log_error("Unable to subscribe to org.bluez.Device signals: %s: %s", err.name, err.message);
918         goto fail;
919     }
920
921     return 0;
922
923 fail:
924     dbus_error_free(&err);
925     pa__done(m);
926
927     return -1;
928 }