[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