From 4a75002ebe385302c513b79fb98f59832d73c8ea Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 29 Jan 2009 20:30:42 +0100 Subject: add missing files --- src/modules/bluetooth/bluetooth-util.c | 805 +++++++++++++++++++++++++++++++++ src/modules/bluetooth/bluetooth-util.h | 77 ++++ 2 files changed, 882 insertions(+) create mode 100644 src/modules/bluetooth/bluetooth-util.c create mode 100644 src/modules/bluetooth/bluetooth-util.h (limited to 'src/modules') diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c new file mode 100644 index 00000000..019b97e8 --- /dev/null +++ b/src/modules/bluetooth/bluetooth-util.c @@ -0,0 +1,805 @@ +/*** + This file is part of PulseAudio. + + Copyright 2008 Joao Paulo Rechi Vita + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "bluetooth-util.h" + +enum mode { + MODE_FIND, + MODE_GET, + MODE_DISCOVER +}; + +struct pa_bluetooth_discovery { + DBusConnection *connection; + PA_LLIST_HEAD(pa_dbus_pending, pending); + + enum mode mode; + + /* If mode == MODE_FIND look for a specific device by its address. + If mode == MODE_GET look for a specific device by its path. */ + const char *looking_for; + pa_bluetooth_device *found_device; + + /* If looking_for is NULL we do long-time discovery */ + pa_hashmap *devices; + pa_bluetooth_device_callback_t callback; + struct userdata *userdata; +}; + +static pa_bluetooth_uuid *uuid_new(const char *uuid) { + pa_bluetooth_uuid *u; + + u = pa_xnew(pa_bluetooth_uuid, 1); + u->uuid = pa_xstrdup(uuid); + PA_LLIST_INIT(pa_bluetooth_uuid, u); + + return u; +} + +static void uuid_free(pa_bluetooth_uuid *u) { + pa_assert(u); + + pa_xfree(u->uuid); + pa_xfree(u); +} + +static pa_bluetooth_device* device_new(const char *path) { + pa_bluetooth_device *d; + + d = pa_xnew(pa_bluetooth_device, 1); + + d->device_info_valid = d->audio_sink_info_valid = d->headset_info_valid = 0; + + d->data = NULL; + + d->name = NULL; + d->path = pa_xstrdup(path); + d->paired = -1; + d->alias = NULL; + d->device_connected = -1; + PA_LLIST_HEAD_INIT(pa_bluetooth_uuid, d->uuids); + d->address = NULL; + d->class = -1; + d->trusted = -1; + + d->audio_sink_connected = -1; + + d->headset_connected = -1; + + return d; +} + +void pa_bluetooth_device_free(pa_bluetooth_device *d) { + pa_bluetooth_uuid *u; + + pa_assert(d); + + while ((u = d->uuids)) { + PA_LLIST_REMOVE(pa_bluetooth_uuid, d->uuids, u); + uuid_free(u); + } + + pa_xfree(d->name); + pa_xfree(d->path); + pa_xfree(d->alias); + pa_xfree(d->address); + pa_xfree(d); +} + +static pa_bool_t device_is_loaded(pa_bluetooth_device *d) { + pa_assert(d); + + return d->device_info_valid && d->audio_sink_info_valid && d->headset_info_valid; +} + +static pa_bool_t device_is_audio(pa_bluetooth_device *d) { + pa_assert(d); + + pa_assert(d->device_info_valid); + pa_assert(d->audio_sink_info_valid); + pa_assert(d->headset_info_valid); + + return d->device_info_valid > 0 && + (d->audio_sink_info_valid > 0 || d->headset_info_valid > 0); +} + +static int parse_device_property(pa_bluetooth_discovery *y, pa_bluetooth_device *d, DBusMessageIter *i) { + const char *key; + DBusMessageIter variant_i; + + pa_assert(y); + pa_assert(d); + pa_assert(i); + + if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) { + pa_log("Property name not a string."); + return -1; + } + + dbus_message_iter_get_basic(i, &key); + + if (!dbus_message_iter_next(i)) { + pa_log("Property value missing"); + return -1; + } + + if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_VARIANT) { + pa_log("Property value not a variant."); + return -1; + } + + dbus_message_iter_recurse(i, &variant_i); + + pa_log_debug("Parsing property org.bluez.Device.%s", key); + + switch (dbus_message_iter_get_arg_type(&variant_i)) { + + case DBUS_TYPE_STRING: { + + const char *value; + dbus_message_iter_get_basic(&variant_i, &value); + + if (pa_streq(key, "Name")) { + pa_xfree(d->name); + d->name = pa_xstrdup(value); + } else if (pa_streq(key, "Alias")) { + pa_xfree(d->alias); + d->alias = pa_xstrdup(value); + } else if (pa_streq(key, "Address")) { + pa_xfree(d->address); + d->address = pa_xstrdup(value); + } + + pa_log_debug("Value %s", value); + + break; + } + + case DBUS_TYPE_BOOLEAN: { + + dbus_bool_t value; + dbus_message_iter_get_basic(&variant_i, &value); + + if (pa_streq(key, "Paired")) + d->paired = !!value; + else if (pa_streq(key, "Connected")) + d->device_connected = !!value; + else if (pa_streq(key, "Trusted")) + d->trusted = !!value; + + pa_log_debug("Value %s", pa_yes_no(value)); + + break; + } + + case DBUS_TYPE_UINT32: { + + uint32_t value; + dbus_message_iter_get_basic(&variant_i, &value); + + if (pa_streq(key, "Class")) + d->class = (int) value; + + pa_log_debug("Value %u", (unsigned) value); + + break; + } + + case DBUS_TYPE_ARRAY: { + + DBusMessageIter ai; + dbus_message_iter_recurse(&variant_i, &ai); + + if (dbus_message_iter_get_arg_type(&ai) == DBUS_TYPE_STRING && + pa_streq(key, "UUIDs")) { + + while (dbus_message_iter_get_arg_type(&ai) != DBUS_TYPE_INVALID) { + pa_bluetooth_uuid *node; + const char *value; + + dbus_message_iter_get_basic(&ai, &value); + node = uuid_new(value); + PA_LLIST_PREPEND(pa_bluetooth_uuid, d->uuids, node); + + if (!dbus_message_iter_next(&ai)) + break; + } + } + + break; + } + } + + return 0; +} + +static int parse_audio_property(pa_bluetooth_discovery *u, int *connected, DBusMessageIter *i) { + const char *key; + DBusMessageIter variant_i; + + pa_assert(u); + pa_assert(connected); + pa_assert(i); + + if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) { + pa_log("Property name not a string."); + return -1; + } + + dbus_message_iter_get_basic(i, &key); + + if (!dbus_message_iter_next(i)) { + pa_log("Property value missing"); + return -1; + } + + if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_VARIANT) { + pa_log("Property value not a variant."); + return -1; + } + + dbus_message_iter_recurse(i, &variant_i); + + pa_log_debug("Parsing property org.bluez.{AudioSink|Headset}.%s", key); + + switch (dbus_message_iter_get_arg_type(&variant_i)) { + + case DBUS_TYPE_BOOLEAN: { + + dbus_bool_t value; + dbus_message_iter_get_basic(&variant_i, &value); + + if (pa_streq(key, "Connected")) + *connected = !!value; + + pa_log_debug("Value %s", pa_yes_no(value)); + + break; + } + } + + return 0; +} + + +static void run_callback(pa_bluetooth_discovery *y, pa_bluetooth_device *d, pa_bool_t good) { + pa_assert(y); + pa_assert(d); + + if (y->mode != MODE_DISCOVER) + return; + + if (!device_is_loaded(d)) + return; + + if (!device_is_audio(d)) + return; + + y->callback(y->userdata, d, good); + +} + +static void get_properties_reply(DBusPendingCall *pending, void *userdata) { + DBusMessage *r; + DBusMessageIter arg_i, element_i; + pa_dbus_pending *p; + pa_bluetooth_device *d; + pa_bluetooth_discovery *y; + int valid; + + pa_assert_se(p = userdata); + pa_assert_se(y = p->context_data); + pa_assert_se(r = dbus_pending_call_steal_reply(pending)); + +/* pa_log_debug("Got %s.GetProperties response for %s", */ +/* dbus_message_get_interface(p->message), */ +/* dbus_message_get_path(p->message)); */ + + d = p->call_data; + + valid = dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR ? -1 : 1; + + if (dbus_message_is_method_call(p->message, "org.bluez.Device", "GetProperties")) + d->device_info_valid = valid; + else if (dbus_message_is_method_call(p->message, "org.bluez.Headset", "GetProperties")) + d->headset_info_valid = valid; + else if (dbus_message_is_method_call(p->message, "org.bluez.AudioSink", "GetProperties")) + d->audio_sink_info_valid = valid; + + if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) { + + if (!dbus_message_is_error(r, DBUS_ERROR_UNKNOWN_METHOD)) + pa_log("Error from GetProperties reply: %s", dbus_message_get_error_name(r)); + + goto finish; + } + + if (!dbus_message_iter_init(r, &arg_i)) { + pa_log("GetProperties reply has no arguments."); + goto finish; + } + + if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) { + pa_log("GetProperties argument is not an array."); + goto finish; + } + + dbus_message_iter_recurse(&arg_i, &element_i); + while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) { + + if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) { + DBusMessageIter dict_i; + + dbus_message_iter_recurse(&element_i, &dict_i); + + if (dbus_message_has_interface(p->message, "org.bluez.Device")) { + if (parse_device_property(y, d, &dict_i) < 0) + goto finish; + + } else if (dbus_message_has_interface(p->message, "org.bluez.Headset")) { + if (parse_audio_property(y, &d->headset_connected, &dict_i) < 0) + goto finish; + + } else if (dbus_message_has_interface(p->message, "org.bluez.AudioSink")) { + if (parse_audio_property(y, &d->audio_sink_connected, &dict_i) < 0) + goto finish; + } + } + + if (!dbus_message_iter_next(&element_i)) + break; + } + +finish: + run_callback(y, d, TRUE); + + dbus_message_unref(r); + + PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p); + pa_dbus_pending_free(p); +} + +static pa_dbus_pending* send_and_add_to_pending(pa_bluetooth_discovery *y, pa_bluetooth_device *d, DBusMessage *m, DBusPendingCallNotifyFunction func) { + pa_dbus_pending *p; + DBusPendingCall *call; + + pa_assert(y); + pa_assert(m); + + pa_assert_se(dbus_connection_send_with_reply(y->connection, m, &call, -1)); + + p = pa_dbus_pending_new(m, call, y, d); + PA_LLIST_PREPEND(pa_dbus_pending, y->pending, p); + dbus_pending_call_set_notify(call, func, p, NULL); + + return p; +} + +static void found_device(pa_bluetooth_discovery *y, const char* path) { + DBusMessage *m; + pa_bluetooth_device *d; + + pa_assert(y); + pa_assert(path); + + d = device_new(path); + + if (y->mode == MODE_DISCOVER) { + pa_assert(y->devices); + pa_hashmap_put(y->devices, d->path, d); + } else { + pa_assert(!y->found_device); + y->found_device = d; + } + + pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Device", "GetProperties")); + send_and_add_to_pending(y, d, m, get_properties_reply); + + pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Headset", "GetProperties")); + send_and_add_to_pending(y, d, m, get_properties_reply); + + pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.AudioSink", "GetProperties")); + send_and_add_to_pending(y, d, m, get_properties_reply); +} + +static void list_devices_reply(DBusPendingCall *pending, void *userdata) { + DBusError e; + DBusMessage *r; + char **paths = NULL; + int num = -1; + pa_dbus_pending *p; + pa_bluetooth_discovery *y; + + pa_assert(pending); + + dbus_error_init(&e); + + pa_assert_se(p = userdata); + pa_assert_se(y = p->context_data); + pa_assert_se(r = dbus_pending_call_steal_reply(pending)); + + if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) { + pa_log("Error from ListDevices reply: %s", dbus_message_get_error_name(r)); + goto end; + } + + if (!dbus_message_get_args(r, &e, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &paths, &num, DBUS_TYPE_INVALID)) { + pa_log("org.bluez.Adapter.ListDevices returned an error: '%s'\n", e.message); + dbus_error_free(&e); + } else { + int i; + + for (i = 0; i < num; ++i) + found_device(y, paths[i]); + } + +end: + if (paths) + dbus_free_string_array (paths); + + dbus_message_unref(r); + + PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p); + pa_dbus_pending_free(p); +} + +static void find_device_reply(DBusPendingCall *pending, void *userdata) { + DBusError e; + DBusMessage *r; + char *path = NULL; + pa_dbus_pending *p; + pa_bluetooth_discovery *y; + + pa_assert(pending); + + dbus_error_init(&e); + + pa_assert_se(p = userdata); + pa_assert_se(y = p->context_data); + pa_assert_se(r = dbus_pending_call_steal_reply(pending)); + + if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) { + pa_log("Error from FindDevice reply: %s", dbus_message_get_error_name(r)); + goto end; + } + + if (!dbus_message_get_args(r, &e, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) { + pa_log("org.bluez.Adapter.FindDevice returned an error: '%s'\n", e.message); + dbus_error_free(&e); + } else + found_device(y, path); + +end: + dbus_message_unref(r); + + PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p); + pa_dbus_pending_free(p); +} + +static void found_adapter(pa_bluetooth_discovery *y, const char *path) { + DBusMessage *m; + + if (y->mode == MODE_FIND) { + pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Adapter", "FindDevice")); + + pa_assert_se(dbus_message_append_args(m, + DBUS_TYPE_STRING, &y->looking_for, + DBUS_TYPE_INVALID)); + + send_and_add_to_pending(y, NULL, m, find_device_reply); + + } else { + pa_assert(y->mode == MODE_DISCOVER); + + pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Adapter", "ListDevices")); + send_and_add_to_pending(y, NULL, m, list_devices_reply); + } +} + +static void list_adapters_reply(DBusPendingCall *pending, void *userdata) { + DBusError e; + DBusMessage *r; + char **paths = NULL; + int num = -1; + pa_dbus_pending *p; + pa_bluetooth_discovery *y; + + pa_assert(pending); + + dbus_error_init(&e); + + pa_assert_se(p = userdata); + pa_assert_se(y = p->context_data); + pa_assert_se(r = dbus_pending_call_steal_reply(pending)); + + if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) { + pa_log("Error from ListAdapters reply: %s", dbus_message_get_error_name(r)); + goto end; + } + + if (!dbus_message_get_args(r, &e, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &paths, &num, DBUS_TYPE_INVALID)) { + pa_log("org.bluez.Manager.ListAdapters returned an error: %s", e.message); + dbus_error_free(&e); + } else { + int i; + + for (i = 0; i < num; ++i) + found_adapter(y, paths[i]); + } + +end: + if (paths) + dbus_free_string_array (paths); + + dbus_message_unref(r); + + PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p); + pa_dbus_pending_free(p); +} + +static void list_adapters(pa_bluetooth_discovery *y) { + DBusMessage *m; + pa_assert(y); + + pa_assert_se(m = dbus_message_new_method_call("org.bluez", "/", "org.bluez.Manager", "ListAdapters")); + send_and_add_to_pending(y, NULL, m, list_adapters_reply); +} + +static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *userdata) { + DBusError err; + pa_bluetooth_discovery *y; + + pa_assert(bus); + pa_assert(m); + + pa_assert_se(y = userdata); + + dbus_error_init(&err); + + pa_log_debug("dbus: interface=%s, path=%s, member=%s\n", + dbus_message_get_interface(m), + dbus_message_get_path(m), + dbus_message_get_member(m)); + + if (dbus_message_is_signal(m, "org.bluez.Adapter", "DeviceRemoved")) { + const char *path; + pa_bluetooth_device *d; + + if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) { + pa_log("Failed to parse org.bluez.Adapter.DeviceRemoved: %s", err.message); + goto fail; + } + + pa_log_debug("Device %s removed", path); + + if ((d = pa_hashmap_remove(y->devices, path))) { + + pa_assert_se(y->mode == MODE_DISCOVER); + run_callback(y, d, FALSE); + + pa_bluetooth_device_free(d); + } + + return DBUS_HANDLER_RESULT_HANDLED; + + } else if (dbus_message_is_signal(m, "org.bluez.Adapter", "DeviceCreated")) { + const char *path; + + if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) { + pa_log("Failed to parse org.bluez.Adapter.DeviceCreated: %s", err.message); + goto fail; + } + + pa_log_debug("Device %s created", path); + + found_device(y, path); + return DBUS_HANDLER_RESULT_HANDLED; + + } else if (dbus_message_is_signal(m, "org.bluez.Manager", "AdapterAdded")) { + const char *path; + + if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) { + pa_log("Failed to parse org.bluez.Manager.AdapterAdded: %s", err.message); + goto fail; + } + + pa_log_debug("Adapter %s created", path); + + found_adapter(y, path); + return DBUS_HANDLER_RESULT_HANDLED; + + } else if (dbus_message_is_signal(m, "org.bluez.Headset", "PropertyChanged") || + dbus_message_is_signal(m, "org.bluez.AudioSink", "PropertyChanged") || + dbus_message_is_signal(m, "org.bluez.Device", "PropertyChanged")) { + + pa_bluetooth_device *d; + + if ((d = pa_hashmap_get(y->devices, dbus_message_get_path(m)))) { + DBusMessageIter arg_i; + + if (!dbus_message_iter_init(m, &arg_i)) { + pa_log("Failed to parse PropertyChanged: %s", err.message); + goto fail; + } + + if (dbus_message_has_interface(m, "org.bluez.Device")) { + if (parse_device_property(y, d, &arg_i) < 0) + goto fail; + + } else if (dbus_message_has_interface(m, "org.bluez.Headset")) { + if (parse_audio_property(y, &d->headset_connected, &arg_i) < 0) + goto fail; + + } else if (dbus_message_has_interface(m, "org.bluez.AudioSink")) { + if (parse_audio_property(y, &d->audio_sink_connected, &arg_i) < 0) + goto fail; + } + + pa_assert_se(y->mode == MODE_DISCOVER); + run_callback(y, d, TRUE); + } + + return DBUS_HANDLER_RESULT_HANDLED; + } + +fail: + dbus_error_free(&err); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +pa_bluetooth_device* pa_bluetooth_find_device(DBusConnection *c, const char* address) { + pa_bluetooth_discovery y; + + memset(&y, 0, sizeof(y)); + y.mode = MODE_FIND; + y.looking_for = address; + y.connection = c; + PA_LLIST_HEAD_INIT(pa_dbus_pending, y.pending); + + list_adapters(&y); + + pa_dbus_sync_pending_list(&y.pending); + pa_assert(!y.pending); + + if (y.found_device) { + pa_assert(device_is_loaded(y.found_device)); + + if (!device_is_audio(y.found_device)) { + pa_bluetooth_device_free(y.found_device); + return NULL; + } + } + + return y.found_device; +} + +pa_bluetooth_device* pa_bluetooth_get_device(DBusConnection *c, const char* path) { + pa_bluetooth_discovery y; + + memset(&y, 0, sizeof(y)); + y.mode = MODE_GET; + y.connection = c; + PA_LLIST_HEAD_INIT(pa_dbus_pending, y.pending); + + found_device(&y, path); + + pa_dbus_sync_pending_list(&y.pending); + pa_assert(!y.pending); + + if (y.found_device) { + pa_assert(device_is_loaded(y.found_device)); + + if (!device_is_audio(y.found_device)) { + pa_bluetooth_device_free(y.found_device); + return NULL; + } + } + + return y.found_device; +} + +pa_bluetooth_discovery* pa_bluetooth_discovery_new(DBusConnection *c, pa_bluetooth_device_callback_t cb, struct userdata *u) { + DBusError err; + pa_bluetooth_discovery *y; + + pa_assert(c); + pa_assert(cb); + + dbus_error_init(&err); + + y = pa_xnew0(pa_bluetooth_discovery, 1); + y->mode = MODE_DISCOVER; + y->connection = c; + y->callback = cb; + y->userdata = u; + y->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + PA_LLIST_HEAD_INIT(pa_dbus_pending, y->pending); + + /* dynamic detection of bluetooth audio devices */ + if (!dbus_connection_add_filter(c, filter_cb, y, NULL)) { + pa_log_error("Failed to add filter function"); + goto fail; + } + + if (pa_dbus_add_matches( + c, &err, + "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterAdded'", + "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'", + "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceCreated'", + "type='signal',sender='org.bluez',interface='org.bluez.Device',member='PropertyChanged'", + "type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'", + "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'", NULL) < 0) { + pa_log("Failed to add D-Bus matches: %s", err.message); + goto fail; + } + + list_adapters(y); + + return y; + +fail: + dbus_error_free(&err); + return NULL; +} + +void pa_bluetooth_discovery_free(pa_bluetooth_discovery *y) { + pa_bluetooth_device *d; + + pa_assert(y); + + pa_dbus_free_pending_list(&y->pending); + + if (y->devices) { + while ((d = pa_hashmap_steal_first(y->devices))) { + run_callback(y, d, FALSE); + pa_bluetooth_device_free(d); + } + + pa_hashmap_free(y->devices, NULL, NULL); + } + + if (y->connection) { + pa_dbus_remove_matches(y->connection, + "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterAdded'", + "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterRemoved'", + "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'", + "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceCreated'", + "type='signal',sender='org.bluez',interface='org.bluez.Device',member='PropertyChanged'", + "type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'", + "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'", NULL); + + dbus_connection_remove_filter(y->connection, filter_cb, y); + } +} + +void pa_bluetooth_discovery_sync(pa_bluetooth_discovery *y) { + pa_assert(y); + + pa_dbus_sync_pending_list(&y->pending); +} diff --git a/src/modules/bluetooth/bluetooth-util.h b/src/modules/bluetooth/bluetooth-util.h new file mode 100644 index 00000000..ea6687a1 --- /dev/null +++ b/src/modules/bluetooth/bluetooth-util.h @@ -0,0 +1,77 @@ +#ifndef foobluetoothutilhfoo +#define foobluetoothutilhfoo + +/*** + This file is part of PulseAudio. + + Copyright 2008 Joao Paulo Rechi Vita + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include +#include +#include + +typedef struct pa_bluetooth_uuid pa_bluetooth_uuid; +typedef struct pa_bluetooth_device pa_bluetooth_device; +typedef struct pa_bluetooth_discovery pa_bluetooth_discovery; + +struct userdata; + +struct pa_bluetooth_uuid { + char *uuid; + PA_LLIST_FIELDS(pa_bluetooth_uuid); +}; + +struct pa_bluetooth_device { + void *data; /* arbitrary information for the one owning the discovery object */ + + int device_info_valid; /* 0: no results yet; 1: good results; -1: bad results ... */ + int audio_sink_info_valid; /* ... same here ... */ + int headset_info_valid; /* ... and here */ + + /* Device information */ + char *name; + char *path; + int paired; + char *alias; + int device_connected; + PA_LLIST_HEAD(pa_bluetooth_uuid, uuids); + char *address; + int class; + int trusted; + + /* AudioSink information */ + int audio_sink_connected; + + /* Headset information */ + int headset_connected; +}; + +void pa_bluetooth_device_free(pa_bluetooth_device *d); + +pa_bluetooth_device* pa_bluetooth_get_device(DBusConnection *c, const char* path); +pa_bluetooth_device* pa_bluetooth_find_device(DBusConnection *c, const char* address); + +typedef void (*pa_bluetooth_device_callback_t)(struct userdata *u, pa_bluetooth_device *d, pa_bool_t good); +pa_bluetooth_discovery* pa_bluetooth_discovery_new(DBusConnection *c, pa_bluetooth_device_callback_t cb, struct userdata *u); +void pa_bluetooth_discovery_free(pa_bluetooth_discovery *d); +void pa_bluetooth_discovery_sync(pa_bluetooth_discovery *d); + +#endif -- cgit