Hi, On Fri, Sep 24, 2010 at 4:32 PM, Luiz Augusto von Dentz <luiz.dentz at gmail.com> wrote: > From: Luiz Augusto von Dentz <luiz.dentz-von at nokia.com> > > Make use of D-Bus to transfer file descriptors. > --- > ?src/modules/bluetooth/bluetooth-util.c ? ? ? ? ?| ?619 ++++++++++++++++++++++- > ?src/modules/bluetooth/bluetooth-util.h ? ? ? ? ?| ? 21 + > ?src/modules/bluetooth/ipc.h ? ? ? ? ? ? ? ? ? ? | ? 28 + > ?src/modules/bluetooth/module-bluetooth-device.c | ?470 +++++++++++++++-- > ?4 files changed, 1065 insertions(+), 73 deletions(-) > > diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c > index c3b08ea..8f1f7e8 100644 > --- a/src/modules/bluetooth/bluetooth-util.c > +++ b/src/modules/bluetooth/bluetooth-util.c > @@ -28,6 +28,38 @@ > ?#include <pulsecore/dbus-shared.h> > > ?#include "bluetooth-util.h" > +#include "ipc.h" > + > +#define HFP_AG_ENDPOINT "/MediaEndpoint/HFPAG" > +#define A2DP_SOURCE_ENDPOINT "/MediaEndpoint/A2DPSource" > +#define A2DP_SINK_ENDPOINT "/MediaEndpoint/A2DPSink" > + > +#define ENDPOINT_INTROSPECT_XML ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? \ > + ? ?DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE ? ? ? ? ? ? ? ? ? ? ? ? ? \ > + ? ?"<node>" ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?\ > + ? ?" <interface name=\"org.bluez.MediaEndpoint\">" ? ? ? ? ? ? ? ? ? ? \ > + ? ?" ?<method name=\"SetConfiguration\">" ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?\ > + ? ?" ? <arg name=\"transport\" direction=\"in\" type=\"o\"/>" ? ? ? ? ?\ > + ? ?" ? <arg name=\"configuration\" direction=\"in\" type=\"ay\"/>" ? ? \ > + ? ?" ?</method>" ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? \ > + ? ?" ?<method name=\"SelectConfiguration\">" ? ? ? ? ? ? ? ? ? ? ? ? ? \ > + ? ?" ? <arg name=\"capabilities\" direction=\"in\" type=\"ay\"/>" ? ? ?\ > + ? ?" ? <arg name=\"configuration\" direction=\"out\" type=\"ay\"/>" ? ?\ > + ? ?" ?</method>" ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? \ > + ? ?" ?<method name=\"ClearConfiguration\">" ? ? ? ? ? ? ? ? ? ? ? ? ? ?\ > + ? ?" ?</method>" ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? \ > + ? ?" ?<method name=\"Release\">" ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? \ > + ? ?" ?</method>" ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? \ > + ? ?" </interface>" ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? \ > + ? ?" <interface name=\"org.freedesktop.DBus.Introspectable\">" ? ? ? ? \ > + ? ?" ?<method name=\"Introspect\">" ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?\ > + ? ?" ? <arg name=\"data\" type=\"s\" direction=\"out\"/>" ? ? ? ? ? ? ?\ > + ? ?" ?</method>" ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? \ > + ? ?" </interface>" ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? \ > + ? ?"</node>" > + > +#define MAX_BITPOOL 64 > +#define MIN_BITPOOL 2U > > ?struct pa_bluetooth_discovery { > ? ? PA_REFCNT_DECLARE; > @@ -86,6 +118,7 @@ static pa_bluetooth_device* device_new(const char *path) { > > ? ? d->name = NULL; > ? ? d->path = pa_xstrdup(path); > + ? ?d->transports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); > ? ? d->paired = -1; > ? ? d->alias = NULL; > ? ? d->device_connected = -1; > @@ -103,11 +136,25 @@ static pa_bluetooth_device* device_new(const char *path) { > ? ? return d; > ?} > > +static void transport_free(pa_bluetooth_transport *t) { > + ? ?pa_assert(t); > + > + ? ?pa_xfree(t->path); > + ? ?pa_xfree(t->config); > + ? ?pa_xfree(t); > +} > + > ?static void device_free(pa_bluetooth_device *d) { > ? ? pa_bluetooth_uuid *u; > + ? ?pa_bluetooth_transport *t; > > ? ? pa_assert(d); > > + ? ?while ((t = pa_hashmap_steal_first(d->transports))) > + ? ? ? ?transport_free(t); > + > + ? ?pa_hashmap_free(d->transports, NULL, NULL); > + > ? ? while ((u = d->uuids)) { > ? ? ? ? PA_LLIST_REMOVE(pa_bluetooth_uuid, d->uuids, u); > ? ? ? ? uuid_free(u); > @@ -331,6 +378,29 @@ static void remove_all_devices(pa_bluetooth_discovery *y) { > ? ? } > ?} > > +static pa_bluetooth_device *found_device(pa_bluetooth_discovery *y, const char* path) { > + ? ?DBusMessage *m; > + ? ?pa_bluetooth_device *d; > + > + ? ?pa_assert(y); > + ? ?pa_assert(path); > + > + ? ?d = pa_hashmap_get(y->devices, path); > + ? ?if (d) > + ? ? ? ?return d; > + > + ? ?d = device_new(path); > + > + ? ?pa_hashmap_put(y->devices, d->path, 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); > + > + ? ?/* Before we read the other properties (Audio, AudioSink, AudioSource, > + ? ? * Headset) we wait that the UUID is read */ > + ? ?return d; > +} > + > ?static void get_properties_reply(DBusPendingCall *pending, void *userdata) { > ? ? DBusMessage *r; > ? ? DBusMessageIter arg_i, element_i; > @@ -343,9 +413,9 @@ static void get_properties_reply(DBusPendingCall *pending, void *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)); */ > + ? ?pa_log_debug("Got %s.GetProperties response for %s", > + ? ? ? ? ? ? ? ?dbus_message_get_interface(p->message), > + ? ? ? ? ? ? ? ?dbus_message_get_path(p->message)); > > ? ? /* We don't use p->call_data here right-away since the device > ? ? ?* might already be invalidated at this point */ > @@ -415,6 +485,7 @@ static void get_properties_reply(DBusPendingCall *pending, void *userdata) { > ? ? ? ? ? ? } ?else if (dbus_message_has_interface(p->message, "org.bluez.HandsfreeGateway")) { > ? ? ? ? ? ? ? ? if (parse_audio_property(y, &d->hfgw_state, &arg_i) < 0) > ? ? ? ? ? ? ? ? ? ? goto finish; > + > ? ? ? ? ? ? } > ? ? ? ? } > > @@ -448,25 +519,36 @@ static pa_dbus_pending* send_and_add_to_pending(pa_bluetooth_discovery *y, pa_bl > ? ? return p; > ?} > > -static void found_device(pa_bluetooth_discovery *y, const char* path) { > - ? ?DBusMessage *m; > - ? ?pa_bluetooth_device *d; > +static void register_endpoint_reply(DBusPendingCall *pending, void *userdata) { > + ? ?DBusError e; > + ? ?DBusMessage *r; > + ? ?pa_dbus_pending *p; > + ? ?pa_bluetooth_discovery *y; > > - ? ?pa_assert(y); > - ? ?pa_assert(path); > + ? ?pa_assert(pending); > > - ? ?if (pa_hashmap_get(y->devices, path)) > - ? ? ? ?return; > + ? ?dbus_error_init(&e); > > - ? ?d = device_new(path); > + ? ?pa_assert_se(p = userdata); > + ? ?pa_assert_se(y = p->context_data); > + ? ?pa_assert_se(r = dbus_pending_call_steal_reply(pending)); > > - ? ?pa_hashmap_put(y->devices, d->path, d); > + ? ?if (dbus_message_is_error(r, DBUS_ERROR_SERVICE_UNKNOWN)) { > + ? ? ? ?pa_log_debug("Bluetooth daemon is apparently not available."); > + ? ? ? ?remove_all_devices(y); > + ? ? ? ?goto finish; > + ? ?} > > - ? ?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); > + ? ?if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) { > + ? ? ? ?pa_log("Error from RegisterEndpoint reply: %s", dbus_message_get_error_name(r)); > + ? ? ? ?goto finish; > + ? ?} > > - ? ?/* Before we read the other properties (Audio, AudioSink, AudioSource, > - ? ? * Headset) we wait that the UUID is read */ > +finish: > + ? ?dbus_message_unref(r); > + > + ? ?PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p); > + ? ?pa_dbus_pending_free(p); > ?} > > ?static void list_devices_reply(DBusPendingCall *pending, void *userdata) { > @@ -516,11 +598,126 @@ finish: > ? ? pa_dbus_pending_free(p); > ?} > > +static void append_variant(DBusMessageIter *iter, int type, const void *val) > +{ > + ? ?DBusMessageIter value; > + ? ?char sig[2] = { type, '\0' }; > + > + ? ?dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, sig, &value); > + > + ? ?dbus_message_iter_append_basic(&value, type, val); > + > + ? ?dbus_message_iter_close_container(iter, &value); > +} > + > +static void append_array_variant(DBusMessageIter *iter, int type, const void *val, int n_elements) > +{ > + ? ?DBusMessageIter variant, array; > + ? ?char type_sig[2] = { type, '\0' }; > + ? ?char array_sig[3] = { DBUS_TYPE_ARRAY, type, '\0' }; > + > + ? ?dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, array_sig, &variant); > + > + ? ?dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, type_sig, &array); > + > + ? ?dbus_message_iter_append_fixed_array(&array, type, val, n_elements); > + > + ? ?dbus_message_iter_close_container(&variant, &array); > + > + ? ?dbus_message_iter_close_container(iter, &variant); > +} > + > +static void dict_append_entry(DBusMessageIter *dict, const char *key, int type, void *val) > +{ > + ? ?DBusMessageIter entry; > + > + ? ?if (type == DBUS_TYPE_STRING) { > + ? ? ? ?const char *str = *((const char **) val); > + ? ? ? ?if (str == NULL) > + ? ? ? ? ? ?return; > + ? ?} > + > + ? ?dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry); > + > + ? ?dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > + > + ? ?append_variant(&entry, type, val); > + > + ? ?dbus_message_iter_close_container(dict, &entry); > +} > + > +static void dict_append_array(DBusMessageIter *dict, const char *key, int type, void *val, int n_elements) > +{ > + ? ?DBusMessageIter entry; > + > + ? ?dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry); > + > + ? ?dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); > + > + ? ?append_array_variant(&entry, type, val, n_elements); > + > + ? ?dbus_message_iter_close_container(dict, &entry); > +} > + > +static void register_endpoint(pa_bluetooth_discovery *y, const char *path, const char *endpoint, const char *uuid) > +{ > + ? ?DBusMessage *m; > + ? ?DBusMessageIter i, d; > + ? ?uint8_t codec = 0; > + > + ? ?pa_log_debug("Registering %s on adapter %s.", endpoint, path); > + > + ? ?pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Media", "RegisterEndpoint")); > + > + ? ?dbus_message_iter_init_append(m, &i); > + > + ? ?dbus_message_iter_append_basic(&i, DBUS_TYPE_OBJECT_PATH, &endpoint); > + > + ? ?dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?&d); > + > + ? ?dict_append_entry(&d, "UUID", DBUS_TYPE_STRING, &uuid); > + > + ? ?dict_append_entry(&d, "Codec", DBUS_TYPE_BYTE, &codec); > + > + ? ?if (pa_streq(uuid, HFP_AG_UUID)) { > + ? ? ? ?uint8_t *caps = NULL; > + ? ? ? ?dict_append_array(&d, "Capabilities", DBUS_TYPE_BYTE, &caps, 0); > + ? ?} else { > + ? ? ? ?sbc_capabilities_raw_t capabilities; > + ? ? ? ?uint8_t *caps = (uint8_t *) &capabilities; > + > + ? ? ? ?capabilities.channel_mode = BT_A2DP_CHANNEL_MODE_MONO | BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL | > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?BT_A2DP_CHANNEL_MODE_STEREO | BT_A2DP_CHANNEL_MODE_JOINT_STEREO; > + ? ? ? ?capabilities.frequency = BT_SBC_SAMPLING_FREQ_16000 | BT_SBC_SAMPLING_FREQ_32000 | > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? BT_SBC_SAMPLING_FREQ_44100 | BT_SBC_SAMPLING_FREQ_48000; > + ? ? ? ?capabilities.allocation_method = BT_A2DP_ALLOCATION_SNR | BT_A2DP_ALLOCATION_LOUDNESS; > + ? ? ? ?capabilities.subbands = BT_A2DP_SUBBANDS_4 | BT_A2DP_SUBBANDS_8; > + ? ? ? ?capabilities.block_length = BT_A2DP_BLOCK_LENGTH_4 | BT_A2DP_BLOCK_LENGTH_8 | > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?BT_A2DP_BLOCK_LENGTH_12 | BT_A2DP_BLOCK_LENGTH_16; > + ? ? ? ?capabilities.min_bitpool = MIN_BITPOOL; > + ? ? ? ?capabilities.max_bitpool = MAX_BITPOOL; > + > + ? ? ? ?dict_append_array(&d, "Capabilities", DBUS_TYPE_BYTE, &caps, sizeof(capabilities)); > + ? ?} > + > + ? ?dbus_message_iter_close_container(&i, &d); > + > + ? ?send_and_add_to_pending(y, NULL, m, register_endpoint_reply); > +} > + > ?static void found_adapter(pa_bluetooth_discovery *y, const char *path) { > ? ? DBusMessage *m; > > ? ? 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); > + > +#ifdef DBUS_TYPE_UNIX_FD > + ? ?register_endpoint(y, path, HFP_AG_ENDPOINT, HFP_AG_UUID); > + ? ?register_endpoint(y, path, A2DP_SOURCE_ENDPOINT, A2DP_SOURCE_UUID); > + ? ?register_endpoint(y, path, A2DP_SINK_ENDPOINT, A2DP_SINK_UUID); > +#endif > ?} > > ?static void list_adapters_reply(DBusPendingCall *pending, void *userdata) { > @@ -769,6 +966,35 @@ const pa_bluetooth_device* pa_bluetooth_discovery_get_by_path(pa_bluetooth_disco > ? ? return NULL; > ?} > > +const pa_bluetooth_transport* pa_bluetooth_discovery_get_transport(pa_bluetooth_discovery *y, const char *path) { > + ? ?pa_bluetooth_device *d; > + ? ?pa_bluetooth_transport *t; > + ? ?void *state = NULL; > + > + ? ?pa_assert(y); > + ? ?pa_assert(PA_REFCNT_VALUE(y) > 0); > + ? ?pa_assert(path); > + > + ? ?while ((d = pa_hashmap_iterate(y->devices, &state, NULL))) > + ? ? ? ?if ((t = pa_hashmap_get(d->transports, path))) > + ? ? ? ? ? ?return t; > + > + ? ?return NULL; > +} > + > +const pa_bluetooth_transport* pa_bluetooth_device_get_transport(const pa_bluetooth_device *d, enum profile profile) { > + ? ?pa_bluetooth_transport *t; > + ? ?void *state = NULL; > + > + ? ?pa_assert(d); > + > + ? ?while ((t = pa_hashmap_iterate(d->transports, &state, NULL))) > + ? ? ? ?if (t->profile == profile) > + ? ? ? ? ? ?return t; > + > + ? ?return NULL; > +} > + > ?static int setup_dbus(pa_bluetooth_discovery *y) { > ? ? DBusError err; > > @@ -785,9 +1011,357 @@ static int setup_dbus(pa_bluetooth_discovery *y) { > ? ? return 0; > ?} > > +static pa_bluetooth_transport *transport_new(const char *path, enum profile p, const uint8_t *config, int size) { > + ? ?pa_bluetooth_transport *t; > + > + ? ?t = pa_xnew0(pa_bluetooth_transport, 1); > + ? ?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); > + ? ?} > + > + ? ?return t; > +} > + > +static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) { > + ? ?pa_bluetooth_discovery *y = userdata; > + ? ?pa_bluetooth_device *d; > + ? ?pa_bluetooth_transport *t; > + ? ?const char *path, *dev_path = NULL, *uuid = NULL; > + ? ?uint8_t *config = NULL; > + ? ?int size = 0; > + ? ?enum profile p; > + ? ?DBusMessageIter args, props; > + ? ?DBusMessage *r; > + > + ? ?dbus_message_iter_init(m, &args); > + > + ? ?dbus_message_iter_get_basic(&args, &path); > + ? ?if (!dbus_message_iter_next(&args)) > + ? ? ? ?goto fail; > + > + ? ?dbus_message_iter_recurse(&args, &props); > + ? ?if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY) > + ? ? ? ?goto fail; > + > + ? ?/* Read transport properties */ > + ? ?while (dbus_message_iter_get_arg_type(&props) == DBUS_TYPE_DICT_ENTRY) { > + ? ? ? ?const char *key; > + ? ? ? ?DBusMessageIter value, entry; > + ? ? ? ?int var; > + > + ? ? ? ?dbus_message_iter_recurse(&props, &entry); > + ? ? ? ?dbus_message_iter_get_basic(&entry, &key); > + > + ? ? ? ?dbus_message_iter_next(&entry); > + ? ? ? ?dbus_message_iter_recurse(&entry, &value); > + > + ? ? ? ?var = dbus_message_iter_get_arg_type(&value); > + ? ? ? ?if (strcasecmp(key, "UUID") == 0) { > + ? ? ? ? ? ?if (var != DBUS_TYPE_STRING) > + ? ? ? ? ? ? ? ?goto fail; > + ? ? ? ? ? ?dbus_message_iter_get_basic(&value, &uuid); > + ? ? ? ?} else if (strcasecmp(key, "Device") == 0) { > + ? ? ? ? ? ?if (var != DBUS_TYPE_OBJECT_PATH) > + ? ? ? ? ? ? ? ?goto fail; > + ? ? ? ? ? ?dbus_message_iter_get_basic(&value, &dev_path); > + ? ? ? ?} else if (strcasecmp(key, "Configuration") == 0) { > + ? ? ? ? ? ?DBusMessageIter array; > + ? ? ? ? ? ?if (var != DBUS_TYPE_ARRAY) > + ? ? ? ? ? ? ? ?goto fail; > + ? ? ? ? ? ?dbus_message_iter_recurse(&value, &array); > + ? ? ? ? ? ?dbus_message_iter_get_fixed_array(&array, &config, &size); > + ? ? ? ?} > + > + ? ? ? ?dbus_message_iter_next(&props); > + ? ?} > + > + ? ?d = found_device(y, dev_path); > + ? ?if (!d) > + ? ? ? ?goto fail; > + > + ? ?if (dbus_message_has_path(m, HFP_AG_ENDPOINT)) > + ? ? ? ?p = PROFILE_HSP; > + ? ?else if (dbus_message_has_path(m, A2DP_SOURCE_ENDPOINT)) > + ? ? ? ?p = PROFILE_A2DP; > + ? ?else > + ? ? ? ?p = PROFILE_A2DP_SOURCE; > + > + ? ?t = transport_new(path, p, config, size); > + ? ?pa_hashmap_put(d->transports, t->path, t); > + > + ? ?pa_log_debug("Transport %s profile %d available", t->path, t->profile); > + > + ? ?pa_assert_se(r = dbus_message_new_method_return(m)); > + > + ? ?return r; > + > +fail: > + ? ?pa_log("org.bluez.MediaEndpoint.SetConfiguration: invalid arguments"); > + ? ?pa_assert_se(r = (dbus_message_new_error(m, "org.bluez.MediaEndpoint.Error.InvalidArguments", > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?"Unable to set configuration"))); > + ? ?return r; > +} > + > +static DBusMessage *endpoint_clear_configuration(DBusConnection *c, DBusMessage *m, void *userdata) { > + ? ?pa_bluetooth_discovery *y = userdata; > + ? ?pa_bluetooth_device *d; > + ? ?pa_bluetooth_transport *t; > + ? ?void *state = NULL; > + ? ?DBusMessage *r; > + ? ?DBusError e; > + ? ?const char *path; > + > + ? ?dbus_error_init(&e); > + > + ? ?if (!dbus_message_get_args(m, &e, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) { > + ? ? ? ?pa_log("org.bluez.MediaEndpoint.ClearConfiguration: %s", e.message); > + ? ? ? ?dbus_error_free(&e); > + ? ? ? ?goto fail; > + ? ?} > + > + ? ?while ((d = pa_hashmap_iterate(y->devices, &state, NULL))) { > + ? ? ? ?if ((t = pa_hashmap_get(d->transports, path))) { > + ? ? ? ? ? ?pa_log_debug("Clearing transport %s profile %d", t->path, t->profile); > + ? ? ? ? ? ?pa_hashmap_remove(d->transports, t->path); > + ? ? ? ? ? ?transport_free(t); > + ? ? ? ? ? ?break; > + ? ? ? ?} > + ? ?} > + > + ? ?pa_assert_se(r = dbus_message_new_method_return(m)); > + > + ? ?return r; > + > +fail: > + ? ?pa_assert_se(r = (dbus_message_new_error(m, "org.bluez.MediaEndpoint.Error.InvalidArguments", > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?"Unable to clear configuration"))); > + ? ?return r; > +} > + > +static uint8_t a2dp_default_bitpool(uint8_t freq, uint8_t mode) { > + > + ? ?switch (freq) { > + ? ? ? ?case BT_SBC_SAMPLING_FREQ_16000: > + ? ? ? ?case BT_SBC_SAMPLING_FREQ_32000: > + ? ? ? ? ? ?return 53; > + > + ? ? ? ?case BT_SBC_SAMPLING_FREQ_44100: > + > + ? ? ? ? ? ?switch (mode) { > + ? ? ? ? ? ? ? ?case BT_A2DP_CHANNEL_MODE_MONO: > + ? ? ? ? ? ? ? ?case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL: > + ? ? ? ? ? ? ? ? ? ?return 31; > + > + ? ? ? ? ? ? ? ?case BT_A2DP_CHANNEL_MODE_STEREO: > + ? ? ? ? ? ? ? ?case BT_A2DP_CHANNEL_MODE_JOINT_STEREO: > + ? ? ? ? ? ? ? ? ? ?return 53; > + > + ? ? ? ? ? ? ? ?default: > + ? ? ? ? ? ? ? ? ? ?pa_log_warn("Invalid channel mode %u", mode); > + ? ? ? ? ? ? ? ? ? ?return 53; > + ? ? ? ? ? ?} > + > + ? ? ? ?case BT_SBC_SAMPLING_FREQ_48000: > + > + ? ? ? ? ? ?switch (mode) { > + ? ? ? ? ? ? ? ?case BT_A2DP_CHANNEL_MODE_MONO: > + ? ? ? ? ? ? ? ?case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL: > + ? ? ? ? ? ? ? ? ? ?return 29; > + > + ? ? ? ? ? ? ? ?case BT_A2DP_CHANNEL_MODE_STEREO: > + ? ? ? ? ? ? ? ?case BT_A2DP_CHANNEL_MODE_JOINT_STEREO: > + ? ? ? ? ? ? ? ? ? ?return 51; > + > + ? ? ? ? ? ? ? ?default: > + ? ? ? ? ? ? ? ? ? ?pa_log_warn("Invalid channel mode %u", mode); > + ? ? ? ? ? ? ? ? ? ?return 51; > + ? ? ? ? ? ?} > + > + ? ? ? ?default: > + ? ? ? ? ? ?pa_log_warn("Invalid sampling freq %u", freq); > + ? ? ? ? ? ?return 53; > + ? ?} > +} > + > +static DBusMessage *endpoint_select_configuration(DBusConnection *c, DBusMessage *m, void *userdata) { > + ? ?pa_bluetooth_discovery *y = userdata; > + ? ?sbc_capabilities_raw_t *cap, config; > + ? ?uint8_t *pconf = (uint8_t *) &config; > + ? ?int i, size; > + ? ?DBusMessage *r; > + ? ?DBusError e; > + > + ? ?static const struct { > + ? ? ? ?uint32_t rate; > + ? ? ? ?uint8_t cap; > + ? ?} freq_table[] = { > + ? ? ? ?{ 16000U, BT_SBC_SAMPLING_FREQ_16000 }, > + ? ? ? ?{ 32000U, BT_SBC_SAMPLING_FREQ_32000 }, > + ? ? ? ?{ 44100U, BT_SBC_SAMPLING_FREQ_44100 }, > + ? ? ? ?{ 48000U, BT_SBC_SAMPLING_FREQ_48000 } > + ? ?}; > + > + ? ?dbus_error_init(&e); > + > + ? ?if (!dbus_message_get_args(m, &e, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &cap, &size, DBUS_TYPE_INVALID)) { > + ? ? ? ?pa_log("org.bluez.MediaEndpoint.SelectConfiguration: %s", e.message); > + ? ? ? ?dbus_error_free(&e); > + ? ? ? ?goto fail; > + ? ?} > + > + ? ?if (dbus_message_has_path(m, HFP_AG_ENDPOINT)) > + ? ? ? ?goto done; > + > + ? ?pa_assert(size == sizeof(config)); > + > + ? ?memset(&config, 0, sizeof(config)); > + > + ? ?/* Find the lowest freq that is at least as high as the requested > + ? ? * sampling rate */ > + ? ?for (i = 0; (unsigned) i < PA_ELEMENTSOF(freq_table); i++) > + ? ? ? ?if (freq_table[i].rate >= y->core->default_sample_spec.rate && (cap->frequency & freq_table[i].cap)) { > + ? ? ? ? ? ?config.frequency = freq_table[i].cap; > + ? ? ? ? ? ?break; > + ? ? ? ?} > + > + ? ?if ((unsigned) i == PA_ELEMENTSOF(freq_table)) { > + ? ? ? ?for (--i; i >= 0; i--) { > + ? ? ? ? ? ?if (cap->frequency & freq_table[i].cap) { > + ? ? ? ? ? ? ? ?config.frequency = freq_table[i].cap; > + ? ? ? ? ? ? ? ?break; > + ? ? ? ? ? ?} > + ? ? ? ?} > + > + ? ? ? ?if (i < 0) { > + ? ? ? ? ? ?pa_log("Not suitable sample rate"); > + ? ? ? ? ? ?goto fail; > + ? ? ? ?} > + ? ?} > + > + ? ?pa_assert((unsigned) i < PA_ELEMENTSOF(freq_table)); > + > + ? ?if (y->core->default_sample_spec.channels <= 1) { > + ? ? ? ?if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) > + ? ? ? ? ? ?config.channel_mode = BT_A2DP_CHANNEL_MODE_MONO; > + ? ?} > + > + ? ?if (y->core->default_sample_spec.channels >= 2) { > + ? ? ? ?if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO) > + ? ? ? ? ? ?config.channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO; > + ? ? ? ?else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) > + ? ? ? ? ? ?config.channel_mode = BT_A2DP_CHANNEL_MODE_STEREO; > + ? ? ? ?else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) > + ? ? ? ? ? ?config.channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL; > + ? ? ? ?else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) { > + ? ? ? ? ? ?config.channel_mode = BT_A2DP_CHANNEL_MODE_MONO; > + ? ? ? ?} else { > + ? ? ? ? ? ?pa_log("No supported channel modes"); > + ? ? ? ? ? ?goto fail; > + ? ? ? ?} > + ? ?} > + > + ? ?if (cap->block_length & BT_A2DP_BLOCK_LENGTH_16) > + ? ? ? ?config.block_length = BT_A2DP_BLOCK_LENGTH_16; > + ? ?else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_12) > + ? ? ? ?config.block_length = BT_A2DP_BLOCK_LENGTH_12; > + ? ?else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_8) > + ? ? ? ?config.block_length = BT_A2DP_BLOCK_LENGTH_8; > + ? ?else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_4) > + ? ? ? ?config.block_length = BT_A2DP_BLOCK_LENGTH_4; > + ? ?else { > + ? ? ? ?pa_log_error("No supported block lengths"); > + ? ? ? ?goto fail; > + ? ?} > + > + ? ?if (cap->subbands & BT_A2DP_SUBBANDS_8) > + ? ? ? ?config.subbands = BT_A2DP_SUBBANDS_8; > + ? ?else if (cap->subbands & BT_A2DP_SUBBANDS_4) > + ? ? ? ?config.subbands = BT_A2DP_SUBBANDS_4; > + ? ?else { > + ? ? ? ?pa_log_error("No supported subbands"); > + ? ? ? ?goto fail; > + ? ?} > + > + ? ?if (cap->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS) > + ? ? ? ?config.allocation_method = BT_A2DP_ALLOCATION_LOUDNESS; > + ? ?else if (cap->allocation_method & BT_A2DP_ALLOCATION_SNR) > + ? ? ? ?config.allocation_method = BT_A2DP_ALLOCATION_SNR; > + > + ? ?config.min_bitpool = (uint8_t) PA_MAX(MIN_BITPOOL, cap->min_bitpool); > + ? ?config.max_bitpool = (uint8_t) PA_MIN(a2dp_default_bitpool(config.frequency, config.channel_mode), cap->max_bitpool); > + > +done: > + ? ?pa_assert_se(r = dbus_message_new_method_return(m)); > + > + ? ?pa_assert_se(dbus_message_append_args( > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? r, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &pconf, size, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DBUS_TYPE_INVALID)); > + > + ? ?return r; > + > +fail: > + ? ?pa_assert_se(r = (dbus_message_new_error(m, "org.bluez.MediaEndpoint.Error.InvalidArguments", > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?"Unable to select configuration"))); > + ? ?return r; > +} > + > +static DBusHandlerResult endpoint_handler(DBusConnection *c, DBusMessage *m, void *userdata) { > + ? ?struct pa_bluetooth_discovery *y = userdata; > + ? ?DBusMessage *r = NULL; > + ? ?DBusError e; > + ? ?const char *path; > + > + ? ?pa_assert(y); > + > + ? ?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)); > + > + ? ?path = dbus_message_get_path(m); > + ? ?dbus_error_init(&e); > + > + ? ?if (!pa_streq(path, A2DP_SOURCE_ENDPOINT) && !pa_streq(path, A2DP_SINK_ENDPOINT) && !pa_streq(path, HFP_AG_ENDPOINT)) > + ? ? ? ?return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; > + > + ? ?if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) { > + ? ? ? ?const char *xml = ENDPOINT_INTROSPECT_XML; > + > + ? ? ? ?pa_assert_se(r = dbus_message_new_method_return(m)); > + ? ? ? ?pa_assert_se(dbus_message_append_args( > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? r, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DBUS_TYPE_STRING, &xml, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DBUS_TYPE_INVALID)); > + > + ? ?} else if (dbus_message_is_method_call(m, "org.bluez.MediaEndpoint", "SetConfiguration")) { > + ? ? ? ?r = endpoint_set_configuration(c, m, userdata); > + ? ?} else if (dbus_message_is_method_call(m, "org.bluez.MediaEndpoint", "SelectConfiguration")) { > + ? ? ? ?r = endpoint_select_configuration(c, m, userdata); > + ? ?} else if (dbus_message_is_method_call(m, "org.bluez.MediaEndpoint", "ClearConfiguration")) > + ? ? ? ?r = endpoint_clear_configuration(c, m, userdata); > + ? ?else > + ? ? ? ?return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; > + > + ? ?if (r) { > + ? ? ? ?pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), r, NULL)); > + ? ? ? ?dbus_message_unref(r); > + ? ?} > + > + ? ?return DBUS_HANDLER_RESULT_HANDLED; > +} > + > ?pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c) { > ? ? DBusError err; > ? ? pa_bluetooth_discovery *y; > + ? ?static const DBusObjectPathVTable vtable_endpoint = { > + ? ? ? ?.message_function = endpoint_handler, > + ? ?}; > > ? ? pa_assert(c); > > @@ -832,6 +1406,12 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c) { > ? ? ? ? goto fail; > ? ? } > > +#ifdef DBUS_TYPE_UNIX_FD > + ? ?pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y->connection), HFP_AG_ENDPOINT, &vtable_endpoint, y)); > + ? ?pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y->connection), A2DP_SOURCE_ENDPOINT, &vtable_endpoint, y)); > + ? ?pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y->connection), A2DP_SINK_ENDPOINT, &vtable_endpoint, y)); > +#endif > + > ? ? list_adapters(y); > > ? ? return y; > @@ -870,6 +1450,11 @@ void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) { > ? ? } > > ? ? if (y->connection) { > +#ifdef DBUS_TYPE_UNIX_FD > + ? ? ? ?dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), HFP_AG_ENDPOINT); > + ? ? ? ?dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), A2DP_SOURCE_ENDPOINT); > + ? ? ? ?dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), A2DP_SINK_ENDPOINT); > +#endif > ? ? ? ? pa_dbus_remove_matches(pa_dbus_connection_get(y->connection), > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?"type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='org.bluez'", > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?"type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterAdded'", > @@ -888,6 +1473,8 @@ void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) { > ? ? ? ? if (y->filter_added) > ? ? ? ? ? ? dbus_connection_remove_filter(pa_dbus_connection_get(y->connection), filter_cb, y); > > + ? ? ? ?dbus_connection_remove_filter(pa_dbus_connection_get(y->connection), filter_cb, y); > + > ? ? ? ? pa_dbus_connection_unref(y->connection); > ? ? } > > diff --git a/src/modules/bluetooth/bluetooth-util.h b/src/modules/bluetooth/bluetooth-util.h > index 9cee3de..ba7e17d 100644 > --- a/src/modules/bluetooth/bluetooth-util.h > +++ b/src/modules/bluetooth/bluetooth-util.h > @@ -25,6 +25,7 @@ > ?#include <dbus/dbus.h> > > ?#include <pulsecore/llist.h> > +#include <pulsecore/strlist.h> > ?#include <pulsecore/macro.h> > ?#include <pulsecore/core-util.h> > > @@ -53,6 +54,22 @@ struct pa_bluetooth_uuid { > ? ? PA_LLIST_FIELDS(pa_bluetooth_uuid); > ?}; > > +enum profile { > + ? ?PROFILE_A2DP, > + ? ?PROFILE_A2DP_SOURCE, > + ? ?PROFILE_HSP, > + ? ?PROFILE_HFGW, > + ? ?PROFILE_OFF > +}; > + > +typedef struct pa_bluetooth_transport { > + ? ?char *path; > + ? ?enum profile profile; > + ? ?uint8_t codec; > + ? ?uint8_t *config; > + ? ?int config_size; > +} pa_bluetooth_transport; > + > ?/* This enum is shared among Audio, Headset, AudioSink, and AudioSource, although not all values are acceptable in all profiles */ > ?typedef enum pa_bt_audio_state { > ? ? PA_BT_AUDIO_STATE_INVALID = -1, > @@ -70,6 +87,7 @@ struct pa_bluetooth_device { > ? ? /* Device information */ > ? ? char *name; > ? ? char *path; > + ? ?pa_hashmap *transports; > ? ? int paired; > ? ? char *alias; > ? ? int device_connected; > @@ -103,6 +121,9 @@ void pa_bluetooth_discovery_sync(pa_bluetooth_discovery *d); > ?const pa_bluetooth_device* pa_bluetooth_discovery_get_by_path(pa_bluetooth_discovery *d, const char* path); > ?const pa_bluetooth_device* pa_bluetooth_discovery_get_by_address(pa_bluetooth_discovery *d, const char* address); > > +const pa_bluetooth_transport* pa_bluetooth_discovery_get_transport(pa_bluetooth_discovery *y, const char *path); > +const pa_bluetooth_transport* pa_bluetooth_device_get_transport(const pa_bluetooth_device *d, enum profile profile); > + > ?pa_hook* pa_bluetooth_discovery_hook(pa_bluetooth_discovery *d); > > ?const char* pa_bluetooth_get_form_factor(uint32_t class); > diff --git a/src/modules/bluetooth/ipc.h b/src/modules/bluetooth/ipc.h > index 2e170f5..9537886 100644 > --- a/src/modules/bluetooth/ipc.h > +++ b/src/modules/bluetooth/ipc.h > @@ -201,6 +201,34 @@ typedef struct { > ? ? ? ?uint8_t max_bitpool; > ?} __attribute__ ((packed)) sbc_capabilities_t; > > +#if __BYTE_ORDER == __LITTLE_ENDIAN > + > +typedef struct { > + ? ? ? uint8_t channel_mode:4; > + ? ? ? uint8_t frequency:4; > + ? ? ? uint8_t allocation_method:2; > + ? ? ? uint8_t subbands:2; > + ? ? ? uint8_t block_length:4; > + ? ? ? uint8_t min_bitpool; > + ? ? ? uint8_t max_bitpool; > +} __attribute__ ((packed)) sbc_capabilities_raw_t; > + > +#elif __BYTE_ORDER == __BIG_ENDIAN > + > +typedef struct { > + ? ? ? uint8_t frequency:4; > + ? ? ? uint8_t channel_mode:4; > + ? ? ? uint8_t block_length:4; > + ? ? ? uint8_t subbands:2; > + ? ? ? uint8_t allocation_method:2; > + ? ? ? uint8_t min_bitpool; > + ? ? ? uint8_t max_bitpool; > +} __attribute__ ((packed)) sbc_capabilities_raw_t; > + > +#else > +#error "Unknown byte order" > +#endif > + > ?typedef struct { > ? ? ? ?codec_capabilities_t capability; > ? ? ? ?uint8_t channel_mode; > diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c > index 61fe369..2920fc9 100644 > --- a/src/modules/bluetooth/module-bluetooth-device.c > +++ b/src/modules/bluetooth/module-bluetooth-device.c > @@ -48,6 +48,7 @@ > ?#include <pulsecore/time-smoother.h> > ?#include <pulsecore/namereg.h> > ?#include <pulsecore/dbus-shared.h> > +#include <pulsecore/llist.h> > > ?#include "module-bluetooth-device-symdef.h" > ?#include "ipc.h" > @@ -129,20 +130,14 @@ struct hsp_info { > ? ? pa_hook_slot *source_state_changed_slot; > ?}; > > -enum profile { > - ? ?PROFILE_A2DP, > - ? ?PROFILE_A2DP_SOURCE, > - ? ?PROFILE_HSP, > - ? ?PROFILE_HFGW, > - ? ?PROFILE_OFF > -}; > - > ?struct userdata { > ? ? pa_core *core; > ? ? pa_module *module; > > ? ? char *address; > ? ? char *path; > + ? ?char *transport; > + > ? ? pa_bluetooth_discovery *discovery; > ? ? pa_bool_t auto_connect; > > @@ -202,10 +197,12 @@ static int service_send(struct userdata *u, const bt_audio_msg_header_t *msg) { > ? ? ssize_t r; > > ? ? pa_assert(u); > - ? ?pa_assert(u->service_fd >= 0); > ? ? pa_assert(msg); > ? ? pa_assert(msg->length > 0); > > + ? ?if (u->service_fd < 0) > + ? ? ? ?return -1; > + > ? ? pa_log_debug("Sending %s -> %s", > ? ? ? ? ? ? ? ? ?pa_strnull(bt_audio_strtype(msg->type)), > ? ? ? ? ? ? ? ? ?pa_strnull(bt_audio_strname(msg->name))); > @@ -745,6 +742,41 @@ static int set_conf(struct userdata *u) { > ?} > > ?/* from IO thread, except in SCO over PCM */ > + > +static int setup_stream(struct userdata *u) { > + ? ?struct pollfd *pollfd; > + ? ?int one; > + > + ? ?pa_make_fd_nonblock(u->stream_fd); > + ? ?pa_make_socket_low_delay(u->stream_fd); > + > + ? ?one = 1; > + ? ?if (setsockopt(u->stream_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)) < 0) > + ? ? ? ?pa_log_warn("Failed to enable SO_TIMESTAMP: %s", pa_cstrerror(errno)); > + > + ? ?pa_log_debug("Stream properly set up, we're ready to roll!"); > + > + ? ?u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1); > + ? ?pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); > + ? ?pollfd->fd = u->stream_fd; > + ? ?pollfd->events = pollfd->revents = 0; > + > + ? ?u->read_index = u->write_index = 0; > + ? ?u->started_at = 0; > + > + ? ?if (u->source) > + ? ? ? ?u->read_smoother = pa_smoother_new( > + ? ? ? ? ? ? ? ?PA_USEC_PER_SEC, > + ? ? ? ? ? ? ? ?PA_USEC_PER_SEC*2, > + ? ? ? ? ? ? ? ?TRUE, > + ? ? ? ? ? ? ? ?TRUE, > + ? ? ? ? ? ? ? ?10, > + ? ? ? ? ? ? ? ?pa_rtclock_now(), > + ? ? ? ? ? ? ? ?TRUE); > + > + ? ?return 0; > +} > + > ?static int start_stream_fd(struct userdata *u) { > ? ? union { > ? ? ? ? bt_audio_msg_header_t rsp; > @@ -754,8 +786,6 @@ static int start_stream_fd(struct userdata *u) { > ? ? ? ? bt_audio_error_t error; > ? ? ? ? uint8_t buf[BT_SUGGESTED_BUFFER_SIZE]; > ? ? } msg; > - ? ?struct pollfd *pollfd; > - ? ?int one; > > ? ? pa_assert(u); > ? ? pa_assert(u->rtpoll); > @@ -781,34 +811,7 @@ static int start_stream_fd(struct userdata *u) { > ? ? ? ? return -1; > ? ? } > > - ? ?pa_make_fd_nonblock(u->stream_fd); > - ? ?pa_make_socket_low_delay(u->stream_fd); > - > - ? ?one = 1; > - ? ?if (setsockopt(u->stream_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)) < 0) > - ? ? ? ?pa_log_warn("Failed to enable SO_TIMESTAMP: %s", pa_cstrerror(errno)); > - > - ? ?pa_log_debug("Stream properly set up, we're ready to roll!"); > - > - ? ?u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1); > - ? ?pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); > - ? ?pollfd->fd = u->stream_fd; > - ? ?pollfd->events = pollfd->revents = 0; > - > - ? ?u->read_index = u->write_index = 0; > - ? ?u->started_at = 0; > - > - ? ?if (u->source) > - ? ? ? ?u->read_smoother = pa_smoother_new( > - ? ? ? ? ? ? ? ?PA_USEC_PER_SEC, > - ? ? ? ? ? ? ? ?PA_USEC_PER_SEC*2, > - ? ? ? ? ? ? ? ?TRUE, > - ? ? ? ? ? ? ? ?TRUE, > - ? ? ? ? ? ? ? ?10, > - ? ? ? ? ? ? ? ?pa_rtclock_now(), > - ? ? ? ? ? ? ? ?TRUE); > - > - ? ?return 0; > + ? ?return setup_stream(u); > ?} > > ?/* from IO thread */ > @@ -852,6 +855,99 @@ static int stop_stream_fd(struct userdata *u) { > ? ? return r; > ?} > > +static void bt_transport_release(struct userdata *u) > +{ > + ? ?DBusMessage *m; > + ? ?DBusError err; > + ? ?const char *accesstype = "rw"; > + ? ?const pa_bluetooth_transport *t; > + > + ? ?dbus_error_init(&err); > + > + ? ?pa_log_debug("Releasing transport %s", u->transport); > + > + ? ?t = pa_bluetooth_discovery_get_transport(u->discovery, u->transport); > + ? ?if (t) { > + ? ? ? ?pa_assert_se(m = dbus_message_new_method_call("org.bluez", u->transport, "org.bluez.MediaTransport", "Release")); > + ? ? ? ?pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_STRING, &accesstype, DBUS_TYPE_INVALID)); > + ? ? ? ?dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->connection), m, -1, &err); > + > + ? ? ? ?if (dbus_error_is_set(&err)) > + ? ? ? ? ? ?pa_log("Failed to release transport %s: %s", u->transport, err.message); > + ? ? ? ?else > + ? ? ? ? ? ?pa_log_info("Transport %s released", u->transport); > + ? ?} > + > + ? ?if (u->rtpoll_item) { > + ? ? ? ?pa_rtpoll_item_free(u->rtpoll_item); > + ? ? ? ?u->rtpoll_item = NULL; > + ? ?} > + > + ? ?if (u->stream_fd >= 0) { > + ? ? ? ?pa_close(u->stream_fd); > + ? ? ? ?u->stream_fd = -1; > + ? ?} > + > + ? ?if (u->read_smoother) { > + ? ? ? ?pa_smoother_free(u->read_smoother); > + ? ? ? ?u->read_smoother = NULL; > + ? ?} > +} > + > +static int bt_transport_acquire(struct userdata *u, pa_bool_t start) > +{ > + ? ?DBusMessage *m, *r; > + ? ?DBusError err; > + ? ?const char *accesstype = "rw"; > + ? ?const pa_bluetooth_transport *t; > + > + ? ?dbus_error_init(&err); > + > + ? ?if (u->stream_fd >= 0) { > + ? ? ? ?if (start) > + ? ? ? ? ? ?goto done; > + ? ? ? ?return 0; > + ? ?} > + > + ? ?pa_log_debug("Acquiring transport %s", u->transport); > + > + ? ?t = pa_bluetooth_discovery_get_transport(u->discovery, u->transport); > + ? ?if (!t) { > + ? ? ? ?pa_log("Transport %s no longer available", u->transport); > + ? ? ? ?pa_xfree(u->transport); > + ? ? ? ?u->transport = NULL; > + ? ? ? ?return -1; > + ? ?} > + > + ? ?pa_assert_se(m = dbus_message_new_method_call("org.bluez", u->transport, "org.bluez.MediaTransport", "Acquire")); > + ? ?pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_STRING, &accesstype, DBUS_TYPE_INVALID)); > + ? ?r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->connection), m, -1, &err); > + > + ? ?if (dbus_error_is_set(&err) || !r) { > + ? ? ? ?pa_log("Failed to acquire transport fd: %s", err.message); > + ? ? ? ?return -1; > + ? ?} > + > + ? ?if (!dbus_message_get_args(r, &err, DBUS_TYPE_UNIX_FD, &u->stream_fd, DBUS_TYPE_INVALID)) { > + ? ? ? ?pa_log("Failed to parse org.bluez.MediaTransport.Acquire(): %s", err.message); > + ? ? ? ?goto fail; > + ? ?} > + > + ? ?dbus_message_unref(r); > + ? ?pa_log_info("Transport %s acquired: fd %d", u->transport, u->stream_fd); > + > + ? ?if (!start) > + ? ? ? ?return 0; > + > +done: > + ? ?pa_log_info("Transport %s resuming", u->transport); > + ? ?return setup_stream(u); > + > +fail: > + ? ?dbus_message_unref(r); > + ? ?return -1; > +} > + > ?/* Run from IO thread */ > ?static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { > ? ? struct userdata *u = PA_SINK(o)->userdata; > @@ -870,11 +966,15 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse > ? ? ? ? ? ? ? ? ? ? pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state)); > > ? ? ? ? ? ? ? ? ? ? /* Stop the device if the source is suspended as well */ > - ? ? ? ? ? ? ? ? ? ?if (!u->source || u->source->state == PA_SOURCE_SUSPENDED) > + ? ? ? ? ? ? ? ? ? ?if (!u->source || u->source->state == PA_SOURCE_SUSPENDED) { > ? ? ? ? ? ? ? ? ? ? ? ? /* We deliberately ignore whether stopping > ? ? ? ? ? ? ? ? ? ? ? ? ?* actually worked. Since the stream_fd is > ? ? ? ? ? ? ? ? ? ? ? ? ?* closed it doesn't really matter */ > - ? ? ? ? ? ? ? ? ? ? ? ?stop_stream_fd(u); > + ? ? ? ? ? ? ? ? ? ? ? ?if (u->transport) > + ? ? ? ? ? ? ? ? ? ? ? ? ? ?bt_transport_release(u); > + ? ? ? ? ? ? ? ? ? ? ? ?else > + ? ? ? ? ? ? ? ? ? ? ? ? ? ?stop_stream_fd(u); > + ? ? ? ? ? ? ? ? ? ?} > > ? ? ? ? ? ? ? ? ? ? break; > > @@ -884,9 +984,13 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse > ? ? ? ? ? ? ? ? ? ? ? ? break; > > ? ? ? ? ? ? ? ? ? ? /* Resume the device if the source was suspended as well */ > - ? ? ? ? ? ? ? ? ? ?if (!u->source || u->source->state == PA_SOURCE_SUSPENDED) > - ? ? ? ? ? ? ? ? ? ? ? ?if (start_stream_fd(u) < 0) > + ? ? ? ? ? ? ? ? ? ?if (!u->source || u->source->state == PA_SOURCE_SUSPENDED) { > + ? ? ? ? ? ? ? ? ? ? ? ?if (u->transport) { > + ? ? ? ? ? ? ? ? ? ? ? ? ? ?if (bt_transport_acquire(u, TRUE) < 0) > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?failed = TRUE; > + ? ? ? ? ? ? ? ? ? ? ? ?} else if (start_stream_fd(u) < 0) > ? ? ? ? ? ? ? ? ? ? ? ? ? ? failed = TRUE; > + ? ? ? ? ? ? ? ? ? ?} > ? ? ? ? ? ? ? ? ? ? break; > > ? ? ? ? ? ? ? ? case PA_SINK_UNLINKED: > @@ -942,8 +1046,12 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off > ? ? ? ? ? ? ? ? ? ? pa_assert(PA_SOURCE_IS_OPENED(u->source->thread_info.state)); > > ? ? ? ? ? ? ? ? ? ? /* Stop the device if the sink is suspended as well */ > - ? ? ? ? ? ? ? ? ? ?if (!u->sink || u->sink->state == PA_SINK_SUSPENDED) > - ? ? ? ? ? ? ? ? ? ? ? ?stop_stream_fd(u); > + ? ? ? ? ? ? ? ? ? ?if (!u->sink || u->sink->state == PA_SINK_SUSPENDED) { > + ? ? ? ? ? ? ? ? ? ? ? ?if (u->transport) > + ? ? ? ? ? ? ? ? ? ? ? ? ? ?bt_transport_release(u); > + ? ? ? ? ? ? ? ? ? ? ? ?else > + ? ? ? ? ? ? ? ? ? ? ? ? ? ?stop_stream_fd(u); > + ? ? ? ? ? ? ? ? ? ?} > > ? ? ? ? ? ? ? ? ? ? if (u->read_smoother) > ? ? ? ? ? ? ? ? ? ? ? ? pa_smoother_pause(u->read_smoother, pa_rtclock_now()); > @@ -955,10 +1063,13 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off > ? ? ? ? ? ? ? ? ? ? ? ? break; > > ? ? ? ? ? ? ? ? ? ? /* Resume the device if the sink was suspended as well */ > - ? ? ? ? ? ? ? ? ? ?if (!u->sink || u->sink->thread_info.state == PA_SINK_SUSPENDED) > - ? ? ? ? ? ? ? ? ? ? ? ?if (start_stream_fd(u) < 0) > + ? ? ? ? ? ? ? ? ? ?if (!u->sink || u->sink->thread_info.state == PA_SINK_SUSPENDED) { > + ? ? ? ? ? ? ? ? ? ? ? ?if (u->transport) { > + ? ? ? ? ? ? ? ? ? ? ? ? ? ?if (bt_transport_acquire(u, TRUE) < 0) > ? ? ? ? ? ? ? ? ? ? ? ? ? ? failed = TRUE; > - > + ? ? ? ? ? ? ? ? ? ? ? ?} else if (start_stream_fd(u) < 0) > + ? ? ? ? ? ? ? ? ? ? ? ? ? ?failed = TRUE; > + ? ? ? ? ? ? ? ? ? ?} > ? ? ? ? ? ? ? ? ? ? /* We don't resume the smoother here. Instead we > ? ? ? ? ? ? ? ? ? ? ?* wait until the first packet arrives */ > ? ? ? ? ? ? ? ? ? ? break; > @@ -1414,7 +1525,10 @@ static void thread_func(void *userdata) { > > ? ? pa_thread_mq_install(&u->thread_mq); > > - ? ?if (start_stream_fd(u) < 0) > + ? ?if (u->transport) { > + ? ? ? ?if (bt_transport_acquire(u, TRUE) < 0) > + ? ? ? ? ? ?goto fail; > + ? ?} else if (start_stream_fd(u) < 0) > ? ? ? ? goto fail; > > ? ? for (;;) { > @@ -1713,13 +1827,19 @@ static void sco_over_pcm_state_update(struct userdata *u) { > ? ? ? ? if ((init_bt(u) < 0) || (init_profile(u) < 0)) > ? ? ? ? ? ? pa_log("Can't resume SCO over PCM"); > > - ? ? ? ?start_stream_fd(u); > + ? ? ? ?if (u->transport) > + ? ? ? ? ? ?bt_transport_acquire(u, TRUE); > + ? ? ? ?else > + ? ? ? ? ? ?start_stream_fd(u); > ? ? } else { > > ? ? ? ? if (u->service_fd < 0) > ? ? ? ? ? ? return; > > - ? ? ? ?stop_stream_fd(u); > + ? ? ? ?if (u->transport) > + ? ? ? ? ? ?bt_transport_release(u); > + ? ? ? ?else > + ? ? ? ? ? ?stop_stream_fd(u); > > ? ? ? ? pa_log_debug("Closing SCO over PCM"); > ? ? ? ? pa_close(u->service_fd); > @@ -1906,6 +2026,218 @@ static void shutdown_bt(struct userdata *u) { > ? ? } > ?} > > +static int bt_transport_config_a2dp(struct userdata *u) > +{ > + ? ?const pa_bluetooth_transport *t; > + ? ?struct a2dp_info *a2dp = &u->a2dp; > + ? ?sbc_capabilities_raw_t *config; > + > + ? ?t = pa_bluetooth_discovery_get_transport(u->discovery, u->transport); > + ? ?pa_assert(t); > + > + ? ?config = (sbc_capabilities_raw_t *) t->config; > + > + ? ?if (a2dp->sbc_initialized) > + ? ? ? ?sbc_reinit(&a2dp->sbc, 0); > + ? ?else > + ? ? ? ?sbc_init(&a2dp->sbc, 0); > + ? ?a2dp->sbc_initialized = TRUE; > + > + ? ?switch (config->frequency) { > + ? ? ? ?case BT_SBC_SAMPLING_FREQ_16000: > + ? ? ? ? ? ?a2dp->sbc.frequency = SBC_FREQ_16000; > + ? ? ? ? ? ?break; > + ? ? ? ?case BT_SBC_SAMPLING_FREQ_32000: > + ? ? ? ? ? ?a2dp->sbc.frequency = SBC_FREQ_32000; > + ? ? ? ? ? ?break; > + ? ? ? ?case BT_SBC_SAMPLING_FREQ_44100: > + ? ? ? ? ? ?a2dp->sbc.frequency = SBC_FREQ_44100; > + ? ? ? ? ? ?break; > + ? ? ? ?case BT_SBC_SAMPLING_FREQ_48000: > + ? ? ? ? ? ?a2dp->sbc.frequency = SBC_FREQ_48000; > + ? ? ? ? ? ?break; > + ? ? ? ?default: > + ? ? ? ? ? ?pa_assert_not_reached(); > + ? ?} > + > + ? ?switch (config->channel_mode) { > + ? ? ? ?case BT_A2DP_CHANNEL_MODE_MONO: > + ? ? ? ? ? ?a2dp->sbc.mode = SBC_MODE_MONO; > + ? ? ? ? ? ?break; > + ? ? ? ?case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL: > + ? ? ? ? ? ?a2dp->sbc.mode = SBC_MODE_DUAL_CHANNEL; > + ? ? ? ? ? ?break; > + ? ? ? ?case BT_A2DP_CHANNEL_MODE_STEREO: > + ? ? ? ? ? ?a2dp->sbc.mode = SBC_MODE_STEREO; > + ? ? ? ? ? ?break; > + ? ? ? ?case BT_A2DP_CHANNEL_MODE_JOINT_STEREO: > + ? ? ? ? ? ?a2dp->sbc.mode = SBC_MODE_JOINT_STEREO; > + ? ? ? ? ? ?break; > + ? ? ? ?default: > + ? ? ? ? ? ?pa_assert_not_reached(); > + ? ?} > + > + ? ?switch (config->allocation_method) { > + ? ? ? ?case BT_A2DP_ALLOCATION_SNR: > + ? ? ? ? ? ?a2dp->sbc.allocation = SBC_AM_SNR; > + ? ? ? ? ? ?break; > + ? ? ? ?case BT_A2DP_ALLOCATION_LOUDNESS: > + ? ? ? ? ? ?a2dp->sbc.allocation = SBC_AM_LOUDNESS; > + ? ? ? ? ? ?break; > + ? ? ? ?default: > + ? ? ? ? ? ?pa_assert_not_reached(); > + ? ?} > + > + ? ?switch (config->subbands) { > + ? ? ? ?case BT_A2DP_SUBBANDS_4: > + ? ? ? ? ? ?a2dp->sbc.subbands = SBC_SB_4; > + ? ? ? ? ? ?break; > + ? ? ? ?case BT_A2DP_SUBBANDS_8: > + ? ? ? ? ? ?a2dp->sbc.subbands = SBC_SB_8; > + ? ? ? ? ? ?break; > + ? ? ? ?default: > + ? ? ? ? ? ?pa_assert_not_reached(); > + ? ?} > + > + ? ?switch (config->block_length) { > + ? ? ? ?case BT_A2DP_BLOCK_LENGTH_4: > + ? ? ? ? ? ?a2dp->sbc.blocks = SBC_BLK_4; > + ? ? ? ? ? ?break; > + ? ? ? ?case BT_A2DP_BLOCK_LENGTH_8: > + ? ? ? ? ? ?a2dp->sbc.blocks = SBC_BLK_8; > + ? ? ? ? ? ?break; > + ? ? ? ?case BT_A2DP_BLOCK_LENGTH_12: > + ? ? ? ? ? ?a2dp->sbc.blocks = SBC_BLK_12; > + ? ? ? ? ? ?break; > + ? ? ? ?case BT_A2DP_BLOCK_LENGTH_16: > + ? ? ? ? ? ?a2dp->sbc.blocks = SBC_BLK_16; > + ? ? ? ? ? ?break; > + ? ? ? ?default: > + ? ? ? ? ? ?pa_assert_not_reached(); > + ? ?} > + > + ? ?a2dp->sbc.bitpool = config->max_bitpool; > + ? ?a2dp->codesize = sbc_get_codesize(&a2dp->sbc); > + ? ?a2dp->frame_length = sbc_get_frame_length(&a2dp->sbc); > + > + ? ?u->block_size = > + ? ? ? ?((u->link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload)) > + ? ? ? ?/ a2dp->frame_length > + ? ? ? ?* a2dp->codesize); > + > + ? ?pa_log_info("SBC parameters:\n\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\tbitpool=%u\n", > + ? ? ? ? ? ? ? ?a2dp->sbc.allocation, a2dp->sbc.subbands, a2dp->sbc.blocks, a2dp->sbc.bitpool); > + > + ? ?return 0; > +} > + > +static int bt_transport_config(struct userdata *u) > +{ > + ? ?if (u->profile == PROFILE_HSP || u->profile == PROFILE_HFGW) { > + ? ? ? ?u->block_size = u->link_mtu; > + ? ? ? ?return 0; > + ? ?} > + > + ? ?return bt_transport_config_a2dp(u); > +} > + > +static int parse_transport_property(struct userdata *u, DBusMessageIter *i) > +{ > + ? ?const char *key; > + ? ?DBusMessageIter variant_i; > + > + ? ?pa_assert(u); > + ? ?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); > + > + ? ?switch (dbus_message_iter_get_arg_type(&variant_i)) { > + > + ? ? ? ?case DBUS_TYPE_UINT16: { > + > + ? ? ? ? ? ?uint16_t value; > + ? ? ? ? ? ?dbus_message_iter_get_basic(&variant_i, &value); > + > + ? ? ? ? ? ?if (pa_streq(key, "OMTU")) > + ? ? ? ? ? ? ? ?u->link_mtu = value; > + > + ? ? ? ? ? ?break; > + ? ? ? ?} > + > + ? ?} > + > + ? ?return 0; > +} > + > +/* Run from main thread */ > +static int bt_transport_open(struct userdata *u) > +{ > + ? ?DBusMessage *m, *r; > + ? ?DBusMessageIter arg_i, element_i; > + ? ?DBusError err; > + > + ? ?if (bt_transport_acquire(u, FALSE) < 0) > + ? ? ? ?return -1; > + > + ? ?dbus_error_init(&err); > + > + ? ?pa_assert_se(m = dbus_message_new_method_call("org.bluez", u->transport, "org.bluez.MediaTransport", "GetProperties")); > + ? ?r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->connection), m, -1, &err); > + > + ? ?if (dbus_error_is_set(&err) || !r) { > + ? ? ? ?pa_log("Failed to get transport properties: %s", err.message); > + ? ? ? ?goto fail; > + ? ?} > + > + ? ?if (!dbus_message_iter_init(r, &arg_i)) { > + ? ? ? ?pa_log("GetProperties reply has no arguments."); > + ? ? ? ?goto fail; > + ? ?} > + > + ? ?if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) { > + ? ? ? ?pa_log("GetProperties argument is not an array."); > + ? ? ? ?goto fail; > + ? ?} > + > + ? ?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); > + > + ? ? ? ? ? ?parse_transport_property(u, &dict_i); > + ? ? ? ?} > + > + ? ? ? ?if (!dbus_message_iter_next(&element_i)) > + ? ? ? ? ? ?break; > + ? ?} > + > + ? ?return bt_transport_config(u); > + > +fail: > + ? ?dbus_message_unref(r); > + ? ?return -1; > +} > + > ?/* Run from main thread */ > ?static int init_bt(struct userdata *u) { > ? ? pa_assert(u); > @@ -1928,8 +2260,30 @@ static int init_bt(struct userdata *u) { > > ?/* Run from main thread */ > ?static int setup_bt(struct userdata *u) { > + ? ?const pa_bluetooth_device *d; > + ? ?const pa_bluetooth_transport *t; > + > ? ? pa_assert(u); > > + ? ?if (!(d = pa_bluetooth_discovery_get_by_path(u->discovery, u->path))) { > + ? ? ? ?pa_log_error("Failed to get device object."); > + ? ? ? ?return -1; > + ? ?} > + > + ? ?/* release transport if exist */ > + ? ?if (u->transport) { > + ? ? ? ?bt_transport_release(u); > + ? ? ? ?pa_xfree(u->transport); > + ? ? ? ?u->transport = NULL; > + ? ?} > + > + ? ?/* check if profile has a transport */ > + ? ?t = pa_bluetooth_device_get_transport(d, u->profile); > + ? ?if (t) { > + ? ? ? ?u->transport = pa_xstrdup(t->path); > + ? ? ? ?return bt_transport_open(u); > + ? ?} > + > ? ? if (get_caps(u, 0) < 0) > ? ? ? ? return -1; > > @@ -2036,7 +2390,10 @@ static int start_thread(struct userdata *u) { > > ?#ifdef NOKIA > ? ? if (USE_SCO_OVER_PCM(u)) { > - ? ? ? ?if (start_stream_fd(u) < 0) > + ? ? ? ?if (u->transport) { > + ? ? ? ? ? ?if (bt_transport_acquire(u, TRUE) < 0) > + ? ? ? ? ? ? ? ?return -1; > + ? ? ? ?} else if (start_stream_fd(u) < 0) > ? ? ? ? ? ? return -1; > > ? ? ? ? pa_sink_ref(u->sink); > @@ -2316,7 +2673,7 @@ static const pa_bluetooth_device* find_device(struct userdata *u, const char *ad > ? ? ? ? } > > ? ? ? ? if (address && !(pa_streq(d->address, address))) { > - ? ? ? ? ? ?pa_log_error("Passed path %s and address %s don't match.", path, address); > + ? ? ? ? ? ?pa_log_error("Passed path %s != %s and address %s don't match.", path, d->path, address); > ? ? ? ? ? ? return NULL; > ? ? ? ? } > > @@ -2429,10 +2786,6 @@ int pa__init(pa_module* m) { > ? ? if (add_card(u, device) < 0) > ? ? ? ? goto fail; > > - ? ?/* Connect to the BT service and query capabilities */ > - ? ?if (init_bt(u) < 0) > - ? ? ? ?goto fail; > - > ? ? if (!dbus_connection_add_filter(pa_dbus_connection_get(u->connection), filter_cb, u, NULL)) { > ? ? ? ? pa_log_error("Failed to add filter function"); > ? ? ? ? goto fail; > @@ -2458,6 +2811,9 @@ int pa__init(pa_module* m) { > ? ? pa_xfree(speaker); > ? ? pa_xfree(mike); > > + ? ?/* Connect to the BT service */ > + ? ?init_bt(u); > + > ? ? if (u->profile != PROFILE_OFF) > ? ? ? ? if (init_profile(u) < 0) > ? ? ? ? ? ? goto fail; > -- > 1.7.1 > > _______________________________________________ > pulseaudio-discuss mailing list > pulseaudio-discuss at mail.0pointer.de > https://tango.0pointer.de/mailman/listinfo/pulseaudio-discuss > It seems the patch finally got into the mailing list, did somebody have time to take a look? -- Luiz Augusto von Dentz Computer Engineer