[pulseaudio] 01/01: Add bluez5 + ofono + HFP patch set
David Henningsson
diwic-guest at moszumanska.debian.org
Mon Nov 30 10:01:48 UTC 2015
This is an automated email from the git hooks/post-receive script.
diwic-guest pushed a commit to branch ubuntu
in repository pulseaudio.
commit eb4f6465dd6819d66c33d82111f552b225bcd892
Author: David Henningsson <david.henningsson at canonical.com>
Date: Mon Nov 30 10:48:06 2015 +0100
Add bluez5 + ofono + HFP patch set
Signed-off-by: David Henningsson <david.henningsson at canonical.com>
---
debian/changelog | 7 +
...luez5-ofono-add-support-for-HFP-gateway-r.patch | 219 ++++++
...th-bluez5-bring-back-SCO-over-PCM-support.patch | 778 +++++++++++++++++++++
...luez5-ofono-add-support-for-spekaer-micro.patch | 78 +++
...luetooth-bluez5-add-support-for-both-mode.patch | 78 +++
...luez5-let-user-specify-a-default-profile-.patch | 105 +++
...luez5-prevent-SCO-sink-source-to-be-suspe.patch | 203 ++++++
...luez5-drop-save-restore-of-SCO-sink-sourc.patch | 71 ++
debian/patches/series | 9 +
9 files changed, 1548 insertions(+)
diff --git a/debian/changelog b/debian/changelog
index a7445c8..66c4cb7 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+pulseaudio (1:7.1-1ubuntu2) UNRELEASED; urgency=medium
+
+ * debian/patches/050*.patch:
+ - Add bluez5 + ofono + HFP patches for Ubuntu touch
+
+ -- David Henningsson <david.henningsson at canonical.com> Mon, 30 Nov 2015 10:46:51 +0100
+
pulseaudio (1:7.1-1ubuntu1) xenial; urgency=medium
* Merge from Debian experimental, remaining changes:
diff --git a/debian/patches/0501-bluetooth-bluez5-ofono-add-support-for-HFP-gateway-r.patch b/debian/patches/0501-bluetooth-bluez5-ofono-add-support-for-HFP-gateway-r.patch
new file mode 100644
index 0000000..46c9802
--- /dev/null
+++ b/debian/patches/0501-bluetooth-bluez5-ofono-add-support-for-HFP-gateway-r.patch
@@ -0,0 +1,219 @@
+From 86873062559af762e78a39febb16452326d0406d Mon Sep 17 00:00:00 2001
+From: Simon Fels <simon.fels at canonical.com>
+Date: Sun, 1 Nov 2015 16:27:20 +0100
+Subject: [PATCH 501/507] bluetooth: bluez5: ofono: add support for HFP gateway
+ role
+
+---
+ src/modules/bluetooth/backend-ofono.c | 88 +++++++++++++++++++++++------------
+ src/modules/bluetooth/bluez5-util.c | 14 ++++++
+ src/modules/bluetooth/bluez5-util.h | 1 +
+ 3 files changed, 73 insertions(+), 30 deletions(-)
+
+diff --git a/src/modules/bluetooth/backend-ofono.c b/src/modules/bluetooth/backend-ofono.c
+index 755df9e..c77be54 100644
+--- a/src/modules/bluetooth/backend-ofono.c
++++ b/src/modules/bluetooth/backend-ofono.c
+@@ -37,6 +37,7 @@
+ #define OFONO_SERVICE "org.ofono"
+ #define HF_AUDIO_AGENT_INTERFACE OFONO_SERVICE ".HandsfreeAudioAgent"
+ #define HF_AUDIO_MANAGER_INTERFACE OFONO_SERVICE ".HandsfreeAudioManager"
++#define HF_AUDIO_CARD_INTERFACE OFONO_SERVICE ".HandsfreeAudioCard"
+
+ #define HF_AUDIO_AGENT_PATH "/HandsfreeAudioAgent"
+
+@@ -151,13 +152,15 @@ static int socket_accept(int sock)
+
+ 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) {
++ if (!optional && card->fd < 0) {
+ DBusMessage *m;
+
++ pa_log_debug("Acquiring transport from ofono for card %s",
++ card->path);
++
+ 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));
+
+@@ -176,12 +179,6 @@ static int hf_audio_agent_transport_acquire(pa_bluetooth_transport *t, bool opti
+
+ 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;
+ }
+
+@@ -190,18 +187,28 @@ static void hf_audio_agent_transport_release(pa_bluetooth_transport *t) {
+
+ pa_assert(card);
+
++ pa_log_debug("Trying to release transport for card %s (fd %d)",
++ card->path, card->fd);
++
+ if (t->state <= PA_BLUETOOTH_TRANSPORT_STATE_IDLE) {
+ pa_log_info("Transport %s already released", t->path);
+ return;
+ }
+
+- if (card->fd < 0)
+- return;
++ if (card->fd > 0) {
++ pa_log_debug("Transport available for card %s (fd %d), releasing now",
++ card->path, card->fd);
+
+- /* shutdown to make sure connection is dropped immediately */
+- shutdown(card->fd, SHUT_RDWR);
+- close(card->fd);
+- card->fd = -1;
++ /* shutdown to make sure connection is dropped immediately */
++ shutdown(card->fd, SHUT_RDWR);
++ close(card->fd);
++ card->fd = -1;
++
++ pa_log_debug("Successfully released transport for card %s", card->path);
++
++ pa_bluetooth_transport_set_state(t, PA_BLUETOOTH_TRANSPORT_STATE_IDLE);
++ }
++}
+ }
+
+ static void hf_audio_agent_card_found(pa_bluetooth_backend *backend, const char *path, DBusMessageIter *props_i) {
+@@ -209,6 +216,7 @@ static void hf_audio_agent_card_found(pa_bluetooth_backend *backend, const char
+ const char *key, *value;
+ struct hf_audio_card *card;
+ pa_bluetooth_device *d;
++ pa_bluetooth_profile_t profile = PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY;
+
+ pa_assert(backend);
+ pa_assert(path);
+@@ -227,23 +235,30 @@ static void hf_audio_agent_card_found(pa_bluetooth_backend *backend, const char
+ 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;
+- }
++ if ((c = dbus_message_iter_get_arg_type(&value_i)) == DBUS_TYPE_STRING) {
++ dbus_message_iter_get_basic(&value_i, &value);
++
++ if (pa_streq(key, "Type")) {
++ if (pa_streq(value, "gateway"))
++ profile = PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT;
++ else if (pa_streq(value, "handsfree"))
++ profile = PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY;
++ } else 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);
++ }
+
+- dbus_message_iter_get_basic(&value_i, &value);
++ pa_log_debug("%s: %s", key, 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);
++ } else if ((c = dbus_message_iter_get_arg_type(&value_i)) == DBUS_TYPE_UINT16) {
++ /* Ignore for now */
++ } else {
++ pa_log_error("Invalid properties for %s: expected 's' or 'q', received '%c'", path, c);
+ }
+
+- pa_log_debug("%s: %s", key, value);
+-
+ dbus_message_iter_next(props_i);
+ }
+
+@@ -253,7 +268,7 @@ static void hf_audio_agent_card_found(pa_bluetooth_backend *backend, const char
+ goto fail;
+ }
+
+- card->transport = pa_bluetooth_transport_new(d, backend->ofono_bus_id, path, PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY, NULL, 0);
++ card->transport = pa_bluetooth_transport_new(d, backend->ofono_bus_id, path, profile, NULL, 0);
+ card->transport->acquire = hf_audio_agent_transport_acquire;
+ card->transport->release = hf_audio_agent_transport_release;
+ card->transport->userdata = card;
+@@ -529,12 +544,25 @@ static DBusMessage *hf_audio_agent_new_connection(DBusConnection *c, DBusMessage
+
+ 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);
++ if (!card || codec != HFP_AUDIO_CODEC_CVSD) {
++ pa_log_warn("New audio connection invalid arguments (path=%s fd=%d, codec=%d, transport [state=%s, profile=%s])",
++ path, fd, codec,
++ card ? pa_bluetooth_transport_state_to_string(card->transport->state) : "unknown",
++ card ? pa_bluetooth_profile_to_string(card->transport->profile) : "unknown");
+ pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.InvalidArguments", "Invalid arguments in method call"));
+ return r;
+ }
+
++ if (card->transport->state == PA_BLUETOOTH_TRANSPORT_STATE_PLAYING) {
++ pa_log_warn("Could not activate new audio connection as it is already active!? "
++ "(path=%s fd=%d, codec=%d, transport [state=%s, profile=%s])",
++ path, fd, codec,
++ pa_bluetooth_transport_state_to_string(card->transport->state),
++ pa_bluetooth_profile_to_string(card->transport->profile));
++ pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.InvalidArguments", "Transport is already active"));
++ return r;
++ }
++
+ pa_log_debug("New audio connection on card %s (fd=%d, codec=%d)", path, fd, codec);
+
+ card->fd = fd;
+diff --git a/src/modules/bluetooth/bluez5-util.c b/src/modules/bluetooth/bluez5-util.c
+index 03c76bf..2f2f277 100644
+--- a/src/modules/bluetooth/bluez5-util.c
++++ b/src/modules/bluetooth/bluez5-util.c
+@@ -1158,6 +1158,20 @@ const char *pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile) {
+ return NULL;
+ }
+
++const char *pa_bluetooth_transport_state_to_string(pa_bluetooth_transport_state_t state)
++{
++ switch (state) {
++ case PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED:
++ return "disconnected";
++ case PA_BLUETOOTH_TRANSPORT_STATE_IDLE:
++ return "idle";
++ case PA_BLUETOOTH_TRANSPORT_STATE_PLAYING:
++ return "playing";
++ }
++
++ return NULL;
++}
++
+ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) {
+ pa_bluetooth_discovery *y = userdata;
+ pa_bluetooth_device *d;
+diff --git a/src/modules/bluetooth/bluez5-util.h b/src/modules/bluetooth/bluez5-util.h
+index d66e8a3..df44c01 100644
+--- a/src/modules/bluetooth/bluez5-util.h
++++ b/src/modules/bluetooth/bluez5-util.h
+@@ -151,6 +151,7 @@ pa_bluetooth_device* pa_bluetooth_discovery_get_device_by_address(pa_bluetooth_d
+ pa_hook* pa_bluetooth_discovery_hook(pa_bluetooth_discovery *y, pa_bluetooth_hook_t hook);
+
+ const char *pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile);
++const char *pa_bluetooth_transport_state_to_string(pa_bluetooth_transport_state_t state);
+
+ #define HEADSET_BACKEND_OFONO 0
+ #define HEADSET_BACKEND_NATIVE 1
+--
+2.6.2
+
diff --git a/debian/patches/0502-bluetooth-bluez5-bring-back-SCO-over-PCM-support.patch b/debian/patches/0502-bluetooth-bluez5-bring-back-SCO-over-PCM-support.patch
new file mode 100644
index 0000000..c9758db
--- /dev/null
+++ b/debian/patches/0502-bluetooth-bluez5-bring-back-SCO-over-PCM-support.patch
@@ -0,0 +1,778 @@
+From 63e70a2730f8a1a4ffe9301a9a33d6072eba8b2b Mon Sep 17 00:00:00 2001
+From: Simon Fels <simon.fels at canonical.com>
+Date: Sun, 1 Nov 2015 16:38:39 +0100
+Subject: [PATCH 502/507] bluetooth: bluez5: bring back SCO over PCM support
+
+---
+ src/modules/bluetooth/module-bluez5-device.c | 402 ++++++++++++++++++++-----
+ src/modules/bluetooth/module-bluez5-discover.c | 19 +-
+ 2 files changed, 346 insertions(+), 75 deletions(-)
+
+diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c
+index 6ebcda2..999c254 100644
+--- a/src/modules/bluetooth/module-bluez5-device.c
++++ b/src/modules/bluetooth/module-bluez5-device.c
+@@ -30,6 +30,7 @@
+ #include <pulse/rtclock.h>
+ #include <pulse/timeval.h>
+
++#include <pulsecore/core.h>
+ #include <pulsecore/core-error.h>
+ #include <pulsecore/core-rtclock.h>
+ #include <pulsecore/core-util.h>
+@@ -43,6 +44,8 @@
+ #include <pulsecore/thread.h>
+ #include <pulsecore/thread-mq.h>
+ #include <pulsecore/time-smoother.h>
++#include <pulsecore/namereg.h>
++#include <pulse/mainloop-api.h>
+
+ #include "a2dp-codecs.h"
+ #include "bluez5-util.h"
+@@ -54,7 +57,9 @@ PA_MODULE_AUTHOR("João Paulo Rechi Vita");
+ PA_MODULE_DESCRIPTION("BlueZ 5 Bluetooth audio sink and source");
+ PA_MODULE_VERSION(PACKAGE_VERSION);
+ PA_MODULE_LOAD_ONCE(false);
+-PA_MODULE_USAGE("path=<device object path>");
++PA_MODULE_USAGE("path=<device object path> "
++ "sco_sink=<name of sink> "
++ "sco_source=<name of source> ");
+
+ #define MAX_PLAYBACK_CATCH_UP_USEC (100 * PA_USEC_PER_MSEC)
+ #define FIXED_LATENCY_PLAYBACK_A2DP (25 * PA_USEC_PER_MSEC)
+@@ -66,8 +71,11 @@ PA_MODULE_USAGE("path=<device object path>");
+ #define BITPOOL_DEC_STEP 5
+ #define HSP_MAX_GAIN 15
+
++#define USE_SCO_OVER_PCM(u) (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT && (u->hsp.sco_sink && u->hsp.sco_source))
+ static const char* const valid_modargs[] = {
+ "path",
++ "sco_sink",
++ "sco_source",
+ NULL
+ };
+
+@@ -96,15 +104,27 @@ typedef struct sbc_info {
+ size_t buffer_size; /* Size of the buffer */
+ } sbc_info_t;
+
++struct hsp_info {
++ pa_sink *sco_sink;
++ void (*sco_sink_set_volume)(pa_sink *s);
++ pa_source *sco_source;
++ void (*sco_source_set_volume)(pa_source *s);
++};
++
+ struct userdata {
+ pa_module *module;
+ pa_core *core;
+
++ pa_modargs *modargs;
++
+ pa_hook_slot *device_connection_changed_slot;
+ pa_hook_slot *transport_state_changed_slot;
+ pa_hook_slot *transport_speaker_gain_changed_slot;
+ pa_hook_slot *transport_microphone_gain_changed_slot;
+
++ pa_hook_slot *sink_state_changed_slot;
++ pa_hook_slot *source_state_changed_slot;
++
+ pa_bluetooth_discovery *discovery;
+ pa_bluetooth_device *device;
+ pa_bluetooth_transport *transport;
+@@ -136,6 +156,10 @@ struct userdata {
+ pa_memchunk write_memchunk;
+ pa_sample_spec sample_spec;
+ struct sbc_info sbc_info;
++ struct hsp_info hsp;
++
++ bool transport_acquire_pending;
++ pa_io_event *stream_event;
+ };
+
+ typedef enum pa_bluetooth_form_factor {
+@@ -712,6 +736,11 @@ static void teardown_stream(struct userdata *u) {
+ u->rtpoll_item = NULL;
+ }
+
++ if (u->stream_event) {
++ u->core->mainloop->io_free(u->stream_event);
++ u->stream_event = NULL;
++ }
++
+ if (u->stream_fd >= 0) {
+ pa_close(u->stream_fd);
+ u->stream_fd = -1;
+@@ -733,18 +762,29 @@ static void teardown_stream(struct userdata *u) {
+ static int transport_acquire(struct userdata *u, bool optional) {
+ pa_assert(u->transport);
+
+- if (u->transport_acquired)
++ if (u->transport_acquire_pending)
++ return -1;
++
++ if (u->transport_acquired) {
++ pa_log_debug("Transport already acquired");
+ return 0;
++ }
++
++ u->transport_acquire_pending = true;
+
+ pa_log_debug("Acquiring transport %s", u->transport->path);
+
+ u->stream_fd = u->transport->acquire(u->transport, optional, &u->read_link_mtu, &u->write_link_mtu);
+- if (u->stream_fd < 0)
++ if (u->stream_fd < 0) {
++ u->transport_acquire_pending = false;
+ return -1;
++ }
+
+ u->transport_acquired = true;
+ pa_log_info("Transport %s acquired: fd %d", u->transport->path, u->stream_fd);
+
++ u->transport_acquire_pending = false;
++
+ return 0;
+ }
+
+@@ -766,6 +806,10 @@ static void transport_release(struct userdata *u) {
+
+ /* Run from I/O thread */
+ static void transport_config_mtu(struct userdata *u) {
++
++ pa_log_debug("Configuring MTU for transport of profile %s",
++ pa_bluetooth_profile_to_string(u->profile));
++
+ 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;
+@@ -779,6 +823,9 @@ static void transport_config_mtu(struct userdata *u) {
+ / u->sbc_info.frame_length * u->sbc_info.codesize;
+ }
+
++ if (USE_SCO_OVER_PCM(u))
++ return;
++
+ 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,
+@@ -794,7 +841,7 @@ static void transport_config_mtu(struct userdata *u) {
+ pa_bytes_to_usec(u->read_block_size, &u->sample_spec));
+ }
+
+-/* Run from I/O thread */
++/* Run from I/O thread except in SCO over PCM */
+ static void setup_stream(struct userdata *u) {
+ struct pollfd *pollfd;
+ int one;
+@@ -943,46 +990,52 @@ static int add_source(struct userdata *u) {
+
+ pa_assert(u->transport);
+
+- pa_source_new_data_init(&data);
+- data.module = u->module;
+- data.card = u->card;
+- data.driver = __FILE__;
+- data.name = pa_sprintf_malloc("bluez_source.%s", u->device->address);
+- 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 (USE_SCO_OVER_PCM(u)) {
++ u->source = u->hsp.sco_source;
++ pa_proplist_sets(u->source->proplist, "bluetooth.protocol", pa_bluetooth_profile_to_string(u->profile));
++ } else {
++ pa_source_new_data_init(&data);
++ data.module = u->module;
++ data.card = u->card;
++ data.driver = __FILE__;
++ data.name = pa_sprintf_malloc("bluez_source.%s", u->device->address);
++ 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;
++ }
+
+- 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;
++ u->source = pa_source_new(u->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
++ pa_source_new_data_done(&data);
++ if (!u->source) {
++ pa_log_error("Failed to create source");
++ return -1;
+ }
+
+- u->source = pa_source_new(u->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
+- pa_source_new_data_done(&data);
+- if (!u->source) {
+- pa_log_error("Failed to create source");
+- return -1;
++ u->source->userdata = u;
++ u->source->parent.process_msg = source_process_msg;
+ }
+
+- u->source->userdata = u;
+- u->source->parent.process_msg = source_process_msg;
+-
+ if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT) {
+ pa_source_set_set_volume_callback(u->source, source_set_volume_cb);
+ u->source->n_volume_steps = 16;
+ }
++
+ return 0;
+ }
+
+@@ -1100,52 +1153,67 @@ static int add_sink(struct userdata *u) {
+
+ pa_assert(u->transport);
+
+- pa_sink_new_data_init(&data);
+- data.module = u->module;
+- data.card = u->card;
+- data.driver = __FILE__;
+- data.name = pa_sprintf_malloc("bluez_sink.%s", u->device->address);
+- 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");
++ if (USE_SCO_OVER_PCM(u)) {
++ pa_proplist *p;
+
+- connect_ports(u, &data, PA_DIRECTION_OUTPUT);
++ u->sink = u->hsp.sco_sink;
++ p = pa_proplist_new();
++ pa_proplist_sets(p, "bluetooth.protocol", pa_bluetooth_profile_to_string(u->profile));
++ pa_proplist_update(u->sink->proplist, PA_UPDATE_MERGE, p);
++ pa_proplist_free(p);
++ } else {
++ pa_sink_new_data_init(&data);
++ data.module = u->module;
++ data.card = u->card;
++ data.driver = __FILE__;
++ data.name = pa_sprintf_malloc("bluez_sink.%s", u->device->address);
++ 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;
++ }
+
+- 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;
++ u->sink = pa_sink_new(u->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
++ pa_sink_new_data_done(&data);
++ if (!u->sink) {
++ pa_log_error("Failed to create sink");
++ return -1;
+ }
+
+- u->sink = pa_sink_new(u->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
+- pa_sink_new_data_done(&data);
+- if (!u->sink) {
+- pa_log_error("Failed to create sink");
+- return -1;
++ u->sink->userdata = u;
++ u->sink->parent.process_msg = sink_process_msg;
+ }
+
+- u->sink->userdata = u;
+- u->sink->parent.process_msg = sink_process_msg;
+-
+ if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT) {
+ pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb);
+ u->sink->n_volume_steps = 16;
+ }
++
+ return 0;
+ }
+
+ /* Run from main thread */
+ static void transport_config(struct userdata *u) {
++
++ pa_log_debug("Configuring transport for profile %s",
++ pa_bluetooth_profile_to_string(u->profile));
++
+ 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;
+@@ -1264,11 +1332,18 @@ static int setup_transport(struct userdata *u) {
+ pa_bluetooth_transport *t;
+
+ pa_assert(u);
+- pa_assert(!u->transport);
++ pa_assert(!u->transport_acquired);
+ pa_assert(u->profile != PA_BLUETOOTH_PROFILE_OFF);
+
++ pa_log_debug("profile %s", pa_bluetooth_profile_to_string(u->profile));
++
+ /* check if profile has a transport */
+ t = u->device->transports[u->profile];
++
++ pa_log_debug("profile %s transport %p transport state %s",
++ pa_bluetooth_profile_to_string(u->profile),
++ t, t ? pa_bluetooth_transport_state_to_string(t->state) : "unknown");
++
+ if (!t || t->state <= PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED) {
+ pa_log_warn("Profile has no transport");
+ return -1;
+@@ -1305,11 +1380,16 @@ static int init_profile(struct userdata *u) {
+ pa_assert(u);
+ pa_assert(u->profile != PA_BLUETOOTH_PROFILE_OFF);
+
++ pa_log_debug("Initializing profile %s", pa_bluetooth_profile_to_string(u->profile));
++
+ if (setup_transport(u) < 0)
+ return -1;
+
+ pa_assert(u->transport);
+
++ pa_log_debug("Transport for profile %s successfully setup",
++ pa_bluetooth_profile_to_string(u->profile));
++
+ if (get_profile_direction (u->profile) & PA_DIRECTION_OUTPUT)
+ if (add_sink(u) < 0)
+ r = -1;
+@@ -1517,6 +1597,63 @@ finish:
+ pa_log_debug("IO thread shutting down");
+ }
+
++static int sco_over_pcm_state_update(struct userdata *u, bool changed)
++{
++ pa_assert(u);
++ pa_assert(USE_SCO_OVER_PCM(u));
++
++ pa_log_debug("Updating SCO over PCM state (profile %s, changed %s, stream fd %d)",
++ pa_bluetooth_profile_to_string(u->profile),
++ changed ? "yes" : "no", u->stream_fd);
++
++ if (PA_SINK_IS_OPENED(pa_sink_get_state(u->hsp.sco_sink)) ||
++ PA_SOURCE_IS_OPENED(pa_source_get_state(u->hsp.sco_source))) {
++
++ if (u->stream_fd >= 0)
++ return 0;
++
++ pa_log_debug("Resuming SCO over PCM");
++
++ if (init_profile(u) < 0) {
++ pa_log("Can't resume SCO over PCM");
++ return -1;
++ }
++
++ setup_stream(u);
++
++ return 0;
++ }
++
++ if (changed) {
++ if (u->stream_fd < 0)
++ return 0;
++
++ if (check_proplist(u) == 1) {
++ pa_log_debug("Suspend prevention active, not closing SCO over PCM");
++ return 0;
++ }
++
++ pa_log_debug("Closing SCO over PCM");
++
++ transport_release(u);
++ }
++
++ return 0;
++}
++
++static void stream_died_cb(pa_mainloop_api *ea, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata)
++{
++ struct userdata *u = userdata;
++
++ pa_assert(u);
++ pa_assert(u->transport);
++
++ pa_log_warn("SCO stream went down");
++
++ pa_bluetooth_transport_set_state(u->transport, PA_BLUETOOTH_TRANSPORT_STATE_IDLE);
++
++}
++
+ /* Run from main thread */
+ static int start_thread(struct userdata *u) {
+ pa_assert(u);
+@@ -1527,6 +1664,25 @@ static int start_thread(struct userdata *u) {
+ u->rtpoll = pa_rtpoll_new();
+ pa_thread_mq_init(&u->thread_mq, u->core->mainloop, u->rtpoll);
+
++ if (USE_SCO_OVER_PCM(u)) {
++ if (sco_over_pcm_state_update(u, false) < 0)
++ return -1;
++
++ pa_log_debug("Installing monitor for SCO stream");
++
++ u->stream_event = u->core->mainloop->io_new(u->core->mainloop,
++ u->stream_fd, PA_IO_EVENT_HANGUP | PA_IO_EVENT_ERROR, stream_died_cb, u);
++ if (!u->stream_event) {
++ pa_log_error("Failed to setup monitoring for SCO stream");
++ return -1;
++ }
++
++ pa_sink_ref(u->sink);
++ pa_source_ref(u->source);
++
++ return 0;
++ }
++
+ if (!(u->thread = pa_thread_new("bluetooth", thread_func, u))) {
+ pa_log_error("Failed to create IO thread");
+ return -1;
+@@ -1557,10 +1713,10 @@ static int start_thread(struct userdata *u) {
+ static void stop_thread(struct userdata *u) {
+ pa_assert(u);
+
+- if (u->sink)
++ if (u->sink && !USE_SCO_OVER_PCM(u))
+ pa_sink_unlink(u->sink);
+
+- if (u->source)
++ if (u->source && !USE_SCO_OVER_PCM(u))
+ pa_source_unlink(u->source);
+
+ if (u->thread) {
+@@ -1582,7 +1738,8 @@ static void stop_thread(struct userdata *u) {
+
+ if (u->transport) {
+ transport_release(u);
+- u->transport = NULL;
++ /* Do not set transport pointer to NULL. When failing to switch
++ * profile NULL u->transport would assert. */
+ }
+
+ if (u->sink) {
+@@ -1840,6 +1997,22 @@ static pa_card_profile *create_card_profile(struct userdata *u, const char *uuid
+ return cp;
+ }
+
++static void save_sco_volume_callbacks(struct userdata *u) {
++ pa_assert(u);
++ pa_assert(USE_SCO_OVER_PCM(u));
++
++ u->hsp.sco_sink_set_volume = u->hsp.sco_sink->set_volume;
++ u->hsp.sco_source_set_volume = u->hsp.sco_source->set_volume;
++}
++
++static void restore_sco_volume_callbacks(struct userdata *u) {
++ pa_assert(u);
++ pa_assert(USE_SCO_OVER_PCM(u));
++
++ pa_sink_set_set_volume_callback(u->hsp.sco_sink, u->hsp.sco_sink_set_volume);
++ pa_source_set_set_volume_callback(u->hsp.sco_source, u->hsp.sco_source_set_volume);
++}
++
+ /* Run from main thread */
+ static int set_profile_cb(pa_card *c, pa_card_profile *new_profile) {
+ struct userdata *u;
+@@ -1851,6 +2024,10 @@ static int set_profile_cb(pa_card *c, pa_card_profile *new_profile) {
+
+ p = PA_CARD_PROFILE_DATA(new_profile);
+
++ pa_log_debug("Setting new profile %s for card (current %s)",
++ pa_bluetooth_profile_to_string(*p),
++ pa_bluetooth_profile_to_string(u->profile));
++
+ if (*p != PA_BLUETOOTH_PROFILE_OFF) {
+ const pa_bluetooth_device *d = u->device;
+
+@@ -1957,6 +2134,11 @@ static int add_card(struct userdata *u) {
+ p = PA_CARD_PROFILE_DATA(u->card->active_profile);
+ u->profile = *p;
+
++ if (USE_SCO_OVER_PCM(u))
++ save_sco_volume_callbacks(u);
++
++ pa_log_debug("Created card (current profile %s)",
++ pa_bluetooth_profile_to_string(u->profile));
+ return 0;
+ }
+
+@@ -1966,13 +2148,15 @@ static void handle_transport_state_change(struct userdata *u, struct pa_bluetoot
+ bool release = false;
+ pa_card_profile *cp;
+ pa_device_port *port;
+- pa_available_t oldavail;
+
+ pa_assert(u);
+ pa_assert(t);
+ pa_assert_se(cp = pa_hashmap_get(u->card->profiles, pa_bluetooth_profile_to_string(t->profile)));
+
+- oldavail = cp->available;
++ pa_log_debug("State of transport for profile %s changed to %s",
++ pa_bluetooth_profile_to_string(t->profile),
++ pa_bluetooth_transport_state_to_string(t->state));
++
+ pa_card_profile_set_available(cp, transport_state_to_availability(t->state));
+
+ /* Update port availability */
+@@ -1983,9 +2167,13 @@ static void handle_transport_state_change(struct userdata *u, struct pa_bluetoot
+
+ /* Acquire or release transport as needed */
+ acquire = (t->state == PA_BLUETOOTH_TRANSPORT_STATE_PLAYING && u->profile == t->profile);
+- release = (oldavail != PA_AVAILABLE_NO && t->state != PA_BLUETOOTH_TRANSPORT_STATE_PLAYING && u->profile == t->profile);
++ release = (t->state != PA_BLUETOOTH_TRANSPORT_STATE_PLAYING && u->profile == t->profile);
+
+ if (acquire && transport_acquire(u, true) >= 0) {
++
++ pa_log_debug("Acquiring transport for profile %s",
++ pa_bluetooth_profile_to_string(t->profile));
++
+ if (u->source) {
+ pa_log_debug("Resuming source %s because its transport state changed to playing", u->source->name);
+
+@@ -2013,6 +2201,9 @@ static void handle_transport_state_change(struct userdata *u, struct pa_bluetoot
+ * BlueZ should probably release the transport automatically, and in
+ * that case we would just mark the transport as released */
+
++ pa_log_debug("Releasing transport for profile %s",
++ pa_bluetooth_profile_to_string(t->profile));
++
+ /* Remote side closed the stream so we consider it PA_SUSPEND_USER */
+ if (u->source) {
+ pa_log_debug("Suspending source %s because the remote end closed the stream", u->source->name);
+@@ -2045,6 +2236,10 @@ static pa_hook_result_t transport_state_changed_cb(pa_bluetooth_discovery *y, pa
+ pa_assert(t);
+ pa_assert(u);
+
++ pa_log_debug("State of transport for profile %s has changed to %s",
++ pa_bluetooth_profile_to_string(t->profile),
++ pa_bluetooth_transport_state_to_string(t->state));
++
+ if (t == u->transport && t->state <= PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED)
+ pa_assert_se(pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, "off"), false) >= 0);
+
+@@ -2102,6 +2297,36 @@ static pa_hook_result_t transport_microphone_gain_changed_cb(pa_bluetooth_discov
+ return PA_HOOK_OK;
+ }
+
++static pa_hook_result_t sink_state_changed_cb(pa_core *c, pa_sink *s, struct userdata *u) {
++ pa_assert(c);
++ pa_sink_assert_ref(s);
++ pa_assert(u);
++
++ pa_log_debug("Sink %s state has changed", s->name);
++
++ if (!USE_SCO_OVER_PCM(u) || s != u->hsp.sco_sink)
++ return PA_HOOK_OK;
++
++ sco_over_pcm_state_update(u, true);
++
++ return PA_HOOK_OK;
++}
++
++static pa_hook_result_t source_state_changed_cb(pa_core *c, pa_source *s, struct userdata *u) {
++ pa_assert(c);
++ pa_source_assert_ref(s);
++ pa_assert(u);
++
++ pa_log_debug("Source %s state has changed", s->name);
++
++ if (!USE_SCO_OVER_PCM(u) || s != u->hsp.sco_source)
++ return PA_HOOK_OK;
++
++ sco_over_pcm_state_update(u, true);
++
++ return PA_HOOK_OK;
++}
++
+ /* 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 *m = BLUETOOTH_MSG(obj);
+@@ -2144,6 +2369,18 @@ int pa__init(pa_module* m) {
+ goto fail;
+ }
+
++ if (pa_modargs_get_value(ma, "sco_sink", NULL) &&
++ !(u->hsp.sco_sink = pa_namereg_get(m->core, pa_modargs_get_value(ma, "sco_sink", NULL), PA_NAMEREG_SINK))) {
++ pa_log("SCO sink not found");
++ goto fail;
++ }
++
++ if (pa_modargs_get_value(ma, "sco_source", NULL) &&
++ !(u->hsp.sco_source = pa_namereg_get(m->core, pa_modargs_get_value(ma, "sco_source", NULL), PA_NAMEREG_SOURCE))) {
++ pa_log("SCO source not found");
++ goto fail;
++ }
++
+ if ((u->discovery = pa_shared_get(u->core, "bluetooth-discovery")))
+ pa_bluetooth_discovery_ref(u->discovery);
+ else {
+@@ -2156,7 +2393,7 @@ int pa__init(pa_module* m) {
+ goto fail;
+ }
+
+- pa_modargs_free(ma);
++ u->modargs = ma;
+
+ u->device_connection_changed_slot =
+ pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED),
+@@ -2172,6 +2409,13 @@ int pa__init(pa_module* m) {
+ u->transport_microphone_gain_changed_slot =
+ pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_MICROPHONE_GAIN_CHANGED), PA_HOOK_NORMAL, (pa_hook_cb_t) transport_microphone_gain_changed_cb, u);
+
++ u->sink_state_changed_slot =
++ pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED],
++ PA_HOOK_NORMAL, (pa_hook_cb_t) sink_state_changed_cb, u);
++
++ u->source_state_changed_slot =
++ pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED],
++ PA_HOOK_NORMAL, (pa_hook_cb_t) source_state_changed_cb, u);
+
+ if (add_card(u) < 0)
+ goto fail;
+@@ -2231,12 +2475,20 @@ void pa__done(pa_module *m) {
+ if (u->transport_microphone_gain_changed_slot)
+ pa_hook_slot_free(u->transport_microphone_gain_changed_slot);
+
++ if (u->sink_state_changed_slot)
++ pa_hook_slot_free(u->sink_state_changed_slot);
++
++ if (u->source_state_changed_slot)
++ pa_hook_slot_free(u->source_state_changed_slot);
+ if (u->sbc_info.buffer)
+ pa_xfree(u->sbc_info.buffer);
+
+ if (u->sbc_info.sbc_initialized)
+ sbc_finish(&u->sbc_info.sbc);
+
++ if (USE_SCO_OVER_PCM(u))
++ restore_sco_volume_callbacks(u);
++
+ if (u->msg)
+ pa_xfree(u->msg);
+
+@@ -2249,6 +2501,8 @@ void pa__done(pa_module *m) {
+ pa_xfree(u->output_port_name);
+ pa_xfree(u->input_port_name);
+
++ if (u->modargs)
++ pa_modargs_free(u->modargs);
+ pa_xfree(u);
+ }
+
+diff --git a/src/modules/bluetooth/module-bluez5-discover.c b/src/modules/bluetooth/module-bluez5-discover.c
+index 1ccc1d1..40ce562 100644
+--- a/src/modules/bluetooth/module-bluez5-discover.c
++++ b/src/modules/bluetooth/module-bluez5-discover.c
+@@ -38,15 +38,20 @@ PA_MODULE_VERSION(PACKAGE_VERSION);
+ PA_MODULE_LOAD_ONCE(true);
+ PA_MODULE_USAGE(
+ "headset=ofono|native|auto"
++ "sco_sink=<name of sink> "
++ "sco_source=<name of source> "
+ );
+
+ static const char* const valid_modargs[] = {
+ "headset",
++ "sco_sink",
++ "sco_source",
+ NULL
+ };
+
+ struct userdata {
+ pa_module *module;
++ pa_modargs *modargs;
+ pa_core *core;
+ pa_hashmap *loaded_device_paths;
+ pa_hook_slot *device_connection_changed_slot;
+@@ -73,6 +78,16 @@ static pa_hook_result_t device_connection_changed_cb(pa_bluetooth_discovery *y,
+ pa_module *m;
+ char *args = pa_sprintf_malloc("path=%s", d->path);
+
++ if (pa_modargs_get_value(u->modargs, "sco_sink", NULL) &&
++ pa_modargs_get_value(u->modargs, "sco_source", NULL)) {
++ char *tmp;
++
++ tmp = pa_sprintf_malloc("%s sco_sink=\"%s\" sco_source=\"%s\"", args,
++ pa_modargs_get_value(u->modargs, "sco_sink", NULL),
++ pa_modargs_get_value(u->modargs, "sco_source", NULL));
++ pa_xfree(args);
++ args = tmp;
++ }
+ pa_log_debug("Loading module-bluez5-device %s", args);
+ m = pa_module_load(u->module->core, "module-bluez5-device", args);
+ pa_xfree(args);
+@@ -123,6 +138,7 @@ int pa__init(pa_module *m) {
+
+ m->userdata = u = pa_xnew0(struct userdata, 1);
+ u->module = m;
++ u->modargs = ma;
+ u->core = m->core;
+ u->loaded_device_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
+@@ -133,7 +149,6 @@ int pa__init(pa_module *m) {
+ pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED),
+ PA_HOOK_NORMAL, (pa_hook_cb_t) device_connection_changed_cb, u);
+
+- pa_modargs_free(ma);
+ return 0;
+
+ fail:
+@@ -159,5 +174,7 @@ void pa__done(pa_module *m) {
+ if (u->loaded_device_paths)
+ pa_hashmap_free(u->loaded_device_paths);
+
++ if (u->modargs)
++ pa_modargs_free(u->modargs);
+ pa_xfree(u);
+ }
+--
+2.6.2
+
diff --git a/debian/patches/0503-bluetooth-bluez5-ofono-add-support-for-spekaer-micro.patch b/debian/patches/0503-bluetooth-bluez5-ofono-add-support-for-spekaer-micro.patch
new file mode 100644
index 0000000..1e40109
--- /dev/null
+++ b/debian/patches/0503-bluetooth-bluez5-ofono-add-support-for-spekaer-micro.patch
@@ -0,0 +1,78 @@
+From 8b4525c069d19f783ea2a70415621da71d1fede6 Mon Sep 17 00:00:00 2001
+From: Simon Fels <simon.fels at canonical.com>
+Date: Sun, 1 Nov 2015 16:40:16 +0100
+Subject: [PATCH 503/507] bluetooth: bluez5: ofono: add support for
+ spekaer/microphone gain setting
+
+---
+ src/modules/bluetooth/backend-ofono.c | 47 +++++++++++++++++++++++++++++++++++
+ 1 file changed, 47 insertions(+)
+
+diff --git a/src/modules/bluetooth/backend-ofono.c b/src/modules/bluetooth/backend-ofono.c
+index c77be54..1c10716 100644
+--- a/src/modules/bluetooth/backend-ofono.c
++++ b/src/modules/bluetooth/backend-ofono.c
+@@ -209,6 +209,51 @@ static void hf_audio_agent_transport_release(pa_bluetooth_transport *t) {
+ pa_bluetooth_transport_set_state(t, PA_BLUETOOTH_TRANSPORT_STATE_IDLE);
+ }
+ }
++
++static void set_property(pa_dbus_connection *conn, const char *bus, const char *path, const char *interface,
++ const char *prop_name, int prop_type, void *prop_value) {
++ DBusMessage *m;
++ DBusMessageIter i;
++
++ pa_assert(conn);
++ pa_assert(path);
++ pa_assert(interface);
++ pa_assert(prop_name);
++
++ pa_assert_se(m = dbus_message_new_method_call(bus, path, interface, "SetProperty"));
++ dbus_message_iter_init_append(m, &i);
++ dbus_message_iter_append_basic(&i, DBUS_TYPE_STRING, &prop_name);
++ pa_dbus_append_basic_variant(&i, prop_type, prop_value);
++
++ dbus_message_set_no_reply(m, true);
++ pa_assert_se(dbus_connection_send(pa_dbus_connection_get(conn), m, NULL));
++ dbus_message_unref(m);
++}
++
++static void hf_audio_card_set_speaker_gain(pa_bluetooth_transport *t, uint16_t gain)
++{
++ struct hf_audio_card *card = t->userdata;
++
++ pa_assert(card);
++
++ pa_log_debug("Setting speaker gain for card %s to %u",
++ card->path, gain);
++
++ set_property(card->backend->connection, OFONO_SERVICE, card->path,
++ HF_AUDIO_CARD_INTERFACE, "SpeakerGain", DBUS_TYPE_UINT16, &gain);
++}
++
++static void hf_audio_card_set_microphone_gain(pa_bluetooth_transport *t, uint16_t gain)
++{
++ struct hf_audio_card *card = t->userdata;
++
++ pa_assert(card);
++
++ pa_log_debug("Setting microphone gain for card %s to %u",
++ card->path, gain);
++
++ set_property(card->backend->connection, OFONO_SERVICE, card->path,
++ HF_AUDIO_CARD_INTERFACE, "MicrophoneGain", DBUS_TYPE_UINT16, &gain);
+ }
+
+ static void hf_audio_agent_card_found(pa_bluetooth_backend *backend, const char *path, DBusMessageIter *props_i) {
+@@ -271,6 +316,8 @@ static void hf_audio_agent_card_found(pa_bluetooth_backend *backend, const char
+ card->transport = pa_bluetooth_transport_new(d, backend->ofono_bus_id, path, profile, NULL, 0);
+ card->transport->acquire = hf_audio_agent_transport_acquire;
+ card->transport->release = hf_audio_agent_transport_release;
++ card->transport->set_speaker_gain = hf_audio_card_set_speaker_gain;
++ card->transport->set_microphone_gain = hf_audio_card_set_microphone_gain;
+ card->transport->userdata = card;
+
+ pa_bluetooth_transport_put(card->transport);
+--
+2.6.2
+
diff --git a/debian/patches/0504-bluetooth-bluez5-add-support-for-both-mode.patch b/debian/patches/0504-bluetooth-bluez5-add-support-for-both-mode.patch
new file mode 100644
index 0000000..90a5796
--- /dev/null
+++ b/debian/patches/0504-bluetooth-bluez5-add-support-for-both-mode.patch
@@ -0,0 +1,78 @@
+From 9d69ecf36d2ef447208d287566727322edf82e2f Mon Sep 17 00:00:00 2001
+From: Simon Fels <simon.fels at canonical.com>
+Date: Sun, 1 Nov 2015 16:41:04 +0100
+Subject: [PATCH 504/507] bluetooth: bluez5: add support for both mode
+
+---
+ src/modules/bluetooth/bluez5-util.c | 4 +++-
+ src/modules/bluetooth/bluez5-util.h | 1 +
+ src/modules/bluetooth/module-bluez5-discover.c | 10 ++++++++--
+ 3 files changed, 12 insertions(+), 3 deletions(-)
+
+diff --git a/src/modules/bluetooth/bluez5-util.c b/src/modules/bluetooth/bluez5-util.c
+index 2f2f277..747384a 100644
+--- a/src/modules/bluetooth/bluez5-util.c
++++ b/src/modules/bluetooth/bluez5-util.c
+@@ -915,7 +915,9 @@ static void get_managed_objects_reply(DBusPendingCall *pending, void *userdata)
+
+ if (!y->ofono_backend && y->headset_backend != HEADSET_BACKEND_NATIVE)
+ y->ofono_backend = pa_bluetooth_ofono_backend_new(y->core, y);
+- if (!y->ofono_backend && !y->native_backend && y->headset_backend != HEADSET_BACKEND_OFONO)
++ if (!y->native_backend && y->headset_backend == HEADSET_BACKEND_BOTH)
++ y->native_backend = pa_bluetooth_native_backend_new(y->core, y);
++ else if (!y->ofono_backend && !y->native_backend)
+ y->native_backend = pa_bluetooth_native_backend_new(y->core, y);
+
+ finish:
+diff --git a/src/modules/bluetooth/bluez5-util.h b/src/modules/bluetooth/bluez5-util.h
+index df44c01..3f97de7 100644
+--- a/src/modules/bluetooth/bluez5-util.h
++++ b/src/modules/bluetooth/bluez5-util.h
+@@ -156,6 +156,7 @@ const char *pa_bluetooth_transport_state_to_string(pa_bluetooth_transport_state_
+ #define HEADSET_BACKEND_OFONO 0
+ #define HEADSET_BACKEND_NATIVE 1
+ #define HEADSET_BACKEND_AUTO 2
++#define HEADSET_BACKEND_BOTH 3
+
+ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *core, int headset_backend);
+ pa_bluetooth_discovery* pa_bluetooth_discovery_ref(pa_bluetooth_discovery *y);
+diff --git a/src/modules/bluetooth/module-bluez5-discover.c b/src/modules/bluetooth/module-bluez5-discover.c
+index 40ce562..831e9e2 100644
+--- a/src/modules/bluetooth/module-bluez5-discover.c
++++ b/src/modules/bluetooth/module-bluez5-discover.c
+@@ -37,7 +37,7 @@ PA_MODULE_DESCRIPTION("Detect available BlueZ 5 Bluetooth audio devices and load
+ PA_MODULE_VERSION(PACKAGE_VERSION);
+ PA_MODULE_LOAD_ONCE(true);
+ PA_MODULE_USAGE(
+- "headset=ofono|native|auto"
++ "headset=ofono|native|auto|both "
+ "sco_sink=<name of sink> "
+ "sco_source=<name of source> "
+ );
+@@ -106,7 +106,11 @@ static pa_hook_result_t device_connection_changed_cb(pa_bluetooth_discovery *y,
+ }
+
+ #ifdef HAVE_BLUEZ_5_NATIVE_HEADSET
++#ifdef HAVE_BLUEZ_5_OFONO_HEADSET
++const char *default_headset_backend = "both";
++#else
+ const char *default_headset_backend = "native";
++#endif
+ #else
+ const char *default_headset_backend = "ofono";
+ #endif
+@@ -131,8 +135,10 @@ int pa__init(pa_module *m) {
+ headset_backend = HEADSET_BACKEND_NATIVE;
+ else if (pa_streq(headset_str, "auto"))
+ headset_backend = HEADSET_BACKEND_AUTO;
++ else if (pa_streq(headset_str, "both"))
++ headset_backend = HEADSET_BACKEND_BOTH;
+ else {
+- pa_log("headset parameter must be either ofono, native or auto (found %s)", headset_str);
++ pa_log("headset parameter must be either ofono, native, auto or both (found %s)", headset_str);
+ goto fail;
+ }
+
+--
+2.6.2
+
diff --git a/debian/patches/0505-bluetooth-bluez5-let-user-specify-a-default-profile-.patch b/debian/patches/0505-bluetooth-bluez5-let-user-specify-a-default-profile-.patch
new file mode 100644
index 0000000..60a3ee1
--- /dev/null
+++ b/debian/patches/0505-bluetooth-bluez5-let-user-specify-a-default-profile-.patch
@@ -0,0 +1,105 @@
+From c4f7b88b81042506fd1920c8daa5d80b4e715276 Mon Sep 17 00:00:00 2001
+From: Simon Fels <simon.fels at canonical.com>
+Date: Sun, 1 Nov 2015 16:42:53 +0100
+Subject: [PATCH 505/507] bluetooth: bluez5: let user specify a default profile
+ to set
+
+---
+ src/modules/bluetooth/module-bluez5-device.c | 32 ++++++++++++++++++++++++++++
+ 1 file changed, 32 insertions(+)
+
+diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c
+index 999c254..07f88d0 100644
+--- a/src/modules/bluetooth/module-bluez5-device.c
++++ b/src/modules/bluetooth/module-bluez5-device.c
+@@ -58,6 +58,7 @@ PA_MODULE_DESCRIPTION("BlueZ 5 Bluetooth audio sink and source");
+ PA_MODULE_VERSION(PACKAGE_VERSION);
+ PA_MODULE_LOAD_ONCE(false);
+ PA_MODULE_USAGE("path=<device object path> "
++ "profile=<a2dp|hsp|hfgw> "
+ "sco_sink=<name of sink> "
+ "sco_source=<name of source> ");
+
+@@ -74,6 +75,7 @@ PA_MODULE_USAGE("path=<device object path> "
+ #define USE_SCO_OVER_PCM(u) (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT && (u->hsp.sco_sink && u->hsp.sco_source))
+ static const char* const valid_modargs[] = {
+ "path",
++ "profile",
+ "sco_sink",
+ "sco_source",
+ NULL
+@@ -158,6 +160,7 @@ struct userdata {
+ struct sbc_info sbc_info;
+ struct hsp_info hsp;
+
++ char *default_profile;
+ bool transport_acquire_pending;
+ pa_io_event *stream_event;
+ };
+@@ -2069,6 +2072,7 @@ static int add_card(struct userdata *u) {
+ pa_bluetooth_profile_t *p;
+ const char *uuid;
+ void *state;
++ const char *default_profile;
+
+ pa_assert(u);
+ pa_assert(u->device);
+@@ -2121,6 +2125,16 @@ static int add_card(struct userdata *u) {
+ *p = PA_BLUETOOTH_PROFILE_OFF;
+ pa_hashmap_put(data.profiles, cp->name, cp);
+
++ if ((default_profile = pa_modargs_get_value(u->modargs, "profile", NULL))) {
++ if (pa_hashmap_get(data.profiles, default_profile)) {
++ pa_card_new_data_set_profile(&data, default_profile);
++ pa_log_debug("Using %s profile as default", default_profile);
++ u->default_profile = pa_xstrdup(default_profile);
++ }
++ else
++ pa_log_warn("Profile '%s' not valid or not supported by device.", default_profile);
++ }
++
+ u->card = pa_card_new(u->core, &data);
+ pa_card_new_data_done(&data);
+ if (!u->card) {
+@@ -2132,6 +2146,15 @@ static int add_card(struct userdata *u) {
+ u->card->set_profile = set_profile_cb;
+
+ p = PA_CARD_PROFILE_DATA(u->card->active_profile);
++
++ if (*p != PA_BLUETOOTH_PROFILE_OFF && (!d->transports[*p] ||
++ d->transports[*p]->state == PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED)) {
++ pa_log_warn("Default profile not connected, selecting off profile");
++ u->card->active_profile = pa_hashmap_get(u->card->profiles, "off");
++ u->card->save_profile = false;
++ }
++
++ p = PA_CARD_PROFILE_DATA(u->card->active_profile);
+ u->profile = *p;
+
+ if (USE_SCO_OVER_PCM(u))
+@@ -2246,6 +2269,11 @@ static pa_hook_result_t transport_state_changed_cb(pa_bluetooth_discovery *y, pa
+ if (t->device == u->device)
+ handle_transport_state_change(u, t);
+
++ /* For the case that we've currently the 'off' profile set we need to move
++ * on to a possible configured default profile. */
++ if (u->profile == PA_BLUETOOTH_PROFILE_OFF && pa_bluetooth_device_any_transport_connected(u->device) && u->default_profile)
++ pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, u->default_profile), false);
++
+ return PA_HOOK_OK;
+ }
+
+@@ -2503,6 +2531,10 @@ void pa__done(pa_module *m) {
+
+ if (u->modargs)
+ pa_modargs_free(u->modargs);
++
++ if (u->default_profile)
++ pa_xfree(u->default_profile);
++
+ pa_xfree(u);
+ }
+
+--
+2.6.2
+
diff --git a/debian/patches/0506-bluetooth-bluez5-prevent-SCO-sink-source-to-be-suspe.patch b/debian/patches/0506-bluetooth-bluez5-prevent-SCO-sink-source-to-be-suspe.patch
new file mode 100644
index 0000000..9cbe51a
--- /dev/null
+++ b/debian/patches/0506-bluetooth-bluez5-prevent-SCO-sink-source-to-be-suspe.patch
@@ -0,0 +1,203 @@
+From 0eec59e95eb6bb396aaa97fddcf71d10813a3b7d Mon Sep 17 00:00:00 2001
+From: Simon Fels <simon.fels at canonical.com>
+Date: Sun, 1 Nov 2015 16:44:45 +0100
+Subject: [PATCH 506/507] bluetooth: bluez5: prevent SCO sink/source to be
+ suspended
+
+This only works in conjunction with changes to the droid module
+as this will set the property according to the current profile
+selected for the droid card.
+---
+ src/modules/bluetooth/module-bluez5-device.c | 83 ++++++++++++++++++++++++++
+ src/modules/bluetooth/module-bluez5-discover.c | 13 ++++
+ 2 files changed, 96 insertions(+)
+
+diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c
+index 07f88d0..f1e887c 100644
+--- a/src/modules/bluetooth/module-bluez5-device.c
++++ b/src/modules/bluetooth/module-bluez5-device.c
+@@ -73,6 +73,7 @@ PA_MODULE_USAGE("path=<device object path> "
+ #define HSP_MAX_GAIN 15
+
+ #define USE_SCO_OVER_PCM(u) (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT && (u->hsp.sco_sink && u->hsp.sco_source))
++
+ static const char* const valid_modargs[] = {
+ "path",
+ "profile",
+@@ -127,6 +128,9 @@ struct userdata {
+ pa_hook_slot *sink_state_changed_slot;
+ pa_hook_slot *source_state_changed_slot;
+
++ pa_hook_slot *sco_sink_proplist_changed_slot;
++ bool prevent_suspend_transport;
++
+ pa_bluetooth_discovery *discovery;
+ pa_bluetooth_device *device;
+ pa_bluetooth_transport *transport;
+@@ -1042,6 +1046,69 @@ static int add_source(struct userdata *u) {
+ return 0;
+ }
+
++#define HSP_PREVENT_SUSPEND_STR "bluetooth.hsp.prevent.suspend.transport"
++
++/* Check and update prevent_suspend_transport value from sco sink proplist.
++ *
++ * Return < 0 if sink proplist doesn't contain HSP_PREVENT_SUSPEND_STR value,
++ * 1 if value is 'true'
++ * 0 if value is something else. */
++static int check_proplist(struct userdata *u) {
++ int ret;
++ const char *str;
++
++ pa_assert(u);
++ pa_assert(u->hsp.sco_sink);
++
++ if ((str = pa_proplist_gets(u->hsp.sco_sink->proplist, HSP_PREVENT_SUSPEND_STR))) {
++ if (pa_streq(str, "true"))
++ ret = 1;
++ else
++ ret = 0;
++ } else
++ ret = -1;
++
++ u->prevent_suspend_transport = ret == 1;
++
++ pa_log_debug("Set %s %s (ret %d)", HSP_PREVENT_SUSPEND_STR, u->prevent_suspend_transport ? "true" : "false", ret);
++
++ return ret;
++}
++
++/* There are cases where keeping the transport running even when sco sink and source are suspended
++ * is needed.
++ * To work with these cases, check sco.sink for bluetooth.hsp.prevent.suspend.transport value, and
++ * when set to true prevent closing the transport when sink suspends.
++ * Also, if the sink&source are suspended when sco-sink suspend.transport value changes to true,
++ * bring sco transport up. When suspend.transport value changes to false while sink&source are suspended,
++ * tear down the transport. */
++static pa_hook_result_t update_allow_release_cb(pa_core *c, pa_sink *s, struct userdata *u) {
++ pa_assert(u);
++ pa_assert(s);
++
++ if (!u->hsp.sco_sink || u->hsp.sco_sink != s)
++ return PA_HOOK_OK;
++
++ if (check_proplist(u) < 0)
++ return PA_HOOK_OK;
++
++ if (!USE_SCO_OVER_PCM(u)) {
++ pa_log_debug("SCO sink not available.");
++ return PA_HOOK_OK;
++ }
++
++ if (!PA_SINK_IS_OPENED(pa_sink_get_state(u->hsp.sco_sink)) &&
++ !PA_SOURCE_IS_OPENED(pa_source_get_state(u->hsp.sco_source))) {
++
++ pa_log_debug("Resuming SCO sink");
++
++ /* Clear all suspend bits, effectively resuming SCO sink for a while. */
++ pa_sink_suspend(s, false, PA_SUSPEND_ALL);
++ }
++
++ return PA_HOOK_OK;
++}
++
+ /* 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;
+@@ -2036,6 +2103,13 @@ static int set_profile_cb(pa_card *c, pa_card_profile *new_profile) {
+
+ if (!d->transports[*p] || d->transports[*p]->state <= PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED) {
+ pa_log_warn("Refused to switch profile to %s: Not connected", new_profile->name);
++
++ /* For the rare case that we were requested to switch to A2DP
++ * but that failed (due the profile got disconnected) we switch
++ * to off */
++ if (*p == PA_BLUETOOTH_PROFILE_A2DP_SINK)
++ pa_assert_se(pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, "off"), false) >= 0);
++
+ return -PA_ERR_IO;
+ }
+ }
+@@ -2162,6 +2236,7 @@ static int add_card(struct userdata *u) {
+
+ pa_log_debug("Created card (current profile %s)",
+ pa_bluetooth_profile_to_string(u->profile));
++
+ return 0;
+ }
+
+@@ -2445,6 +2520,10 @@ int pa__init(pa_module* m) {
+ pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED],
+ PA_HOOK_NORMAL, (pa_hook_cb_t) source_state_changed_cb, u);
+
++ u->sco_sink_proplist_changed_slot =
++ pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED],
++ PA_HOOK_NORMAL, (pa_hook_cb_t) update_allow_release_cb, u);
++
+ if (add_card(u) < 0)
+ goto fail;
+
+@@ -2508,6 +2587,10 @@ void pa__done(pa_module *m) {
+
+ if (u->source_state_changed_slot)
+ pa_hook_slot_free(u->source_state_changed_slot);
++
++ if (u->sco_sink_proplist_changed_slot)
++ pa_hook_slot_free(u->sco_sink_proplist_changed_slot);
++
+ if (u->sbc_info.buffer)
+ pa_xfree(u->sbc_info.buffer);
+
+diff --git a/src/modules/bluetooth/module-bluez5-discover.c b/src/modules/bluetooth/module-bluez5-discover.c
+index 831e9e2..1f249cc 100644
+--- a/src/modules/bluetooth/module-bluez5-discover.c
++++ b/src/modules/bluetooth/module-bluez5-discover.c
+@@ -38,12 +38,14 @@ PA_MODULE_VERSION(PACKAGE_VERSION);
+ PA_MODULE_LOAD_ONCE(true);
+ PA_MODULE_USAGE(
+ "headset=ofono|native|auto|both "
++ "profile=<a2dp|hsp|hfgw> "
+ "sco_sink=<name of sink> "
+ "sco_source=<name of source> "
+ );
+
+ static const char* const valid_modargs[] = {
+ "headset",
++ "profile",
+ "sco_sink",
+ "sco_source",
+ NULL
+@@ -78,6 +80,15 @@ static pa_hook_result_t device_connection_changed_cb(pa_bluetooth_discovery *y,
+ pa_module *m;
+ char *args = pa_sprintf_malloc("path=%s", d->path);
+
++ if (pa_modargs_get_value(u->modargs, "profile", NULL)) {
++ char *profile;
++
++ profile = pa_sprintf_malloc("%s profile=\"%s\"", args,
++ pa_modargs_get_value(u->modargs, "profile", NULL));
++ pa_xfree(args);
++ args = profile;
++ }
++
+ if (pa_modargs_get_value(u->modargs, "sco_sink", NULL) &&
+ pa_modargs_get_value(u->modargs, "sco_source", NULL)) {
+ char *tmp;
+@@ -88,6 +99,7 @@ static pa_hook_result_t device_connection_changed_cb(pa_bluetooth_discovery *y,
+ pa_xfree(args);
+ args = tmp;
+ }
++
+ pa_log_debug("Loading module-bluez5-device %s", args);
+ m = pa_module_load(u->module->core, "module-bluez5-device", args);
+ pa_xfree(args);
+@@ -182,5 +194,6 @@ void pa__done(pa_module *m) {
+
+ if (u->modargs)
+ pa_modargs_free(u->modargs);
++
+ pa_xfree(u);
+ }
+--
+2.6.2
+
diff --git a/debian/patches/0507-bluetooth-bluez5-drop-save-restore-of-SCO-sink-sourc.patch b/debian/patches/0507-bluetooth-bluez5-drop-save-restore-of-SCO-sink-sourc.patch
new file mode 100644
index 0000000..9d408f0
--- /dev/null
+++ b/debian/patches/0507-bluetooth-bluez5-drop-save-restore-of-SCO-sink-sourc.patch
@@ -0,0 +1,71 @@
+From 0603a31eb0a5d6a0247b1f754501230f97749aca Mon Sep 17 00:00:00 2001
+From: Simon Fels <simon.fels at canonical.com>
+Date: Sun, 1 Nov 2015 16:51:01 +0100
+Subject: [PATCH 507/507] bluetooth: bluez5: drop save/restore of SCO
+ sink/source volume set callbacks
+
+Still needs to be verified that this can be dropped.
+---
+ src/modules/bluetooth/module-bluez5-device.c | 24 ------------------------
+ 1 file changed, 24 deletions(-)
+
+diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c
+index f1e887c..c153aa5 100644
+--- a/src/modules/bluetooth/module-bluez5-device.c
++++ b/src/modules/bluetooth/module-bluez5-device.c
+@@ -109,9 +109,7 @@ typedef struct sbc_info {
+
+ struct hsp_info {
+ pa_sink *sco_sink;
+- void (*sco_sink_set_volume)(pa_sink *s);
+ pa_source *sco_source;
+- void (*sco_source_set_volume)(pa_source *s);
+ };
+
+ struct userdata {
+@@ -2067,22 +2065,6 @@ static pa_card_profile *create_card_profile(struct userdata *u, const char *uuid
+ return cp;
+ }
+
+-static void save_sco_volume_callbacks(struct userdata *u) {
+- pa_assert(u);
+- pa_assert(USE_SCO_OVER_PCM(u));
+-
+- u->hsp.sco_sink_set_volume = u->hsp.sco_sink->set_volume;
+- u->hsp.sco_source_set_volume = u->hsp.sco_source->set_volume;
+-}
+-
+-static void restore_sco_volume_callbacks(struct userdata *u) {
+- pa_assert(u);
+- pa_assert(USE_SCO_OVER_PCM(u));
+-
+- pa_sink_set_set_volume_callback(u->hsp.sco_sink, u->hsp.sco_sink_set_volume);
+- pa_source_set_set_volume_callback(u->hsp.sco_source, u->hsp.sco_source_set_volume);
+-}
+-
+ /* Run from main thread */
+ static int set_profile_cb(pa_card *c, pa_card_profile *new_profile) {
+ struct userdata *u;
+@@ -2231,9 +2213,6 @@ static int add_card(struct userdata *u) {
+ p = PA_CARD_PROFILE_DATA(u->card->active_profile);
+ u->profile = *p;
+
+- if (USE_SCO_OVER_PCM(u))
+- save_sco_volume_callbacks(u);
+-
+ pa_log_debug("Created card (current profile %s)",
+ pa_bluetooth_profile_to_string(u->profile));
+
+@@ -2597,9 +2576,6 @@ void pa__done(pa_module *m) {
+ if (u->sbc_info.sbc_initialized)
+ sbc_finish(&u->sbc_info.sbc);
+
+- if (USE_SCO_OVER_PCM(u))
+- restore_sco_volume_callbacks(u);
+-
+ if (u->msg)
+ pa_xfree(u->msg);
+
+--
+2.6.2
+
diff --git a/debian/patches/series b/debian/patches/series
index 51ea47d..03a7000 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -23,3 +23,12 @@
0409-Trust-store-patch.patch
0410-Add-thread-to-activate-trust-store-interface.patch
0417-increase-timeout-check-apparmor.patch
+
+# Ubuntu touch: enable bluez5 HFP over ofono support
+0501-bluetooth-bluez5-ofono-add-support-for-HFP-gateway-r.patch
+0502-bluetooth-bluez5-bring-back-SCO-over-PCM-support.patch
+0503-bluetooth-bluez5-ofono-add-support-for-spekaer-micro.patch
+0504-bluetooth-bluez5-add-support-for-both-mode.patch
+0505-bluetooth-bluez5-let-user-specify-a-default-profile-.patch
+0506-bluetooth-bluez5-prevent-SCO-sink-source-to-be-suspe.patch
+0507-bluetooth-bluez5-drop-save-restore-of-SCO-sink-sourc.patch
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-pulseaudio/pulseaudio.git
More information about the pkg-pulseaudio-devel
mailing list