2 This file is part of PulseAudio.
4 Copyright 2008 Joao Paulo Rechi Vita
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.
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.
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
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>
37 #include "dbus-util.h"
38 #include "module-bluetooth-discover-symdef.h"
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);
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"
52 PA_LLIST_FIELDS(struct uuid);
59 struct adapter *adapter;
62 PA_LLIST_HEAD(struct uuid, uuid_list);
66 const char *audio_profile;
67 uint32_t module_index;
68 PA_LLIST_FIELDS(struct device);
76 PA_LLIST_HEAD(struct device, device_list);
77 PA_LLIST_FIELDS(struct adapter);
82 pa_dbus_connection *conn;
83 PA_LLIST_HEAD(struct adapter, adapter_list);
86 static struct uuid *uuid_new(const char *uuid) {
89 node = pa_xnew(struct uuid, 1);
90 node->uuid = pa_xstrdup(uuid);
91 PA_LLIST_INIT(struct uuid, node);
96 static void uuid_free(struct uuid *uuid) {
103 static struct device *device_new(struct adapter *adapter, const char *object_path) {
106 node = pa_xnew(struct device, 1);
108 node->object_path = pa_xstrdup(object_path);
110 node->adapter = adapter;
112 node->connected = -1;
113 PA_LLIST_HEAD_INIT(struct uuid, node->uuid_list);
114 node->address = NULL;
117 node->audio_profile = NULL;
118 node->module_index = PA_INVALID_INDEX;
119 PA_LLIST_INIT(struct device, node);
124 static void device_free(struct device *device) {
129 while ((i = device->uuid_list)) {
130 PA_LLIST_REMOVE(struct uuid, device->uuid_list, i);
134 pa_xfree(device->name);
135 pa_xfree(device->object_path);
136 pa_xfree(device->alias);
137 pa_xfree(device->address);
141 static struct adapter *adapter_new(const char *object_path) {
142 struct adapter *node;
144 node = pa_xnew(struct adapter, 1);
145 node->object_path = pa_xstrdup(object_path);
147 node->address = NULL;
150 PA_LLIST_HEAD_INIT(struct device, node->device_list);
151 PA_LLIST_INIT(struct adapter, node);
156 static void adapter_free(struct adapter *adapter) {
161 while ((i = adapter->device_list)) {
162 PA_LLIST_REMOVE(struct device, adapter->device_list, i);
166 pa_xfree(adapter->object_path);
167 pa_xfree(adapter->mode);
168 pa_xfree(adapter->address);
169 pa_xfree(adapter->name);
173 static struct adapter* adapter_find(struct userdata *u, const char *path) {
176 for (i = u->adapter_list; i; i = i->next)
177 if (pa_streq(i->object_path, path))
183 static struct device* device_find(struct userdata *u, const char *path) {
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))
195 static const char *yes_no_na(int b) {
202 static void print_devices(struct adapter *a) {
207 for (i = a->device_list; i; i = i->next) {
210 if (pa_streq(i->object_path, "/DEVICE_HEAD"))
213 pa_log_debug("\t[ %s ]\n"
218 "\t\tConnected = %s\n"
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));
228 pa_log_debug("\t\tUUIDs = ");
229 for (j = i->uuid_list; j; j = j->next) {
231 if (pa_streq(j->uuid, "UUID_HEAD"))
234 pa_log_debug("\t\t %s", j->uuid);
237 pa_log_debug("\t\tAddress = %s\n"
242 yes_no_na(i->trusted));
246 static void print_adapters(struct userdata *u) {
251 for (i = u->adapter_list; i; i = i->next) {
253 if (pa_streq(i->object_path, "/ADAPTER_HEAD"))
264 pa_strnull(i->address));
270 static const char *strip_object_path(const char *op) {
273 if ((slash = strrchr(op, '/')))
279 static void load_module_for_device(struct userdata *u, struct device *d) {
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))
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);
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);
302 pa_log_debug("Failed to load module for device %s", d->object_path);
306 d->module_index = m->index;
309 static void load_modules(struct userdata *u) {
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);
320 static int parse_adapter_property(struct userdata *u, struct adapter *a, DBusMessageIter *i) {
322 DBusMessageIter variant_i;
328 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) {
329 pa_log("Property name not a string.");
333 dbus_message_iter_get_basic(i, &key);
335 if (!dbus_message_iter_next(i)) {
336 pa_log("Property value missing");
340 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_VARIANT) {
341 pa_log("Property value not a variant.");
345 dbus_message_iter_recurse(i, &variant_i);
347 if (dbus_message_iter_get_arg_type(&variant_i) == DBUS_TYPE_STRING) {
349 dbus_message_iter_get_basic(&variant_i, &value);
351 if (pa_streq(key, "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")) {
359 a->name = pa_xstrdup(value);
366 static int get_adapter_properties(struct userdata *u, struct adapter *a) {
368 DBusMessage *m = NULL, *r = NULL;
369 DBusMessageIter arg_i, element_i;
376 pa_assert_se(m = dbus_message_new_method_call("org.bluez", a->object_path, "org.bluez.Adapter", "GetProperties"));
378 r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->conn), m, -1, &e);
381 pa_log("org.bluez.Adapter.GetProperties failed: %s", e.message);
385 if (!dbus_message_iter_init(r, &arg_i)) {
386 pa_log("org.bluez.Adapter.GetProperties reply has no arguments");
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");
395 dbus_message_iter_recurse(&arg_i, &element_i);
396 while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
398 if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
399 DBusMessageIter dict_i;
401 dbus_message_iter_recurse(&element_i, &dict_i);
403 if (parse_adapter_property(u, a, &dict_i) < 0)
407 if (!dbus_message_iter_next(&element_i))
415 dbus_message_unref(m);
417 dbus_message_unref(r);
424 static int detect_adapters(struct userdata *u) {
426 DBusMessage *m = NULL, *r = NULL;
427 DBusMessageIter arg_i, element_i;
428 struct adapter *adapter_list_i;
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);
439 pa_log("org.bluez.Manager.ListAdapters failed: %s", e.message);
443 if (!dbus_message_iter_init(r, &arg_i)) {
444 pa_log("org.bluez.Manager.ListAdapters reply has no arguments");
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");
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) {
457 struct adapter *node;
460 dbus_message_iter_get_basic(&element_i, &value);
461 node = adapter_new(value);
462 PA_LLIST_PREPEND(struct adapter, u->adapter_list, node);
465 if (!dbus_message_iter_next(&element_i))
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);
477 dbus_message_unref(m);
479 dbus_message_unref(r);
485 static int parse_device_property(struct userdata *u, struct device *d, DBusMessageIter *i) {
487 DBusMessageIter variant_i;
493 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) {
494 pa_log("Property name not a string.");
498 dbus_message_iter_get_basic(i, &key);
500 if (!dbus_message_iter_next(i)) {
501 pa_log("Property value missing");
505 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_VARIANT) {
506 pa_log("Property value not a variant.");
510 dbus_message_iter_recurse(i, &variant_i);
512 pa_log_debug("Parsing device property %s", key);
514 switch (dbus_message_iter_get_arg_type(&variant_i)) {
516 case DBUS_TYPE_STRING: {
519 dbus_message_iter_get_basic(&variant_i, &value);
521 if (pa_streq(key, "Name")) {
523 d->name = pa_xstrdup(value);
524 } else if (pa_streq(key, "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);
535 case DBUS_TYPE_BOOLEAN: {
538 dbus_message_iter_get_basic(&variant_i, &value);
540 if (pa_streq(key, "Paired"))
542 else if (pa_streq(key, "Connected"))
543 d->connected = !!value;
544 else if (pa_streq(key, "Trusted"))
545 d->trusted = !!value;
550 case DBUS_TYPE_UINT32: {
553 dbus_message_iter_get_basic(&variant_i, &value);
555 if (pa_streq(key, "Class"))
556 d->class = (int) value;
561 case DBUS_TYPE_ARRAY: {
564 dbus_message_iter_recurse(&variant_i, &ai);
566 if (dbus_message_iter_get_arg_type(&ai) == DBUS_TYPE_STRING &&
567 pa_streq(key, "UUIDs")) {
569 d->audio_profile = NULL;
571 while (dbus_message_iter_get_arg_type(&ai) != DBUS_TYPE_INVALID) {
575 dbus_message_iter_get_basic(&ai, &value);
576 node = uuid_new(value);
577 PA_LLIST_PREPEND(struct uuid, d->uuid_list, node);
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)) &&
585 d->audio_profile = "hsp";
587 if (!dbus_message_iter_next(&ai))
599 static int get_device_properties(struct userdata *u, struct device *d) {
601 DBusMessage *m = NULL, *r = NULL;
602 DBusMessageIter arg_i, element_i;
610 pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->object_path, "org.bluez.Device", "GetProperties"));
612 r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->conn), m, -1, &e);
615 pa_log("org.bluez.Device.GetProperties failed: %s", e.message);
619 if (!dbus_message_iter_init(r, &arg_i)) {
620 pa_log("org.bluez.Device.GetProperties reply has no arguments");
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");
629 dbus_message_iter_recurse(&arg_i, &element_i);
630 while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
632 if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
633 DBusMessageIter dict_i;
635 dbus_message_iter_recurse(&element_i, &dict_i);
637 if (parse_device_property(u, d, &dict_i) < 0)
641 if (!dbus_message_iter_next(&element_i))
649 dbus_message_unref(m);
651 dbus_message_unref(r);
658 static int detect_devices(struct userdata *u) {
660 DBusMessage *m = NULL, *r = NULL;
661 DBusMessageIter arg_i, element_i;
662 struct adapter *adapter_list_i;
663 struct device *device_list_i;
670 /* get devices of each adapter */
671 for (adapter_list_i = u->adapter_list; adapter_list_i; adapter_list_i = adapter_list_i->next) {
673 pa_assert_se(m = dbus_message_new_method_call("org.bluez", adapter_list_i->object_path, "org.bluez.Adapter", "ListDevices"));
675 r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->conn), m, -1, &e);
678 pa_log("org.bluez.Adapter.ListDevices failed: %s", e.message);
682 if (!dbus_message_iter_init(r, &arg_i)) {
683 pa_log("org.bluez.Adapter.ListDevices reply has no arguments");
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");
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) {
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);
701 if (!dbus_message_iter_next(&element_i))
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);
715 dbus_message_unref(m);
717 dbus_message_unref(r);
724 static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *msg, void *userdata) {
725 DBusMessageIter arg_i;
735 dbus_error_init(&err);
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));
742 if (dbus_message_is_signal(msg, "org.bluez.Manager", "AdapterAdded")) {
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");
749 struct adapter *node;
751 dbus_message_iter_get_basic(&arg_i, &value);
752 pa_log_debug("hcid: adapter %s added", value);
754 node = adapter_new(value);
755 PA_LLIST_PREPEND(struct adapter, u->adapter_list, node);
757 get_adapter_properties(u, node);
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");
768 dbus_message_iter_get_basic(&arg_i, &value);
769 pa_log_debug("hcid: adapter %s removed", value);
771 if ((a = adapter_find(u, value))) {
772 PA_LLIST_REMOVE(struct adapter, u->adapter_list, a);
777 } else if (dbus_message_is_signal(msg, "org.bluez.Adapter", "PropertyChanged")) {
779 if (!dbus_message_iter_init(msg, &arg_i))
780 pa_log("dbus: message has no parameters");
784 if ((a = adapter_find(u, dbus_message_get_path(msg))))
785 parse_adapter_property(u, a, &arg_i);
788 } else if (dbus_message_is_signal(msg, "org.bluez.Adapter", "DeviceCreated")) {
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");
795 struct adapter *adapter;
797 if (!(adapter = adapter_find(u, dbus_message_get_path(msg))))
798 pa_log("dbus: failed to find adapter for object path");
802 dbus_message_iter_get_basic(&arg_i, &value);
803 pa_log_debug("hcid: device %s created", value);
805 node = device_new(adapter, value);
806 PA_LLIST_PREPEND(struct device, adapter->device_list, node);
808 get_device_properties(u, node);
809 load_module_for_device(u, node);
813 } else if (dbus_message_is_signal(msg, "org.bluez.Adapter", "DeviceRemoved")) {
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");
822 dbus_message_iter_get_basic(&arg_i, &value);
823 pa_log_debug("hcid: device %s removed", value);
825 if ((d = device_find(u, value))) {
826 PA_LLIST_REMOVE(struct device, d->adapter->device_list, d);
831 } else if (dbus_message_is_signal(msg, "org.bluez.Device", "PropertyChanged")) {
833 if (!dbus_message_iter_init(msg, &arg_i))
834 pa_log("dbus: message has no parameters");
838 if ((d = device_find(u, dbus_message_get_path(msg)))) {
839 parse_device_property(u, d, &arg_i);
841 /* Hmm, something changed, let's try to reconnect if we
842 * aren't connected yet */
843 load_module_for_device(u, d);
848 dbus_error_free(&err);
849 return DBUS_HANDLER_RESULT_HANDLED;
852 void pa__done(pa_module* m) {
858 if (!(u = m->userdata))
861 while ((i = u->adapter_list)) {
862 PA_LLIST_REMOVE(struct adapter, u->adapter_list, i);
867 pa_dbus_connection_unref(u->conn);
872 int pa__init(pa_module* m) {
877 dbus_error_init(&err);
879 m->userdata = u = pa_xnew(struct userdata, 1);
881 PA_LLIST_HEAD_INIT(struct adapter, u->adapter_list);
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);
890 /* static detection of bluetooth audio devices */
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");
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);
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);
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);
924 dbus_error_free(&err);