Login
[x]
Log in using an account from:
Fedora Account System
Red Hat Associate
Red Hat Customer
Or login using a Red Hat Bugzilla account
Forgot Password
Login:
Hide Forgot
Create an Account
Red Hat Bugzilla – Attachment 949217 Details for
Bug 1045548
HSP/HFP Profile not available for Bluetooth headset in Fedora 20, was available in Fedora 19
[?]
New
Simple Search
Advanced Search
My Links
Browse
Requests
Reports
Current State
Search
Tabular reports
Graphical reports
Duplicates
Other Reports
User Changes
Plotly Reports
Bug Status
Bug Severity
Non-Defaults
|
Product Dashboard
Help
Page Help!
Bug Writing Guidelines
What's new
Browser Support Policy
5.0.4.rh83 Release notes
FAQ
Guides index
User guide
Web Services
Contact
Legal
This site requires JavaScript to be enabled to function correctly, please enable it.
[patch]
Backport HSP patches from pulseaudio upstream
bluetooth-hsp.patch (text/plain), 98.05 KB, created by
Mauro Carvalho Chehab
on 2014-10-22 02:04:52 UTC
(
hide
)
Description:
Backport HSP patches from pulseaudio upstream
Filename:
MIME Type:
Creator:
Mauro Carvalho Chehab
Created:
2014-10-22 02:04:52 UTC
Size:
98.05 KB
patch
obsolete
>commit fd1e930750c7cc20006decd162a44798cec9508e >Author: Mauro Carvalho Chehab <mchehab@osg.samsung.com> >Date: Tue Oct 21 20:42:40 2014 -0200 > > Add support for Bluetooth HSP > > Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com> > >diff --git a/bluetooth-hsp-support.patch b/bluetooth-hsp-support.patch >new file mode 100644 >index 000000000000..dee16babf179 >--- /dev/null >+++ b/bluetooth-hsp-support.patch >@@ -0,0 +1,2385 @@ >+commit a67421f4cfcf6269b2a30a07f8da47b9880c730f >+Author: Mauro Carvalho Chehab <mchehab@osg.samsung.com> >+Date: Tue Oct 21 20:34:47 2014 -0200 >+ >+Backports the following patches: >+ >+0df4d56 bluetooth: Move stuff to pa_bluetooth_transport_put/unlink() >+380a7fc bluetooth: bluez4: Add profile name to sinks and sources >+eb108b3 bluetooth: Allow policy module to pick 'off' profile >+7fac520 bluetooth: Switch transport state to idle in case of HUP >+75bf00a bluetooth: Implement org.ofono.HandsfreeAudioAgent.NewConnection() >+f7c7cd1 bluetooth: Implement org.ofono.HandsfreeAudioAgent.Release() >+b1a09eb bluetooth: Handle CardRemoved signal >+8f838b1 bluetooth: Handle CardAdded signal >+0e7f303 bluetooth: Track oFono service >+c4c4de5 bluetooth: Implement transport release for hf_audio_agent transports >+8dd4aa1 bluetooth: Implement transport acquire for hf_audio_agent transports >+c098665 bluetooth: Parse HandsfreeAudioCard properties >+a5a0506 bluetooth: List HandsfreeAudioCard objects from oFono >+374c28a bluetooth: Register/Unregister Handsfree Audio Agent with oFono >+d7a8ccc bluetooth: Create hf_dbus_send_and_add_to_pending() for oFono backend >+98d3d85 bluetooth: Monitor D-Bus signals >+f508b34 bluetooth: Add discovery to pa_bluetooth_backend_new >+5c2ed8a bluetooth: Only create backend instance once objects are listed >+1918034 bluetooth: Add pa_bluetooth_transport_unlink >+4e041f7 bluetooth: Add pa_bluetooth_transport_set_state >+638d0a5 bluetooth: Always initialize profile->available >+6d88a13 bluetooth: Create oFono backend >+dca5d07 bluetooth: Create NULL backend >+2198048 bluetooth: Add BlueZ 5 headset profile names in policy module >+bdef2db bluetooth: Assert transport has a matching profile >+1f0de01 bluetooth: Add basic support for HEADSET profiles >+15ae55e bluetooth: Refactor POLLHUP handling >+de0d803 bluetooth: Refactor device validity management >+a61d065 bluetooth: Add "valid" flag to pa_bluetooth_adapter >+5881368 bluetooth: Notify the main thread of a stream fd HUP >+01feb3c bluetooth: Rename variable to improve code readability >+09933e5 bluetooth: Change BlueZ 5 card profile name from a2dp to a2dp_sink >+7fe34cf bluetooth: Fix lines going over column 128 >+3af39c6 bluetooth: Fix a copy-paste error in log message >+be9672b bluetooth: Remove redundant assignments >+305409c Fix a few "it's -> its" typos >+3f21c21 bluetooth: Fix timing to count based on decoded data >+4a5f48e bluetooth: Don't abort on SBC decoding error >+ >+Those adds support for Headsets on bluetoooth. >+ >+diff --git a/configure.ac b/configure.ac >+index 4854711..4d0ba74 100644 >+--- a/configure.ac >++++ b/configure.ac >+@@ -1022,6 +1022,21 @@ AS_IF([test "x$HAVE_BLUEZ_4" = "x1" || test "x$HAVE_BLUEZ_5" = "x1"], HAVE_BLUEZ >+ AC_SUBST(HAVE_BLUEZ) >+ AM_CONDITIONAL([HAVE_BLUEZ], [test "x$HAVE_BLUEZ" = x1]) >+ >++## Bluetooth Headset profiles backend ## >++ >++AC_ARG_WITH(bluetooth_headset_backend, >++ AS_HELP_STRING([--with-bluetooth-headset-backend=<ofono|null>],[Backend for Bluetooth headset profiles (ofono)])) >++if test -z "$with_bluetooth_headset_backend" ; then >++ BLUETOOTH_HEADSET_BACKEND=ofono >++else >++ BLUETOOTH_HEADSET_BACKEND=$with_bluetooth_headset_backend >++fi >++ >++AS_IF([test "x$BLUETOOTH_HEADSET_BACKEND" != "xofono && "test "x$BLUETOOTH_HEADSET_BACKEND" != "xnull"], >++ [AC_MSG_ERROR([*** Invalid Bluetooth Headset backend])]) >++ >++AC_SUBST(BLUETOOTH_HEADSET_BACKEND) >++ >+ #### UDEV support (optional) #### >+ >+ AC_ARG_ENABLE([udev], >+@@ -1485,6 +1500,7 @@ echo " >+ Enable D-Bus: ${ENABLE_DBUS} >+ Enable BlueZ 4: ${ENABLE_BLUEZ_4} >+ Enable BlueZ 5: ${ENABLE_BLUEZ_5} >++ headset backed: ${BLUETOOTH_HEADSET_BACKEND} >+ Enable udev: ${ENABLE_UDEV} >+ Enable HAL->udev compat: ${ENABLE_HAL_COMPAT} >+ Enable systemd login: ${ENABLE_SYSTEMD} >+diff --git a/src/Makefile.am b/src/Makefile.am >+index 857fda3..aab81d8 100644 >+--- a/src/Makefile.am >++++ b/src/Makefile.am >+@@ -2070,7 +2070,8 @@ module_bluez4_device_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) $(SBC_CFLAGS) >+ libbluez5_util_la_SOURCES = \ >+ modules/bluetooth/bluez5-util.c \ >+ modules/bluetooth/bluez5-util.h \ >+- modules/bluetooth/a2dp-codecs.h >++ modules/bluetooth/a2dp-codecs.h \ >++ modules/bluetooth/backend-@BLUETOOTH_HEADSET_BACKEND@.c >+ libbluez5_util_la_LDFLAGS = -avoid-version >+ libbluez5_util_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS) >+ libbluez5_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) >+diff --git a/src/modules/bluetooth/backend-null.c b/src/modules/bluetooth/backend-null.c >+new file mode 100644 >+index 0000000..6e621a7 >+--- /dev/null >++++ b/src/modules/bluetooth/backend-null.c >+@@ -0,0 +1,37 @@ >++/*** >++ This file is part of PulseAudio. >++ >++ Copyright 2013 João 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.1 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/log.h> >++ >++#include "bluez5-util.h" >++ >++pa_bluetooth_backend *pa_bluetooth_backend_new(pa_core *c, pa_bluetooth_discovery *y) { >++ pa_log_debug("Bluetooth Headset Backend API support disabled"); >++ return NULL; >++} >++ >++void pa_bluetooth_backend_free(pa_bluetooth_backend *b) { >++ /* Nothing to do here */ >++} >+diff --git a/src/modules/bluetooth/backend-ofono.c b/src/modules/bluetooth/backend-ofono.c >+new file mode 100644 >+index 0000000..ba10ed6 >+--- /dev/null >++++ b/src/modules/bluetooth/backend-ofono.c >+@@ -0,0 +1,661 @@ >++/*** >++ This file is part of PulseAudio. >++ >++ Copyright 2013 João 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.1 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 <errno.h> >++#include <poll.h> >++ >++#include <pulsecore/core-util.h> >++#include <pulsecore/dbus-shared.h> >++#include <pulsecore/shared.h> >++#include <pulsecore/core-error.h> >++ >++#include "bluez5-util.h" >++ >++#define HFP_AUDIO_CODEC_CVSD 0x01 >++#define HFP_AUDIO_CODEC_MSBC 0x02 >++ >++#define OFONO_SERVICE "org.ofono" >++#define HF_AUDIO_AGENT_INTERFACE OFONO_SERVICE ".HandsfreeAudioAgent" >++#define HF_AUDIO_MANAGER_INTERFACE OFONO_SERVICE ".HandsfreeAudioManager" >++ >++#define HF_AUDIO_AGENT_PATH "/HandsfreeAudioAgent" >++ >++#define HF_AUDIO_AGENT_XML \ >++ DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ >++ "<node>" \ >++ " <interface name=\"org.freedesktop.DBus.Introspectable\">" \ >++ " <method name=\"Introspect\">" \ >++ " <arg direction=\"out\" type=\"s\" />" \ >++ " </method>" \ >++ " </interface>" \ >++ " <interface name=\"org.ofono.HandsfreeAudioAgent\">" \ >++ " <method name=\"Release\">" \ >++ " </method>" \ >++ " <method name=\"NewConnection\">" \ >++ " <arg direction=\"in\" type=\"o\" name=\"card_path\" />" \ >++ " <arg direction=\"in\" type=\"h\" name=\"sco_fd\" />" \ >++ " <arg direction=\"in\" type=\"y\" name=\"codec\" />" \ >++ " </method>" \ >++ " </interface>" \ >++ "</node>" >++ >++struct hf_audio_card { >++ pa_bluetooth_backend *backend; >++ char *path; >++ char *remote_address; >++ char *local_address; >++ >++ int fd; >++ uint8_t codec; >++ >++ pa_bluetooth_transport *transport; >++}; >++ >++struct pa_bluetooth_backend { >++ pa_core *core; >++ pa_bluetooth_discovery *discovery; >++ pa_dbus_connection *connection; >++ pa_hashmap *cards; >++ char *ofono_bus_id; >++ >++ PA_LLIST_HEAD(pa_dbus_pending, pending); >++}; >++ >++static pa_dbus_pending* hf_dbus_send_and_add_to_pending(pa_bluetooth_backend *backend, DBusMessage *m, >++ DBusPendingCallNotifyFunction func, void *call_data) { >++ pa_dbus_pending *p; >++ DBusPendingCall *call; >++ >++ pa_assert(backend); >++ pa_assert(m); >++ >++ pa_assert_se(dbus_connection_send_with_reply(pa_dbus_connection_get(backend->connection), m, &call, -1)); >++ >++ p = pa_dbus_pending_new(pa_dbus_connection_get(backend->connection), m, call, backend, call_data); >++ PA_LLIST_PREPEND(pa_dbus_pending, backend->pending, p); >++ dbus_pending_call_set_notify(call, func, p, NULL); >++ >++ return p; >++} >++ >++static struct hf_audio_card *hf_audio_card_new(pa_bluetooth_backend *backend, const char *path) { >++ struct hf_audio_card *card = pa_xnew0(struct hf_audio_card, 1); >++ >++ card->path = pa_xstrdup(path); >++ card->backend = backend; >++ card->fd = -1; >++ >++ return card; >++} >++ >++static void hf_audio_card_free(struct hf_audio_card *card) { >++ pa_assert(card); >++ >++ if (card->transport) >++ pa_bluetooth_transport_free(card->transport); >++ >++ pa_xfree(card->path); >++ pa_xfree(card->remote_address); >++ pa_xfree(card->local_address); >++ pa_xfree(card); >++} >++ >++static int socket_accept(int sock) >++{ >++ char c; >++ struct pollfd pfd; >++ >++ if (sock < 0) >++ return -ENOTCONN; >++ >++ memset(&pfd, 0, sizeof(pfd)); >++ pfd.fd = sock; >++ pfd.events = POLLOUT; >++ >++ if (poll(&pfd, 1, 0) < 0) >++ return -errno; >++ >++ /* >++ * If socket already writable then it is not in defer setup state, >++ * otherwise it needs to be read to authorize the connection. >++ */ >++ if ((pfd.revents & POLLOUT)) >++ return 0; >++ >++ /* Enable socket by reading 1 byte */ >++ if (read(sock, &c, 1) < 0) >++ return -errno; >++ >++ return 0; >++} >++ >++static int hf_audio_agent_transport_acquire(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu) { >++ struct hf_audio_card *card = t->userdata; >++ int err; >++ >++ pa_assert(card); >++ >++ if (!optional) { >++ DBusMessage *m; >++ >++ pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, "org.ofono.HandsfreeAudioCard", "Connect")); >++ pa_assert_se(dbus_connection_send(pa_dbus_connection_get(card->backend->connection), m, NULL)); >++ >++ return -1; >++ } >++ >++ /* The correct block size should take into account the SCO MTU from >++ * the Bluetooth adapter and (for adapters in the USB bus) the MxPS >++ * value from the Isoc USB endpoint in use by btusb and should be >++ * made available to userspace by the Bluetooth kernel subsystem. >++ * Meanwhile the empiric value 48 will be used. */ >++ if (imtu) >++ *imtu = 48; >++ if (omtu) >++ *omtu = 48; >++ >++ t->codec = card->codec; >++ >++ err = socket_accept(card->fd); >++ if (err < 0) { >++ pa_log_error("Deferred setup failed on fd %d: %s", card->fd, pa_cstrerror(-err)); >++ return -1; >++ } >++ >++ return card->fd; >++} >++ >++static void hf_audio_agent_transport_release(pa_bluetooth_transport *t) { >++ struct hf_audio_card *card = t->userdata; >++ >++ pa_assert(card); >++ >++ if (t->state <= PA_BLUETOOTH_TRANSPORT_STATE_IDLE) { >++ pa_log_info("Transport %s already released", t->path); >++ return; >++ } >++ >++ if (card->fd < 0) >++ return; >++ >++ /* shutdown to make sure connection is dropped immediately */ >++ shutdown(card->fd, SHUT_RDWR); >++ close(card->fd); >++ card->fd = -1; >++} >++ >++static void hf_audio_agent_card_found(pa_bluetooth_backend *backend, const char *path, DBusMessageIter *props_i) { >++ DBusMessageIter i, value_i; >++ const char *key, *value; >++ struct hf_audio_card *card; >++ pa_bluetooth_device *d; >++ >++ pa_assert(backend); >++ pa_assert(path); >++ pa_assert(props_i); >++ >++ pa_log_debug("New HF card found: %s", path); >++ >++ card = hf_audio_card_new(backend, path); >++ >++ while (dbus_message_iter_get_arg_type(props_i) != DBUS_TYPE_INVALID) { >++ char c; >++ >++ dbus_message_iter_recurse(props_i, &i); >++ >++ dbus_message_iter_get_basic(&i, &key); >++ dbus_message_iter_next(&i); >++ dbus_message_iter_recurse(&i, &value_i); >++ >++ if ((c = dbus_message_iter_get_arg_type(&value_i)) != DBUS_TYPE_STRING) { >++ pa_log_error("Invalid properties for %s: expected 's', received '%c'", path, c); >++ goto fail; >++ } >++ >++ dbus_message_iter_get_basic(&value_i, &value); >++ >++ if (pa_streq(key, "RemoteAddress")) { >++ pa_xfree(card->remote_address); >++ card->remote_address = pa_xstrdup(value); >++ } else if (pa_streq(key, "LocalAddress")) { >++ pa_xfree(card->local_address); >++ card->local_address = pa_xstrdup(value); >++ } >++ >++ pa_log_debug("%s: %s", key, value); >++ >++ dbus_message_iter_next(props_i); >++ } >++ >++ d = pa_bluetooth_discovery_get_device_by_address(backend->discovery, card->remote_address, card->local_address); >++ if (!d) { >++ pa_log_error("Device doesnt exist for %s", path); >++ goto fail; >++ } >++ >++ card->transport = pa_bluetooth_transport_new(d, backend->ofono_bus_id, path, PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY, NULL, 0); >++ card->transport->acquire = hf_audio_agent_transport_acquire; >++ card->transport->release = hf_audio_agent_transport_release; >++ card->transport->userdata = card; >++ >++ pa_bluetooth_transport_put(card->transport); >++ pa_hashmap_put(backend->cards, card->path, card); >++ >++ return; >++ >++fail: >++ hf_audio_card_free(card); >++} >++ >++static void hf_audio_agent_card_removed(pa_bluetooth_backend *backend, const char *path) { >++ struct hf_audio_card *card; >++ >++ pa_assert(backend); >++ pa_assert(path); >++ >++ pa_log_debug("HF card removed: %s", path); >++ >++ card = pa_hashmap_remove(backend->cards, path); >++ if (!card) >++ return; >++ >++ hf_audio_card_free(card); >++} >++ >++static void hf_audio_agent_get_cards_reply(DBusPendingCall *pending, void *userdata) { >++ DBusMessage *r; >++ pa_dbus_pending *p; >++ pa_bluetooth_backend *backend; >++ DBusMessageIter i, array_i, struct_i, props_i; >++ >++ pa_assert_se(p = userdata); >++ pa_assert_se(backend = 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("Failed to get a list of handsfree audio cards from ofono: %s: %s", >++ dbus_message_get_error_name(r), pa_dbus_get_error_message(r)); >++ goto finish; >++ } >++ >++ if (!dbus_message_iter_init(r, &i) || !pa_streq(dbus_message_get_signature(r), "a(oa{sv})")) { >++ pa_log_error("Invalid arguments in GetCards() reply"); >++ goto finish; >++ } >++ >++ dbus_message_iter_recurse(&i, &array_i); >++ while (dbus_message_iter_get_arg_type(&array_i) != DBUS_TYPE_INVALID) { >++ const char *path; >++ >++ dbus_message_iter_recurse(&array_i, &struct_i); >++ dbus_message_iter_get_basic(&struct_i, &path); >++ dbus_message_iter_next(&struct_i); >++ >++ dbus_message_iter_recurse(&struct_i, &props_i); >++ >++ hf_audio_agent_card_found(backend, path, &props_i); >++ >++ dbus_message_iter_next(&array_i); >++ } >++ >++finish: >++ dbus_message_unref(r); >++ >++ PA_LLIST_REMOVE(pa_dbus_pending, backend->pending, p); >++ pa_dbus_pending_free(p); >++} >++ >++static void hf_audio_agent_get_cards(pa_bluetooth_backend *hf) { >++ DBusMessage *m; >++ >++ pa_assert(hf); >++ >++ pa_assert_se(m = dbus_message_new_method_call(OFONO_SERVICE, "/", HF_AUDIO_MANAGER_INTERFACE, "GetCards")); >++ hf_dbus_send_and_add_to_pending(hf, m, hf_audio_agent_get_cards_reply, NULL); >++} >++ >++static void hf_audio_agent_register_reply(DBusPendingCall *pending, void *userdata) { >++ DBusMessage *r; >++ pa_dbus_pending *p; >++ pa_bluetooth_backend *backend; >++ >++ pa_assert_se(p = userdata); >++ pa_assert_se(backend = 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("Failed to register as a handsfree audio agent with ofono: %s: %s", >++ dbus_message_get_error_name(r), pa_dbus_get_error_message(r)); >++ goto finish; >++ } >++ >++ backend->ofono_bus_id = pa_xstrdup(dbus_message_get_sender(r)); >++ >++ hf_audio_agent_get_cards(backend); >++ >++finish: >++ dbus_message_unref(r); >++ >++ PA_LLIST_REMOVE(pa_dbus_pending, backend->pending, p); >++ pa_dbus_pending_free(p); >++} >++ >++static void hf_audio_agent_register(pa_bluetooth_backend *hf) { >++ DBusMessage *m; >++ uint8_t codecs[2]; >++ const uint8_t *pcodecs = codecs; >++ int ncodecs = 0; >++ const char *path = HF_AUDIO_AGENT_PATH; >++ >++ pa_assert(hf); >++ >++ pa_assert_se(m = dbus_message_new_method_call(OFONO_SERVICE, "/", HF_AUDIO_MANAGER_INTERFACE, "Register")); >++ >++ codecs[ncodecs++] = HFP_AUDIO_CODEC_CVSD; >++ >++ pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &pcodecs, ncodecs, >++ DBUS_TYPE_INVALID)); >++ >++ hf_dbus_send_and_add_to_pending(hf, m, hf_audio_agent_register_reply, NULL); >++} >++ >++static void hf_audio_agent_unregister(pa_bluetooth_backend *backend) { >++ DBusMessage *m; >++ const char *path = HF_AUDIO_AGENT_PATH; >++ >++ pa_assert(backend); >++ pa_assert(backend->connection); >++ >++ if (backend->ofono_bus_id) { >++ pa_assert_se(m = dbus_message_new_method_call(backend->ofono_bus_id, "/", HF_AUDIO_MANAGER_INTERFACE, "Unregister")); >++ pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)); >++ pa_assert_se(dbus_connection_send(pa_dbus_connection_get(backend->connection), m, NULL)); >++ >++ pa_xfree(backend->ofono_bus_id); >++ backend->ofono_bus_id = NULL; >++ } >++} >++ >++static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *data) { >++ const char *sender; >++ DBusError err; >++ pa_bluetooth_backend *backend = data; >++ >++ pa_assert(bus); >++ pa_assert(m); >++ pa_assert(backend); >++ >++ sender = dbus_message_get_sender(m); >++ if (!pa_safe_streq(backend->ofono_bus_id, sender) && !pa_streq("org.freedesktop.DBus", sender)) >++ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; >++ >++ dbus_error_init(&err); >++ >++ if (dbus_message_is_signal(m, "org.freedesktop.DBus", "NameOwnerChanged")) { >++ const char *name, *old_owner, *new_owner; >++ >++ if (!dbus_message_get_args(m, &err, >++ DBUS_TYPE_STRING, &name, >++ DBUS_TYPE_STRING, &old_owner, >++ DBUS_TYPE_STRING, &new_owner, >++ DBUS_TYPE_INVALID)) { >++ pa_log_error("Failed to parse org.freedesktop.DBus.NameOwnerChanged: %s", err.message); >++ goto fail; >++ } >++ >++ if (pa_streq(name, OFONO_SERVICE)) { >++ >++ if (old_owner && *old_owner) { >++ pa_log_debug("oFono disappeared"); >++ >++ pa_hashmap_remove_all(backend->cards); >++ >++ pa_xfree(backend->ofono_bus_id); >++ backend->ofono_bus_id = NULL; >++ } >++ >++ if (new_owner && *new_owner) { >++ pa_log_debug("oFono appeared"); >++ hf_audio_agent_register(backend); >++ } >++ } >++ >++ } else if (dbus_message_is_signal(m, "org.ofono.HandsfreeAudioManager", "CardAdded")) { >++ const char *p; >++ DBusMessageIter arg_i, props_i; >++ >++ if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "oa{sv}")) { >++ pa_log_error("Failed to parse org.ofono.HandsfreeAudioManager.CardAdded"); >++ goto fail; >++ } >++ >++ dbus_message_iter_get_basic(&arg_i, &p); >++ >++ pa_assert_se(dbus_message_iter_next(&arg_i)); >++ pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_ARRAY); >++ >++ dbus_message_iter_recurse(&arg_i, &props_i); >++ >++ hf_audio_agent_card_found(backend, p, &props_i); >++ } else if (dbus_message_is_signal(m, "org.ofono.HandsfreeAudioManager", "CardRemoved")) { >++ const char *p; >++ >++ if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &p, DBUS_TYPE_INVALID)) { >++ pa_log_error("Failed to parse org.ofono.HandsfreeAudioManager.CardRemoved: %s", err.message); >++ goto fail; >++ } >++ >++ hf_audio_agent_card_removed(backend, p); >++ } >++ >++fail: >++ dbus_error_free(&err); >++ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; >++} >++ >++static DBusMessage *hf_audio_agent_release(DBusConnection *c, DBusMessage *m, void *data) { >++ DBusMessage *r; >++ const char *sender; >++ pa_bluetooth_backend *backend = data; >++ >++ pa_assert(backend); >++ >++ sender = dbus_message_get_sender(m); >++ if (!pa_safe_streq(backend->ofono_bus_id, sender)) { >++ pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.NotAllowed", "Operation is not allowed by this sender")); >++ return r; >++ } >++ >++ pa_log_debug("HF audio agent has been unregistered by oFono (%s)", backend->ofono_bus_id); >++ >++ pa_hashmap_remove_all(backend->cards); >++ >++ pa_xfree(backend->ofono_bus_id); >++ backend->ofono_bus_id = NULL; >++ >++ pa_assert_se(r = dbus_message_new_method_return(m)); >++ >++ return r; >++} >++ >++static DBusMessage *hf_audio_agent_new_connection(DBusConnection *c, DBusMessage *m, void *data) { >++ DBusMessage *r; >++ const char *sender, *path; >++ int fd; >++ uint8_t codec; >++ struct hf_audio_card *card; >++ pa_bluetooth_backend *backend = data; >++ >++ pa_assert(backend); >++ >++ sender = dbus_message_get_sender(m); >++ if (!pa_safe_streq(backend->ofono_bus_id, sender)) { >++ pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.NotAllowed", "Operation is not allowed by this sender")); >++ return r; >++ } >++ >++ if (dbus_message_get_args(m, NULL, >++ DBUS_TYPE_OBJECT_PATH, &path, >++ DBUS_TYPE_UNIX_FD, &fd, >++ DBUS_TYPE_BYTE, &codec, >++ DBUS_TYPE_INVALID) == FALSE) { >++ pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.InvalidArguments", "Invalid arguments in method call")); >++ return r; >++ } >++ >++ card = pa_hashmap_get(backend->cards, path); >++ >++ if (!card || codec != HFP_AUDIO_CODEC_CVSD || card->transport->state == PA_BLUETOOTH_TRANSPORT_STATE_PLAYING) { >++ pa_log_warn("New audio connection invalid arguments (path=%s fd=%d, codec=%d)", path, fd, codec); >++ pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.InvalidArguments", "Invalid arguments in method call")); >++ return r; >++ } >++ >++ pa_log_debug("New audio connection on card %s (fd=%d, codec=%d)", path, fd, codec); >++ >++ card->fd = fd; >++ card->transport->codec = codec; >++ >++ pa_bluetooth_transport_set_state(card->transport, PA_BLUETOOTH_TRANSPORT_STATE_PLAYING); >++ >++ pa_assert_se(r = dbus_message_new_method_return(m)); >++ >++ return r; >++} >++ >++static DBusHandlerResult hf_audio_agent_handler(DBusConnection *c, DBusMessage *m, void *data) { >++ pa_bluetooth_backend *backend = data; >++ DBusMessage *r = NULL; >++ const char *path, *interface, *member; >++ >++ pa_assert(backend); >++ >++ path = dbus_message_get_path(m); >++ interface = dbus_message_get_interface(m); >++ member = dbus_message_get_member(m); >++ >++ if (!pa_streq(path, HF_AUDIO_AGENT_PATH)) >++ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; >++ >++ pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member); >++ >++ if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) { >++ const char *xml = HF_AUDIO_AGENT_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, HF_AUDIO_AGENT_INTERFACE, "NewConnection")) >++ r = hf_audio_agent_new_connection(c, m, data); >++ else if (dbus_message_is_method_call(m, HF_AUDIO_AGENT_INTERFACE, "Release")) >++ r = hf_audio_agent_release(c, m, data); >++ else >++ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; >++ >++ if (r) { >++ pa_assert_se(dbus_connection_send(pa_dbus_connection_get(backend->connection), r, NULL)); >++ dbus_message_unref(r); >++ } >++ >++ return DBUS_HANDLER_RESULT_HANDLED; >++} >++ >++pa_bluetooth_backend *pa_bluetooth_backend_new(pa_core *c, pa_bluetooth_discovery *y) { >++ pa_bluetooth_backend *backend; >++ DBusError err; >++ static const DBusObjectPathVTable vtable_hf_audio_agent = { >++ .message_function = hf_audio_agent_handler, >++ }; >++ >++ pa_assert(c); >++ >++ backend = pa_xnew0(pa_bluetooth_backend, 1); >++ backend->core = c; >++ backend->discovery = y; >++ backend->cards = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, >++ (pa_free_cb_t) hf_audio_card_free); >++ >++ dbus_error_init(&err); >++ >++ if (!(backend->connection = pa_dbus_bus_get(c, DBUS_BUS_SYSTEM, &err))) { >++ pa_log("Failed to get D-Bus connection: %s", err.message); >++ dbus_error_free(&err); >++ pa_xfree(backend); >++ return NULL; >++ } >++ >++ /* dynamic detection of handsfree audio cards */ >++ if (!dbus_connection_add_filter(pa_dbus_connection_get(backend->connection), filter_cb, backend, NULL)) { >++ pa_log_error("Failed to add filter function"); >++ pa_dbus_connection_unref(backend->connection); >++ pa_xfree(backend); >++ return NULL; >++ } >++ >++ if (pa_dbus_add_matches(pa_dbus_connection_get(backend->connection), &err, >++ "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'," >++ "arg0='" OFONO_SERVICE "'", >++ "type='signal',sender='" OFONO_SERVICE "',interface='" HF_AUDIO_MANAGER_INTERFACE "',member='CardAdded'", >++ "type='signal',sender='" OFONO_SERVICE "',interface='" HF_AUDIO_MANAGER_INTERFACE "',member='CardRemoved'", >++ NULL) < 0) { >++ pa_log("Failed to add oFono D-Bus matches: %s", err.message); >++ dbus_connection_remove_filter(pa_dbus_connection_get(backend->connection), filter_cb, backend); >++ pa_dbus_connection_unref(backend->connection); >++ pa_xfree(backend); >++ return NULL; >++ } >++ >++ pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(backend->connection), HF_AUDIO_AGENT_PATH, >++ &vtable_hf_audio_agent, backend)); >++ >++ hf_audio_agent_register(backend); >++ >++ return backend; >++} >++ >++void pa_bluetooth_backend_free(pa_bluetooth_backend *backend) { >++ pa_assert(backend); >++ >++ pa_dbus_free_pending_list(&backend->pending); >++ >++ hf_audio_agent_unregister(backend); >++ >++ dbus_connection_unregister_object_path(pa_dbus_connection_get(backend->connection), HF_AUDIO_AGENT_PATH); >++ >++ pa_dbus_remove_matches(pa_dbus_connection_get(backend->connection), >++ "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'," >++ "arg0='" OFONO_SERVICE "'", >++ "type='signal',sender='" OFONO_SERVICE "',interface='" HF_AUDIO_MANAGER_INTERFACE "',member='CardAdded'", >++ "type='signal',sender='" OFONO_SERVICE "',interface='" HF_AUDIO_MANAGER_INTERFACE "',member='CardRemoved'", >++ NULL); >++ >++ dbus_connection_remove_filter(pa_dbus_connection_get(backend->connection), filter_cb, backend); >++ >++ pa_dbus_connection_unref(backend->connection); >++ >++ pa_hashmap_free(backend->cards); >++ >++ pa_xfree(backend); >++} >+diff --git a/src/modules/bluetooth/bluez5-util.c b/src/modules/bluetooth/bluez5-util.c >+index 7eecc18..1ee2f33 100644 >+--- a/src/modules/bluetooth/bluez5-util.c >++++ b/src/modules/bluetooth/bluez5-util.c >+@@ -87,6 +87,7 @@ struct pa_bluetooth_discovery { >+ pa_hashmap *devices; >+ pa_hashmap *transports; >+ >++ pa_bluetooth_backend *backend; >+ PA_LLIST_HEAD(pa_dbus_pending, pending); >+ }; >+ >+@@ -148,8 +149,6 @@ pa_bluetooth_transport *pa_bluetooth_transport_new(pa_bluetooth_device *d, const >+ memcpy(t->config, config, size); >+ } >+ >+- pa_assert_se(pa_hashmap_put(d->discovery->transports, t->path, t) >= 0); >+- >+ return t; >+ } >+ >+@@ -166,7 +165,7 @@ static const char *transport_state_to_string(pa_bluetooth_transport_state_t stat >+ return "invalid"; >+ } >+ >+-static void transport_state_changed(pa_bluetooth_transport *t, pa_bluetooth_transport_state_t state) { >++void pa_bluetooth_transport_set_state(pa_bluetooth_transport *t, pa_bluetooth_transport_state_t state) { >+ bool old_any_connected; >+ >+ pa_assert(t); >+@@ -180,8 +179,6 @@ static void transport_state_changed(pa_bluetooth_transport *t, pa_bluetooth_tran >+ 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); >+ >+@@ -190,13 +187,26 @@ static void transport_state_changed(pa_bluetooth_transport *t, pa_bluetooth_tran >+ } >+ >+ void pa_bluetooth_transport_put(pa_bluetooth_transport *t) { >+- transport_state_changed(t, PA_BLUETOOTH_TRANSPORT_STATE_IDLE); >++ pa_assert(t); >++ >++ t->device->transports[t->profile] = t; >++ pa_assert_se(pa_hashmap_put(t->device->discovery->transports, t->path, t) >= 0); >++ pa_bluetooth_transport_set_state(t, PA_BLUETOOTH_TRANSPORT_STATE_IDLE); >+ } >+ >+-void pa_bluetooth_transport_free(pa_bluetooth_transport *t) { >++void pa_bluetooth_transport_unlink(pa_bluetooth_transport *t) { >+ pa_assert(t); >+ >++ pa_bluetooth_transport_set_state(t, PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED); >+ pa_hashmap_remove(t->device->discovery->transports, t->path); >++ t->device->transports[t->profile] = NULL; >++} >++ >++void pa_bluetooth_transport_free(pa_bluetooth_transport *t) { >++ pa_assert(t); >++ >++ pa_bluetooth_transport_unlink(t); >++ >+ pa_xfree(t->owner); >+ pa_xfree(t->path); >+ pa_xfree(t->config); >+@@ -278,7 +288,7 @@ bool pa_bluetooth_device_any_transport_connected(const pa_bluetooth_device *d) { >+ >+ pa_assert(d); >+ >+- if (d->device_info_valid != 1) >++ if (!d->valid) >+ return false; >+ >+ for (i = 0; i < PA_BLUETOOTH_PROFILE_COUNT; i++) >+@@ -327,7 +337,7 @@ static void parse_transport_property(pa_bluetooth_transport *t, DBusMessageIter >+ return; >+ } >+ >+- transport_state_changed(t, state); >++ pa_bluetooth_transport_set_state(t, state); >+ } >+ >+ break; >+@@ -378,9 +388,8 @@ pa_bluetooth_device* pa_bluetooth_discovery_get_device_by_path(pa_bluetooth_disc >+ pa_assert(PA_REFCNT_VALUE(y) > 0); >+ pa_assert(path); >+ >+- if ((d = pa_hashmap_get(y->devices, path))) >+- if (d->device_info_valid == 1) >+- return d; >++ if ((d = pa_hashmap_get(y->devices, path)) && d->valid) >++ return d; >+ >+ return NULL; >+ } >+@@ -395,7 +404,7 @@ pa_bluetooth_device* pa_bluetooth_discovery_get_device_by_address(pa_bluetooth_d >+ pa_assert(local); >+ >+ while ((d = pa_hashmap_iterate(y->devices, &state, NULL))) >+- if (d->device_info_valid == 1 && pa_streq(d->address, remote) && pa_streq(d->adapter->address, local)) >++ if (d->valid && pa_streq(d->address, remote) && pa_streq(d->adapter->address, local)) >+ return d; >+ >+ return NULL; >+@@ -412,15 +421,12 @@ static void device_free(pa_bluetooth_device *d) { >+ if (!(t = d->transports[i])) >+ continue; >+ >+- transport_state_changed(t, PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED); >+ pa_bluetooth_transport_free(t); >+ } >+ >+ if (d->uuids) >+ pa_hashmap_free(d->uuids); >+ >+- d->discovery = NULL; >+- d->adapter = NULL; >+ pa_xfree(d->path); >+ pa_xfree(d->alias); >+ pa_xfree(d->address); >+@@ -439,22 +445,54 @@ static void device_remove(pa_bluetooth_discovery *y, const char *path) { >+ } >+ } >+ >+-static void set_device_info_valid(pa_bluetooth_device *device, int valid) { >++static void device_set_valid(pa_bluetooth_device *device, bool valid) { >+ bool old_any_connected; >+ >+ pa_assert(device); >+- pa_assert(valid == -1 || valid == 0 || valid == 1); >+ >+- if (valid == device->device_info_valid) >++ if (valid == device->valid) >+ return; >+ >+ old_any_connected = pa_bluetooth_device_any_transport_connected(device); >+- device->device_info_valid = valid; >++ device->valid = valid; >+ >+ if (pa_bluetooth_device_any_transport_connected(device) != old_any_connected) >+ pa_hook_fire(&device->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED], device); >+ } >+ >++static void device_update_valid(pa_bluetooth_device *d) { >++ pa_assert(d); >++ >++ if (!d->properties_received) { >++ pa_assert(!d->valid); >++ return; >++ } >++ >++ /* Check if mandatory properties are set. */ >++ if (!d->address || !d->adapter_path || !d->alias) { >++ device_set_valid(d, false); >++ return; >++ } >++ >++ if (!d->adapter || !d->adapter->valid) { >++ device_set_valid(d, false); >++ return; >++ } >++ >++ device_set_valid(d, true); >++} >++ >++static void device_set_adapter(pa_bluetooth_device *device, pa_bluetooth_adapter *adapter) { >++ pa_assert(device); >++ >++ if (adapter == device->adapter) >++ return; >++ >++ device->adapter = adapter; >++ >++ device_update_valid(device); >++} >++ >+ static pa_bluetooth_adapter* adapter_create(pa_bluetooth_discovery *y, const char *path) { >+ pa_bluetooth_adapter *a; >+ >+@@ -478,10 +516,8 @@ static void adapter_free(pa_bluetooth_adapter *a) { >+ pa_assert(a->discovery); >+ >+ PA_HASHMAP_FOREACH(d, a->discovery->devices, state) >+- if (d->adapter == a) { >+- set_device_info_valid(d, -1); >+- d->adapter = NULL; >+- } >++ if (d->adapter == a) >++ device_set_adapter(d, NULL); >+ >+ pa_xfree(a->path); >+ pa_xfree(a->address); >+@@ -499,7 +535,7 @@ static void adapter_remove(pa_bluetooth_discovery *y, const char *path) { >+ } >+ } >+ >+-static void parse_device_property(pa_bluetooth_device *d, DBusMessageIter *i, bool is_property_change) { >++static void parse_device_property(pa_bluetooth_device *d, DBusMessageIter *i) { >+ const char *key; >+ DBusMessageIter variant_i; >+ >+@@ -524,7 +560,7 @@ static void parse_device_property(pa_bluetooth_device *d, DBusMessageIter *i, bo >+ d->alias = pa_xstrdup(value); >+ pa_log_debug("%s: %s", key, value); >+ } else if (pa_streq(key, "Address")) { >+- if (is_property_change) { >++ if (d->properties_received) { >+ pa_log_warn("Device property 'Address' expected to be constant but changed for %s, ignoring", d->path); >+ return; >+ } >+@@ -547,7 +583,7 @@ static void parse_device_property(pa_bluetooth_device *d, DBusMessageIter *i, bo >+ >+ if (pa_streq(key, "Adapter")) { >+ >+- if (is_property_change) { >++ if (d->properties_received) { >+ pa_log_warn("Device property 'Adapter' expected to be constant but changed for %s, ignoring", d->path); >+ return; >+ } >+@@ -558,11 +594,6 @@ static void parse_device_property(pa_bluetooth_device *d, DBusMessageIter *i, bo >+ } >+ >+ d->adapter_path = pa_xstrdup(value); >+- >+- d->adapter = pa_hashmap_get(d->discovery->adapters, value); >+- if (!d->adapter) >+- pa_log_info("Device %s: 'Adapter' property references an unknown adapter %s.", d->path, value); >+- >+ pa_log_debug("%s: %s", key, value); >+ } >+ >+@@ -612,7 +643,7 @@ static void parse_device_property(pa_bluetooth_device *d, DBusMessageIter *i, bo >+ } >+ } >+ >+-static int parse_device_properties(pa_bluetooth_device *d, DBusMessageIter *i, bool is_property_change) { >++static void parse_device_properties(pa_bluetooth_device *d, DBusMessageIter *i) { >+ DBusMessageIter element_i; >+ >+ dbus_message_iter_recurse(i, &element_i); >+@@ -621,23 +652,16 @@ static int parse_device_properties(pa_bluetooth_device *d, DBusMessageIter *i, b >+ DBusMessageIter dict_i; >+ >+ dbus_message_iter_recurse(&element_i, &dict_i); >+- parse_device_property(d, &dict_i, is_property_change); >++ parse_device_property(d, &dict_i); >+ dbus_message_iter_next(&element_i); >+ } >+ >+- if (!d->address || !d->adapter_path || !d->alias) { >+- pa_log_error("Non-optional information missing for device %s", d->path); >+- set_device_info_valid(d, -1); >+- return -1; >+- } >+- >+- if (!is_property_change && d->adapter) >+- set_device_info_valid(d, 1); >++ if (!d->properties_received) { >++ d->properties_received = true; >+ >+- /* If d->adapter is NULL, device_info_valid will be left as 0, and updated >+- * after all interfaces have been parsed. */ >+- >+- return 0; >++ if (!d->address || !d->adapter_path || !d->alias) >++ pa_log_error("Non-optional information missing for device %s", d->path); >++ } >+ } >+ >+ static void parse_adapter_properties(pa_bluetooth_adapter *a, DBusMessageIter *i, bool is_property_change) { >+@@ -676,6 +700,7 @@ static void parse_adapter_properties(pa_bluetooth_adapter *a, DBusMessageIter *i >+ >+ dbus_message_iter_get_basic(&variant_i, &value); >+ a->address = pa_xstrdup(value); >++ a->valid = true; >+ } >+ >+ dbus_message_iter_next(&element_i); >+@@ -781,7 +806,7 @@ static void parse_interfaces_and_properties(pa_bluetooth_discovery *y, DBusMessa >+ pa_bluetooth_adapter *a; >+ >+ if ((a = pa_hashmap_get(y->adapters, path))) { >+- pa_log_error("Found duplicated D-Bus path for device %s", path); >++ pa_log_error("Found duplicated D-Bus path for adapter %s", path); >+ return; >+ } else >+ a = adapter_create(y, path); >+@@ -789,7 +814,8 @@ static void parse_interfaces_and_properties(pa_bluetooth_discovery *y, DBusMessa >+ pa_log_debug("Adapter %s found", path); >+ >+ parse_adapter_properties(a, &iface_i, false); >+- if (!a->address) >++ >++ if (!a->valid) >+ return; >+ >+ register_endpoint(y, path, A2DP_SOURCE_ENDPOINT, PA_BLUETOOTH_UUID_A2DP_SOURCE); >+@@ -798,7 +824,7 @@ static void parse_interfaces_and_properties(pa_bluetooth_discovery *y, DBusMessa >+ } else if (pa_streq(interface, BLUEZ_DEVICE_INTERFACE)) { >+ >+ if ((d = pa_hashmap_get(y->devices, path))) { >+- if (d->device_info_valid != 0) { >++ if (d->properties_received) { >+ pa_log_error("Found duplicated D-Bus path for device %s", path); >+ return; >+ } >+@@ -807,7 +833,7 @@ static void parse_interfaces_and_properties(pa_bluetooth_discovery *y, DBusMessa >+ >+ pa_log_debug("Device %s found", d->path); >+ >+- parse_device_properties(d, &iface_i, false); >++ parse_device_properties(d, &iface_i); >+ >+ } else >+ pa_log_debug("Unknown interface %s found, skipping", interface); >+@@ -816,16 +842,17 @@ static void parse_interfaces_and_properties(pa_bluetooth_discovery *y, DBusMessa >+ } >+ >+ PA_HASHMAP_FOREACH(d, y->devices, state) { >+- if (d->device_info_valid != 0) >+- continue; >++ if (d->properties_received && !d->tried_to_link_with_adapter) { >++ if (d->adapter_path) { >++ device_set_adapter(d, pa_hashmap_get(d->discovery->adapters, d->adapter_path)); >+ >+- if (!d->adapter && d->adapter_path) { >+- d->adapter = pa_hashmap_get(d->discovery->adapters, d->adapter_path); >+- if (!d->adapter || !d->adapter->address) { >+- pa_log_error("Device %s is child of nonexistent or corrupted adapter %s", d->path, d->adapter_path); >+- set_device_info_valid(d, -1); >+- } else >+- set_device_info_valid(d, 1); >++ if (!d->adapter) >++ pa_log("Device %s points to a nonexistent adapter %s.", d->path, d->adapter_path); >++ else if (!d->adapter->valid) >++ pa_log("Device %s points to an invalid adapter %s.", d->path, d->adapter_path); >++ } >++ >++ d->tried_to_link_with_adapter = true; >+ } >+ } >+ >+@@ -870,6 +897,9 @@ static void get_managed_objects_reply(DBusPendingCall *pending, void *userdata) >+ >+ y->objects_listed = true; >+ >++ if (!y->backend) >++ y->backend = pa_bluetooth_backend_new(y->core, y); >++ >+ finish: >+ dbus_message_unref(r); >+ >+@@ -922,6 +952,10 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us >+ pa_hashmap_remove_all(y->devices); >+ pa_hashmap_remove_all(y->adapters); >+ y->objects_listed = false; >++ if (y->backend) { >++ pa_bluetooth_backend_free(y->backend); >++ y->backend = NULL; >++ } >+ } >+ >+ if (new_owner && *new_owner) { >+@@ -1019,12 +1053,10 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us >+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; >+ } >+ >+- if (d->device_info_valid != 1) { >+- pa_log_warn("Properties changed in a device which information is unknown or invalid"); >++ if (!d->properties_received) >+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; >+- } >+ >+- parse_device_properties(d, &arg_i, true); >++ parse_device_properties(d, &arg_i); >+ } else if (pa_streq(iface, BLUEZ_MEDIA_TRANSPORT_INTERFACE)) { >+ pa_bluetooth_transport *t; >+ >+@@ -1093,6 +1125,10 @@ const char *pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile) { >+ return "a2dp_sink"; >+ case PA_BLUETOOTH_PROFILE_A2DP_SOURCE: >+ return "a2dp_source"; >++ case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT: >++ return "headset_head_unit"; >++ case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY: >++ return "headset_audio_gateway"; >+ case PA_BLUETOOTH_PROFILE_OFF: >+ return "off"; >+ } >+@@ -1228,12 +1264,12 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage >+ } >+ >+ if ((d = pa_hashmap_get(y->devices, dev_path))) { >+- if (d->device_info_valid == -1) { >++ if (!d->valid) { >+ pa_log_error("Information about device %s is invalid", dev_path); >+ goto fail2; >+ } >+ } else { >+- /* InterfacesAdded signal is probably on it's way, device_info_valid is kept as 0. */ >++ /* InterfacesAdded signal is probably on its way, device_info_valid is kept as 0. */ >+ pa_log_warn("SetConfiguration() received for unknown device %s", dev_path); >+ d = device_create(y, dev_path); >+ } >+@@ -1249,7 +1285,7 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage >+ pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), r, NULL)); >+ dbus_message_unref(r); >+ >+- d->transports[p] = t = pa_bluetooth_transport_new(d, sender, path, p, config, size); >++ t = pa_bluetooth_transport_new(d, sender, path, p, config, size); >+ t->acquire = bluez5_transport_acquire_cb; >+ t->release = bluez5_transport_release_cb; >+ pa_bluetooth_transport_put(t); >+@@ -1412,7 +1448,6 @@ static DBusMessage *endpoint_clear_configuration(DBusConnection *conn, DBusMessa >+ >+ if ((t = pa_hashmap_get(y->transports, path))) { >+ pa_log_debug("Clearing transport %s profile %s", t->path, pa_bluetooth_profile_to_string(t->profile)); >+- transport_state_changed(t, PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED); >+ pa_bluetooth_transport_free(t); >+ } >+ >+@@ -1611,6 +1646,9 @@ void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) { >+ pa_hashmap_free(y->transports); >+ } >+ >++ if (y->backend) >++ pa_bluetooth_backend_free(y->backend); >++ >+ if (y->connection) { >+ >+ if (y->matches_added) >+diff --git a/src/modules/bluetooth/bluez5-util.h b/src/modules/bluetooth/bluez5-util.h >+index bbc5b71..8db4a17 100644 >+--- a/src/modules/bluetooth/bluez5-util.h >++++ b/src/modules/bluetooth/bluez5-util.h >+@@ -26,11 +26,16 @@ >+ >+ #define PA_BLUETOOTH_UUID_A2DP_SOURCE "0000110a-0000-1000-8000-00805f9b34fb" >+ #define PA_BLUETOOTH_UUID_A2DP_SINK "0000110b-0000-1000-8000-00805f9b34fb" >++#define PA_BLUETOOTH_UUID_HSP_HS "00001108-0000-1000-8000-00805f9b34fb" >++#define PA_BLUETOOTH_UUID_HSP_AG "00001112-0000-1000-8000-00805f9b34fb" >++#define PA_BLUETOOTH_UUID_HFP_HF "0000111e-0000-1000-8000-00805f9b34fb" >++#define PA_BLUETOOTH_UUID_HFP_AG "0000111f-0000-1000-8000-00805f9b34fb" >+ >+ 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 struct pa_bluetooth_backend pa_bluetooth_backend; >+ >+ typedef enum pa_bluetooth_hook { >+ PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED, /* Call data: pa_bluetooth_device */ >+@@ -41,6 +46,8 @@ typedef enum pa_bluetooth_hook { >+ typedef enum profile { >+ PA_BLUETOOTH_PROFILE_A2DP_SINK, >+ PA_BLUETOOTH_PROFILE_A2DP_SOURCE, >++ PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT, >++ PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY, >+ PA_BLUETOOTH_PROFILE_OFF >+ } pa_bluetooth_profile_t; >+ #define PA_BLUETOOTH_PROFILE_COUNT PA_BLUETOOTH_PROFILE_OFF >+@@ -76,7 +83,9 @@ struct pa_bluetooth_device { >+ pa_bluetooth_discovery *discovery; >+ pa_bluetooth_adapter *adapter; >+ >+- int device_info_valid; /* 0: no results yet; 1: good results; -1: bad results ... */ >++ bool properties_received; >++ bool tried_to_link_with_adapter; >++ bool valid; >+ >+ /* Device information */ >+ char *path; >+@@ -93,12 +102,19 @@ struct pa_bluetooth_adapter { >+ pa_bluetooth_discovery *discovery; >+ char *path; >+ char *address; >++ >++ bool valid; >+ }; >+ >++pa_bluetooth_backend *pa_bluetooth_backend_new(pa_core *c, pa_bluetooth_discovery *y); >++void pa_bluetooth_backend_free(pa_bluetooth_backend *b); >++ >+ 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_set_state(pa_bluetooth_transport *t, pa_bluetooth_transport_state_t state); >+ void pa_bluetooth_transport_put(pa_bluetooth_transport *t); >++void pa_bluetooth_transport_unlink(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); >+diff --git a/src/modules/bluetooth/module-bluetooth-policy.c b/src/modules/bluetooth/module-bluetooth-policy.c >+index 06f9f1c..860868f 100644 >+--- a/src/modules/bluetooth/module-bluetooth-policy.c >++++ b/src/modules/bluetooth/module-bluetooth-policy.c >+@@ -41,17 +41,19 @@ PA_MODULE_VERSION(PACKAGE_VERSION); >+ PA_MODULE_LOAD_ONCE(true); >+ PA_MODULE_USAGE( >+ "a2dp_source=<Handle a2dp_source card profile (sink role)?> " >+- "hfgw=<Handle hfgw card profile (headset role)?>"); >++ "ag=<Handle headset_audio_gateway card profile (headset role)?> " >++ "hfgw=<Handle hfgw card profile (headset role)?> DEPRECATED"); >+ >+ static const char* const valid_modargs[] = { >+ "a2dp_source", >++ "ag", >+ "hfgw", >+ NULL >+ }; >+ >+ struct userdata { >+ bool enable_a2dp_source; >+- bool enable_hfgw; >++ bool enable_ag; >+ pa_hook_slot *source_put_slot; >+ pa_hook_slot *sink_put_slot; >+ pa_hook_slot *profile_available_changed_slot; >+@@ -79,9 +81,10 @@ static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, >+ if (!s) >+ return PA_HOOK_OK; >+ >+- if (u->enable_a2dp_source && pa_streq(s, "a2dp_source")) /* A2DP profile (we're doing sink role) */ >++ if (u->enable_a2dp_source && pa_streq(s, "a2dp_source")) >+ role = "music"; >+- else if (u->enable_hfgw && pa_streq(s, "hfgw")) /* HFP profile (we're doing headset role) */ >++ /* TODO: remove hfgw when we remove BlueZ 4 support */ >++ else if (u->enable_ag && (pa_streq(s, "hfgw") || pa_streq(s, "headset_audio_gateway"))) >+ role = "phone"; >+ else { >+ pa_log_debug("Profile %s cannot be selected for loopback", s); >+@@ -89,7 +92,8 @@ static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, >+ } >+ >+ /* Load module-loopback */ >+- args = pa_sprintf_malloc("source=\"%s\" source_dont_move=\"true\" sink_input_properties=\"media.role=%s\"", source->name, role); >++ args = pa_sprintf_malloc("source=\"%s\" source_dont_move=\"true\" sink_input_properties=\"media.role=%s\"", source->name, >++ role); >+ (void) pa_module_load(c, "module-loopback", args); >+ pa_xfree(args); >+ >+@@ -118,7 +122,8 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, void * >+ if (!s) >+ return PA_HOOK_OK; >+ >+- if (u->enable_hfgw && pa_streq(s, "hfgw")) /* HFP profile (we're doing headset role) */ >++ /* TODO: remove hfgw when we remove BlueZ 4 support */ >++ if (u->enable_ag && (pa_streq(s, "hfgw") || pa_streq(s, "headset_audio_gateway"))) >+ role = "phone"; >+ else { >+ pa_log_debug("Profile %s cannot be selected for loopback", s); >+@@ -126,7 +131,8 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, void * >+ } >+ >+ /* Load module-loopback */ >+- args = pa_sprintf_malloc("sink=\"%s\" sink_dont_move=\"true\" source_output_properties=\"media.role=%s\"", sink->name, role); >++ args = pa_sprintf_malloc("sink=\"%s\" sink_dont_move=\"true\" source_output_properties=\"media.role=%s\"", sink->name, >++ role); >+ (void) pa_module_load(c, "module-loopback", args); >+ pa_xfree(args); >+ >+@@ -137,12 +143,9 @@ static pa_card_profile *find_best_profile(pa_card *card) { >+ void *state; >+ pa_card_profile *profile; >+ pa_card_profile *result = card->active_profile; >+- pa_card_profile *off; >+- >+- pa_assert_se(off = pa_hashmap_get(card->profiles, "off")); >+ >+ PA_HASHMAP_FOREACH(profile, card->profiles, state) { >+- if (profile->available == PA_AVAILABLE_NO || profile == off) >++ if (profile->available == PA_AVAILABLE_NO) >+ continue; >+ >+ if (result == NULL || >+@@ -151,7 +154,7 @@ static pa_card_profile *find_best_profile(pa_card *card) { >+ result = profile; >+ } >+ >+- return result ? result : off; >++ return result; >+ } >+ >+ static pa_hook_result_t profile_available_hook_callback(pa_core *c, pa_card_profile *profile, void *userdata) { >+@@ -170,7 +173,9 @@ static pa_hook_result_t profile_available_hook_callback(pa_core *c, pa_card_prof >+ return PA_HOOK_OK; >+ >+ /* Do not automatically switch profiles for headsets, just in case */ >+- if (pa_streq(profile->name, "hsp") || pa_streq(profile->name, "a2dp")) >++ /* TODO: remove a2dp and hsp when we remove BlueZ 4 support */ >++ if (pa_streq(profile->name, "hsp") || pa_streq(profile->name, "a2dp") || pa_streq(profile->name, "a2dp_sink") || >++ pa_streq(profile->name, "headset_head_unit")) >+ return PA_HOOK_OK; >+ >+ is_active_profile = card->active_profile == profile; >+@@ -233,15 +238,21 @@ int pa__init(pa_module *m) { >+ goto fail; >+ } >+ >+- u->enable_hfgw = true; >+- if (pa_modargs_get_value_boolean(ma, "hfgw", &u->enable_hfgw) < 0) { >++ u->enable_ag = true; >++ if (pa_modargs_get_value_boolean(ma, "hfgw", &u->enable_ag) < 0) { >+ pa_log("Failed to parse hfgw argument."); >+ goto fail; >+ } >++ if (pa_modargs_get_value_boolean(ma, "ag", &u->enable_ag) < 0) { >++ pa_log("Failed to parse ag argument."); >++ goto fail; >++ } >+ >+- u->source_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_NORMAL, (pa_hook_cb_t) source_put_hook_callback, u); >++ u->source_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_NORMAL, >++ (pa_hook_cb_t) source_put_hook_callback, u); >+ >+- u->sink_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_put_hook_callback, u); >++ u->sink_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_NORMAL, >++ (pa_hook_cb_t) sink_put_hook_callback, u); >+ >+ u->profile_available_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_CARD_PROFILE_AVAILABLE_CHANGED], >+ PA_HOOK_NORMAL, (pa_hook_cb_t) profile_available_hook_callback, u); >+diff --git a/src/modules/bluetooth/module-bluez4-device.c b/src/modules/bluetooth/module-bluez4-device.c >+index 83e603f..0be0c57 100644 >+--- a/src/modules/bluetooth/module-bluez4-device.c >++++ b/src/modules/bluetooth/module-bluez4-device.c >+@@ -881,6 +881,7 @@ static int a2dp_process_push(struct userdata *u) { >+ void *d; >+ ssize_t l; >+ size_t to_write, to_decode; >++ size_t total_written = 0; >+ >+ a2dp_prepare_buffer(u); >+ >+@@ -907,17 +908,12 @@ static int a2dp_process_push(struct userdata *u) { >+ >+ pa_assert((size_t) l <= a2dp->buffer_size); >+ >+- u->read_index += (uint64_t) l; >+- >+ /* TODO: get timestamp from rtp */ >+ if (!found_tstamp) { >+ /* pa_log_warn("Couldn't find SO_TIMESTAMP data in auxiliary recvmsg() data!"); */ >+ tstamp = pa_rtclock_now(); >+ } >+ >+- pa_smoother_put(u->read_smoother, tstamp, pa_bytes_to_usec(u->read_index, &u->sample_spec)); >+- pa_smoother_resume(u->read_smoother, tstamp, true); >+- >+ p = (uint8_t*) a2dp->buffer + sizeof(*header) + sizeof(*payload); >+ to_decode = l - sizeof(*header) - sizeof(*payload); >+ >+@@ -937,12 +933,14 @@ static int a2dp_process_push(struct userdata *u) { >+ pa_log_error("SBC decoding error (%li)", (long) decoded); >+ pa_memblock_release(memchunk.memblock); >+ pa_memblock_unref(memchunk.memblock); >+- return -1; >++ return 0; >+ } >+ >+ /* pa_log_debug("SBC: decoded: %lu; written: %lu", (unsigned long) decoded, (unsigned long) written); */ >+ /* pa_log_debug("SBC: frame_length: %lu; codesize: %lu", (unsigned long) a2dp->frame_length, (unsigned long) a2dp->codesize); */ >+ >++ total_written += written; >++ >+ /* Reset frame length, it can be changed due to bitpool change */ >+ a2dp->frame_length = sbc_get_frame_length(&a2dp->sbc); >+ >+@@ -958,6 +956,10 @@ static int a2dp_process_push(struct userdata *u) { >+ to_write -= written; >+ } >+ >++ u->read_index += (uint64_t) total_written; >++ pa_smoother_put(u->read_smoother, tstamp, pa_bytes_to_usec(u->read_index, &u->sample_spec)); >++ pa_smoother_resume(u->read_smoother, tstamp, true); >++ >+ memchunk.length -= to_write; >+ >+ pa_memblock_release(memchunk.memblock); >+@@ -1039,10 +1041,12 @@ static void thread_func(void *userdata) { >+ if (n_read < 0) >+ goto io_fail; >+ >+- /* We just read something, so we are supposed to write something, too */ >+- pending_read_bytes += n_read; >+- do_write += pending_read_bytes / u->write_block_size; >+- pending_read_bytes = pending_read_bytes % u->write_block_size; >++ if (n_read > 0) { >++ /* We just read something, so we are supposed to write something, too */ >++ pending_read_bytes += n_read; >++ do_write += pending_read_bytes / u->write_block_size; >++ pending_read_bytes = pending_read_bytes % u->write_block_size; >++ } >+ } >+ } >+ >+@@ -1367,7 +1371,7 @@ static void source_set_volume_cb(pa_source *s) { >+ } >+ >+ /* Run from main thread */ >+-static char *get_name(const char *type, pa_modargs *ma, const char *device_id, bool *namereg_fail) { >++static char *get_name(const char *type, pa_modargs *ma, const char *device_id, const char *profile, bool *namereg_fail) { >+ char *t; >+ const char *n; >+ >+@@ -1392,7 +1396,10 @@ static char *get_name(const char *type, pa_modargs *ma, const char *device_id, b >+ *namereg_fail = false; >+ } >+ >+- return pa_sprintf_malloc("bluez_%s.%s", type, n); >++ if (profile) >++ return pa_sprintf_malloc("bluez_%s.%s.%s", type, n, profile); >++ else >++ return pa_sprintf_malloc("bluez_%s.%s", type, n); >+ } >+ >+ static int sco_over_pcm_state_update(struct userdata *u, bool changed) { >+@@ -1563,7 +1570,7 @@ static int add_sink(struct userdata *u) { >+ if (u->profile == PA_BLUEZ4_PROFILE_HSP) >+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone"); >+ data.card = u->card; >+- data.name = get_name("sink", u->modargs, u->address, &b); >++ data.name = get_name("sink", u->modargs, u->address, pa_bluez4_profile_to_string(u->profile), &b); >+ data.namereg_fail = b; >+ >+ if (pa_modargs_get_proplist(u->modargs, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) { >+@@ -1634,7 +1641,7 @@ static int add_source(struct userdata *u) { >+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone"); >+ >+ data.card = u->card; >+- data.name = get_name("source", u->modargs, u->address, &b); >++ data.name = get_name("source", u->modargs, u->address, pa_bluez4_profile_to_string(u->profile), &b); >+ data.namereg_fail = b; >+ >+ if (pa_modargs_get_proplist(u->modargs, "source_properties", data.proplist, PA_UPDATE_REPLACE) < 0) { >+@@ -2263,7 +2270,7 @@ static int add_card(struct userdata *u) { >+ pa_proplist_sets(data.proplist, "bluez.path", device->path); >+ pa_proplist_setf(data.proplist, "bluez.class", "0x%06x", (unsigned) device->class); >+ pa_proplist_sets(data.proplist, "bluez.alias", device->alias); >+- data.name = get_name("card", u->modargs, device->address, &b); >++ data.name = get_name("card", u->modargs, device->address, NULL, &b); >+ data.namereg_fail = b; >+ >+ if (pa_modargs_get_proplist(u->modargs, "card_properties", data.proplist, PA_UPDATE_REPLACE) < 0) { >+diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c >+index 287e763..48e498b 100644 >+--- a/src/modules/bluetooth/module-bluez5-device.c >++++ b/src/modules/bluetooth/module-bluez5-device.c >+@@ -33,6 +33,7 @@ >+ #include <pulse/timeval.h> >+ >+ #include <pulsecore/core-error.h> >++#include <pulsecore/core-rtclock.h> >+ #include <pulsecore/core-util.h> >+ #include <pulsecore/i18n.h> >+ #include <pulsecore/module.h> >+@@ -59,7 +60,9 @@ PA_MODULE_USAGE("path=<device object path>"); >+ >+ #define MAX_PLAYBACK_CATCH_UP_USEC (100 * PA_USEC_PER_MSEC) >+ #define FIXED_LATENCY_PLAYBACK_A2DP (25 * PA_USEC_PER_MSEC) >++#define FIXED_LATENCY_PLAYBACK_SCO (125 * PA_USEC_PER_MSEC) >+ #define FIXED_LATENCY_RECORD_A2DP (25 * PA_USEC_PER_MSEC) >++#define FIXED_LATENCY_RECORD_SCO (25 * PA_USEC_PER_MSEC) >+ >+ #define BITPOOL_DEC_LIMIT 32 >+ #define BITPOOL_DEC_STEP 5 >+@@ -71,6 +74,7 @@ static const char* const valid_modargs[] = { >+ >+ enum { >+ BLUETOOTH_MESSAGE_IO_THREAD_FAILED, >++ BLUETOOTH_MESSAGE_STREAM_FD_HUP, >+ BLUETOOTH_MESSAGE_MAX >+ }; >+ >+@@ -235,6 +239,157 @@ static void connect_ports(struct userdata *u, void *new_data, pa_direction_t dir >+ } >+ >+ /* Run from IO thread */ >++static int sco_process_render(struct userdata *u) { >++ ssize_t l; >++ pa_memchunk memchunk; >++ >++ pa_assert(u); >++ pa_assert(u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || >++ u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY); >++ pa_assert(u->sink); >++ >++ pa_sink_render_full(u->sink, u->write_block_size, &memchunk); >++ >++ pa_assert(memchunk.length == u->write_block_size); >++ >++ for (;;) { >++ const void *p; >++ >++ /* Now write that data to the socket. The socket is of type >++ * SEQPACKET, and we generated the data of the MTU size, so this >++ * should just work. */ >++ >++ p = (const uint8_t *) pa_memblock_acquire_chunk(&memchunk); >++ l = pa_write(u->stream_fd, p, memchunk.length, &u->stream_write_type); >++ pa_memblock_release(memchunk.memblock); >++ >++ pa_assert(l != 0); >++ >++ if (l > 0) >++ break; >++ >++ if (errno == EINTR) >++ /* Retry right away if we got interrupted */ >++ continue; >++ else if (errno == EAGAIN) >++ /* Hmm, apparently the socket was not writable, give up for now */ >++ return 0; >++ >++ pa_log_error("Failed to write data to SCO socket: %s", pa_cstrerror(errno)); >++ return -1; >++ } >++ >++ pa_assert((size_t) l <= memchunk.length); >++ >++ if ((size_t) l != memchunk.length) { >++ pa_log_error("Wrote memory block to socket only partially! %llu written, wanted to write %llu.", >++ (unsigned long long) l, >++ (unsigned long long) memchunk.length); >++ return -1; >++ } >++ >++ u->write_index += (uint64_t) memchunk.length; >++ pa_memblock_unref(memchunk.memblock); >++ >++ return 1; >++} >++ >++/* Run from IO thread */ >++static int sco_process_push(struct userdata *u) { >++ ssize_t l; >++ pa_memchunk memchunk; >++ struct cmsghdr *cm; >++ struct msghdr m; >++ bool found_tstamp = false; >++ pa_usec_t tstamp = 0; >++ >++ pa_assert(u); >++ pa_assert(u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || >++ u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY); >++ pa_assert(u->source); >++ pa_assert(u->read_smoother); >++ >++ memchunk.memblock = pa_memblock_new(u->core->mempool, u->read_block_size); >++ memchunk.index = memchunk.length = 0; >++ >++ for (;;) { >++ void *p; >++ uint8_t aux[1024]; >++ struct iovec iov; >++ >++ pa_zero(m); >++ pa_zero(aux); >++ pa_zero(iov); >++ >++ m.msg_iov = &iov; >++ m.msg_iovlen = 1; >++ m.msg_control = aux; >++ m.msg_controllen = sizeof(aux); >++ >++ p = pa_memblock_acquire(memchunk.memblock); >++ iov.iov_base = p; >++ iov.iov_len = pa_memblock_get_length(memchunk.memblock); >++ l = recvmsg(u->stream_fd, &m, 0); >++ pa_memblock_release(memchunk.memblock); >++ >++ if (l > 0) >++ break; >++ >++ if (l < 0 && errno == EINTR) >++ /* Retry right away if we got interrupted */ >++ continue; >++ >++ pa_memblock_unref(memchunk.memblock); >++ >++ if (l < 0 && errno == EAGAIN) >++ /* Hmm, apparently the socket was not readable, give up for now. */ >++ return 0; >++ >++ pa_log_error("Failed to read data from SCO socket: %s", l < 0 ? pa_cstrerror(errno) : "EOF"); >++ return -1; >++ } >++ >++ pa_assert((size_t) l <= pa_memblock_get_length(memchunk.memblock)); >++ >++ /* In some rare occasions, we might receive packets of a very strange >++ * size. This could potentially be possible if the SCO packet was >++ * received partially over-the-air, or more probably due to hardware >++ * issues in our Bluetooth adapter. In these cases, in order to avoid >++ * an assertion failure due to unaligned data, just discard the whole >++ * packet */ >++ if (!pa_frame_aligned(l, &u->sample_spec)) { >++ pa_log_warn("SCO packet received of unaligned size: %zu", l); >++ pa_memblock_unref(memchunk.memblock); >++ return -1; >++ } >++ >++ memchunk.length = (size_t) l; >++ u->read_index += (uint64_t) l; >++ >++ for (cm = CMSG_FIRSTHDR(&m); cm; cm = CMSG_NXTHDR(&m, cm)) >++ if (cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SO_TIMESTAMP) { >++ struct timeval *tv = (struct timeval*) CMSG_DATA(cm); >++ pa_rtclock_from_wallclock(tv); >++ tstamp = pa_timeval_load(tv); >++ found_tstamp = true; >++ break; >++ } >++ >++ if (!found_tstamp) { >++ pa_log_warn("Couldn't find SO_TIMESTAMP data in auxiliary recvmsg() data!"); >++ tstamp = pa_rtclock_now(); >++ } >++ >++ pa_smoother_put(u->read_smoother, tstamp, pa_bytes_to_usec(u->read_index, &u->sample_spec)); >++ pa_smoother_resume(u->read_smoother, tstamp, true); >++ >++ pa_source_post(u->source, &memchunk); >++ pa_memblock_unref(memchunk.memblock); >++ >++ return l; >++} >++ >++/* Run from IO thread */ >+ static void a2dp_prepare_buffer(struct userdata *u) { >+ size_t min_buffer_size = PA_MAX(u->read_link_mtu, u->write_link_mtu); >+ >+@@ -402,6 +557,7 @@ static int a2dp_process_push(struct userdata *u) { >+ void *d; >+ ssize_t l; >+ size_t to_write, to_decode; >++ size_t total_written = 0; >+ >+ a2dp_prepare_buffer(u); >+ >+@@ -428,17 +584,12 @@ static int a2dp_process_push(struct userdata *u) { >+ >+ pa_assert((size_t) l <= sbc_info->buffer_size); >+ >+- u->read_index += (uint64_t) l; >+- >+ /* TODO: get timestamp from rtp */ >+ if (!found_tstamp) { >+ /* pa_log_warn("Couldn't find SO_TIMESTAMP data in auxiliary recvmsg() data!"); */ >+ tstamp = pa_rtclock_now(); >+ } >+ >+- pa_smoother_put(u->read_smoother, tstamp, pa_bytes_to_usec(u->read_index, &u->sample_spec)); >+- pa_smoother_resume(u->read_smoother, tstamp, true); >+- >+ p = (uint8_t*) sbc_info->buffer + sizeof(*header) + sizeof(*payload); >+ to_decode = l - sizeof(*header) - sizeof(*payload); >+ >+@@ -458,9 +609,11 @@ static int a2dp_process_push(struct userdata *u) { >+ pa_log_error("SBC decoding error (%li)", (long) decoded); >+ pa_memblock_release(memchunk.memblock); >+ pa_memblock_unref(memchunk.memblock); >+- return -1; >++ return 0; >+ } >+ >++ total_written += written; >++ >+ /* Reset frame length, it can be changed due to bitpool change */ >+ sbc_info->frame_length = sbc_get_frame_length(&sbc_info->sbc); >+ >+@@ -476,6 +629,10 @@ static int a2dp_process_push(struct userdata *u) { >+ to_write -= written; >+ } >+ >++ u->read_index += (uint64_t) total_written; >++ pa_smoother_put(u->read_smoother, tstamp, pa_bytes_to_usec(u->read_index, &u->sample_spec)); >++ pa_smoother_resume(u->read_smoother, tstamp, true); >++ >+ memchunk.length -= to_write; >+ >+ pa_memblock_release(memchunk.memblock); >+@@ -608,24 +765,31 @@ static void transport_release(struct userdata *u) { >+ >+ /* Run from I/O thread */ >+ static void transport_config_mtu(struct userdata *u) { >+- u->read_block_size = >+- (u->read_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload)) >+- / u->sbc_info.frame_length * u->sbc_info.codesize; >++ if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) { >++ u->read_block_size = u->read_link_mtu; >++ u->write_block_size = u->write_link_mtu; >++ } else { >++ u->read_block_size = >++ (u->read_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload)) >++ / u->sbc_info.frame_length * u->sbc_info.codesize; >+ >+- u->write_block_size = >+- (u->write_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload)) >+- / u->sbc_info.frame_length * u->sbc_info.codesize; >++ u->write_block_size = >++ (u->write_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload)) >++ / u->sbc_info.frame_length * u->sbc_info.codesize; >++ } >+ >+ if (u->sink) { >+ pa_sink_set_max_request_within_thread(u->sink, u->write_block_size); >+ pa_sink_set_fixed_latency_within_thread(u->sink, >+- FIXED_LATENCY_PLAYBACK_A2DP + >++ (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK ? >++ FIXED_LATENCY_PLAYBACK_A2DP : FIXED_LATENCY_PLAYBACK_SCO) + >+ pa_bytes_to_usec(u->write_block_size, &u->sample_spec)); >+ } >+ >+ if (u->source) >+ pa_source_set_fixed_latency_within_thread(u->source, >+- FIXED_LATENCY_RECORD_A2DP + >++ (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE ? >++ FIXED_LATENCY_RECORD_A2DP : FIXED_LATENCY_RECORD_SCO) + >+ pa_bytes_to_usec(u->read_block_size, &u->sample_spec)); >+ } >+ >+@@ -752,15 +916,19 @@ static int add_source(struct userdata *u) { >+ data.namereg_fail = false; >+ pa_proplist_sets(data.proplist, "bluetooth.protocol", pa_bluetooth_profile_to_string(u->profile)); >+ pa_source_new_data_set_sample_spec(&data, &u->sample_spec); >++ if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT) >++ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone"); >+ >+ connect_ports(u, &data, PA_DIRECTION_INPUT); >+ >+ if (!u->transport_acquired) >+ switch (u->profile) { >+ case PA_BLUETOOTH_PROFILE_A2DP_SOURCE: >++ case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY: >+ data.suspend_cause = PA_SUSPEND_USER; >+ break; >+ case PA_BLUETOOTH_PROFILE_A2DP_SINK: >++ case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT: >+ case PA_BLUETOOTH_PROFILE_OFF: >+ pa_assert_not_reached(); >+ break; >+@@ -867,14 +1035,20 @@ static int add_sink(struct userdata *u) { >+ data.namereg_fail = false; >+ pa_proplist_sets(data.proplist, "bluetooth.protocol", pa_bluetooth_profile_to_string(u->profile)); >+ pa_sink_new_data_set_sample_spec(&data, &u->sample_spec); >++ if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT) >++ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone"); >+ >+ connect_ports(u, &data, PA_DIRECTION_OUTPUT); >+ >+ if (!u->transport_acquired) >+ switch (u->profile) { >++ case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY: >++ data.suspend_cause = PA_SUSPEND_USER; >++ break; >+ case PA_BLUETOOTH_PROFILE_A2DP_SINK: >+ /* Profile switch should have failed */ >+ case PA_BLUETOOTH_PROFILE_A2DP_SOURCE: >++ case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT: >+ case PA_BLUETOOTH_PROFILE_OFF: >+ pa_assert_not_reached(); >+ break; >+@@ -895,111 +1069,117 @@ static int add_sink(struct userdata *u) { >+ >+ /* Run from main thread */ >+ static void transport_config(struct userdata *u) { >+- sbc_info_t *sbc_info = &u->sbc_info; >+- a2dp_sbc_t *config; >++ if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) { >++ u->sample_spec.format = PA_SAMPLE_S16LE; >++ u->sample_spec.channels = 1; >++ u->sample_spec.rate = 8000; >++ } else { >++ sbc_info_t *sbc_info = &u->sbc_info; >++ a2dp_sbc_t *config; >+ >+- pa_assert(u->transport); >++ pa_assert(u->transport); >+ >+- u->sample_spec.format = PA_SAMPLE_S16LE; >+- config = (a2dp_sbc_t *) u->transport->config; >++ u->sample_spec.format = PA_SAMPLE_S16LE; >++ config = (a2dp_sbc_t *) u->transport->config; >+ >+- if (sbc_info->sbc_initialized) >+- sbc_reinit(&sbc_info->sbc, 0); >+- else >+- sbc_init(&sbc_info->sbc, 0); >+- sbc_info->sbc_initialized = true; >++ if (sbc_info->sbc_initialized) >++ sbc_reinit(&sbc_info->sbc, 0); >++ else >++ sbc_init(&sbc_info->sbc, 0); >++ sbc_info->sbc_initialized = true; >+ >+- switch (config->frequency) { >+- case SBC_SAMPLING_FREQ_16000: >+- sbc_info->sbc.frequency = SBC_FREQ_16000; >+- u->sample_spec.rate = 16000U; >+- break; >+- case SBC_SAMPLING_FREQ_32000: >+- sbc_info->sbc.frequency = SBC_FREQ_32000; >+- u->sample_spec.rate = 32000U; >+- break; >+- case SBC_SAMPLING_FREQ_44100: >+- sbc_info->sbc.frequency = SBC_FREQ_44100; >+- u->sample_spec.rate = 44100U; >+- break; >+- case SBC_SAMPLING_FREQ_48000: >+- sbc_info->sbc.frequency = SBC_FREQ_48000; >+- u->sample_spec.rate = 48000U; >+- break; >+- default: >+- pa_assert_not_reached(); >+- } >++ switch (config->frequency) { >++ case SBC_SAMPLING_FREQ_16000: >++ sbc_info->sbc.frequency = SBC_FREQ_16000; >++ u->sample_spec.rate = 16000U; >++ break; >++ case SBC_SAMPLING_FREQ_32000: >++ sbc_info->sbc.frequency = SBC_FREQ_32000; >++ u->sample_spec.rate = 32000U; >++ break; >++ case SBC_SAMPLING_FREQ_44100: >++ sbc_info->sbc.frequency = SBC_FREQ_44100; >++ u->sample_spec.rate = 44100U; >++ break; >++ case SBC_SAMPLING_FREQ_48000: >++ sbc_info->sbc.frequency = SBC_FREQ_48000; >++ u->sample_spec.rate = 48000U; >++ break; >++ default: >++ pa_assert_not_reached(); >++ } >+ >+- switch (config->channel_mode) { >+- case SBC_CHANNEL_MODE_MONO: >+- sbc_info->sbc.mode = SBC_MODE_MONO; >+- u->sample_spec.channels = 1; >+- break; >+- case SBC_CHANNEL_MODE_DUAL_CHANNEL: >+- sbc_info->sbc.mode = SBC_MODE_DUAL_CHANNEL; >+- u->sample_spec.channels = 2; >+- break; >+- case SBC_CHANNEL_MODE_STEREO: >+- sbc_info->sbc.mode = SBC_MODE_STEREO; >+- u->sample_spec.channels = 2; >+- break; >+- case SBC_CHANNEL_MODE_JOINT_STEREO: >+- sbc_info->sbc.mode = SBC_MODE_JOINT_STEREO; >+- u->sample_spec.channels = 2; >+- break; >+- default: >+- pa_assert_not_reached(); >+- } >++ switch (config->channel_mode) { >++ case SBC_CHANNEL_MODE_MONO: >++ sbc_info->sbc.mode = SBC_MODE_MONO; >++ u->sample_spec.channels = 1; >++ break; >++ case SBC_CHANNEL_MODE_DUAL_CHANNEL: >++ sbc_info->sbc.mode = SBC_MODE_DUAL_CHANNEL; >++ u->sample_spec.channels = 2; >++ break; >++ case SBC_CHANNEL_MODE_STEREO: >++ sbc_info->sbc.mode = SBC_MODE_STEREO; >++ u->sample_spec.channels = 2; >++ break; >++ case SBC_CHANNEL_MODE_JOINT_STEREO: >++ sbc_info->sbc.mode = SBC_MODE_JOINT_STEREO; >++ u->sample_spec.channels = 2; >++ break; >++ default: >++ pa_assert_not_reached(); >++ } >+ >+- switch (config->allocation_method) { >+- case SBC_ALLOCATION_SNR: >+- sbc_info->sbc.allocation = SBC_AM_SNR; >+- break; >+- case SBC_ALLOCATION_LOUDNESS: >+- sbc_info->sbc.allocation = SBC_AM_LOUDNESS; >+- break; >+- default: >+- pa_assert_not_reached(); >+- } >++ switch (config->allocation_method) { >++ case SBC_ALLOCATION_SNR: >++ sbc_info->sbc.allocation = SBC_AM_SNR; >++ break; >++ case SBC_ALLOCATION_LOUDNESS: >++ sbc_info->sbc.allocation = SBC_AM_LOUDNESS; >++ break; >++ default: >++ pa_assert_not_reached(); >++ } >+ >+- switch (config->subbands) { >+- case SBC_SUBBANDS_4: >+- sbc_info->sbc.subbands = SBC_SB_4; >+- break; >+- case SBC_SUBBANDS_8: >+- sbc_info->sbc.subbands = SBC_SB_8; >+- break; >+- default: >+- pa_assert_not_reached(); >+- } >++ switch (config->subbands) { >++ case SBC_SUBBANDS_4: >++ sbc_info->sbc.subbands = SBC_SB_4; >++ break; >++ case SBC_SUBBANDS_8: >++ sbc_info->sbc.subbands = SBC_SB_8; >++ break; >++ default: >++ pa_assert_not_reached(); >++ } >+ >+- switch (config->block_length) { >+- case SBC_BLOCK_LENGTH_4: >+- sbc_info->sbc.blocks = SBC_BLK_4; >+- break; >+- case SBC_BLOCK_LENGTH_8: >+- sbc_info->sbc.blocks = SBC_BLK_8; >+- break; >+- case SBC_BLOCK_LENGTH_12: >+- sbc_info->sbc.blocks = SBC_BLK_12; >+- break; >+- case SBC_BLOCK_LENGTH_16: >+- sbc_info->sbc.blocks = SBC_BLK_16; >+- break; >+- default: >+- pa_assert_not_reached(); >+- } >++ switch (config->block_length) { >++ case SBC_BLOCK_LENGTH_4: >++ sbc_info->sbc.blocks = SBC_BLK_4; >++ break; >++ case SBC_BLOCK_LENGTH_8: >++ sbc_info->sbc.blocks = SBC_BLK_8; >++ break; >++ case SBC_BLOCK_LENGTH_12: >++ sbc_info->sbc.blocks = SBC_BLK_12; >++ break; >++ case SBC_BLOCK_LENGTH_16: >++ sbc_info->sbc.blocks = SBC_BLK_16; >++ break; >++ default: >++ pa_assert_not_reached(); >++ } >+ >+- sbc_info->min_bitpool = config->min_bitpool; >+- sbc_info->max_bitpool = config->max_bitpool; >++ sbc_info->min_bitpool = config->min_bitpool; >++ sbc_info->max_bitpool = config->max_bitpool; >+ >+- /* Set minimum bitpool for source to get the maximum possible block_size */ >+- sbc_info->sbc.bitpool = u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK ? sbc_info->max_bitpool : sbc_info->min_bitpool; >+- sbc_info->codesize = sbc_get_codesize(&sbc_info->sbc); >+- sbc_info->frame_length = sbc_get_frame_length(&sbc_info->sbc); >++ /* Set minimum bitpool for source to get the maximum possible block_size */ >++ sbc_info->sbc.bitpool = u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK ? sbc_info->max_bitpool : sbc_info->min_bitpool; >++ sbc_info->codesize = sbc_get_codesize(&sbc_info->sbc); >++ sbc_info->frame_length = sbc_get_frame_length(&sbc_info->sbc); >+ >+- pa_log_info("SBC parameters: allocation=%u, subbands=%u, blocks=%u, bitpool=%u", >+- sbc_info->sbc.allocation, sbc_info->sbc.subbands, sbc_info->sbc.blocks, sbc_info->sbc.bitpool); >++ pa_log_info("SBC parameters: allocation=%u, subbands=%u, blocks=%u, bitpool=%u", >++ sbc_info->sbc.allocation, sbc_info->sbc.subbands, sbc_info->sbc.blocks, sbc_info->sbc.bitpool); >++ } >+ } >+ >+ /* Run from main thread */ >+@@ -1019,7 +1199,7 @@ static int setup_transport(struct userdata *u) { >+ >+ u->transport = t; >+ >+- if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE) >++ if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) >+ transport_acquire(u, true); /* In case of error, the sink/sources will be created suspended */ >+ else if (transport_acquire(u, false) < 0) >+ return -1; /* We need to fail here until the interactions with module-suspend-on-idle and alike get improved */ >+@@ -1040,11 +1220,13 @@ static int init_profile(struct userdata *u) { >+ >+ pa_assert(u->transport); >+ >+- if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) >++ if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || >++ u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) >+ if (add_sink(u) < 0) >+ r = -1; >+ >+- if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE) >++ if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || >++ u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) >+ if (add_source(u) < 0) >+ r = -1; >+ >+@@ -1079,6 +1261,24 @@ static void thread_func(void *userdata) { >+ >+ pollfd = u->rtpoll_item ? pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL) : NULL; >+ >++ if (pollfd && (pollfd->revents & ~(POLLOUT|POLLIN))) { >++ pa_log_info("FD error: %s%s%s%s", >++ pollfd->revents & POLLERR ? "POLLERR " :"", >++ pollfd->revents & POLLHUP ? "POLLHUP " :"", >++ pollfd->revents & POLLPRI ? "POLLPRI " :"", >++ pollfd->revents & POLLNVAL ? "POLLNVAL " :""); >++ >++ if (pollfd->revents & POLLHUP) { >++ pollfd = NULL; >++ teardown_stream(u); >++ do_write = 0; >++ pending_read_bytes = 0; >++ writable = false; >++ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u->msg), BLUETOOTH_MESSAGE_STREAM_FD_HUP, NULL, 0, NULL, NULL); >++ } else >++ goto fail; >++ } >++ >+ if (u->source && PA_SOURCE_IS_LINKED(u->source->thread_info.state)) { >+ >+ /* We should send two blocks to the device before we expect >+@@ -1090,15 +1290,20 @@ static void thread_func(void *userdata) { >+ if (pollfd && (pollfd->revents & POLLIN)) { >+ int n_read; >+ >+- n_read = a2dp_process_push(u); >++ if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE) >++ n_read = a2dp_process_push(u); >++ else >++ n_read = sco_process_push(u); >+ >+ if (n_read < 0) >+- goto io_fail; >++ goto fail; >+ >+- /* We just read something, so we are supposed to write something, too */ >+- pending_read_bytes += n_read; >+- do_write += pending_read_bytes / u->write_block_size; >+- pending_read_bytes = pending_read_bytes % u->write_block_size; >++ if (n_read > 0) { >++ /* We just read something, so we are supposed to write something, too */ >++ pending_read_bytes += n_read; >++ do_write += pending_read_bytes / u->write_block_size; >++ pending_read_bytes = pending_read_bytes % u->write_block_size; >++ } >+ } >+ } >+ >+@@ -1159,8 +1364,13 @@ static void thread_func(void *userdata) { >+ if (u->write_index <= 0) >+ u->started_at = pa_rtclock_now(); >+ >+- if ((n_written = a2dp_process_render(u)) < 0) >+- goto io_fail; >++ if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) { >++ if ((n_written = a2dp_process_render(u)) < 0) >++ goto fail; >++ } else { >++ if ((n_written = sco_process_render(u)) < 0) >++ goto fail; >++ } >+ >+ if (n_written == 0) >+ pa_log("Broken kernel: we got EAGAIN on write() after POLLOUT!"); >+@@ -1207,30 +1417,6 @@ static void thread_func(void *userdata) { >+ transport_release(u); >+ goto finish; >+ } >+- >+- pollfd = u->rtpoll_item ? pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL) : NULL; >+- >+- if (pollfd && (pollfd->revents & ~(POLLOUT|POLLIN))) { >+- pa_log_info("FD error: %s%s%s%s", >+- pollfd->revents & POLLERR ? "POLLERR " :"", >+- pollfd->revents & POLLHUP ? "POLLHUP " :"", >+- pollfd->revents & POLLPRI ? "POLLPRI " :"", >+- pollfd->revents & POLLNVAL ? "POLLNVAL " :""); >+- goto io_fail; >+- } >+- >+- continue; >+- >+-io_fail: >+- /* In case of HUP, just tear down the streams */ >+- if (!pollfd || (pollfd->revents & POLLHUP) == 0) >+- goto fail; >+- >+- do_write = 0; >+- pending_read_bytes = 0; >+- writable = false; >+- >+- teardown_stream(u); >+ } >+ >+ fail: >+@@ -1364,6 +1550,8 @@ static pa_direction_t get_profile_direction(pa_bluetooth_profile_t p) { >+ static const pa_direction_t profile_direction[] = { >+ [PA_BLUETOOTH_PROFILE_A2DP_SINK] = PA_DIRECTION_OUTPUT, >+ [PA_BLUETOOTH_PROFILE_A2DP_SOURCE] = PA_DIRECTION_INPUT, >++ [PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT] = PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT, >++ [PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY] = PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT, >+ [PA_BLUETOOTH_PROFILE_OFF] = 0 >+ }; >+ >+@@ -1520,9 +1708,7 @@ static pa_card_profile *create_card_profile(struct userdata *u, const char *uuid >+ pa_assert_se(output_port = pa_hashmap_get(ports, u->output_port_name)); >+ >+ if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SINK)) { >+- /* TODO: Change this profile's name to a2dp_sink, to reflect the remote >+- * device's role and be consistent with the a2dp source profile */ >+- cp = pa_card_profile_new("a2dp", _("High Fidelity Playback (A2DP Sink)"), sizeof(pa_bluetooth_profile_t)); >++ cp = pa_card_profile_new("a2dp_sink", _("High Fidelity Playback (A2DP Sink)"), sizeof(pa_bluetooth_profile_t)); >+ cp->priority = 10; >+ cp->n_sinks = 1; >+ cp->n_sources = 0; >+@@ -1543,10 +1729,38 @@ static pa_card_profile *create_card_profile(struct userdata *u, const char *uuid >+ >+ p = PA_CARD_PROFILE_DATA(cp); >+ *p = PA_BLUETOOTH_PROFILE_A2DP_SOURCE; >++ } else if (pa_streq(uuid, PA_BLUETOOTH_UUID_HSP_HS) || pa_streq(uuid, PA_BLUETOOTH_UUID_HFP_HF)) { >++ cp = pa_card_profile_new("headset_head_unit", _("Headset Head Unit (HSP/HFP)"), sizeof(pa_bluetooth_profile_t)); >++ cp->priority = 20; >++ cp->n_sinks = 1; >++ cp->n_sources = 1; >++ cp->max_sink_channels = 1; >++ cp->max_source_channels = 1; >++ pa_hashmap_put(input_port->profiles, cp->name, cp); >++ pa_hashmap_put(output_port->profiles, cp->name, cp); >++ >++ p = PA_CARD_PROFILE_DATA(cp); >++ *p = PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT; >++ } else if (pa_streq(uuid, PA_BLUETOOTH_UUID_HSP_AG) || pa_streq(uuid, PA_BLUETOOTH_UUID_HFP_AG)) { >++ cp = pa_card_profile_new("headset_audio_gateway", _("Headset Audio Gateway (HSP/HFP)"), sizeof(pa_bluetooth_profile_t)); >++ cp->priority = 20; >++ cp->n_sinks = 1; >++ cp->n_sources = 1; >++ cp->max_sink_channels = 1; >++ cp->max_source_channels = 1; >++ pa_hashmap_put(input_port->profiles, cp->name, cp); >++ pa_hashmap_put(output_port->profiles, cp->name, cp); >++ >++ p = PA_CARD_PROFILE_DATA(cp); >++ *p = PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY; >+ } >+ >+- if (cp && u->device->transports[*p]) >+- cp->available = transport_state_to_availability(u->device->transports[*p]->state); >++ if (cp) { >++ if (u->device->transports[*p]) >++ cp->available = transport_state_to_availability(u->device->transports[*p]->state); >++ else >++ cp->available = PA_AVAILABLE_NO; >++ } >+ >+ return cp; >+ } >+@@ -1680,10 +1894,8 @@ static void handle_transport_state_change(struct userdata *u, struct pa_bluetoot >+ >+ pa_assert(u); >+ pa_assert(t); >++ pa_assert_se(cp = pa_hashmap_get(u->card->profiles, pa_bluetooth_profile_to_string(t->profile))); >+ >+- /* Update profile availability */ >+- if (!(cp = pa_hashmap_get(u->card->profiles, pa_bluetooth_profile_to_string(t->profile)))) >+- return; >+ pa_card_profile_set_available(cp, transport_state_to_availability(t->state)); >+ >+ /* Update port availability */ >+@@ -1767,15 +1979,19 @@ static pa_hook_result_t transport_state_changed_cb(pa_bluetooth_discovery *y, pa >+ >+ /* Run from main thread context */ >+ static int device_process_msg(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) { >+- struct bluetooth_msg *u = BLUETOOTH_MSG(obj); >++ struct bluetooth_msg *m = BLUETOOTH_MSG(obj); >++ struct userdata *u = m->card->userdata; >+ >+ switch (code) { >+ case BLUETOOTH_MESSAGE_IO_THREAD_FAILED: >+- if (u->card->module->unload_requested) >++ if (m->card->module->unload_requested) >+ break; >+ >+ pa_log_debug("Switching the profile to off due to IO thread failure."); >+- pa_assert_se(pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, "off"), false) >= 0); >++ pa_assert_se(pa_card_set_profile(m->card, pa_hashmap_get(m->card->profiles, "off"), false) >= 0); >++ break; >++ case BLUETOOTH_MESSAGE_STREAM_FD_HUP: >++ pa_bluetooth_transport_set_state(u->transport, PA_BLUETOOTH_TRANSPORT_STATE_IDLE); >+ break; >+ } >+ >+diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c >+index d86c158..9621f3e 100644 >+--- a/src/modules/module-device-manager.c >++++ b/src/modules/module-device-manager.c >+@@ -502,7 +502,7 @@ static inline struct entry *load_or_initialize_entry(struct userdata *u, struct >+ entry->description = pa_xstrdup(old->description); >+ entry->icon = pa_xstrdup(old->icon); >+ } else { >+- /* This is a new device, so make sure we write it's priority list correctly */ >++ /* This is a new device, so make sure we write its priority list correctly */ >+ role_indexes_t max_priority; >+ pa_datum key; >+ bool done; >+@@ -1328,7 +1328,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio >+ } >+ >+ pa_hashmap_free(h); >+- pa_log_error("Client specified an unknown device in it's reorder list."); >++ pa_log_error("Client specified an unknown device in its reorder list."); >+ goto fail; >+ } >+ entry_free(e); >+diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c >+index 9689e79..5a4bce7 100644 >+--- a/src/modules/module-stream-restore.c >++++ b/src/modules/module-stream-restore.c >+@@ -1912,7 +1912,7 @@ static void entry_apply(struct userdata *u, const char *name, struct entry *e) { >+ removed the sink element from the rule. */ >+ si->save_sink = false; >+ /* This is cheating a bit. The sink input itself has not changed >+- but the rules governing it's routing have, so we fire this event >++ but the rules governing its routing have, so we fire this event >+ such that other routing modules (e.g. module-device-manager) >+ will pick up the change and reapply their routing */ >+ pa_subscription_post(si->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, si->index); >+@@ -1960,7 +1960,7 @@ static void entry_apply(struct userdata *u, const char *name, struct entry *e) { >+ removed the source element from the rule. */ >+ so->save_source = false; >+ /* This is cheating a bit. The source output itself has not changed >+- but the rules governing it's routing have, so we fire this event >++ but the rules governing its routing have, so we fire this event >+ such that other routing modules (e.g. module-device-manager) >+ will pick up the change and reapply their routing */ >+ pa_subscription_post(so->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, so->index); >+diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h >+index dc3cddc..e55a479 100644 >+--- a/src/pulse/proplist.h >++++ b/src/pulse/proplist.h >+@@ -71,7 +71,7 @@ PA_C_DECL_BEGIN >+ /** For streams: the name of a filter that is desired, e.g.\ "echo-cancel" or "equalizer-sink". Differs from PA_PROP_FILTER_WANT in that it forces PulseAudio to apply the filter, regardless of whether PulseAudio thinks it makes sense to do so or not. If this is set, PA_PROP_FILTER_WANT is ignored. In other words, you almost certainly do not want to use this. \since 1.0 */ >+ #define PA_PROP_FILTER_APPLY "filter.apply" >+ >+-/** For streams: the name of a filter that should specifically suppressed (i.e.\ overrides PA_PROP_FILTER_WANT). Useful for the times that PA_PROP_FILTER_WANT is automatically added (e.g. echo-cancellation for phone streams when $VOIP_APP does it's own, internal AEC) \since 1.0 */ >++/** For streams: the name of a filter that should specifically suppressed (i.e.\ overrides PA_PROP_FILTER_WANT). Useful for the times that PA_PROP_FILTER_WANT is automatically added (e.g. echo-cancellation for phone streams when $VOIP_APP does its own, internal AEC) \since 1.0 */ >+ #define PA_PROP_FILTER_SUPPRESS "filter.suppress" >+ >+ /** For event sound streams: XDG event sound name. e.g.\ "message-new-email" (Event sound streams are those with media.role set to "event") */ >+diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c >+index f4647b8..47ee0aa 100644 >+--- a/src/pulsecore/sink.c >++++ b/src/pulsecore/sink.c >+@@ -2056,7 +2056,7 @@ void pa_sink_set_volume( >+ >+ /* Let's 'push' the reference volume if necessary */ >+ pa_cvolume_merge(&new_reference_volume, &s->reference_volume, &root_sink->real_volume); >+- /* If the sink and it's root don't have the same number of channels, we need to remap */ >++ /* If the sink and its root don't have the same number of channels, we need to remap */ >+ if (s != root_sink && !pa_channel_map_equal(&s->channel_map, &root_sink->channel_map)) >+ pa_cvolume_remap(&new_reference_volume, &s->channel_map, &root_sink->channel_map); >+ update_reference_volume(root_sink, &new_reference_volume, &root_sink->channel_map, save); >+diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c >+index af4c6ec..853bb6c 100644 >+--- a/src/pulsecore/source.c >++++ b/src/pulsecore/source.c >+@@ -1651,7 +1651,7 @@ void pa_source_set_volume( >+ >+ /* Let's 'push' the reference volume if necessary */ >+ pa_cvolume_merge(&new_reference_volume, &s->reference_volume, &root_source->real_volume); >+- /* If the source and it's root don't have the same number of channels, we need to remap */ >++ /* If the source and its root don't have the same number of channels, we need to remap */ >+ if (s != root_source && !pa_channel_map_equal(&s->channel_map, &root_source->channel_map)) >+ pa_cvolume_remap(&new_reference_volume, &s->channel_map, &root_source->channel_map); >+ update_reference_volume(root_source, &new_reference_volume, &root_source->channel_map, save); >diff --git a/pulseaudio.spec b/pulseaudio.spec >index 133b52790a52..c77a79daa380 100644 >--- a/pulseaudio.spec >+++ b/pulseaudio.spec >@@ -18,7 +18,7 @@ > Name: pulseaudio > Summary: Improved Linux Sound Server > Version: %{pa_major}%{?pa_minor:.%{pa_minor}} >-Release: 7%{?gitcommit:.git%{shortcommit}}%{?dist} >+Release: 8%{?gitcommit:.git%{shortcommit}}%{?dist} > License: LGPLv2+ > URL: http://www.freedesktop.org/wiki/Software/PulseAudio > %if 0%{?gitrel} >@@ -43,7 +43,11 @@ Patch501: pulseaudio-x11_device_manager.patch > # set X-KDE-autostart-phase=1 > Patch502: pulseaudio-4.0-kde_autostart_phase.patch > >+Patch599: tbd-misses-bool.patch >+Patch600: bluetooth-hsp-support.patch >+ > BuildRequires: m4 >+BuildRequires: libtool > BuildRequires: libtool-ltdl-devel > BuildRequires: intltool > BuildRequires: pkgconfig >@@ -230,6 +234,9 @@ This package contains GDM integration hooks for the PulseAudio sound server. > %patch501 -p1 -b .x11_device_manager > %patch502 -p1 -b .kde_autostart_phase > >+%patch599 -p1 -b .tbd_misses_bool >+%patch600 -p1 -b .bluetooth_hsp_support >+ > sed -i.no_consolekit -e \ > 's/^load-module module-console-kit/#load-module module-console-kit/' \ > src/daemon/default.pa.in >@@ -245,7 +252,7 @@ sed -i -e 's|"/lib /usr/lib|"/%{_lib} %{_libdir}|' configure > %endif > > %build >- >+./bootstrap.sh > %configure \ > --disable-static \ > --disable-rpath \ >@@ -559,6 +566,9 @@ exit 0 > %attr(0600, gdm, gdm) %{_localstatedir}/lib/gdm/.pulse/default.pa > > %changelog >+* Tue Oct 21 2014 Mauro Carvalho Chehab <mchehab@osg.samsung.com> - 5.0-8 >+- Add support for bluetooth HSP >+ > * Wed Jul 16 2014 Rex Dieter <rdieter@fedoraproject.org> 5.0-7 > - Provide padsp-32, /usr/bin/padsp is native arch only (#856146) >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 1045548
: 949217 |
949822