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. --- src/modules/bluetooth/bluez5-util.c | 130 ++++++++++++++++++++++++++++++++++++ src/modules/bluetooth/bluez5-util.h | 44 ++++++++++++ 2 files changed, 174 insertions(+) diff --git a/src/modules/bluetooth/bluez5-util.c b/src/modules/bluetooth/bluez5-util.c index 2eac58f..550f23d 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; @@ -45,8 +46,117 @@ struct pa_bluetooth_discovery { bool filter_added; pa_hook hooks[PA_BLUETOOTH_HOOK_MAX]; 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_acquire_cb acquire, + pa_bluetooth_transport_release_cb release, void *userdata) { + 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; + t->acquire = acquire; + t->release = release; + t->userdata = userdata; + + if (size > 0) { + t->config = pa_xnew(uint8_t, size); + memcpy(t->config, config, size); + } + + t->state = PA_BLUETOOTH_TRANSPORT_STATE_IDLE; + + pa_assert_se(pa_hashmap_put(d->discovery->transports, t->path, t) >= 0); + + return t; +} + +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); +} + static pa_bluetooth_device* pa_bluetooth_discovery_create_device(pa_bluetooth_discovery *y, const char *path) { pa_bluetooth_device *d; @@ -93,8 +203,22 @@ pa_bluetooth_device* pa_bluetooth_discovery_get_device_by_address(pa_bluetooth_d } static void pa_bluetooth_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; + + d->transports[i] = NULL; + t->state = PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED; + pa_hook_fire(&d->discovery->hooks[PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED], t); + pa_bluetooth_transport_free(t); + } + d->discovery = NULL; pa_xfree(d->path); pa_xfree(d->alias); @@ -189,6 +313,7 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c) { PA_REFCNT_INIT(y); y->core = c; 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); @@ -249,6 +374,11 @@ void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) { pa_hashmap_free(y->devices, NULL); } + if (y->transports) { + pa_assert(pa_hashmap_isempty(y->transports)); + pa_hashmap_free(y->transports, NULL); + } + if (y->connection) { pa_dbus_remove_matches(pa_dbus_connection_get(y->connection), "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'" diff --git a/src/modules/bluetooth/bluez5-util.h b/src/modules/bluetooth/bluez5-util.h index d318956..52de5ff 100644 --- a/src/modules/bluetooth/bluez5-util.h +++ b/src/modules/bluetooth/bluez5-util.h @@ -24,14 +24,50 @@ #include <pulsecore/core.h> +typedef struct pa_bluetooth_transport pa_bluetooth_transport; typedef struct pa_bluetooth_device pa_bluetooth_device; 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 { + PROFILE_A2DP_SINK, + PROFILE_A2DP_SOURCE, + PROFILE_OFF +} pa_bluetooth_profile_t; +#define PA_BLUETOOTH_PROFILE_COUNT 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; @@ -44,8 +80,16 @@ struct pa_bluetooth_device { char *remote; char *local; int class_of_device; + + pa_bluetooth_transport *transports[PA_BLUETOOTH_PROFILE_COUNT]; }; +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_acquire_cb acquire, + pa_bluetooth_transport_release_cb release, void *userdata); +void pa_bluetooth_transport_free(pa_bluetooth_transport *t); + 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.7.11.7