From: Jo?o Paulo Rechi Vita <jprvita@xxxxxxxxxxxxx> Create the pa_bluetooth_transport structure to store information about the bluetooth transport and utility functions to manipulate this structure. The acquire() and release() operations are function pointers in the pa_bluetooth_transport structure to make possible for different transport backends to provide different implementations of these operations. Thre is also a userdata field for the transport backend provide data for the acquire/release functions. This commit also creates a new function pa_bluetooth_device_any_transport_connected() to check if there is any audio connection between the host and a remote device. --- src/modules/bluetooth/bluez5-util.c | 176 ++++++++++++++++++++++++++++++++++++ src/modules/bluetooth/bluez5-util.h | 46 ++++++++++ 2 files changed, 222 insertions(+) diff --git a/src/modules/bluetooth/bluez5-util.c b/src/modules/bluetooth/bluez5-util.c index 6196c11..9ca4b9e 100644 --- a/src/modules/bluetooth/bluez5-util.c +++ b/src/modules/bluetooth/bluez5-util.c @@ -36,6 +36,7 @@ #include "bluez5-util.h" #define BLUEZ_SERVICE "org.bluez" +#define BLUEZ_MEDIA_TRANSPORT_INTERFACE BLUEZ_SERVICE ".MediaTransport1" struct pa_bluetooth_discovery { PA_REFCNT_DECLARE; @@ -47,8 +48,165 @@ struct pa_bluetooth_discovery { pa_hook hooks[PA_BLUETOOTH_HOOK_MAX]; pa_hashmap *adapters; pa_hashmap *devices; + pa_hashmap *transports; }; +pa_bluetooth_transport *pa_bluetooth_transport_new(pa_bluetooth_device *d, const char *owner, const char *path, + pa_bluetooth_profile_t p, const uint8_t *config, size_t size) { + pa_bluetooth_transport *t; + + t = pa_xnew0(pa_bluetooth_transport, 1); + t->device = d; + t->owner = pa_xstrdup(owner); + t->path = pa_xstrdup(path); + t->profile = p; + t->config_size = size; + + if (size > 0) { + t->config = pa_xnew(uint8_t, size); + memcpy(t->config, config, size); + } + + pa_assert_se(pa_hashmap_put(d->discovery->transports, t->path, t) >= 0); + + return t; +} + +static const char *transport_state_to_string(pa_bluetooth_transport_state_t state) { + switch(state) { + case PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED: + return "disconnected"; + case PA_BLUETOOTH_TRANSPORT_STATE_IDLE: + return "idle"; + case PA_BLUETOOTH_TRANSPORT_STATE_PLAYING: + return "playing"; + } + + return "invalid"; +} + +static void transport_state_changed(pa_bluetooth_transport *t, pa_bluetooth_transport_state_t state) { + bool old_any_connected; + + pa_assert(t); + + if (t->state == state) + return; + + old_any_connected = pa_bluetooth_device_any_transport_connected(t->device); + + pa_log_debug("Transport %s state changed from %s to %s", + t->path, transport_state_to_string(t->state), transport_state_to_string(state)); + + t->state = state; + if (state == PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED) + t->device->transports[t->profile] = NULL; + + pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED], t); + + if (old_any_connected != pa_bluetooth_device_any_transport_connected(t->device)) + pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED], t->device); +} + +void pa_bluetooth_transport_put(pa_bluetooth_transport *t) { + transport_state_changed(t, PA_BLUETOOTH_TRANSPORT_STATE_IDLE); +} + +void pa_bluetooth_transport_free(pa_bluetooth_transport *t) { + pa_assert(t); + + pa_hashmap_remove(t->device->discovery->transports, t->path); + pa_xfree(t->owner); + pa_xfree(t->path); + pa_xfree(t->config); + pa_xfree(t); +} + +static int bluez5_transport_acquire_cb(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu) { + DBusMessage *m, *r; + DBusError err; + int ret; + uint16_t i, o; + const char *method = optional ? "TryAcquire" : "Acquire"; + + pa_assert(t); + pa_assert(t->device); + pa_assert(t->device->discovery); + + pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, BLUEZ_MEDIA_TRANSPORT_INTERFACE, method)); + + dbus_error_init(&err); + + r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(t->device->discovery->connection), m, -1, &err); + if (!r) { + if (optional && pa_streq(err.name, "org.bluez.Error.NotAvailable")) + pa_log_info("Failed optional acquire of unavailable transport %s", t->path); + else + pa_log_error("Transport %s() failed for transport %s (%s)", method, t->path, err.message); + + dbus_error_free(&err); + return -1; + } + + if (!dbus_message_get_args(r, &err, DBUS_TYPE_UNIX_FD, &ret, DBUS_TYPE_UINT16, &i, DBUS_TYPE_UINT16, &o, + DBUS_TYPE_INVALID)) { + pa_log_error("Failed to parse %s() reply: %s", method, err.message); + dbus_error_free(&err); + ret = -1; + goto finish; + } + + if (imtu) + *imtu = i; + + if (omtu) + *omtu = o; + +finish: + dbus_message_unref(r); + return ret; +} + +static void bluez5_transport_release_cb(pa_bluetooth_transport *t) { + DBusMessage *m; + DBusError err; + + pa_assert(t); + pa_assert(t->device); + pa_assert(t->device->discovery); + + dbus_error_init(&err); + + if (t->state <= PA_BLUETOOTH_TRANSPORT_STATE_IDLE) { + pa_log_info("Transport %s auto-released by BlueZ or already released", t->path); + return; + } + + pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, BLUEZ_MEDIA_TRANSPORT_INTERFACE, "Release")); + dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(t->device->discovery->connection), m, -1, &err); + + if (dbus_error_is_set(&err)) { + pa_log_error("Failed to release transport %s: %s", t->path, err.message); + dbus_error_free(&err); + } else + pa_log_info("Transport %s released", t->path); +} + +bool pa_bluetooth_device_any_transport_connected(const pa_bluetooth_device *d) { + unsigned i; + + pa_assert(d); + + if (d->device_info_valid != 1) + return false; + + for (i = 0; i < PA_BLUETOOTH_PROFILE_COUNT; i++) + if (d->transports[i] && d->transports[i]->state != PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED) + return true; + + return false; +} + static pa_bluetooth_device* device_create(pa_bluetooth_discovery *y, const char *path) { pa_bluetooth_device *d; @@ -95,8 +253,20 @@ pa_bluetooth_device* pa_bluetooth_discovery_get_device_by_address(pa_bluetooth_d } static void device_free(pa_bluetooth_device *d) { + unsigned i; + pa_assert(d); + for (i = 0; i < PA_BLUETOOTH_PROFILE_COUNT; i++) { + pa_bluetooth_transport *t; + + if (!(t = d->transports[i])) + continue; + + transport_state_changed(t, PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED); + pa_bluetooth_transport_free(t); + } + d->discovery = NULL; d->adapter = NULL; pa_xfree(d->path); @@ -222,6 +392,7 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c) { y->core = c; y->adapters = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); y->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + y->transports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); for (i = 0; i < PA_BLUETOOTH_HOOK_MAX; i++) pa_hook_init(&y->hooks[i], y); @@ -288,6 +459,11 @@ void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) { pa_hashmap_free(y->adapters); } + if (y->transports) { + pa_assert(pa_hashmap_isempty(y->transports)); + pa_hashmap_free(y->transports); + } + if (y->connection) { if (y->matches_added) diff --git a/src/modules/bluetooth/bluez5-util.h b/src/modules/bluetooth/bluez5-util.h index 00784f6..57419f2 100644 --- a/src/modules/bluetooth/bluez5-util.h +++ b/src/modules/bluetooth/bluez5-util.h @@ -24,15 +24,51 @@ #include <pulsecore/core.h> +typedef struct pa_bluetooth_transport pa_bluetooth_transport; typedef struct pa_bluetooth_device pa_bluetooth_device; typedef struct pa_bluetooth_adapter pa_bluetooth_adapter; typedef struct pa_bluetooth_discovery pa_bluetooth_discovery; typedef enum pa_bluetooth_hook { PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED, /* Call data: pa_bluetooth_device */ + PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED, /* Call data: pa_bluetooth_transport */ PA_BLUETOOTH_HOOK_MAX } pa_bluetooth_hook_t; +typedef enum profile { + PA_BLUETOOTH_PROFILE_A2DP_SINK, + PA_BLUETOOTH_PROFILE_A2DP_SOURCE, + PA_BLUETOOTH_PROFILE_OFF +} pa_bluetooth_profile_t; +#define PA_BLUETOOTH_PROFILE_COUNT PA_BLUETOOTH_PROFILE_OFF + +typedef enum pa_bluetooth_transport_state { + PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED, + PA_BLUETOOTH_TRANSPORT_STATE_IDLE, + PA_BLUETOOTH_TRANSPORT_STATE_PLAYING +} pa_bluetooth_transport_state_t; + +typedef int (*pa_bluetooth_transport_acquire_cb)(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu); +typedef void (*pa_bluetooth_transport_release_cb)(pa_bluetooth_transport *t); + +struct pa_bluetooth_transport { + pa_bluetooth_device *device; + + char *owner; + char *path; + pa_bluetooth_profile_t profile; + + uint8_t codec; + uint8_t *config; + size_t config_size; + + pa_bluetooth_transport_state_t state; + + pa_bluetooth_transport_acquire_cb acquire; + pa_bluetooth_transport_release_cb release; + void *userdata; +}; + struct pa_bluetooth_device { pa_bluetooth_discovery *discovery; pa_bluetooth_adapter *adapter; @@ -44,6 +80,8 @@ struct pa_bluetooth_device { char *alias; char *address; uint32_t class_of_device; + + pa_bluetooth_transport *transports[PA_BLUETOOTH_PROFILE_COUNT]; }; struct pa_bluetooth_adapter { @@ -52,6 +90,14 @@ struct pa_bluetooth_adapter { char *address; }; +pa_bluetooth_transport *pa_bluetooth_transport_new(pa_bluetooth_device *d, const char *owner, const char *path, + pa_bluetooth_profile_t p, const uint8_t *config, size_t size); + +void pa_bluetooth_transport_put(pa_bluetooth_transport *t); +void pa_bluetooth_transport_free(pa_bluetooth_transport *t); + +bool pa_bluetooth_device_any_transport_connected(const pa_bluetooth_device *d); + pa_bluetooth_device* pa_bluetooth_discovery_get_device_by_path(pa_bluetooth_discovery *y, const char *path); pa_bluetooth_device* pa_bluetooth_discovery_get_device_by_address(pa_bluetooth_discovery *y, const char *remote, const char *local); -- 1.8.3.1