diff --git a/spa/plugins/bluez5/backend-native.c b/spa/plugins/bluez5/backend-native.c
index 8d5354d31..906e5268d 100644
--- a/spa/plugins/bluez5/backend-native.c
+++ b/spa/plugins/bluez5/backend-native.c
@@ -48,6 +48,8 @@ SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.bluez5.native");
#define PROP_KEY_ROLES "bluez5.roles"
#define PROP_KEY_HEADSET_ROLES "bluez5.headset-roles"
#define PROP_KEY_HFP_DISABLE_NREC "bluez5.hfp-hf.disable-nrec"
+#define PROP_KEY_HFP_DEFAULT_MIC_VOL "bluez5.hfp-hf.default-mic-volume"
+#define PROP_KEY_HFP_DEFAULT_SPEAKER_VOL "bluez5.hfp-hf.default-speaker-volume"
#define HFP_CODEC_SWITCH_INITIAL_TIMEOUT_MSEC 5000
#define HFP_CODEC_SWITCH_TIMEOUT_MSEC 20000
@@ -100,6 +102,8 @@ struct impl {
#define DEFAULT_ENABLED_PROFILES (SPA_BT_PROFILE_HFP_HF | SPA_BT_PROFILE_HFP_AG)
enum spa_bt_profile enabled_profiles;
bool hfp_disable_nrec;
+ int hfp_default_mic_volume;
+ int hfp_default_speaker_volume;
struct spa_source sco;
@@ -460,7 +464,7 @@ static void rfcomm_send_error(const struct rfcomm *rfcomm, enum cmee_error error
rfcomm_send_reply(rfcomm, "ERROR");
}
-static bool rfcomm_volume_enabled(struct rfcomm *rfcomm)
+static bool rfcomm_hw_volume_enabled(struct rfcomm *rfcomm)
{
return rfcomm->device != NULL
&& (rfcomm->device->hw_volume_profiles & rfcomm->profile);
@@ -470,9 +474,6 @@ static void rfcomm_emit_volume_changed(struct rfcomm *rfcomm, int id, int hw_vol
{
struct spa_bt_transport_volume *t_volume;
- if (!rfcomm_volume_enabled(rfcomm))
- return;
-
if ((id == SPA_BT_VOLUME_ID_RX || id == SPA_BT_VOLUME_ID_TX) && hw_volume >= 0) {
rfcomm->volumes[id].active = true;
rfcomm->volumes[id].hw_volume = hw_volume;
@@ -480,17 +481,24 @@ static void rfcomm_emit_volume_changed(struct rfcomm *rfcomm, int id, int hw_vol
spa_log_debug(rfcomm->backend->log, "volume changed %d", hw_volume);
- if (rfcomm->transport == NULL || !rfcomm->has_volume)
- return;
+ if (rfcomm_hw_volume_enabled(rfcomm)) {
+ if (rfcomm->transport == NULL || !rfcomm->has_volume)
+ return;
- for (int i = 0; i < SPA_BT_VOLUME_ID_TERM ; ++i) {
- t_volume = &rfcomm->transport->volumes[i];
- t_volume->active = rfcomm->volumes[i].active;
- t_volume->volume = (float)
- spa_bt_volume_hw_to_linear(rfcomm->volumes[i].hw_volume, t_volume->hw_volume_max);
+ for (int i = 0; i < SPA_BT_VOLUME_ID_TERM ; ++i) {
+ t_volume = &rfcomm->transport->volumes[i];
+ t_volume->active = rfcomm->volumes[i].active;
+ t_volume->volume = (float)
+ spa_bt_volume_hw_to_linear(rfcomm->volumes[i].hw_volume, t_volume->hw_volume_max);
+ }
+
+ spa_bt_transport_emit_volume_changed(rfcomm->transport);
}
- spa_bt_transport_emit_volume_changed(rfcomm->transport);
+ if (rfcomm->telephony_ag) {
+ rfcomm->telephony_ag->volume[id] = hw_volume;
+ telephony_ag_notify_updated_props(rfcomm->telephony_ag);
+ }
}
#ifdef HAVE_BLUEZ_5_BACKEND_HSP_NATIVE
@@ -534,18 +542,21 @@ static bool rfcomm_send_volume_cmd(struct rfcomm *rfcomm, int id)
{
struct spa_bt_transport_volume *t_volume;
const char *format;
- int hw_volume;
+ int hw_volume = rfcomm->volumes[id].hw_volume;
- if (!rfcomm_volume_enabled(rfcomm))
- return false;
+ if (rfcomm_hw_volume_enabled(rfcomm)) {
+ t_volume = rfcomm->transport ? &rfcomm->transport->volumes[id] : NULL;
- t_volume = rfcomm->transport ? &rfcomm->transport->volumes[id] : NULL;
+ if (t_volume && t_volume->active) {
+ hw_volume = spa_bt_volume_linear_to_hw(t_volume->volume, t_volume->hw_volume_max);
+ rfcomm->volumes[id].hw_volume = hw_volume;
+ }
+ }
- if (!(t_volume && t_volume->active))
- return false;
-
- hw_volume = spa_bt_volume_linear_to_hw(t_volume->volume, t_volume->hw_volume_max);
- rfcomm->volumes[id].hw_volume = hw_volume;
+ if (rfcomm->telephony_ag) {
+ rfcomm->telephony_ag->volume[id] = hw_volume;
+ telephony_ag_notify_updated_props(rfcomm->telephony_ag);
+ }
if (id == SPA_BT_VOLUME_ID_TX)
format = "AT+VGM";
@@ -1898,6 +1909,70 @@ static void hfp_hf_transport_activate(void *data, enum spa_bt_telephony_error *e
*err = BT_TELEPHONY_ERROR_NONE;
}
+static void hfp_hf_set_speaker_volume(void *data, uint8_t volume, enum spa_bt_telephony_error *err, uint8_t *cme_error)
+{
+ struct rfcomm *rfcomm = data;
+ struct impl *backend = rfcomm->backend;
+ struct spa_bt_transport_volume *t_volume;
+ char reply[20];
+ bool res;
+
+ rfcomm->volumes[SPA_BT_VOLUME_ID_RX].hw_volume = volume;
+ if (rfcomm_hw_volume_enabled(rfcomm)) {
+ t_volume = rfcomm->transport ? &rfcomm->transport->volumes[SPA_BT_VOLUME_ID_RX] : NULL;
+
+ if (t_volume && t_volume->active) {
+ t_volume->volume = (float) spa_bt_volume_hw_to_linear(volume, t_volume->hw_volume_max);
+ spa_bt_transport_emit_volume_changed(rfcomm->transport);
+ }
+ }
+
+ rfcomm_send_volume_cmd(rfcomm, SPA_BT_VOLUME_ID_RX);
+ res = hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply));
+ if (!res || !spa_strstartswith(reply, "OK")) {
+ spa_log_info(backend->log, "Failed to send AT+VGS");
+ if (res)
+ hfp_hf_get_error_from_reply(reply, err, cme_error);
+ else
+ *err = BT_TELEPHONY_ERROR_FAILED;
+ return;
+ }
+
+ *err = BT_TELEPHONY_ERROR_NONE;
+}
+
+static void hfp_hf_set_microphone_volume(void *data, uint8_t volume, enum spa_bt_telephony_error *err, uint8_t *cme_error)
+{
+ struct rfcomm *rfcomm = data;
+ struct impl *backend = rfcomm->backend;
+ struct spa_bt_transport_volume *t_volume;
+ char reply[20];
+ bool res;
+
+ rfcomm->volumes[SPA_BT_VOLUME_ID_TX].hw_volume = volume;
+ if (rfcomm_hw_volume_enabled(rfcomm)) {
+ t_volume = rfcomm->transport ? &rfcomm->transport->volumes[SPA_BT_VOLUME_ID_TX] : NULL;
+
+ if (t_volume && t_volume->active) {
+ t_volume->volume = (float) spa_bt_volume_hw_to_linear(volume, t_volume->hw_volume_max);
+ spa_bt_transport_emit_volume_changed(rfcomm->transport);
+ }
+ }
+
+ rfcomm_send_volume_cmd(rfcomm, SPA_BT_VOLUME_ID_TX);
+ res = hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply));
+ if (!res || !spa_strstartswith(reply, "OK")) {
+ spa_log_info(backend->log, "Failed to send AT+VGM");
+ if (res)
+ hfp_hf_get_error_from_reply(reply, err, cme_error);
+ else
+ *err = BT_TELEPHONY_ERROR_FAILED;
+ return;
+ }
+
+ *err = BT_TELEPHONY_ERROR_NONE;
+}
+
static const struct spa_bt_telephony_ag_callbacks telephony_ag_callbacks = {
SPA_VERSION_BT_TELEPHONY_AG_CALLBACKS,
.dial = hfp_hf_dial,
@@ -1909,6 +1984,8 @@ static const struct spa_bt_telephony_ag_callbacks telephony_ag_callbacks = {
.create_multiparty = hfp_hf_create_multiparty,
.send_tones = hfp_hf_send_tones,
.transport_activate = hfp_hf_transport_activate,
+ .set_speaker_volume = hfp_hf_set_speaker_volume,
+ .set_microphone_volume = hfp_hf_set_microphone_volume,
};
static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
@@ -2342,6 +2419,8 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
rfcomm->telephony_ag = telephony_ag_new(backend->telephony, 0);
rfcomm->telephony_ag->address = strdup(rfcomm->device->address);
+ rfcomm->telephony_ag->volume[SPA_BT_VOLUME_ID_RX] = rfcomm->volumes[SPA_BT_VOLUME_ID_RX].hw_volume = backend->hfp_default_speaker_volume;
+ rfcomm->telephony_ag->volume[SPA_BT_VOLUME_ID_TX] = rfcomm->volumes[SPA_BT_VOLUME_ID_TX].hw_volume = backend->hfp_default_mic_volume;
telephony_ag_set_callbacks(rfcomm->telephony_ag,
&telephony_ag_callbacks, rfcomm);
if (rfcomm->transport) {
@@ -2979,7 +3058,7 @@ static int rfcomm_ag_set_volume(struct spa_bt_transport *t, int id)
const char *format;
int value;
- if (!rfcomm_volume_enabled(rfcomm)
+ if (!rfcomm_hw_volume_enabled(rfcomm)
|| !(rfcomm->profile & SPA_BT_PROFILE_HEADSET_HEAD_UNIT)
|| !(rfcomm->has_volume && rfcomm->volumes[id].active))
return -ENOTSUP;
@@ -3013,7 +3092,7 @@ static int sco_set_volume_cb(void *data, int id, float volume)
struct rfcomm *rfcomm = td->rfcomm;
int value;
- if (!rfcomm_volume_enabled(rfcomm)
+ if (!rfcomm_hw_volume_enabled(rfcomm)
|| !(rfcomm->profile & SPA_BT_PROFILE_HEADSET_HEAD_UNIT)
|| !(rfcomm->has_volume && rfcomm->volumes[id].active))
return -ENOTSUP;
@@ -3350,7 +3429,7 @@ static DBusHandlerResult profile_new_connection(DBusConnection *conn, DBusMessag
if (rfcomm_new_transport(rfcomm, HFP_AUDIO_CODEC_CVSD) < 0)
goto fail_need_memory;
- rfcomm->has_volume = rfcomm_volume_enabled(rfcomm);
+ rfcomm->has_volume = rfcomm_hw_volume_enabled(rfcomm);
if (profile == SPA_BT_PROFILE_HSP_AG) {
rfcomm->hs_state = hsp_hs_init1;
@@ -3383,10 +3462,8 @@ static DBusHandlerResult profile_new_connection(DBusConnection *conn, DBusMessag
rfcomm->codec_negotiation_supported = false;
}
- if (rfcomm_volume_enabled(rfcomm)) {
- rfcomm->has_volume = true;
- hf_features |= SPA_BT_HFP_HF_FEATURE_REMOTE_VOLUME_CONTROL;
- }
+ rfcomm->has_volume = true;
+ hf_features |= SPA_BT_HFP_HF_FEATURE_REMOTE_VOLUME_CONTROL;
/* send command to AG with the features supported by Hands-Free */
rfcomm_send_cmd(rfcomm, "AT+BRSF=%u", hf_features);
@@ -3394,7 +3471,7 @@ static DBusHandlerResult profile_new_connection(DBusConnection *conn, DBusMessag
rfcomm->hf_state = hfp_hf_brsf;
}
- if (rfcomm_volume_enabled(rfcomm) && (profile == SPA_BT_PROFILE_HFP_HF || profile == SPA_BT_PROFILE_HSP_HS)) {
+ if (rfcomm_hw_volume_enabled(rfcomm) && (profile == SPA_BT_PROFILE_HFP_HF || profile == SPA_BT_PROFILE_HSP_HS)) {
uint32_t device_features;
if (spa_bt_quirks_get_features(backend->quirks, d->adapter, d, &device_features) == 0) {
rfcomm->broken_mic_hw_volume = !(device_features & SPA_BT_FEATURE_HW_VOLUME_MIC);
@@ -3944,6 +4021,29 @@ static void parse_hfp_disable_nrec(struct impl *backend, const struct spa_dict *
backend->hfp_disable_nrec = false;
}
+static void parse_hfp_default_volumes(struct impl *backend, const struct spa_dict *info)
+{
+ const char *str;
+ int vol = -1;
+
+ if ((str = spa_dict_lookup(info, PROP_KEY_HFP_DEFAULT_MIC_VOL)) != NULL)
+ spa_atoi32(str, &vol, 10);
+
+ if (vol >= 0 && vol <= 15)
+ backend->hfp_default_mic_volume = vol;
+ else
+ backend->hfp_default_mic_volume = SPA_BT_VOLUME_HS_MAX;
+
+ vol = -1;
+ if ((str = spa_dict_lookup(info, PROP_KEY_HFP_DEFAULT_SPEAKER_VOL)) != NULL)
+ spa_atoi32(str, &vol, 10);
+
+ if (vol >= 0 && vol <= 15)
+ backend->hfp_default_speaker_volume = vol;
+ else
+ backend->hfp_default_speaker_volume = SPA_BT_VOLUME_HS_MAX;
+}
+
static const struct spa_bt_backend_implementation backend_impl = {
SPA_VERSION_BT_BACKEND_IMPLEMENTATION,
.free = backend_native_free,
@@ -4002,6 +4102,7 @@ struct spa_bt_backend *backend_native_new(struct spa_bt_monitor *monitor,
goto fail;
parse_hfp_disable_nrec(backend, info);
+ parse_hfp_default_volumes(backend, info);
#ifdef HAVE_BLUEZ_5_BACKEND_HSP_NATIVE
if (!dbus_connection_register_object_path(backend->conn,
diff --git a/spa/plugins/bluez5/telephony.c b/spa/plugins/bluez5/telephony.c
index c2f9d6741..f5887053d 100644
--- a/spa/plugins/bluez5/telephony.c
+++ b/spa/plugins/bluez5/telephony.c
@@ -117,6 +117,8 @@
" " \
" " \
" " \
+ " " \
+ " " \
" " \
" " \
" " \
@@ -205,6 +207,7 @@ struct agimpl {
struct callimpl *dial_return;
struct {
+ int volume[SPA_BT_VOLUME_ID_TERM];
struct spa_bt_telephony_ag_transport transport;
} prev;
};
@@ -235,6 +238,8 @@ struct callimpl {
#define ag_emit_create_multiparty(s,e,cme) ag_emit(s,create_multiparty,0,e,cme)
#define ag_emit_send_tones(s,t,e,cme) ag_emit(s,send_tones,0,t,e,cme)
#define ag_emit_transport_activate(s,e,cme) ag_emit(s,transport_activate,0,e,cme)
+#define ag_emit_set_speaker_volume(s,v,e,cme) ag_emit(s,set_speaker_volume,0,v,e,cme)
+#define ag_emit_set_microphone_volume(s,v,e,cme) ag_emit(s,set_microphone_volume,0,v,e,cme)
#define call_emit(c,m,v,...) spa_callbacks_call(&c->callbacks, struct spa_bt_telephony_call_callbacks, m, v, ##__VA_ARGS__)
#define call_emit_answer(s,e,cme) call_emit(s,answer,0,e,cme)
@@ -516,6 +521,14 @@ void telephony_free(struct spa_bt_telephony *telephony)
free(impl);
}
+static void telephony_ag_commit_properties(struct spa_bt_telephony_ag *ag)
+{
+ struct agimpl *agimpl = SPA_CONTAINER_OF(ag, struct agimpl, this);
+ for (int i = 0; i < SPA_BT_VOLUME_ID_TERM; ++i) {
+ agimpl->prev.volume[i] = ag->volume[i];
+ }
+}
+
static void telephony_ag_transport_commit_properties(struct spa_bt_telephony_ag *ag)
{
struct agimpl *agimpl = SPA_CONTAINER_OF(ag, struct agimpl, this);
@@ -538,6 +551,7 @@ static const char * const * transport_state_to_string(int state)
static bool
dbus_iter_append_ag_properties(DBusMessageIter *i, struct spa_bt_telephony_ag *ag, bool all)
{
+ struct agimpl *agimpl = SPA_CONTAINER_OF(ag, struct agimpl, this);
DBusMessageIter dict, entry, variant;
bool changed = false;
@@ -558,6 +572,32 @@ dbus_iter_append_ag_properties(DBusMessageIter *i, struct spa_bt_telephony_ag *a
changed = true;
}
+ if (all || ag->volume[SPA_BT_VOLUME_ID_RX] != agimpl->prev.volume[SPA_BT_VOLUME_ID_RX]) {
+ dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry);
+ const char *name = "SpeakerVolume";
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &name);
+ dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
+ DBUS_TYPE_BYTE_AS_STRING,
+ &variant);
+ dbus_message_iter_append_basic(&variant, DBUS_TYPE_BYTE, &ag->volume[SPA_BT_VOLUME_ID_RX]);
+ dbus_message_iter_close_container(&entry, &variant);
+ dbus_message_iter_close_container(&dict, &entry);
+ changed = true;
+ }
+
+ if (all || ag->volume[SPA_BT_VOLUME_ID_TX] != agimpl->prev.volume[SPA_BT_VOLUME_ID_TX]) {
+ dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry);
+ const char *name = "MicrophoneVolume";
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &name);
+ dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
+ DBUS_TYPE_BYTE_AS_STRING,
+ &variant);
+ dbus_message_iter_append_basic(&variant, DBUS_TYPE_BYTE, &ag->volume[SPA_BT_VOLUME_ID_TX]);
+ dbus_message_iter_close_container(&entry, &variant);
+ dbus_message_iter_close_container(&dict, &entry);
+ changed = true;
+ }
+
dbus_message_iter_close_container(i, &dict);
return changed;
}
@@ -709,6 +749,28 @@ static DBusMessage *ag_properties_get(struct agimpl *agimpl, DBusMessage *m)
&agimpl->this.address);
dbus_message_iter_close_container(&i, &v);
return r;
+ } else if (spa_streq(name, "SpeakerVolume")) {
+ r = dbus_message_new_method_return(m);
+ if (r == NULL)
+ return NULL;
+ dbus_message_iter_init_append(r, &i);
+ dbus_message_iter_open_container(&i, DBUS_TYPE_VARIANT,
+ DBUS_TYPE_BYTE_AS_STRING, &v);
+ dbus_message_iter_append_basic(&v, DBUS_TYPE_BYTE,
+ &agimpl->this.volume[SPA_BT_VOLUME_ID_RX]);
+ dbus_message_iter_close_container(&i, &v);
+ return r;
+ } else if (spa_streq(name, "MicrophoneVolume")) {
+ r = dbus_message_new_method_return(m);
+ if (r == NULL)
+ return NULL;
+ dbus_message_iter_init_append(r, &i);
+ dbus_message_iter_open_container(&i, DBUS_TYPE_VARIANT,
+ DBUS_TYPE_BYTE_AS_STRING, &v);
+ dbus_message_iter_append_basic(&v, DBUS_TYPE_BYTE,
+ &agimpl->this.volume[SPA_BT_VOLUME_ID_TX]);
+ dbus_message_iter_close_container(&i, &v);
+ return r;
}
} else if (spa_streq(iface, PW_TELEPHONY_AG_TRANSPORT_IFACE)) {
if (spa_streq(name, "Codec")) {
@@ -796,7 +858,38 @@ static DBusMessage *ag_properties_set(struct agimpl *agimpl, DBusMessage *m)
DBUS_TYPE_INVALID))
return NULL;
- if (spa_streq(iface, PW_TELEPHONY_AG_TRANSPORT_IFACE)) {
+ if (spa_streq(iface, PW_TELEPHONY_AG_IFACE)) {
+ enum spa_bt_telephony_error err = BT_TELEPHONY_ERROR_FAILED;
+ uint8_t cme_error;
+
+ if (spa_streq(name, "SpeakerVolume")) {
+ dbus_message_iter_init(m, &i);
+ dbus_message_iter_next(&i); /* skip iface */
+ dbus_message_iter_next(&i); /* skip name */
+ dbus_message_iter_recurse(&i, &variant); /* value */
+ dbus_message_iter_get_basic(&variant, &agimpl->this.volume[SPA_BT_VOLUME_ID_RX]);
+
+ if (ag_emit_set_speaker_volume(agimpl, agimpl->this.volume[SPA_BT_VOLUME_ID_RX], &err, &cme_error) &&
+ err == BT_TELEPHONY_ERROR_NONE)
+ return dbus_message_new_method_return(m);
+
+ return dbus_message_new_error(m, telephony_error_to_dbus (err),
+ telephony_error_to_description (err, cme_error));
+ } else if (spa_streq(name, "MicrophoneVolume")) {
+ dbus_message_iter_init(m, &i);
+ dbus_message_iter_next(&i); /* skip iface */
+ dbus_message_iter_next(&i); /* skip name */
+ dbus_message_iter_recurse(&i, &variant); /* value */
+ dbus_message_iter_get_basic(&variant, &agimpl->this.volume[SPA_BT_VOLUME_ID_TX]);
+
+ if (ag_emit_set_microphone_volume(agimpl, agimpl->this.volume[SPA_BT_VOLUME_ID_TX], &err, &cme_error) &&
+ err == BT_TELEPHONY_ERROR_NONE)
+ return dbus_message_new_method_return(m);
+
+ return dbus_message_new_error(m, telephony_error_to_dbus (err),
+ telephony_error_to_description (err, cme_error));
+ }
+ } else if (spa_streq(iface, PW_TELEPHONY_AG_TRANSPORT_IFACE)) {
if (spa_streq(name, "RejectSCO")) {
dbus_message_iter_init(m, &i);
dbus_message_iter_next(&i); /* skip iface */
@@ -1219,7 +1312,38 @@ void telephony_ag_unregister(struct spa_bt_telephony_ag *ag)
agimpl->path = NULL;
}
-/* send message to notify about property changes */
+/* send message to notify about volume property changes */
+void telephony_ag_notify_updated_props(struct spa_bt_telephony_ag *ag)
+{
+ struct agimpl *agimpl = SPA_CONTAINER_OF(ag, struct agimpl, this);
+ struct impl *impl = SPA_CONTAINER_OF(agimpl->this.telephony, struct impl, this);
+
+ spa_autoptr(DBusMessage) msg = NULL;
+ const char *interface = PW_TELEPHONY_AG_IFACE;
+ DBusMessageIter i, a;
+
+ msg = dbus_message_new_signal(agimpl->path,
+ DBUS_INTERFACE_PROPERTIES,
+ "PropertiesChanged");
+
+ dbus_message_iter_init_append(msg, &i);
+ dbus_message_iter_append_basic(&i, DBUS_TYPE_STRING, &interface);
+
+ if (!dbus_iter_append_ag_properties(&i, ag, false))
+ return;
+
+ dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING, &a);
+ dbus_message_iter_close_container(&i, &a);
+
+ if (!dbus_connection_send(impl->conn, msg, NULL)){
+ spa_log_warn(impl->log, "sending PropertiesChanged failed");
+ }
+
+ telephony_ag_commit_properties(ag);
+}
+
+/* send message to notify about transport property changes */
void telephony_ag_transport_notify_updated_props(struct spa_bt_telephony_ag *ag)
{
struct agimpl *agimpl = SPA_CONTAINER_OF(ag, struct agimpl, this);
diff --git a/spa/plugins/bluez5/telephony.h b/spa/plugins/bluez5/telephony.h
index 8ba85ec47..13961b7db 100644
--- a/spa/plugins/bluez5/telephony.h
+++ b/spa/plugins/bluez5/telephony.h
@@ -45,6 +45,7 @@ struct spa_bt_telephony_ag {
/* D-Bus properties */
char *address;
+ int volume[SPA_BT_VOLUME_ID_TERM];
struct spa_bt_telephony_ag_transport transport;
};
@@ -76,6 +77,9 @@ struct spa_bt_telephony_ag_callbacks {
void (*send_tones)(void *data, const char *tones, enum spa_bt_telephony_error *err, uint8_t *cme_error);
void (*transport_activate)(void *data, enum spa_bt_telephony_error *err, uint8_t *cme_error);
+
+ void (*set_speaker_volume)(void *data, uint8_t volume, enum spa_bt_telephony_error *err, uint8_t *cme_error);
+ void (*set_microphone_volume)(void *data, uint8_t volume, enum spa_bt_telephony_error *err, uint8_t *cme_error);
};
struct spa_bt_telephony_call_callbacks {
@@ -103,6 +107,7 @@ void telephony_ag_set_callbacks(struct spa_bt_telephony_ag *ag,
const struct spa_bt_telephony_ag_callbacks *cbs,
void *data);
+void telephony_ag_notify_updated_props(struct spa_bt_telephony_ag *ag);
void telephony_ag_transport_notify_updated_props(struct spa_bt_telephony_ag *ag);
/* register/unregister AudioGateway object on the bus */