f9c7ffa9b6
PulseAudio is used for handling all audio on postmarketOS. This also involves Bluetooth audio such as A2DP, HSP and HFP audio. In the case of HFP/HSP, the HF and AG can interact with each other through AT commands defined in the Bluetooth HFP 1.8 spec. This set of patches implements HFP support to allow Bluetooth devices to accept/reject/hangup calls, dial numbers, DTMF tone generation, query signal strength, roaming status, service status, AG battery level, call status, etc. More details in the upstream MR: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/693 Available in edge for testing this merge request with a broader user base. Not intended for backporting to stable branches. [ci:skip-build]: already built successfully in CI
194 lines
7.4 KiB
Diff
194 lines
7.4 KiB
Diff
From 5aebba47f8215d5c580602a34082fc27081d963d Mon Sep 17 00:00:00 2001
|
|
From: Dylan Van Assche <me@dylanvanassche.be>
|
|
Date: Tue, 5 Apr 2022 20:10:05 +0200
|
|
Subject: [PATCH 02/26] 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.
|
|
---
|
|
src/modules/bluetooth/backend-native.c | 93 ++++++++++++++++++++++++--
|
|
1 file changed, 87 insertions(+), 6 deletions(-)
|
|
|
|
diff --git a/src/modules/bluetooth/backend-native.c b/src/modules/bluetooth/backend-native.c
|
|
index f6b85e90d..d26edc1c9 100644
|
|
--- a/src/modules/bluetooth/backend-native.c
|
|
+++ b/src/modules/bluetooth/backend-native.c
|
|
@@ -39,6 +39,11 @@
|
|
#include "bluez5-util.h"
|
|
#include "bt-codec-msbc.h"
|
|
|
|
+#define MANDATORY_CALL_INDICATORS \
|
|
+ "(\"call\",(0-1))," \
|
|
+ "(\"callsetup\",(0-3))," \
|
|
+ "(\"callheld\",(0-2))" \
|
|
+
|
|
struct pa_bluetooth_backend {
|
|
pa_core *core;
|
|
pa_dbus_connection *connection;
|
|
@@ -47,6 +52,8 @@ struct pa_bluetooth_backend {
|
|
bool enable_shared_profiles;
|
|
bool enable_hsp_hs;
|
|
bool enable_hfp_hf;
|
|
+ bool cmer_indicator_reporting_enabled;
|
|
+ uint32_t cind_enabled_indicators;
|
|
|
|
PA_LLIST_HEAD(pa_dbus_pending, pending);
|
|
};
|
|
@@ -97,6 +104,20 @@ enum hfp_ag_features {
|
|
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 */
|
|
static uint32_t hfp_features =
|
|
/* HFP 1.6 requires this */
|
|
@@ -588,8 +609,9 @@ 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)
|
|
{
|
|
+ struct pa_bluetooth_discovery *discovery = t->device->discovery;
|
|
struct hfp_config *c = t->config;
|
|
- int indicator, val;
|
|
+ int indicator, mode, val;
|
|
char str[5];
|
|
const char *r;
|
|
size_t len;
|
|
@@ -607,6 +629,34 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
|
|
c->supports_indicators = !!(1 << HFP_HF_INDICATORS);
|
|
c->state = 1;
|
|
|
|
+ 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) {
|
|
c->support_msbc = false;
|
|
@@ -637,10 +687,8 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
|
|
rfcomm_write_response(fd, "+CIND: "
|
|
/* many indicators can be supported, only call and
|
|
* callheld are mandatory, so that's all we reply */
|
|
- "(\"service\",(0-1)),"
|
|
- "(\"call\",(0-1)),"
|
|
- "(\"callsetup\",(0-3)),"
|
|
- "(\"callheld\",(0-2))");
|
|
+ MANDATORY_CALL_INDICATORS ",",
|
|
+ "(\"service\",(0-1))");
|
|
c->state = 2;
|
|
|
|
return true;
|
|
@@ -650,7 +698,24 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
|
|
|
|
return true;
|
|
} 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_msbc && pa_bluetooth_discovery_get_enable_msbc(t->device->discovery)) {
|
|
@@ -740,6 +805,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) {
|
|
pa_bluetooth_transport *t = userdata;
|
|
+ pa_bluetooth_discovery *discovery = t->device->discovery;
|
|
+ int i;
|
|
|
|
pa_assert(io);
|
|
pa_assert(t);
|
|
@@ -860,6 +927,11 @@ static void rfcomm_io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_i
|
|
return;
|
|
|
|
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_free(t);
|
|
}
|
|
@@ -1188,6 +1260,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 *backend;
|
|
DBusError err;
|
|
+ int i;
|
|
|
|
pa_log_debug("Bluetooth Headset Backend API support using the native backend");
|
|
|
|
@@ -1220,6 +1293,14 @@ pa_bluetooth_backend *pa_bluetooth_native_backend_new(pa_core *c, pa_bluetooth_d
|
|
if (backend->enable_shared_profiles)
|
|
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;
|
|
}
|
|
|
|
--
|
|
2.35.1
|
|
|