mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-10-29 05:40:23 -04:00
bluetooth: add AT+BIA support
AT+BIA is used to enable/disable CIND indicators by Bluetooth HFP spec. By default, all indicators are enabled on connection. AT+BIA will configure which indicators should be disabled then, the disabled indicators may be enabled later on again with AT+BIA. When the connection is lost and recovered, all indicators are enabled again. The HF will reconfigure the indicators again with an AT+BIA command. Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/631>
This commit is contained in:
parent
b05e34e092
commit
cca0d69375
1 changed files with 89 additions and 9 deletions
|
|
@ -39,6 +39,11 @@
|
||||||
#include "bluez5-util.h"
|
#include "bluez5-util.h"
|
||||||
#include "bt-codec-msbc.h"
|
#include "bt-codec-msbc.h"
|
||||||
|
|
||||||
|
#define MANDATORY_CALL_INDICATORS \
|
||||||
|
"(\"call\",(0-1))," \
|
||||||
|
"(\"callsetup\",(0-3))," \
|
||||||
|
"(\"callheld\",(0-2))" \
|
||||||
|
|
||||||
struct pa_bluetooth_backend {
|
struct pa_bluetooth_backend {
|
||||||
pa_core *core;
|
pa_core *core;
|
||||||
pa_dbus_connection *connection;
|
pa_dbus_connection *connection;
|
||||||
|
|
@ -47,6 +52,8 @@ struct pa_bluetooth_backend {
|
||||||
bool enable_shared_profiles;
|
bool enable_shared_profiles;
|
||||||
bool enable_hsp_hs;
|
bool enable_hsp_hs;
|
||||||
bool enable_hfp_hf;
|
bool enable_hfp_hf;
|
||||||
|
bool cmer_indicator_reporting_enabled;
|
||||||
|
uint32_t cind_enabled_indicators;
|
||||||
|
|
||||||
PA_LLIST_HEAD(pa_dbus_pending, pending);
|
PA_LLIST_HEAD(pa_dbus_pending, pending);
|
||||||
};
|
};
|
||||||
|
|
@ -97,6 +104,20 @@ enum hfp_ag_features {
|
||||||
HFP_AG_INDICATORS = 10,
|
HFP_AG_INDICATORS = 10,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Always keep this struct in sync with indicator discovery of AT+CIND=?
|
||||||
|
* These indicators are used in bitflags and intentionally start at 1
|
||||||
|
* since AT+CIND indicators start at index 1.
|
||||||
|
*/
|
||||||
|
typedef enum pa_bluetooth_ag_to_hf_indicators {
|
||||||
|
CIND_CALL_INDICATOR = 1,
|
||||||
|
CIND_CALL_SETUP_INDICATOR = 2,
|
||||||
|
CIND_CALL_HELD_INDICATOR = 3,
|
||||||
|
CIND_SERVICE_INDICATOR = 4,
|
||||||
|
CIND_BATT_CHG_INDICATOR = 5,
|
||||||
|
CIND_INDICATOR_MAX = 6
|
||||||
|
} pa_bluetooth_ag_to_hf_indicators_t;
|
||||||
|
|
||||||
/* gateway features we support, which is as little as we can get away with */
|
/* gateway features we support, which is as little as we can get away with */
|
||||||
static uint32_t hfp_features =
|
static uint32_t hfp_features =
|
||||||
/* HFP 1.6 requires this */
|
/* HFP 1.6 requires this */
|
||||||
|
|
@ -588,12 +609,13 @@ static pa_volume_t set_source_volume(pa_bluetooth_transport *t, pa_volume_t volu
|
||||||
|
|
||||||
static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf)
|
static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf)
|
||||||
{
|
{
|
||||||
|
struct pa_bluetooth_discovery *discovery = t->device->discovery;
|
||||||
struct hfp_config *c = t->config;
|
struct hfp_config *c = t->config;
|
||||||
int indicator, val;
|
int indicator, mode, val;
|
||||||
char str[5];
|
char str[5];
|
||||||
const char *r;
|
const char *r;
|
||||||
size_t len;
|
size_t len;
|
||||||
const char *state;
|
const char *state = NULL;
|
||||||
|
|
||||||
/* first-time initialize selected codec to CVSD */
|
/* first-time initialize selected codec to CVSD */
|
||||||
if (c->selected_codec == 0)
|
if (c->selected_codec == 0)
|
||||||
|
|
@ -608,11 +630,38 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
|
||||||
c->state = 1;
|
c->state = 1;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
} else if (sscanf(buf, "AT+BIA=%s", str) == 1) {
|
||||||
|
/* Indicators start with index 1 and follow the order of the AT+CIND=? response */
|
||||||
|
indicator = 1;
|
||||||
|
|
||||||
|
while ((r = pa_split_in_place(str, ",", &len, &state))) {
|
||||||
|
/* Ignore updates to mandantory indicators which are always ON */
|
||||||
|
if (indicator == CIND_CALL_INDICATOR
|
||||||
|
|| indicator == CIND_CALL_SETUP_INDICATOR
|
||||||
|
|| indicator == CIND_CALL_HELD_INDICATOR)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Indicators may have no value and should be skipped */
|
||||||
|
if (len == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (len == 1 && r[0] == '1')
|
||||||
|
discovery->native_backend->cind_enabled_indicators |= (1 << indicator);
|
||||||
|
else if (len == 1 && r[0] == '0')
|
||||||
|
discovery->native_backend->cind_enabled_indicators &= ~(1 << indicator);
|
||||||
|
else {
|
||||||
|
pa_log_error("Unable to parse indicator of AT+BIA command: %s", buf);
|
||||||
|
rfcomm_write_response(fd, "ERROR");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
indicator++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
} else if (sscanf(buf, "AT+BAC=%3s", str) == 1) {
|
} else if (sscanf(buf, "AT+BAC=%3s", str) == 1) {
|
||||||
c->support_msbc = false;
|
c->support_msbc = false;
|
||||||
|
|
||||||
state = NULL;
|
|
||||||
|
|
||||||
/* check if codec id 2 (mSBC) is in the list of supported codecs */
|
/* check if codec id 2 (mSBC) is in the list of supported codecs */
|
||||||
while ((r = pa_split_in_place(str, ",", &len, &state))) {
|
while ((r = pa_split_in_place(str, ",", &len, &state))) {
|
||||||
if (len == 1 && r[0] == '2') {
|
if (len == 1 && r[0] == '2') {
|
||||||
|
|
@ -637,10 +686,8 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
|
||||||
rfcomm_write_response(fd, "+CIND: "
|
rfcomm_write_response(fd, "+CIND: "
|
||||||
/* many indicators can be supported, only call and
|
/* many indicators can be supported, only call and
|
||||||
* callheld are mandatory, so that's all we reply */
|
* callheld are mandatory, so that's all we reply */
|
||||||
"(\"service\",(0-1)),"
|
MANDATORY_CALL_INDICATORS ",",
|
||||||
"(\"call\",(0-1)),"
|
"(\"service\",(0-1))");
|
||||||
"(\"callsetup\",(0-3)),"
|
|
||||||
"(\"callheld\",(0-2))");
|
|
||||||
c->state = 2;
|
c->state = 2;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -650,7 +697,24 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else if ((c->state == 2 || c->state == 3) && pa_startswith(buf, "AT+CMER=")) {
|
} else if ((c->state == 2 || c->state == 3) && pa_startswith(buf, "AT+CMER=")) {
|
||||||
rfcomm_write_response(fd, "OK");
|
if (sscanf(buf, "AT+CMER=%d,%*d,%*d,%d", &mode, &val) == 2) {
|
||||||
|
/* Bluetooth HFP spec only defines mode == 3 */
|
||||||
|
if (mode != 3) {
|
||||||
|
pa_log_warn("Unexpected mode for AT+CMER: %d", mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Configure CMER event reporting */
|
||||||
|
discovery->native_backend->cmer_indicator_reporting_enabled = !!val;
|
||||||
|
|
||||||
|
pa_log_debug("Event indications enabled? %s", pa_yes_no(val));
|
||||||
|
|
||||||
|
rfcomm_write_response(fd, "OK");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
pa_log_error("Unable to parse AT+CMER command: %s", buf);
|
||||||
|
rfcomm_write_response(fd, "ERROR");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (c->support_codec_negotiation) {
|
if (c->support_codec_negotiation) {
|
||||||
if (c->support_msbc && pa_bluetooth_discovery_get_enable_msbc(t->device->discovery)) {
|
if (c->support_msbc && pa_bluetooth_discovery_get_enable_msbc(t->device->discovery)) {
|
||||||
|
|
@ -740,6 +804,8 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
|
||||||
|
|
||||||
static void rfcomm_io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
|
static void rfcomm_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_bluetooth_transport *t = userdata;
|
||||||
|
pa_bluetooth_discovery *discovery = t->device->discovery;
|
||||||
|
int i;
|
||||||
|
|
||||||
pa_assert(io);
|
pa_assert(io);
|
||||||
pa_assert(t);
|
pa_assert(t);
|
||||||
|
|
@ -860,6 +926,11 @@ static void rfcomm_io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_i
|
||||||
return;
|
return;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
/* Service Connection lost, reset indicators and event reporting to default values */
|
||||||
|
discovery->native_backend->cmer_indicator_reporting_enabled = false;
|
||||||
|
for (i = 1; i < CIND_INDICATOR_MAX; i++)
|
||||||
|
discovery->native_backend->cind_enabled_indicators |= (1 << i);
|
||||||
|
|
||||||
pa_bluetooth_transport_unlink(t);
|
pa_bluetooth_transport_unlink(t);
|
||||||
pa_bluetooth_transport_free(t);
|
pa_bluetooth_transport_free(t);
|
||||||
}
|
}
|
||||||
|
|
@ -1188,6 +1259,7 @@ void pa_bluetooth_native_backend_enable_shared_profiles(pa_bluetooth_backend *na
|
||||||
pa_bluetooth_backend *pa_bluetooth_native_backend_new(pa_core *c, pa_bluetooth_discovery *y, bool enable_shared_profiles) {
|
pa_bluetooth_backend *pa_bluetooth_native_backend_new(pa_core *c, pa_bluetooth_discovery *y, bool enable_shared_profiles) {
|
||||||
pa_bluetooth_backend *backend;
|
pa_bluetooth_backend *backend;
|
||||||
DBusError err;
|
DBusError err;
|
||||||
|
int i;
|
||||||
|
|
||||||
pa_log_debug("Bluetooth Headset Backend API support using the native backend");
|
pa_log_debug("Bluetooth Headset Backend API support using the native backend");
|
||||||
|
|
||||||
|
|
@ -1220,6 +1292,14 @@ pa_bluetooth_backend *pa_bluetooth_native_backend_new(pa_core *c, pa_bluetooth_d
|
||||||
if (backend->enable_shared_profiles)
|
if (backend->enable_shared_profiles)
|
||||||
native_backend_apply_profile_registration_change(backend, true);
|
native_backend_apply_profile_registration_change(backend, true);
|
||||||
|
|
||||||
|
/* All CIND indicators are enabled by default until overriden by AT+BIA */
|
||||||
|
for (i = 1; i < CIND_INDICATOR_MAX; i++)
|
||||||
|
backend->cind_enabled_indicators |= (1 << i);
|
||||||
|
|
||||||
|
/* While all CIND indicators are enabled, event reporting is not enabled by default */
|
||||||
|
backend->cmer_indicator_reporting_enabled = false;
|
||||||
|
|
||||||
|
|
||||||
return backend;
|
return backend;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue