summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWim Taymans <wtaymans@redhat.com>2015-02-16 11:58:36 +0100
committerWim Taymans <wtaymans@redhat.com>2015-02-23 15:47:20 +0100
commit20736fc7429e0fe4b21b459ab2d602d05cdb884c (patch)
treeb414756dd7acdf0e7377901cbf685da46641fde8
parentaa3a27d5cdad9dd43b9fe79a13fd09b50d4b7682 (diff)
backend-native: add support for the HSP HeadeSet profilehsp-hs-profile
In addition to the HSP Audio Gateway, also add support for the HeadSet profile in the native bluetooth backend. With this profile you can use pulseaudio as a headset. In the headset role, we create source and sink to receive and send the samples from the gateway, respectively. The loopback device will automatically link these to a sink and source for playback. Because this makes the source the speaker and the sink the microphone, we need to reverse the roles of source and sink compared to the gateway role. In the gateway role, adjusting the sink volume generates a +VGS command to set the volume on the headset. Likewise, receiving AT+VGS updates the sink volume. In the headset role, receiving a +VGS should set the source volume and any source volume changes should be reported back to the gateway with AT+VGS.
-rw-r--r--src/modules/bluetooth/backend-native.c238
-rw-r--r--src/modules/bluetooth/module-bluez5-device.c32
2 files changed, 222 insertions, 48 deletions
diff --git a/src/modules/bluetooth/backend-native.c b/src/modules/bluetooth/backend-native.c
index 8d9d95c61..a33edc371 100644
--- a/src/modules/bluetooth/backend-native.c
+++ b/src/modules/bluetooth/backend-native.c
@@ -44,9 +44,11 @@ struct pa_bluetooth_backend {
PA_LLIST_HEAD(pa_dbus_pending, pending);
};
-struct transport_rfcomm {
+struct transport_data {
int rfcomm_fd;
pa_io_event *rfcomm_io;
+ int sco_fd;
+ pa_io_event *sco_io;
pa_mainloop_api *mainloop;
};
@@ -59,6 +61,9 @@ struct transport_rfcomm {
#define BLUEZ_PROFILE_INTERFACE BLUEZ_SERVICE ".Profile1"
#define HSP_AG_PROFILE "/Profile/HSPAGProfile"
+#define HSP_HS_PROFILE "/Profile/HSPHSProfile"
+
+#define HSP_HS_DEFAULT_CHANNEL 3
#define PROFILE_INTROSPECT_XML \
DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
@@ -100,7 +105,7 @@ static pa_dbus_pending* send_and_add_to_pending(pa_bluetooth_backend *backend, D
return p;
}
-static int bluez5_sco_acquire_cb(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu) {
+static int sco_do_connect(pa_bluetooth_transport *t) {
pa_bluetooth_device *d = t->device;
struct sockaddr_sco addr;
int err, i;
@@ -137,12 +142,51 @@ static int bluez5_sco_acquire_cb(pa_bluetooth_transport *t, bool optional, size_
addr.sco_family = AF_BLUETOOTH;
bacpy(&addr.sco_bdaddr, &dst);
- pa_log_info ("doing connect\n");
+ pa_log_info ("doing connect");
err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS)) {
pa_log_error("connect(): %s", pa_cstrerror(errno));
goto fail_close;
}
+ return sock;
+
+fail_close:
+ close(sock);
+ return -1;
+}
+
+static int sco_do_accept(pa_bluetooth_transport *t) {
+ struct transport_data *trd = t->userdata;
+ struct sockaddr_sco addr;
+ socklen_t optlen;
+ int sock;
+
+ memset(&addr, 0, sizeof(addr));
+ optlen = sizeof(addr);
+
+ pa_log_info ("doing accept");
+ sock = accept(trd->sco_fd, (struct sockaddr *) &addr, &optlen);
+ if (sock < 0) {
+ if (errno != EAGAIN)
+ pa_log_error("accept(): %s", pa_cstrerror(errno));
+ goto fail;
+ }
+ return sock;
+
+fail:
+ return -1;
+}
+
+static int bluez5_sco_acquire_cb(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu) {
+ int sock;
+
+ if (optional)
+ sock = sco_do_accept(t);
+ else
+ sock = sco_do_connect(t);
+
+ if (sock < 0)
+ goto fail;
/* The "48" below is hardcoded until we get meaningful MTU values exposed
* by the kernel */
@@ -155,8 +199,7 @@ static int bluez5_sco_acquire_cb(pa_bluetooth_transport *t, bool optional, size_
return sock;
-fail_close:
- close(sock);
+fail:
return -1;
}
@@ -165,6 +208,62 @@ static void bluez5_sco_release_cb(pa_bluetooth_transport *t) {
/* device will close the SCO socket for us */
}
+static void sco_io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
+ pa_bluetooth_transport *t = userdata;
+
+ pa_assert(io);
+ pa_assert(t);
+
+ if (events & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) {
+ pa_log_error("error listening SCO connection: %s", pa_cstrerror(errno));
+ goto fail;
+ }
+
+ pa_log_info("SCO incomming connection: changing state to PLAYING");
+ pa_bluetooth_transport_set_state (t, PA_BLUETOOTH_TRANSPORT_STATE_PLAYING);
+
+fail:
+ return;
+}
+
+static int bluez5_sco_listen(pa_bluetooth_transport *t) {
+ struct transport_data *trd = t->userdata;
+ struct sockaddr_sco addr;
+ int sock;
+
+ sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, BTPROTO_SCO);
+ if (sock < 0) {
+ pa_log_error("socket(SEQPACKET, SCO) %s", pa_cstrerror(errno));
+ return -1;
+ }
+
+ /* Bind to local address */
+ memset(&addr, 0, sizeof(addr));
+ addr.sco_family = AF_BLUETOOTH;
+ bacpy(&addr.sco_bdaddr, BDADDR_ANY);
+
+ if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ pa_log_error("bind(): %s", pa_cstrerror(errno));
+ goto fail_close;
+ }
+
+ pa_log_info ("doing listen");
+ if (listen(sock, 1) < 0) {
+ pa_log_error("listen(): %s", pa_cstrerror(errno));
+ goto fail_close;
+ }
+
+ trd->sco_fd = sock;
+ trd->sco_io = trd->mainloop->io_new(trd->mainloop, sock, PA_IO_EVENT_INPUT|PA_IO_EVENT_ERROR|PA_IO_EVENT_HANGUP,
+ sco_io_callback, t);
+
+ return sock;
+
+fail_close:
+ close(sock);
+ return -1;
+}
+
static void register_profile_reply(DBusPendingCall *pending, void *userdata) {
DBusMessage *r;
pa_dbus_pending *p;
@@ -200,8 +299,10 @@ finish:
static void register_profile(pa_bluetooth_backend *b, const char *profile, const char *uuid) {
DBusMessage *m;
DBusMessageIter i, d;
+ dbus_bool_t autoconnect;
+ dbus_uint16_t version, chan;
- pa_log_debug("Registering Profile %s", profile);
+ pa_log_debug("Registering Profile %s %s", profile, uuid);
pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, "/org/bluez", BLUEZ_PROFILE_MANAGER_INTERFACE, "RegisterProfile"));
@@ -210,6 +311,14 @@ static void register_profile(pa_bluetooth_backend *b, const char *profile, const
dbus_message_iter_append_basic(&i, DBUS_TYPE_STRING, &uuid);
dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING
DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &d);
+ if (pa_streq (uuid, PA_BLUETOOTH_UUID_HSP_HS)) {
+ autoconnect = 0;
+ pa_dbus_append_basic_variant_dict_entry(&d, "AutoConnect", DBUS_TYPE_BOOLEAN, &autoconnect);
+ chan = HSP_HS_DEFAULT_CHANNEL;
+ pa_dbus_append_basic_variant_dict_entry(&d, "Channel", DBUS_TYPE_UINT16, &chan);
+ version = 0x0102;
+ pa_dbus_append_basic_variant_dict_entry(&d, "Version", DBUS_TYPE_UINT16, &version);
+ }
dbus_message_iter_close_container(&i, &d);
send_and_add_to_pending(b, m, register_profile_reply, pa_xstrdup(profile));
@@ -229,29 +338,37 @@ static void rfcomm_io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_i
if (events & PA_IO_EVENT_INPUT) {
char buf[512];
ssize_t len;
- int gain;
+ int gain, dummy, do_reply = FALSE;
len = read(fd, buf, 511);
buf[len] = 0;
pa_log_debug("RFCOMM << %s", buf);
- if (sscanf(buf, "AT+VGS=%d", &gain) == 1) {
- t->speaker_gain = gain;
- pa_hook_fire(pa_bluetooth_discovery_hook(t->device->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_SPEAKER_GAIN_CHANGED), t);
-
- } else if (sscanf(buf, "AT+VGM=%d", &gain) == 1) {
- t->microphone_gain = gain;
- pa_hook_fire(pa_bluetooth_discovery_hook(t->device->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_MICROPHONE_GAIN_CHANGED), t);
+ if (sscanf(buf, "AT+VGS=%d", &gain) == 1 || sscanf(buf, "\r\n+VGM=%d\r\n", &gain) == 1) {
+ t->speaker_gain = gain;
+ pa_hook_fire(pa_bluetooth_discovery_hook(t->device->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_SPEAKER_GAIN_CHANGED), t);
+ do_reply = TRUE;
+
+ } else if (sscanf(buf, "AT+VGM=%d", &gain) == 1 || sscanf(buf, "\r\n+VGS=%d\r\n", &gain) == 1) {
+ t->microphone_gain = gain;
+ pa_hook_fire(pa_bluetooth_discovery_hook(t->device->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_MICROPHONE_GAIN_CHANGED), t);
+ do_reply = TRUE;
+ } else if (sscanf(buf, "AT+CKPD=%d", &dummy) == 1) {
+ do_reply = TRUE;
+ } else {
+ do_reply = FALSE;
}
- pa_log_debug("RFCOMM >> OK");
+ if (do_reply) {
+ pa_log_debug("RFCOMM >> OK");
- len = write(fd, "\r\nOK\r\n", 6);
+ len = write(fd, "\r\nOK\r\n", 6);
- /* we ignore any errors, it's not critical and real errors should
- * be caught with the HANGUP and ERROR events handled above */
- if (len < 0)
- pa_log_error("RFCOMM write error: %s", pa_cstrerror(errno));
+ /* we ignore any errors, it's not critical and real errors should
+ * be caught with the HANGUP and ERROR events handled above */
+ if (len < 0)
+ pa_log_error("RFCOMM write error: %s", pa_cstrerror(errno));
+ }
}
return;
@@ -263,18 +380,23 @@ fail:
}
static void transport_destroy(pa_bluetooth_transport *t) {
- struct transport_rfcomm *trfc = t->userdata;
+ struct transport_data *trd = t->userdata;
- trfc->mainloop->io_free(trfc->rfcomm_io);
+ if (trd->sco_io) {
+ trd->mainloop->io_free(trd->sco_io);
+ shutdown(trd->sco_fd, SHUT_RDWR);
+ close (trd->sco_fd);
+ }
- shutdown(trfc->rfcomm_fd, SHUT_RDWR);
- close (trfc->rfcomm_fd);
+ trd->mainloop->io_free(trd->rfcomm_io);
+ shutdown(trd->rfcomm_fd, SHUT_RDWR);
+ close (trd->rfcomm_fd);
- pa_xfree(trfc);
+ pa_xfree(trd);
}
static void set_speaker_gain(pa_bluetooth_transport *t, uint16_t gain) {
- struct transport_rfcomm *trfc = t->userdata;
+ struct transport_data *trd = t->userdata;
char buf[512];
ssize_t len, written;
@@ -283,17 +405,22 @@ static void set_speaker_gain(pa_bluetooth_transport *t, uint16_t gain) {
t->speaker_gain = gain;
- len = sprintf(buf, "\r\n+VGS=%d\r\n", gain);
- pa_log_debug("RFCOMM >> +VGS=%d", gain);
+ if (t->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT) {
+ len = sprintf(buf, "\r\n+VGS=%d\r\n", gain);
+ pa_log_debug("RFCOMM >> +VGS=%d", gain);
+ } else {
+ len = sprintf(buf, "AT+VGM=%d\r", gain);
+ pa_log_debug("RFCOMM >> AT+VGM=%d", gain);
+ }
- written = write(trfc->rfcomm_fd, buf, len);
+ written = write(trd->rfcomm_fd, buf, len);
if (written != len)
pa_log_error("RFCOMM write error: %s", pa_cstrerror(errno));
}
static void set_microphone_gain(pa_bluetooth_transport *t, uint16_t gain) {
- struct transport_rfcomm *trfc = t->userdata;
+ struct transport_data *trd = t->userdata;
char buf[512];
ssize_t len, written;
@@ -302,10 +429,15 @@ static void set_microphone_gain(pa_bluetooth_transport *t, uint16_t gain) {
t->microphone_gain = gain;
- len = sprintf(buf, "\r\n+VGM=%d\r\n", gain);
- pa_log_debug("RFCOMM >> +VGM=%d", gain);
+ if (t->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT) {
+ len = sprintf(buf, "\r\n+VGM=%d\r\n", gain);
+ pa_log_debug("RFCOMM >> +VGM=%d", gain);
+ } else {
+ len = sprintf(buf, "AT+VGS=%d\r", gain);
+ pa_log_debug("RFCOMM >> AT+VGS=%d", gain);
+ }
- written = write (trfc->rfcomm_fd, buf, len);
+ written = write (trd->rfcomm_fd, buf, len);
if (written != len)
pa_log_error("RFCOMM write error: %s", pa_cstrerror(errno));
@@ -321,7 +453,7 @@ static DBusMessage *profile_new_connection(DBusConnection *conn, DBusMessage *m,
const char *sender, *path, PA_UNUSED *handler;
DBusMessageIter arg_i;
char *pathfd;
- struct transport_rfcomm *trfc;
+ struct transport_data *trd;
if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "oha{sv}")) {
pa_log_error("Invalid signature found in NewConnection");
@@ -329,7 +461,14 @@ static DBusMessage *profile_new_connection(DBusConnection *conn, DBusMessage *m,
}
handler = dbus_message_get_path(m);
- pa_assert(pa_streq(handler, HSP_AG_PROFILE));
+ if (pa_streq(handler, HSP_AG_PROFILE)) {
+ p = PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT;
+ } else if (pa_streq(handler, HSP_HS_PROFILE)) {
+ p = PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY;
+ } else {
+ pa_log_error("Invalid handler");
+ goto fail;
+ }
pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_OBJECT_PATH);
dbus_message_iter_get_basic(&arg_i, &path);
@@ -345,11 +484,11 @@ static DBusMessage *profile_new_connection(DBusConnection *conn, DBusMessage *m,
pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_UNIX_FD);
dbus_message_iter_get_basic(&arg_i, &fd);
- pa_log_debug("dbus: NewConnection path=%s, fd=%d", path, fd);
+ pa_log_debug("dbus: NewConnection path=%s, fd=%d, profile %s", path, fd,
+ pa_bluetooth_profile_to_string(p));
sender = dbus_message_get_sender(m);
- p = PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT;
pathfd = pa_sprintf_malloc ("%s/fd%d", path, fd);
d->transports[p] = t = pa_bluetooth_transport_new(d, sender, pathfd, p, NULL, 0);
pa_xfree(pathfd);
@@ -360,12 +499,14 @@ static DBusMessage *profile_new_connection(DBusConnection *conn, DBusMessage *m,
t->set_speaker_gain = set_speaker_gain;
t->set_microphone_gain = set_microphone_gain;
- trfc = pa_xnew0(struct transport_rfcomm, 1);
- trfc->rfcomm_fd = fd;
- trfc->mainloop = b->core->mainloop;
- trfc->rfcomm_io = trfc->mainloop->io_new(b->core->mainloop, fd, PA_IO_EVENT_INPUT|PA_IO_EVENT_HANGUP,
+ trd = pa_xnew0(struct transport_data, 1);
+ trd->rfcomm_fd = fd;
+ trd->mainloop = b->core->mainloop;
+ trd->rfcomm_io = trd->mainloop->io_new(b->core->mainloop, fd, PA_IO_EVENT_INPUT|PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR,
rfcomm_io_callback, t);
- t->userdata = trfc;
+ t->userdata = trd;
+
+ bluez5_sco_listen(t);
pa_bluetooth_transport_put(t);
@@ -401,7 +542,7 @@ static DBusHandlerResult profile_handler(DBusConnection *c, DBusMessage *m, void
pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member);
- if (!pa_streq(path, HSP_AG_PROFILE))
+ if (!pa_streq(path, HSP_AG_PROFILE) && !pa_streq(path, HSP_HS_PROFILE))
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
@@ -411,6 +552,8 @@ static DBusHandlerResult profile_handler(DBusConnection *c, DBusMessage *m, void
pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID));
} else if (dbus_message_is_method_call(m, BLUEZ_PROFILE_INTERFACE, "Release")) {
+ pa_log_debug("Release not handled");
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
} else if (dbus_message_is_method_call(m, BLUEZ_PROFILE_INTERFACE, "RequestDisconnection")) {
r = profile_request_disconnection(c, m, userdata);
} else if (dbus_message_is_method_call(m, BLUEZ_PROFILE_INTERFACE, "NewConnection"))
@@ -440,6 +583,10 @@ static void profile_init(pa_bluetooth_backend *b, pa_bluetooth_profile_t profile
object_name = HSP_AG_PROFILE;
uuid = PA_BLUETOOTH_UUID_HSP_AG;
break;
+ case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY:
+ object_name = HSP_HS_PROFILE;
+ uuid = PA_BLUETOOTH_UUID_HSP_HS;
+ break;
default:
pa_assert_not_reached();
break;
@@ -456,6 +603,9 @@ static void profile_done(pa_bluetooth_backend *b, pa_bluetooth_profile_t profile
case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT:
dbus_connection_unregister_object_path(pa_dbus_connection_get(b->connection), HSP_AG_PROFILE);
break;
+ case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY:
+ dbus_connection_unregister_object_path(pa_dbus_connection_get(b->connection), HSP_HS_PROFILE);
+ break;
default:
pa_assert_not_reached();
break;
@@ -481,6 +631,7 @@ pa_bluetooth_backend *pa_bluetooth_native_backend_new(pa_core *c, pa_bluetooth_d
backend->discovery = y;
+ profile_init(backend, PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY);
profile_init(backend, PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT);
return backend;
@@ -491,6 +642,7 @@ void pa_bluetooth_native_backend_free(pa_bluetooth_backend *backend) {
pa_dbus_free_pending_list(&backend->pending);
+ profile_done(backend, PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY);
profile_done(backend, PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT);
pa_dbus_connection_unref(backend->connection);
diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c
index 7238e6f81..8075b5ba8 100644
--- a/src/modules/bluetooth/module-bluez5-device.c
+++ b/src/modules/bluetooth/module-bluez5-device.c
@@ -151,6 +151,9 @@ typedef enum pa_bluetooth_form_factor {
PA_BLUETOOTH_FORM_FACTOR_PHONE,
} pa_bluetooth_form_factor_t;
+static void sink_set_volume_cb(pa_sink *s);
+static void source_set_volume_cb(pa_source *s);
+
/* Run from main thread */
static pa_bluetooth_form_factor_t form_factor_from_class(uint32_t class_of_device) {
unsigned major, minor;
@@ -896,6 +899,13 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
return 0;
}
+ case PA_SOURCE_MESSAGE_SET_SHARED_VOLUME: {
+ if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) {
+ source_set_volume_cb (u->source);
+ }
+ break;
+ }
+
}
r = pa_source_process_msg(o, code, data, offset, chunk);
@@ -916,7 +926,6 @@ static void source_set_volume_cb(pa_source *s) {
pa_assert(u);
pa_assert(u->source == s);
- pa_assert(u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT);
if (u->transport->set_microphone_gain == NULL)
return;
@@ -1053,6 +1062,13 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
return 0;
}
+
+ case PA_SINK_MESSAGE_SET_SHARED_VOLUME: {
+ if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) {
+ sink_set_volume_cb (u->sink);
+ }
+ break;
+ }
}
r = pa_sink_process_msg(o, code, data, offset, chunk);
@@ -1073,7 +1089,6 @@ static void sink_set_volume_cb(pa_sink *s) {
pa_assert(u);
pa_assert(u->sink == s);
- pa_assert(u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT);
if (u->transport->set_speaker_gain == NULL)
return;
@@ -1270,7 +1285,7 @@ static int setup_transport(struct userdata *u) {
/* check if profile has a transport */
t = u->device->transports[u->profile];
if (!t || t->state <= PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED) {
- pa_log_warn("Profile has no transport");
+ pa_log_warn("Profile %d has no transport", u->profile);
return -1;
}
@@ -2073,7 +2088,10 @@ static pa_hook_result_t transport_speaker_gain_changed_cb(pa_bluetooth_discovery
volume++;
pa_cvolume_set(&v, u->sample_spec.channels, volume);
- pa_sink_volume_changed(u->sink, &v);
+ if (t->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT)
+ pa_sink_volume_changed(u->sink, &v);
+ else
+ pa_sink_set_volume(u->sink, &v, true, true);
return PA_HOOK_OK;
}
@@ -2097,7 +2115,11 @@ static pa_hook_result_t transport_microphone_gain_changed_cb(pa_bluetooth_discov
volume++;
pa_cvolume_set(&v, u->sample_spec.channels, volume);
- pa_source_volume_changed(u->source, &v);
+
+ if (t->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT)
+ pa_source_volume_changed(u->source, &v);
+ else
+ pa_source_set_volume(u->source, &v, true, true);
return PA_HOOK_OK;
}