With BlueZ 5 volume updates for HFP will go through transport interface. Note that this is backward compatible. --- src/modules/bluetooth/bluetooth-util.c | 62 +++++++++++++++---- src/modules/bluetooth/bluetooth-util.h | 4 ++ src/modules/bluetooth/module-bluetooth-device.c | 75 +++++++++++++++++++++++ 3 files changed, 130 insertions(+), 11 deletions(-) diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c index 272b6ce..c0d5287 100644 --- a/src/modules/bluetooth/bluetooth-util.c +++ b/src/modules/bluetooth/bluetooth-util.c @@ -754,21 +754,49 @@ int pa_bluetooth_transport_parse_property(pa_bluetooth_transport *t, DBusMessage dbus_message_iter_recurse(i, &variant_i); - switch (dbus_message_iter_get_arg_type(&variant_i)) { + if (pa_streq(key, "NREC")) { + dbus_bool_t value; - case DBUS_TYPE_BOOLEAN: { + if (dbus_message_iter_get_arg_type(&variant_i) != DBUS_TYPE_BOOLEAN) { + pa_log("Property value not a boolean."); + return -1; + } - dbus_bool_t value; - dbus_message_iter_get_basic(&variant_i, &value); + dbus_message_iter_get_basic(&variant_i, &value); - if (pa_streq(key, "NREC") && t->nrec != value) { - t->nrec = value; - pa_log_debug("Transport %s: Property 'NREC' changed to %s.", t->path, t->nrec ? "True" : "False"); - pa_hook_fire(&t->hooks[PA_BLUETOOTH_TRANSPORT_HOOK_NREC_CHANGED], NULL); - } + if (t->nrec != value) { + t->nrec = value; + pa_log_debug("Transport %s: Property 'NREC' changed to %s.", t->path, t->nrec ? "True" : "False"); + pa_hook_fire(&t->hooks[PA_BLUETOOTH_TRANSPORT_HOOK_NREC_CHANGED], NULL); + } + } else if(pa_streq(key,"OutputGain")) { + uint16_t value; - break; - } + if (dbus_message_iter_get_arg_type(&variant_i) != DBUS_TYPE_UINT16) { + pa_log("Property value not an uint16."); + return -1; + } + + dbus_message_iter_get_basic(&variant_i, &value); + + if (t->output_gain != value) { + t->output_gain = value; + pa_hook_fire(&t->hooks[PA_BLUETOOTH_TRANSPORT_HOOK_OUTPUT_GAIN_CHANGED], NULL); + } + } else if(pa_streq(key,"InputGain")) { + uint16_t value; + + if (dbus_message_iter_get_arg_type(&variant_i) != DBUS_TYPE_UINT16) { + pa_log("Property value not an uint16."); + return -1; + } + + dbus_message_iter_get_basic(&variant_i, &value); + + if (t->input_gain != value) { + t->input_gain = value; + pa_hook_fire(&t->hooks[PA_BLUETOOTH_TRANSPORT_HOOK_INPUT_GAIN_CHANGED], NULL); + } } return 0; @@ -1104,6 +1132,8 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage uint8_t *config = NULL; int size = 0; pa_bool_t nrec = FALSE; + uint16_t output_gain = 15; + uint16_t input_gain = 15; enum profile p; DBusMessageIter args, props; DBusMessage *r; @@ -1151,6 +1181,14 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage goto fail; dbus_message_iter_recurse(&value, &array); dbus_message_iter_get_fixed_array(&array, &config, &size); + } else if (strcasecmp(key, "OutputGain") == 0) { + if (var != DBUS_TYPE_UINT16) + goto fail; + dbus_message_iter_get_basic(&value, &output_gain); + } else if (strcasecmp(key, "InputGain") == 0) { + if (var != DBUS_TYPE_UINT16) + goto fail; + dbus_message_iter_get_basic(&value, &input_gain); } dbus_message_iter_next(&props); @@ -1172,6 +1210,8 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage t = transport_new(y, path, p, config, size); if (nrec) t->nrec = nrec; + t->output_gain = output_gain; + t->input_gain = input_gain; pa_hashmap_put(d->transports, t->path, t); pa_log_debug("Transport %s profile %d available", t->path, t->profile); diff --git a/src/modules/bluetooth/bluetooth-util.h b/src/modules/bluetooth/bluetooth-util.h index 8a3f2ad..5fba105 100644 --- a/src/modules/bluetooth/bluetooth-util.h +++ b/src/modules/bluetooth/bluetooth-util.h @@ -67,6 +67,8 @@ enum profile { typedef enum pa_bluetooth_transport_hook { PA_BLUETOOTH_TRANSPORT_HOOK_NREC_CHANGED, /* Call data: NULL. */ PA_BLUETOOTH_TRANSPORT_HOOK_REMOVED, /* Call data: NULL. */ + PA_BLUETOOTH_TRANSPORT_HOOK_OUTPUT_GAIN_CHANGED, /* Call data: NULL. */ + PA_BLUETOOTH_TRANSPORT_HOOK_INPUT_GAIN_CHANGED, /* Call data: NULL. */ PA_BLUETOOTH_TRANSPORT_HOOK_MAX } pa_bluetooth_transport_hook_t; @@ -78,6 +80,8 @@ struct pa_bluetooth_transport { uint8_t *config; int config_size; pa_bool_t nrec; + uint16_t output_gain; + uint16_t input_gain; pa_hook hooks[PA_BLUETOOTH_TRANSPORT_HOOK_MAX]; }; diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c index 8c2efa4..656852d 100644 --- a/src/modules/bluetooth/module-bluetooth-device.c +++ b/src/modules/bluetooth/module-bluetooth-device.c @@ -124,6 +124,8 @@ struct hsp_info { pa_hook_slot *sink_state_changed_slot; pa_hook_slot *source_state_changed_slot; pa_hook_slot *nrec_changed_slot; + pa_hook_slot *output_gain_slot; + pa_hook_slot *input_gain_slot; }; struct bluetooth_msg { @@ -1423,6 +1425,19 @@ fail: return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } +static void send_property_update(struct userdata *u, const char *name, int type, void *data) +{ + DBusMessage *m; + DBusMessageIter iter; + + pa_assert_se(m = dbus_message_new_method_call("org.bluez", u->transport->path, "org.bluez.MediaTransport", "SetProperty")); + dbus_message_iter_init_append(m, &iter); + pa_assert_se(dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name)); + pa_dbus_append_basic_variant(&iter, DBUS_TYPE_UINT16, data); + pa_assert_se(dbus_connection_send(pa_dbus_connection_get(u->connection), m, NULL)); + dbus_message_unref(m); +} + /* Run from main thread */ static void sink_set_volume_cb(pa_sink *s) { DBusMessage *m; @@ -1430,6 +1445,7 @@ static void sink_set_volume_cb(pa_sink *s) { pa_volume_t volume; struct userdata *u; char *k; + const char *name = "OutputGain"; pa_assert(s); pa_assert(s->core); @@ -1459,6 +1475,8 @@ static void sink_set_volume_cb(pa_sink *s) { pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID)); pa_assert_se(dbus_connection_send(pa_dbus_connection_get(u->connection), m, NULL)); dbus_message_unref(m); + + send_property_update(u, name, DBUS_TYPE_UINT16, &gain); } /* Run from main thread */ @@ -1468,6 +1486,7 @@ static void source_set_volume_cb(pa_source *s) { pa_volume_t volume; struct userdata *u; char *k; + const char *name = "InputGain"; pa_assert(s); pa_assert(s->core); @@ -1497,6 +1516,8 @@ static void source_set_volume_cb(pa_source *s) { pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID)); pa_assert_se(dbus_connection_send(pa_dbus_connection_get(u->connection), m, NULL)); dbus_message_unref(m); + + send_property_update(u, name, DBUS_TYPE_UINT16, &gain); } /* Run from main thread */ @@ -1599,6 +1620,44 @@ static pa_hook_result_t nrec_changed_cb(pa_bluetooth_transport *t, void *call_da return PA_HOOK_OK; } +static pa_hook_result_t output_gain_changed_cb(pa_bluetooth_transport *t, void *call_data, struct userdata *u) { + pa_volume_t volume; + pa_cvolume v; + + pa_assert(t); + pa_assert(u); + + volume = (pa_volume_t) (t->output_gain * PA_VOLUME_NORM / HSP_MAX_GAIN); + + /* increment volume by one to correct rounding errors */ + if (volume < PA_VOLUME_NORM) + volume++; + + pa_cvolume_set(&v, u->sample_spec.channels, volume); + pa_sink_volume_changed(u->sink, &v); + + return PA_HOOK_OK; +} + +static pa_hook_result_t input_gain_changed_cb(pa_bluetooth_transport *t, void *call_data, struct userdata *u) { + pa_volume_t volume; + pa_cvolume v; + + pa_assert(t); + pa_assert(u); + + volume = (pa_volume_t) (t->input_gain * PA_VOLUME_NORM / HSP_MAX_GAIN); + + /* increment volume by one to correct rounding errors */ + if (volume < PA_VOLUME_NORM) + volume++; + + pa_cvolume_set(&v, u->sample_spec.channels, volume); + pa_source_volume_changed(u->source, &v); + + return PA_HOOK_OK; +} + static void connect_ports(struct userdata *u, void *sink_or_source_new_data, pa_direction_t direction) { union { pa_sink_new_data *sink_new_data; @@ -1748,6 +1807,9 @@ static int add_sink(struct userdata *u) { k = pa_sprintf_malloc("bluetooth-device@%p", (void*) u->sink); pa_shared_set(u->core, k, u); pa_xfree(k); + + if (u->transport && !u->hsp.output_gain_slot) + u->hsp.output_gain_slot = pa_hook_connect(&u->transport->hooks[PA_BLUETOOTH_TRANSPORT_HOOK_OUTPUT_GAIN_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) output_gain_changed_cb, u); } return 0; @@ -1832,6 +1894,9 @@ static int add_source(struct userdata *u) { k = pa_sprintf_malloc("bluetooth-device@%p", (void*) u->source); pa_shared_set(u->core, k, u); pa_xfree(k); + + if (u->transport && !u->hsp.input_gain_slot) + u->hsp.input_gain_slot = pa_hook_connect(&u->transport->hooks[PA_BLUETOOTH_TRANSPORT_HOOK_INPUT_GAIN_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) input_gain_changed_cb, u); } return 0; @@ -2057,6 +2122,16 @@ static void stop_thread(struct userdata *u) { u->hsp.nrec_changed_slot = NULL; } + if (u->hsp.output_gain_slot) { + pa_hook_slot_free(u->hsp.output_gain_slot); + u->hsp.output_gain_slot = NULL; + } + + if (u->hsp.input_gain_slot) { + pa_hook_slot_free(u->hsp.input_gain_slot); + u->hsp.input_gain_slot = NULL; + } + if (u->transport_removed_slot) { pa_hook_slot_free(u->transport_removed_slot); u->transport_removed_slot = NULL; -- 1.7.9.5