mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-11-01 22:58:47 -04:00
add missing files
This commit is contained in:
parent
a71fa021a3
commit
4a75002ebe
2 changed files with 882 additions and 0 deletions
805
src/modules/bluetooth/bluetooth-util.c
Normal file
805
src/modules/bluetooth/bluetooth-util.c
Normal file
|
|
@ -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 <config.h>
|
||||
#endif
|
||||
|
||||
#include <pulsecore/core-util.h>
|
||||
#include <modules/dbus-util.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
77
src/modules/bluetooth/bluetooth-util.h
Normal file
77
src/modules/bluetooth/bluetooth-util.h
Normal file
|
|
@ -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 <dbus/dbus.h>
|
||||
|
||||
#include <pulsecore/llist.h>
|
||||
#include <pulsecore/macro.h>
|
||||
#include <pulsecore/core-util.h>
|
||||
|
||||
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
|
||||
Loading…
Add table
Add a link
Reference in a new issue