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
197 lines
8.4 KiB
Diff
197 lines
8.4 KiB
Diff
From 78e255eefb417275e73f8ed96ce08bf7c3a1dff9 Mon Sep 17 00:00:00 2001
|
|
From: Dylan Van Assche <me@dylanvanassche.be>
|
|
Date: Tue, 5 Apr 2022 20:39:11 +0200
|
|
Subject: [PATCH 04/26] bluetooth: hook up UPower backend
|
|
|
|
Hook up the UPower backend to backend-native to report
|
|
the host battery level to the HF.
|
|
---
|
|
src/modules/bluetooth/backend-native.c | 100 +++++++++++++++++++++++--
|
|
src/modules/bluetooth/bluez5-util.h | 2 +
|
|
2 files changed, 95 insertions(+), 7 deletions(-)
|
|
|
|
diff --git a/src/modules/bluetooth/backend-native.c b/src/modules/bluetooth/backend-native.c
|
|
index d26edc1c9..a490d6efd 100644
|
|
--- a/src/modules/bluetooth/backend-native.c
|
|
+++ b/src/modules/bluetooth/backend-native.c
|
|
@@ -38,6 +38,7 @@
|
|
|
|
#include "bluez5-util.h"
|
|
#include "bt-codec-msbc.h"
|
|
+#include "upower.h"
|
|
|
|
#define MANDATORY_CALL_INDICATORS \
|
|
"(\"call\",(0-1))," \
|
|
@@ -49,6 +50,8 @@ struct pa_bluetooth_backend {
|
|
pa_dbus_connection *connection;
|
|
pa_bluetooth_discovery *discovery;
|
|
pa_hook_slot *adapter_uuids_changed_slot;
|
|
+ pa_hook_slot *host_battery_level_changed_slot;
|
|
+ pa_upower_backend *upower;
|
|
bool enable_shared_profiles;
|
|
bool enable_hsp_hs;
|
|
bool enable_hfp_hf;
|
|
@@ -683,17 +686,27 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
|
|
|
|
return true;
|
|
} else if (c->state == 1 && pa_startswith(buf, "AT+CIND=?")) {
|
|
- /* we declare minimal no indicators */
|
|
- rfcomm_write_response(fd, "+CIND: "
|
|
- /* many indicators can be supported, only call and
|
|
- * callheld are mandatory, so that's all we reply */
|
|
- MANDATORY_CALL_INDICATORS ",",
|
|
- "(\"service\",(0-1))");
|
|
+ /* UPower backend available, declare support for more indicators */
|
|
+ if (discovery->native_backend->upower) {
|
|
+ rfcomm_write_response(fd, "+CIND: "
|
|
+ MANDATORY_CALL_INDICATORS ","
|
|
+ "(\"service\",(0-1)),"
|
|
+ "(\"battchg\",(0-5))");
|
|
+
|
|
+ /* Minimal indicators supported without any additional backend */
|
|
+ } else {
|
|
+ rfcomm_write_response(fd, "+CIND: "
|
|
+ MANDATORY_CALL_INDICATORS ","
|
|
+ "(\"service\",(0-1))");
|
|
+ }
|
|
c->state = 2;
|
|
|
|
return true;
|
|
} else if (c->state == 2 && pa_startswith(buf, "AT+CIND?")) {
|
|
- rfcomm_write_response(fd, "+CIND: 0,0,0,0");
|
|
+ if (discovery->native_backend->upower)
|
|
+ rfcomm_write_response(fd, "+CIND: 0,0,0,0,%u", pa_upower_get_battery_level(discovery->native_backend->upower));
|
|
+ else
|
|
+ rfcomm_write_response(fd, "+CIND: 0,0,0,0");
|
|
c->state = 3;
|
|
|
|
return true;
|
|
@@ -803,6 +816,67 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
|
|
return true;
|
|
}
|
|
|
|
+static int get_rfcomm_fd (pa_bluetooth_discovery *discovery) {
|
|
+ struct pa_bluetooth_transport *t;
|
|
+ struct transport_data *trd = NULL;
|
|
+ pa_bluetooth_profile_t profiles[] = { PA_BLUETOOTH_PROFILE_HSP_HS, PA_BLUETOOTH_PROFILE_HSP_AG, PA_BLUETOOTH_PROFILE_HFP_HF };
|
|
+ int i;
|
|
+ bool found_transport = false;
|
|
+ void *state = NULL;
|
|
+
|
|
+ /* Find RFCOMM transport by checking if a HSP or HFP profile transport is available */
|
|
+ while ((t = pa_hashmap_iterate(discovery->transports, &state, NULL))) {
|
|
+ /* Skip non-connected transports */
|
|
+ if (!t || t->state == PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED) {
|
|
+ pa_log_debug("Profile %d disconnected or unavailable", i);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ /* Break when an RFCOMM capable transport profile is available */
|
|
+ for (i = 0; i < (sizeof profiles / sizeof *profiles); i++) {
|
|
+ if (t->profile == profiles[i]) {
|
|
+ trd = t->userdata;
|
|
+ found_transport = true;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (found_transport)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* Skip if RFCOMM channel is not available yet */
|
|
+ if (!trd) {
|
|
+ pa_log_info("RFCOMM not available yet, skipping notification");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ return trd->rfcomm_fd;
|
|
+}
|
|
+
|
|
+static pa_hook_result_t host_battery_level_changed_cb(pa_bluetooth_discovery *y, const pa_upower_backend *u, pa_bluetooth_backend *b) {
|
|
+ int rfcomm_fd;
|
|
+
|
|
+ pa_assert(y);
|
|
+ pa_assert(u);
|
|
+ pa_assert(b);
|
|
+
|
|
+ /* Get RFCOMM channel if available */
|
|
+ rfcomm_fd = get_rfcomm_fd (y);
|
|
+ if (rfcomm_fd < 0)
|
|
+ return PA_HOOK_OK;
|
|
+
|
|
+ /* Notify HF about AG battery level change over RFCOMM */
|
|
+ if (b->cmer_indicator_reporting_enabled && (b->cind_enabled_indicators & (1 << CIND_BATT_CHG_INDICATOR))) {
|
|
+ rfcomm_write_response(rfcomm_fd, "+CIEV: %d,%d", CIND_BATT_CHG_INDICATOR, u->battery_level);
|
|
+ pa_log_debug("HG notified of AG's battery level change");
|
|
+ /* Skip notification if indicator is disabled or event reporting is completely disabled */
|
|
+ } else
|
|
+ pa_log_debug("Battery level change indicator disabled, skipping notification");
|
|
+
|
|
+ return PA_HOOK_OK;
|
|
+}
|
|
+
|
|
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;
|
|
@@ -1284,6 +1358,10 @@ pa_bluetooth_backend *pa_bluetooth_native_backend_new(pa_core *c, pa_bluetooth_d
|
|
pa_hook_connect(pa_bluetooth_discovery_hook(y, PA_BLUETOOTH_HOOK_ADAPTER_UUIDS_CHANGED), PA_HOOK_NORMAL,
|
|
(pa_hook_cb_t) adapter_uuids_changed_cb, backend);
|
|
|
|
+ backend->host_battery_level_changed_slot =
|
|
+ pa_hook_connect(pa_bluetooth_discovery_hook(y, PA_BLUETOOTH_HOOK_HOST_BATTERY_LEVEL_CHANGED), PA_HOOK_NORMAL,
|
|
+ (pa_hook_cb_t) host_battery_level_changed_cb, backend);
|
|
+
|
|
if (!backend->enable_hsp_hs && !backend->enable_hfp_hf)
|
|
pa_log_warn("Both HSP HS and HFP HF bluetooth profiles disabled in native backend. Native backend will not register for headset connections.");
|
|
|
|
@@ -1293,6 +1371,8 @@ 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);
|
|
|
|
+ backend->upower = pa_upower_backend_new(c, y);
|
|
+
|
|
/* 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);
|
|
@@ -1312,12 +1392,18 @@ void pa_bluetooth_native_backend_free(pa_bluetooth_backend *backend) {
|
|
if (backend->adapter_uuids_changed_slot)
|
|
pa_hook_slot_free(backend->adapter_uuids_changed_slot);
|
|
|
|
+ if (backend->host_battery_level_changed_slot)
|
|
+ pa_hook_slot_free(backend->host_battery_level_changed_slot);
|
|
+
|
|
if (backend->enable_shared_profiles)
|
|
native_backend_apply_profile_registration_change(backend, false);
|
|
|
|
if (backend->enable_hsp_hs)
|
|
profile_done(backend, PA_BLUETOOTH_PROFILE_HSP_HS);
|
|
|
|
+ if (backend->upower)
|
|
+ pa_upower_backend_free(backend->upower);
|
|
+
|
|
pa_dbus_connection_unref(backend->connection);
|
|
|
|
pa_xfree(backend);
|
|
diff --git a/src/modules/bluetooth/bluez5-util.h b/src/modules/bluetooth/bluez5-util.h
|
|
index f899d9d0c..a24a0b823 100644
|
|
--- a/src/modules/bluetooth/bluez5-util.h
|
|
+++ b/src/modules/bluetooth/bluez5-util.h
|
|
@@ -63,12 +63,14 @@ typedef struct pa_bluetooth_device pa_bluetooth_device;
|
|
typedef struct pa_bluetooth_adapter pa_bluetooth_adapter;
|
|
typedef struct pa_bluetooth_discovery pa_bluetooth_discovery;
|
|
typedef struct pa_bluetooth_backend pa_bluetooth_backend;
|
|
+typedef struct pa_upower_backend pa_upower_backend;
|
|
|
|
typedef enum pa_bluetooth_hook {
|
|
PA_BLUETOOTH_HOOK_ADAPTER_UUIDS_CHANGED, /* Call data: pa_bluetooth_adapter */
|
|
PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED, /* Call data: pa_bluetooth_device */
|
|
PA_BLUETOOTH_HOOK_DEVICE_UNLINK, /* Call data: pa_bluetooth_device */
|
|
PA_BLUETOOTH_HOOK_DEVICE_BATTERY_LEVEL_CHANGED, /* Call data: pa_bluetooth_device */
|
|
+ PA_BLUETOOTH_HOOK_HOST_BATTERY_LEVEL_CHANGED, /* Call data: pa_upower_backend */
|
|
PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED, /* Call data: pa_bluetooth_transport */
|
|
PA_BLUETOOTH_HOOK_TRANSPORT_SOURCE_VOLUME_CHANGED, /* Call data: pa_bluetooth_transport */
|
|
PA_BLUETOOTH_HOOK_TRANSPORT_SINK_VOLUME_CHANGED, /* Call data: pa_bluetooth_transport */
|
|
--
|
|
2.35.1
|
|
|