mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-29 05:40:27 -04:00
bluez5: backend-native: Add volume support to HFP HF
The volume synchronization could be done even if there's no audio link and so no transport opened. This patch allows to send the Speaker (AT+VGS) and Microphone (AT+VGM) commands at the end of the SLC. And to exchange volume updates using the telephony DBus interface, even without a transport.
This commit is contained in:
parent
48a32e4ced
commit
1af1fc846c
3 changed files with 261 additions and 31 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -117,6 +117,8 @@
|
|||
" <property name='Address' type='s' access='read'>" \
|
||||
" <annotation name='org.freedesktop.DBus.Property.EmitsChangedSignal' value='const'/>" \
|
||||
" </property>" \
|
||||
" <property name='SpeakerVolume' type='y' access='readwrite'/>" \
|
||||
" <property name='MicrophoneVolume' type='y' access='readwrite'/>" \
|
||||
" </interface>" \
|
||||
" <interface name='" PW_TELEPHONY_AG_TRANSPORT_IFACE "'>" \
|
||||
" <property name='State' type='s' access='read'/>" \
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue