temp/pulseaudio: drop (MR 5163)

Upstream MR has been closed by Dylan 2 months ago, and this feature is
already present in pipewire. We are doing considerably amount of work to
get Pipewire production-ready in postmarketOS, including features to let
users choose. Given HFP support is actually a nice-to-have feature, but
not a great requirement, our fork is outdated (aports is on 17.0), and that
the upstream MR has been dropped and is not being worked on, it might be
time to drop this.
This commit is contained in:
Pablo Correa Gómez 2024-05-21 10:26:17 +02:00
parent 263d00790e
commit be9ac83d73
No known key found for this signature in database
GPG key ID: 7A342565FF635F79
32 changed files with 0 additions and 4790 deletions

View file

@ -1,110 +0,0 @@
From 355c4cc3f70abee505d2aad0a37d453a44ead1c2 Mon Sep 17 00:00:00 2001
From: Dylan Van Assche <me@dylanvanassche.be>
Date: Wed, 6 Apr 2022 08:15:13 +0200
Subject: [PATCH 01/26] bluez5-util: move pa_bluetooth_discovery to header
---
src/modules/bluetooth/bluez5-util.c | 23 -----------------------
src/modules/bluetooth/bluez5-util.h | 24 ++++++++++++++++++++++++
src/modules/meson.build | 2 +-
3 files changed, 25 insertions(+), 24 deletions(-)
diff --git a/src/modules/bluetooth/bluez5-util.c b/src/modules/bluetooth/bluez5-util.c
index 83f2932e9..2f8d6faf1 100644
--- a/src/modules/bluetooth/bluez5-util.c
+++ b/src/modules/bluetooth/bluez5-util.c
@@ -31,7 +31,6 @@
#include <pulsecore/core.h>
#include <pulsecore/core-error.h>
#include <pulsecore/core-util.h>
-#include <pulsecore/dbus-shared.h>
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
#include <pulsecore/refcnt.h>
@@ -128,28 +127,6 @@ static uint16_t volume_to_a2dp_gain(pa_volume_t volume) {
return gain;
}
-struct pa_bluetooth_discovery {
- PA_REFCNT_DECLARE;
-
- pa_core *core;
- pa_dbus_connection *connection;
- bool filter_added;
- bool matches_added;
- bool objects_listed;
- pa_hook hooks[PA_BLUETOOTH_HOOK_MAX];
- pa_hashmap *adapters;
- pa_hashmap *devices;
- pa_hashmap *transports;
- pa_bluetooth_profile_status_t profiles_status[PA_BLUETOOTH_PROFILE_COUNT];
-
- int headset_backend;
- pa_bluetooth_backend *ofono_backend, *native_backend;
- PA_LLIST_HEAD(pa_dbus_pending, pending);
- bool enable_native_hsp_hs;
- bool enable_native_hfp_hf;
- bool enable_msbc;
-};
-
static pa_dbus_pending* send_and_add_to_pending(pa_bluetooth_discovery *y, DBusMessage *m,
DBusPendingCallNotifyFunction func, void *call_data) {
pa_dbus_pending *p;
diff --git a/src/modules/bluetooth/bluez5-util.h b/src/modules/bluetooth/bluez5-util.h
index 86eb6301e..f899d9d0c 100644
--- a/src/modules/bluetooth/bluez5-util.h
+++ b/src/modules/bluetooth/bluez5-util.h
@@ -22,6 +22,7 @@
***/
#include <pulsecore/core.h>
+#include <pulsecore/dbus-shared.h>
#include "a2dp-codec-util.h"
@@ -134,6 +135,29 @@ struct pa_bluetooth_transport {
void *userdata;
};
+struct pa_bluetooth_discovery {
+ PA_REFCNT_DECLARE;
+
+ pa_core *core;
+ pa_dbus_connection *connection;
+ bool filter_added;
+ bool matches_added;
+ bool objects_listed;
+ pa_hook hooks[PA_BLUETOOTH_HOOK_MAX];
+ pa_hashmap *adapters;
+ pa_hashmap *devices;
+ pa_hashmap *transports;
+ pa_bluetooth_profile_status_t profiles_status[PA_BLUETOOTH_PROFILE_COUNT];
+
+ int headset_backend;
+ pa_bluetooth_backend *ofono_backend, *native_backend;
+ PA_LLIST_HEAD(pa_dbus_pending, pending);
+ bool enable_native_hsp_hs;
+ bool enable_native_hfp_hf;
+ bool enable_msbc;
+};
+
+
struct pa_bluetooth_device {
pa_bluetooth_discovery *discovery;
pa_bluetooth_adapter *adapter;
diff --git a/src/modules/meson.build b/src/modules/meson.build
index 1ae172971..08c754948 100644
--- a/src/modules/meson.build
+++ b/src/modules/meson.build
@@ -123,7 +123,7 @@ if cdata.has('HAVE_BLUEZ_5')
all_modules += [
[ 'module-bluetooth-discover', 'bluetooth/module-bluetooth-discover.c' ],
[ 'module-bluetooth-policy', 'bluetooth/module-bluetooth-policy.c', [], [], [dbus_dep] ],
- [ 'module-bluez5-device', 'bluetooth/module-bluez5-device.c', [], [], [], libbluez5_util ],
+ [ 'module-bluez5-device', 'bluetooth/module-bluez5-device.c', [], [], [dbus_dep], libbluez5_util ],
[ 'module-bluez5-discover', 'bluetooth/module-bluez5-discover.c', [], [], [dbus_dep], libbluez5_util ],
]
endif
--
2.35.1

View file

@ -1,194 +0,0 @@
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

View file

@ -1,393 +0,0 @@
From 0c8451cae9c585793fa98fb99864d5228127bbf9 Mon Sep 17 00:00:00 2001
From: Dylan Van Assche <me@dylanvanassche.be>
Date: Tue, 5 Apr 2022 20:26:09 +0200
Subject: [PATCH 03/26] bluetooth: add UPower backend
UPower provides information about the power supply and battery
level of the host. Add a backend to retrieve the host battery level.
---
src/modules/bluetooth/meson.build | 4 +-
src/modules/bluetooth/upower.c | 300 ++++++++++++++++++++++++++++++
src/modules/bluetooth/upower.h | 41 ++++
3 files changed, 344 insertions(+), 1 deletion(-)
create mode 100644 src/modules/bluetooth/upower.c
create mode 100644 src/modules/bluetooth/upower.h
diff --git a/src/modules/bluetooth/meson.build b/src/modules/bluetooth/meson.build
index ca77ee6aa..0703d5086 100644
--- a/src/modules/bluetooth/meson.build
+++ b/src/modules/bluetooth/meson.build
@@ -16,6 +16,8 @@ libbluez5_util_headers = [
if get_option('bluez5-native-headset')
libbluez5_util_sources += [ 'backend-native.c' ]
+ libbluez5_util_sources += [ 'upower.c' ]
+ libbluez5_util_headers += [ 'upower.h' ]
endif
if get_option('bluez5-ofono-headset')
@@ -35,7 +37,7 @@ libbluez5_util = shared_library('bluez5-util',
c_args : [pa_c_args, server_c_args],
link_args : [nodelete_link_args],
include_directories : [configinc, topinc],
- dependencies : [libpulse_dep, libpulsecommon_dep, libpulsecore_dep, bluez_dep, dbus_dep, sbc_dep, libintl_dep, bluez5_gst_dep, bluez5_gstapp_dep],
+ dependencies : [libpulse_dep, libpulsecommon_dep, libpulsecore_dep, bluez_dep, dbus_dep, sbc_dep, libintl_dep, bluez5_gst_dep, bluez5_gstapp_dep, libm_dep],
install : true,
install_rpath : privlibdir,
install_dir : modlibexecdir,
diff --git a/src/modules/bluetooth/upower.c b/src/modules/bluetooth/upower.c
new file mode 100644
index 000000000..b5cb89bfe
--- /dev/null
+++ b/src/modules/bluetooth/upower.c
@@ -0,0 +1,300 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyrigth 2022 Dylan Van Assche <me@dylanvanassche.be>
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <math.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-shared.h>
+#include <pulsecore/log.h>
+#include <pulse/timeval.h>
+#include <pulse/rtclock.h>
+
+#include "upower.h"
+
+static pa_dbus_pending* send_and_add_to_pending(pa_upower_backend *backend, DBusMessage *m,
+ DBusPendingCallNotifyFunction func, void *call_data) {
+
+ pa_dbus_pending *p;
+ DBusPendingCall *call;
+
+ pa_assert(backend);
+ pa_assert(m);
+
+ pa_assert_se(dbus_connection_send_with_reply(pa_dbus_connection_get(backend->connection), m, &call, -1));
+
+ p = pa_dbus_pending_new(pa_dbus_connection_get(backend->connection), m, call, backend, call_data);
+ PA_LLIST_PREPEND(pa_dbus_pending, backend->pending, p);
+ dbus_pending_call_set_notify(call, func, p, NULL);
+
+ return p;
+}
+
+static void parse_percentage(pa_upower_backend *b, DBusMessageIter *i) {
+ double percentage;
+ unsigned int battery_level;
+
+ pa_assert(i);
+ pa_assert(dbus_message_iter_get_arg_type(i) == DBUS_TYPE_DOUBLE);
+
+ dbus_message_iter_get_basic(i, &percentage);
+ battery_level = (unsigned int) round(percentage / 20.0);
+
+ if (battery_level != b->battery_level) {
+ b->battery_level = battery_level;
+ pa_log_debug("AG battery level updated (%d/5)", b->battery_level);
+ pa_hook_fire(pa_bluetooth_discovery_hook(b->discovery, PA_BLUETOOTH_HOOK_HOST_BATTERY_LEVEL_CHANGED), b);
+ }
+}
+
+static void get_percentage_reply(DBusPendingCall *pending, void *userdata) {
+ pa_dbus_pending *p;
+ pa_upower_backend *b;
+ DBusMessage *r;
+ DBusMessageIter arg_i, variant_i;
+
+ pa_assert(pending);
+ pa_assert_se(p = userdata);
+ pa_assert_se(b = p->context_data);
+ pa_assert_se(r = dbus_pending_call_steal_reply(pending));
+
+ if (dbus_message_is_error(r, DBUS_ERROR_UNKNOWN_METHOD)) {
+ pa_log_warn("UPower D-Bus Display Device not available");
+ goto finish;
+ }
+
+ if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
+ pa_log_error("Get() failed: %s: %s", dbus_message_get_error_name(r), pa_dbus_get_error_message(r));
+ goto finish;
+ }
+
+ if (!dbus_message_iter_init(r, &arg_i) || !pa_streq(dbus_message_get_signature(r), "v")) {
+ pa_log_error("Invalid reply signature for Get()");
+ goto finish;
+ }
+
+ dbus_message_iter_recurse(&arg_i, &variant_i);
+ parse_percentage(b, &variant_i);
+
+finish:
+ dbus_message_unref(r);
+
+ PA_LLIST_REMOVE(pa_dbus_pending, b->pending, p);
+ pa_dbus_pending_free(p);
+}
+
+static const char *check_variant_property(DBusMessageIter *i) {
+ const char *key;
+
+ pa_assert(i);
+
+ if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) {
+ pa_log_error("Property name not a string.");
+ return NULL;
+ }
+
+ dbus_message_iter_get_basic(i, &key);
+
+ if (!dbus_message_iter_next(i)) {
+ pa_log_error("Property value missing");
+ return NULL;
+ }
+
+ if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_VARIANT) {
+ pa_log_error("Property value not a variant.");
+ return NULL;
+ }
+
+ return key;
+}
+
+static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *data) {
+ DBusError err;
+ DBusMessage *m2;
+ static const char* upower_device_interface = UPOWER_SERVICE UPOWER_DEVICE_INTERFACE;
+ static const char* percentage_property = "Percentage";
+ pa_upower_backend *b = data;
+ const char *path, *interface, *member;
+
+ pa_assert(bus);
+ pa_assert(m);
+ pa_assert(b);
+
+ dbus_error_init(&err);
+
+ path = dbus_message_get_path(m);
+ interface = dbus_message_get_interface(m);
+ member = dbus_message_get_member(m);
+
+ pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member);
+
+ /* UPower D-Bus status change */
+ if (dbus_message_is_signal(m, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) {
+ const char *name, *old_owner, *new_owner;
+
+ if (!dbus_message_get_args(m, &err,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_STRING, &old_owner,
+ DBUS_TYPE_STRING, &new_owner,
+ DBUS_TYPE_INVALID)) {
+ pa_log_error("Failed to parse " DBUS_INTERFACE_DBUS ".NameOwnerChanged: %s", err.message);
+ goto fail;
+ }
+
+ if (pa_streq(name, UPOWER_SERVICE)) {
+
+ /* UPower disappeared from D-Bus */
+ if (old_owner && *old_owner) {
+ pa_log_debug("UPower disappeared from D-Bus");
+ b->battery_level = 0;
+ pa_hook_fire(pa_bluetooth_discovery_hook(b->discovery, PA_BLUETOOTH_HOOK_HOST_BATTERY_LEVEL_CHANGED), b);
+ }
+
+ /* UPower appeared on D-Bus */
+ if (new_owner && *new_owner) {
+ pa_log_debug("UPower appeared on D-Bus");
+
+ /* Update battery level */
+ pa_assert_se(m2 = dbus_message_new_method_call(UPOWER_SERVICE, UPOWER_DISPLAY_DEVICE_OBJECT, DBUS_INTERFACE_PROPERTIES, "Get"));
+ pa_assert_se(dbus_message_append_args(m2,
+ DBUS_TYPE_STRING, &upower_device_interface,
+ DBUS_TYPE_STRING, &percentage_property,
+ DBUS_TYPE_INVALID));
+ send_and_add_to_pending(b, m2, get_percentage_reply, NULL);
+ }
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ /* UPower battery level property updates */
+ } else if (dbus_message_is_signal(m, DBUS_INTERFACE_PROPERTIES, "PropertiesChanged")) {
+ DBusMessageIter arg_i, element_i;
+
+ if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "sa{sv}as")) {
+ pa_log_error("Invalid signature found in PropertiesChanged");
+ goto fail;
+ }
+
+ /* Skip interface name */
+ pa_assert_se(dbus_message_iter_next(&arg_i));
+ pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_ARRAY);
+
+ dbus_message_iter_recurse(&arg_i, &element_i);
+
+ /* Parse UPower property updates */
+ while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter dict_i, variant_i;
+ const char *key;
+
+ dbus_message_iter_recurse(&element_i, &dict_i);
+
+ /* Retrieve property name */
+ key = check_variant_property(&dict_i);
+ if (key == NULL) {
+ pa_log_error("Received invalid property!");
+ break;
+ }
+
+ dbus_message_iter_recurse(&dict_i, &variant_i);
+
+ if(pa_streq(path, UPOWER_DISPLAY_DEVICE_OBJECT)) {
+ pa_log_debug("UPower Device property updated: %s", key);
+
+ if(pa_streq(key, "Percentage"))
+ parse_percentage(b, &variant_i);
+ }
+
+ dbus_message_iter_next(&element_i);
+ }
+ }
+
+fail:
+ dbus_error_free(&err);
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+unsigned int pa_upower_get_battery_level(pa_upower_backend *backend) {
+ return backend->battery_level;
+}
+
+pa_upower_backend *pa_upower_backend_new(pa_core *c, pa_bluetooth_discovery *d) {
+ pa_upower_backend *backend;
+ DBusError err;
+ DBusMessage *m;
+ static const char* upower_device_interface = UPOWER_SERVICE UPOWER_DEVICE_INTERFACE;
+ static const char* percentage_property = "Percentage";
+
+ pa_log_debug("Native backend enabled UPower battery status reporting");
+
+ backend = pa_xnew0(pa_upower_backend, 1);
+ backend->core = c;
+ backend->discovery = d;
+
+ /* Get DBus connection */
+ dbus_error_init(&err);
+ if (!(backend->connection = pa_dbus_bus_get(c, DBUS_BUS_SYSTEM, &err))) {
+ pa_log("Failed to get D-Bus connection: %s", err.message);
+ dbus_error_free(&err);
+ pa_xfree(backend);
+ return NULL;
+ }
+
+ /* Add filter callback for DBus connection */
+ if (!dbus_connection_add_filter(pa_dbus_connection_get(backend->connection), filter_cb, backend, NULL)) {
+ pa_log_error("Failed to add filter function");
+ pa_dbus_connection_unref(backend->connection);
+ pa_xfree(backend);
+ return NULL;
+ }
+
+ /* Register for battery level changes from UPower */
+ if (pa_dbus_add_matches(pa_dbus_connection_get(backend->connection), &err,
+ "type='signal',sender='" DBUS_SERVICE_DBUS "',interface='" DBUS_INTERFACE_DBUS "',member='NameOwnerChanged',"
+ "arg0='" UPOWER_SERVICE "'",
+ "type='signal',sender='" UPOWER_SERVICE "',interface='" DBUS_INTERFACE_PROPERTIES "',member='PropertiesChanged'",
+ NULL) < 0) {
+ pa_log("Failed to add UPower D-Bus matches: %s", err.message);
+ dbus_connection_remove_filter(pa_dbus_connection_get(backend->connection), filter_cb, backend);
+ pa_dbus_connection_unref(backend->connection);
+ pa_xfree(backend);
+ return NULL;
+ }
+
+ /* Initialize battery level by requesting it from UPower */
+ pa_assert_se(m = dbus_message_new_method_call(UPOWER_SERVICE, UPOWER_DISPLAY_DEVICE_OBJECT, DBUS_INTERFACE_PROPERTIES, "Get"));
+ pa_assert_se(dbus_message_append_args(m,
+ DBUS_TYPE_STRING, &upower_device_interface,
+ DBUS_TYPE_STRING, &percentage_property,
+ DBUS_TYPE_INVALID));
+ send_and_add_to_pending(backend, m, get_percentage_reply, NULL);
+
+ return backend;
+}
+
+void pa_upower_backend_free(pa_upower_backend *backend) {
+ pa_assert(backend);
+
+ pa_dbus_free_pending_list(&backend->pending);
+
+ pa_dbus_connection_unref(backend->connection);
+
+ pa_xfree(backend);
+}
+
diff --git a/src/modules/bluetooth/upower.h b/src/modules/bluetooth/upower.h
new file mode 100644
index 000000000..3e9ee9291
--- /dev/null
+++ b/src/modules/bluetooth/upower.h
@@ -0,0 +1,41 @@
+#pragma once
+
+/***
+ This file is part of PulseAudio.
+
+ Copyrigth 2022 Dylan Van Assche <me@dylanvanassche.be>
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "bluez5-util.h"
+
+#define UPOWER_SERVICE "org.freedesktop.UPower"
+#define UPOWER_DEVICE_INTERFACE ".Device"
+#define UPOWER_DISPLAY_DEVICE_OBJECT "/org/freedesktop/UPower/devices/DisplayDevice"
+
+struct pa_upower_backend {
+ pa_core *core;
+ pa_dbus_connection *connection;
+ pa_bluetooth_discovery *discovery;
+ unsigned int battery_level;
+
+ PA_LLIST_HEAD(pa_dbus_pending, pending);
+};
+
+pa_upower_backend *pa_upower_backend_new(pa_core *c, pa_bluetooth_discovery *d);
+void pa_upower_backend_free(pa_upower_backend *backend);
+void pa_upower_register_transport(pa_upower_backend *backend, pa_bluetooth_transport *t);
+void pa_upower_unregister_transport(pa_upower_backend *backend);
+unsigned int pa_upower_get_battery_level(pa_upower_backend *backend);
--
2.35.1

View file

@ -1,197 +0,0 @@
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

File diff suppressed because it is too large Load diff

View file

@ -1,40 +0,0 @@
From 72b92267a8f906a3ecd10c571407802810e01bcc Mon Sep 17 00:00:00 2001
From: Dylan Van Assche <me@dylanvanassche.be>
Date: Wed, 13 Apr 2022 18:27:44 +0200
Subject: [PATCH 06/26] bluetooth: only reply OK for supported AT cmds
PulseAudio should always be honest to Bluetooth devices even when audio is connected.
---
src/modules/bluetooth/backend-native.c | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/src/modules/bluetooth/backend-native.c b/src/modules/bluetooth/backend-native.c
index a490d6efd..1fadb4b14 100644
--- a/src/modules/bluetooth/backend-native.c
+++ b/src/modules/bluetooth/backend-native.c
@@ -2,6 +2,7 @@
This file is part of PulseAudio.
Copyright 2014 Wim Taymans <wim.taymans at gmail.com>
+ Copyright 2021-2022 Dylan Van Assche <me at dylanvanassche.be>
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
@@ -808,12 +809,8 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
return false;
}
- /*
- * once we're fully connected, just reply OK to everything
- * it will just be the headset sending the occasional status
- * update, but we process only the ones we care about
- */
- return true;
+ /* Unsupported commands return ERROR */
+ return false
}
static int get_rfcomm_fd (pa_bluetooth_discovery *discovery) {
--
2.35.1

View file

@ -1,61 +0,0 @@
From 81045feccb25b627c2c6cfc105c27b2599573fd2 Mon Sep 17 00:00:00 2001
From: Dylan Van Assche <me@dylanvanassche.be>
Date: Wed, 13 Apr 2022 18:29:08 +0200
Subject: [PATCH 07/26] bluetooth: Always reply to AT+CIND
Once the stateful negotiation is complete, still reply
to AT+CIND? and AT+CIND=? since we will report actual data here
such as cellular service status, roaming, signal, and battery level.
---
src/modules/bluetooth/backend-native.c | 22 ++++++++++++++++++----
1 file changed, 18 insertions(+), 4 deletions(-)
diff --git a/src/modules/bluetooth/backend-native.c b/src/modules/bluetooth/backend-native.c
index 1fadb4b14..77792146a 100644
--- a/src/modules/bluetooth/backend-native.c
+++ b/src/modules/bluetooth/backend-native.c
@@ -686,7 +686,7 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
/* no state change */
return true;
- } else if (c->state == 1 && pa_startswith(buf, "AT+CIND=?")) {
+ } else if (c->state >= 1 && pa_startswith(buf, "AT+CIND=?")) {
/* UPower backend available, declare support for more indicators */
if (discovery->native_backend->upower) {
rfcomm_write_response(fd, "+CIND: "
@@ -700,15 +700,29 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
MANDATORY_CALL_INDICATORS ","
"(\"service\",(0-1))");
}
- c->state = 2;
+
+ /*
+ * Only change codec negotiation state when the first AT+CIND=? command is received
+ * Bluetooth HFP 1.8 specification specifies that this AT command is issued 'at least once'.
+ */
+ if (c->state < 2)
+ c->state = 2;
return true;
- } else if (c->state == 2 && pa_startswith(buf, "AT+CIND?")) {
+ } else if (c->state >= 2 && pa_startswith(buf, "AT+CIND?")) {
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;
+
+ /*
+ * Only change codec negotiation state when the first AT+CIND? command is received.
+ * The Bluetooth HFP 1.8 specification does not define how many times this command is issued.
+ * Some HF devices issue this one multiple times breaking the codec negotiation stateful state,
+ * so only update the state once.
+ */
+ if (c->state < 3)
+ c->state = 3;
return true;
} else if ((c->state == 2 || c->state == 3) && pa_startswith(buf, "AT+CMER=")) {
--
2.35.1

View file

@ -1,197 +0,0 @@
From ff2d7b76bf4878f47a2df1261219c9d9e14f8c9e Mon Sep 17 00:00:00 2001
From: Dylan Van Assche <me@dylanvanassche.be>
Date: Wed, 13 Apr 2022 19:56:41 +0200
Subject: [PATCH 08/26] bluetooth: support AT+CMEE
AT+CMEE is now supported and extended error messages are returned
when an ERROR occurs. By default, these error codes are disabled,
the HF must enable them explicitely with AT+CMEE=1 (numeric mode).
---
src/modules/bluetooth/backend-native.c | 71 +++++++++++++++++++++-----
src/modules/bluetooth/bluez5-util.h | 6 +++
2 files changed, 65 insertions(+), 12 deletions(-)
diff --git a/src/modules/bluetooth/backend-native.c b/src/modules/bluetooth/backend-native.c
index 77792146a..3110c5ef2 100644
--- a/src/modules/bluetooth/backend-native.c
+++ b/src/modules/bluetooth/backend-native.c
@@ -57,6 +57,7 @@ struct pa_bluetooth_backend {
bool enable_hsp_hs;
bool enable_hfp_hf;
bool cmer_indicator_reporting_enabled;
+ bool cmee_extended_error_reporting_enabled;
uint32_t cind_enabled_indicators;
PA_LLIST_HEAD(pa_dbus_pending, pending);
@@ -125,7 +126,7 @@ typedef enum pa_bluetooth_ag_to_hf_indicators {
/* gateway features we support, which is as little as we can get away with */
static uint32_t hfp_features =
/* HFP 1.6 requires this */
- (1 << HFP_AG_ESTATUS ) | (1 << HFP_AG_CODECS) | (1 << HFP_AG_INDICATORS);
+ (1 << HFP_AG_ESTATUS ) | (1 << HFP_AG_CODECS) | (1 << HFP_AG_INDICATORS) | (1 << HFP_AG_EERR);
#define HSP_AG_PROFILE "/Profile/HSPAGProfile"
#define HFP_AG_PROFILE "/Profile/HFPAGProfile"
@@ -256,6 +257,14 @@ static void rfcomm_write_response(int fd, const char *fmt, ...)
va_end(ap);
}
+/* Send Extended Error messages if enabled */
+static void rfcomm_write_error(pa_bluetooth_backend *b, int fd, unsigned int error) {
+ if (b->cmee_extended_error_reporting_enabled)
+ rfcomm_write_response(fd, "+CME ERROR: %d", error);
+ else
+ rfcomm_write_response(fd, "ERROR");
+}
+
static int sco_setsockopt_enable_bt_voice(pa_bluetooth_transport *t, int fd) {
/* the mSBC codec requires a special transparent eSCO connection */
struct bt_voice voice;
@@ -621,6 +630,30 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
size_t len;
const char *state;
+ /* Extended error reporting. Described in Bluetooth HFP 1.8 spec. */
+ if (sscanf(buf, "AT+CMEE=%d", &val) == 1) {
+ switch(val) {
+ /* Disable extended error reporting */
+ case 0:
+ discovery->native_backend->cmee_extended_error_reporting_enabled = false;
+ return true;
+ /* Enable extend error reporting in numeric mode */
+ case 1:
+ discovery->native_backend->cmee_extended_error_reporting_enabled = true;
+ return true;
+ /* Enable extend error reporting in verbose mode */
+ case 2:
+ pa_log_warn("Extended Error Reporting verbose mode is unsupported by Bluetooth HFP 1.8");
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_OPERATION_NOT_SUPPORTED);
+ return false;
+ default:
+ pa_log_warn("Unknown AT+CMEE value: %d", val);
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_AG_FAILURE);
+ return false;
+ }
+ pa_assert_not_reached();
+ }
+
/* first-time initialize selected codec to CVSD */
if (c->selected_codec == 0)
c->selected_codec = 1;
@@ -655,7 +688,7 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
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");
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_AG_FAILURE);
return false;
}
@@ -741,7 +774,7 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
}
else {
pa_log_error("Unable to parse AT+CMER command: %s", buf);
- rfcomm_write_response(fd, "ERROR");
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_AG_FAILURE);
return false;
}
@@ -767,7 +800,7 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
pa_bluetooth_transport_reconfigure(t, pa_bluetooth_get_hf_codec("mSBC"), sco_transport_write, sco_setsockopt_enable_bt_voice);
} else {
pa_assert_fp(val != 1 && val != 2);
- rfcomm_write_response(fd, "ERROR");
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_AG_FAILURE);
return false;
}
@@ -798,14 +831,14 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
pa_log_notice("Battery Level: %d%%", val);
if (val < 0 || val > 100) {
pa_log_error("Battery HF indicator %d out of [0, 100] range", val);
- rfcomm_write_response(fd, "ERROR");
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_AG_FAILURE);
return false;
}
pa_bluetooth_device_report_battery_level(t->device, val, "HFP 1.7 HF indicator");
break;
default:
pa_log_error("Unknown HF indicator %u", indicator);
- rfcomm_write_response(fd, "ERROR");
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_OPERATION_NOT_SUPPORTED);
return false;
}
return true;
@@ -819,12 +852,23 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
if (c->state != 5) {
pa_log_error("HFP negotiation failed in state %d with inbound %s\n",
c->state, buf);
- rfcomm_write_response(fd, "ERROR");
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_AG_FAILURE);
return false;
}
- /* Unsupported commands return ERROR */
- return false
+ /*
+ * Unsupported commands return ERROR.
+ * Unsupported AT commands from HFP 1.8:
+ * - AT+BINP
+ * - AT+BLDN
+ * - ATD>
+ * - AT+BVRA
+ * - AT+BTRH
+ * - AT+CHLD
+ * - AT+CCWA
+ */
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_OPERATION_NOT_SUPPORTED);
+ return false;
}
static int get_rfcomm_fd (pa_bluetooth_discovery *discovery) {
@@ -996,12 +1040,13 @@ static void rfcomm_io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_i
break;
}
}
- if (!do_reply)
- rfcomm_write_response(fd, "ERROR");
+ if (!do_reply) {
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_AG_FAILURE);
+ }
} else if (t->config) { /* t->config is only non-null for hfp profile */
do_reply = hfp_rfcomm_handle(fd, t, buf);
} else {
- rfcomm_write_response(fd, "ERROR");
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_AG_FAILURE);
do_reply = false;
}
@@ -1391,6 +1436,8 @@ pa_bluetooth_backend *pa_bluetooth_native_backend_new(pa_core *c, pa_bluetooth_d
/* While all CIND indicators are enabled, event reporting is not enabled by default */
backend->cmer_indicator_reporting_enabled = false;
+ /* CMEE is disabled by default */
+ backend->cmee_extended_error_reporting_enabled = false;
return backend;
}
diff --git a/src/modules/bluetooth/bluez5-util.h b/src/modules/bluetooth/bluez5-util.h
index f69176425..4cc5c2882 100644
--- a/src/modules/bluetooth/bluez5-util.h
+++ b/src/modules/bluetooth/bluez5-util.h
@@ -211,6 +211,12 @@ struct pa_bluetooth_adapter {
bool battery_provider_registered;
};
+/* extended error reporting values, described in Bluetooth HFP 1.8 spec */
+typedef enum pa_bluetooth_cmee {
+ CMEE_AG_FAILURE = 0,
+ CMEE_OPERATION_NOT_SUPPORTED = 4,
+} pa_bluetooth_cmee_t;
+
#ifdef HAVE_BLUEZ_5_OFONO_HEADSET
pa_bluetooth_backend *pa_bluetooth_ofono_backend_new(pa_core *c, pa_bluetooth_discovery *y);
void pa_bluetooth_ofono_backend_free(pa_bluetooth_backend *b);
--
2.35.1

View file

@ -1,48 +0,0 @@
From 0dc5b589220e830ab18ddbcf90f2cfbe1c4927f7 Mon Sep 17 00:00:00 2001
From: Dylan Van Assche <me@dylanvanassche.be>
Date: Wed, 13 Apr 2022 20:25:47 +0200
Subject: [PATCH 09/26] bluetooth: support AT+NREC
AT+NREC=0 disables Noise Reduction and Echo Canceling in the AG.
Bluetooth HFP 1.8 only supports the disable operation, so return
an CME ERROR extended error code if another value is send by the HF.
---
src/modules/bluetooth/backend-native.c | 7 +++++++
src/modules/bluetooth/bluez5-util.h | 3 ++-
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/src/modules/bluetooth/backend-native.c b/src/modules/bluetooth/backend-native.c
index 3110c5ef2..394c2a4da 100644
--- a/src/modules/bluetooth/backend-native.c
+++ b/src/modules/bluetooth/backend-native.c
@@ -652,6 +652,13 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
return false;
}
pa_assert_not_reached();
+ } else if (sscanf(buf, "AT+NREC=%d", &val) == 1) {
+ if (val == 0)
+ return true;
+
+ /* Noise Redction and Echo Canceling can only have value '0' (disable) following Bluetooth HFP 1.8 */
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_OPERATION_NOT_ALLOWED);
+ return false;
}
/* first-time initialize selected codec to CVSD */
diff --git a/src/modules/bluetooth/bluez5-util.h b/src/modules/bluetooth/bluez5-util.h
index 4cc5c2882..fa26a8361 100644
--- a/src/modules/bluetooth/bluez5-util.h
+++ b/src/modules/bluetooth/bluez5-util.h
@@ -214,7 +214,8 @@ struct pa_bluetooth_adapter {
/* extended error reporting values, described in Bluetooth HFP 1.8 spec */
typedef enum pa_bluetooth_cmee {
CMEE_AG_FAILURE = 0,
- CMEE_OPERATION_NOT_SUPPORTED = 4,
+ CMEE_OPERATION_NOT_ALLOWED = 3,
+ CMEE_OPERATION_NOT_SUPPORTED = 4
} pa_bluetooth_cmee_t;
#ifdef HAVE_BLUEZ_5_OFONO_HEADSET
--
2.35.1

View file

@ -1,33 +0,0 @@
From 79d8cf24785fc52a3d0cee48590143fb409201b8 Mon Sep 17 00:00:00 2001
From: Dylan Van Assche <me@dylanvanassche.be>
Date: Thu, 14 Apr 2022 19:15:34 +0200
Subject: [PATCH 10/26] bluetooth: support AT+BCC
HFs send AT+BCC to (re)start codec negotiations.
Support this command by (re)setting the codec negotiations state and
reply OK.
---
src/modules/bluetooth/backend-native.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/modules/bluetooth/backend-native.c b/src/modules/bluetooth/backend-native.c
index 394c2a4da..89a174f9a 100644
--- a/src/modules/bluetooth/backend-native.c
+++ b/src/modules/bluetooth/backend-native.c
@@ -666,7 +666,12 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
c->selected_codec = 1;
/* stateful negotiation */
- if (c->state == 0 && sscanf(buf, "AT+BRSF=%d", &val) == 1) {
+ if (strstr(buf, "AT+BCC")) {
+ pa_log_debug("HF asked to start codec negotiations");
+ c->state = 0;
+ return true;
+ }
+ else if (c->state == 0 && sscanf(buf, "AT+BRSF=%d", &val) == 1) {
c->capabilities = val;
pa_log_info("HFP capabilities returns 0x%x", val);
rfcomm_write_response(fd, "+BRSF: %d", hfp_features);
--
2.35.1

View file

@ -1,165 +0,0 @@
From ccac2886c516aa0ac2e76406b782de0abc700e92 Mon Sep 17 00:00:00 2001
From: Dylan Van Assche <me@dylanvanassche.be>
Date: Thu, 14 Apr 2022 19:47:46 +0200
Subject: [PATCH 11/26] bluetooth: support AT+COPS
AT+COPS=3,X sets the operator name format and AT+COPS? returns
the current network operator name. Supports this command when
ModemManager is available to provide this information.
---
src/modules/bluetooth/backend-native.c | 72 +++++++++++++++++++++++++-
src/modules/bluetooth/bluez5-util.h | 11 +++-
2 files changed, 81 insertions(+), 2 deletions(-)
diff --git a/src/modules/bluetooth/backend-native.c b/src/modules/bluetooth/backend-native.c
index 89a174f9a..c0c14acf3 100644
--- a/src/modules/bluetooth/backend-native.c
+++ b/src/modules/bluetooth/backend-native.c
@@ -40,6 +40,7 @@
#include "bluez5-util.h"
#include "bt-codec-msbc.h"
#include "upower.h"
+#include "modemmanager.h"
#define MANDATORY_CALL_INDICATORS \
"(\"call\",(0-1))," \
@@ -53,12 +54,14 @@ struct pa_bluetooth_backend {
pa_hook_slot *adapter_uuids_changed_slot;
pa_hook_slot *host_battery_level_changed_slot;
pa_upower_backend *upower;
+ pa_modemmanager_backend *modemmanager;
bool enable_shared_profiles;
bool enable_hsp_hs;
bool enable_hfp_hf;
bool cmer_indicator_reporting_enabled;
bool cmee_extended_error_reporting_enabled;
uint32_t cind_enabled_indicators;
+ pa_bluetooth_cops_t cops_format;
PA_LLIST_HEAD(pa_dbus_pending, pending);
};
@@ -624,7 +627,7 @@ 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, mode, val;
+ int indicator, mode, val, val2;
char str[5];
const char *r;
size_t len;
@@ -659,6 +662,68 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
/* Noise Redction and Echo Canceling can only have value '0' (disable) following Bluetooth HFP 1.8 */
rfcomm_write_error(discovery->native_backend, fd, CMEE_OPERATION_NOT_ALLOWED);
return false;
+ } else if (sscanf(buf, "AT+COPS=%d,%d", &val, &val2) == 2) {
+ /* Return error if ModemManager is unavailable */
+ if (!discovery->native_backend->modemmanager || !pa_modemmanager_has_modem(discovery->native_backend->modemmanager)) {
+ pa_log_debug("ModemManager backend unavailable, cannot set AT+COPS format");
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_NO_CONNECTION_TO_PHONE);
+ return false;
+ }
+
+ /* Only AT+COPS=3,X is allowed by Bluetooth HFP 1.8 */
+ if (val != 3)
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_OPERATION_NOT_ALLOWED);
+
+ /* Set AT+COPS format */
+ if (val2 == 0) {
+ discovery->native_backend->cops_format = COPS_LONG_FORMAT;
+ return true;
+ } else if (val2 == 2) {
+ discovery->native_backend->cops_format = COPS_NUMERIC_FORMAT;
+ return true;
+ } else {
+ discovery->native_backend->cops_format = COPS_UNKNOWN_FORMAT;
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_OPERATION_NOT_ALLOWED);
+ return false;
+ }
+ } else if (strstr(buf, "AT+COPS?")) {
+ /* Return error if ModemManager is unavailable */
+ if (!discovery->native_backend->modemmanager || !pa_modemmanager_has_modem(discovery->native_backend->modemmanager)) {
+ pa_log_debug("ModemManager backend unavailable, cannot answer AT+COPS?");
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_NO_CONNECTION_TO_PHONE);
+ return false;
+ } else if (!pa_modemmanager_has_service(discovery->native_backend->modemmanager)) {
+ pa_log_debug("No network service, cannot answer AT+COPS?");
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_NO_NETWORK_SERVICE);
+ return false;
+ }
+
+ switch(discovery->native_backend->cops_format) {
+ case COPS_LONG_FORMAT: {
+ char *operator_name = pa_modemmanager_get_operator_name(discovery->native_backend->modemmanager);
+ if (operator_name) {
+ rfcomm_write_response(fd, "+COPS: 0,0,\"%s\"", operator_name);
+ return true;
+ }
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_NO_NETWORK_SERVICE);
+ return false;
+ }
+ case COPS_NUMERIC_FORMAT: {
+ char *operator_code = pa_modemmanager_get_operator_code(discovery->native_backend->modemmanager);
+ if (operator_code) {
+ rfcomm_write_response(fd, "+COPS: 2,0,\"%s\"", operator_code);
+ return true;
+ }
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_NO_NETWORK_SERVICE);
+ return false;
+ }
+ case COPS_UNKNOWN_FORMAT: {
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_AG_FAILURE);
+ return false;
+ }
+ default:
+ pa_assert_not_reached();
+ }
}
/* first-time initialize selected codec to CVSD */
@@ -1441,6 +1506,8 @@ pa_bluetooth_backend *pa_bluetooth_native_backend_new(pa_core *c, pa_bluetooth_d
backend->upower = pa_upower_backend_new(c, y);
+ backend->modemmanager = pa_modemmanager_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);
@@ -1473,6 +1540,9 @@ void pa_bluetooth_native_backend_free(pa_bluetooth_backend *backend) {
if (backend->upower)
pa_upower_backend_free(backend->upower);
+
+ if (backend->modemmanager)
+ pa_modemmanager_backend_free(backend->modemmanager);
pa_dbus_connection_unref(backend->connection);
diff --git a/src/modules/bluetooth/bluez5-util.h b/src/modules/bluetooth/bluez5-util.h
index fa26a8361..9ac9880a8 100644
--- a/src/modules/bluetooth/bluez5-util.h
+++ b/src/modules/bluetooth/bluez5-util.h
@@ -214,10 +214,19 @@ struct pa_bluetooth_adapter {
/* extended error reporting values, described in Bluetooth HFP 1.8 spec */
typedef enum pa_bluetooth_cmee {
CMEE_AG_FAILURE = 0,
+ CMEE_NO_CONNECTION_TO_PHONE = 1,
CMEE_OPERATION_NOT_ALLOWED = 3,
- CMEE_OPERATION_NOT_SUPPORTED = 4
+ CMEE_OPERATION_NOT_SUPPORTED = 4,
+ CMEE_NO_NETWORK_SERVICE = 30
} pa_bluetooth_cmee_t;
+/* AT+COPS format, described in Bluetooth HFP 1.8 spec */
+typedef enum pa_bluetooth_cops {
+ COPS_LONG_FORMAT = 0,
+ COPS_NUMERIC_FORMAT = 1,
+ COPS_UNKNOWN_FORMAT = 2
+} pa_bluetooth_cops_t;
+
#ifdef HAVE_BLUEZ_5_OFONO_HEADSET
pa_bluetooth_backend *pa_bluetooth_ofono_backend_new(pa_core *c, pa_bluetooth_discovery *y);
void pa_bluetooth_ofono_backend_free(pa_bluetooth_backend *b);
--
2.35.1

View file

@ -1,68 +0,0 @@
From 739ca09cc36553703d2b24ba99b64f8641fcf987 Mon Sep 17 00:00:00 2001
From: Dylan Van Assche <me@dylanvanassche.be>
Date: Thu, 14 Apr 2022 20:16:24 +0200
Subject: [PATCH 12/26] bluetooth: support AT+CNUM
AT+CNUM returns the phone number of the subscriber.
Supports this AT command when ModemManager is available.
---
src/modules/bluetooth/backend-native.c | 25 +++++++++++++++++++++++++
src/modules/bluetooth/bluez5-util.h | 6 ++++++
2 files changed, 31 insertions(+)
diff --git a/src/modules/bluetooth/backend-native.c b/src/modules/bluetooth/backend-native.c
index c0c14acf3..19ca497d5 100644
--- a/src/modules/bluetooth/backend-native.c
+++ b/src/modules/bluetooth/backend-native.c
@@ -724,6 +724,31 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
default:
pa_assert_not_reached();
}
+ } else if (strstr(buf, "AT+CNUM")) {
+ char *number;
+ unsigned int type;
+
+ /* Return error if ModemManager is unavailable */
+ if (!discovery->native_backend->modemmanager || !pa_modemmanager_has_modem(discovery->native_backend->modemmanager)) {
+ pa_log_debug("ModemManager backend unavailable, cannot answer AT+CNUM");
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_NO_CONNECTION_TO_PHONE);
+ return false;
+ }
+
+ number = pa_modemmanager_get_own_number(discovery->native_backend->modemmanager);
+ if (!number) {
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_AG_FAILURE);
+ return false;
+ }
+
+ /* International numbers start with '+' */
+ if (strncmp("+", number, 1) == 0)
+ type = CLIP_INTERNATIONAL_NUMBER;
+ else
+ type = CLIP_NATIONAL_NUMBER;
+
+ rfcomm_write_response(fd, "+CNUM: ,\"%s\",%d,,4", number, type);
+ return true;
}
/* first-time initialize selected codec to CVSD */
diff --git a/src/modules/bluetooth/bluez5-util.h b/src/modules/bluetooth/bluez5-util.h
index 9ac9880a8..638a793b7 100644
--- a/src/modules/bluetooth/bluez5-util.h
+++ b/src/modules/bluetooth/bluez5-util.h
@@ -227,6 +227,12 @@ typedef enum pa_bluetooth_cops {
COPS_UNKNOWN_FORMAT = 2
} pa_bluetooth_cops_t;
+/* CLIP number formats, described in Bluetooth HFP 1.8 spec */
+typedef enum pa_bluetooth_clip {
+ CLIP_INTERNATIONAL_NUMBER = 145,
+ CLIP_NATIONAL_NUMBER = 129
+} pa_bluetooth_clip_t;
+
#ifdef HAVE_BLUEZ_5_OFONO_HEADSET
pa_bluetooth_backend *pa_bluetooth_ofono_backend_new(pa_core *c, pa_bluetooth_discovery *y);
void pa_bluetooth_ofono_backend_free(pa_bluetooth_backend *b);
--
2.35.1

View file

@ -1,67 +0,0 @@
From fafe1fc716a9482e2ea883fb79edf90a7c89d33d Mon Sep 17 00:00:00 2001
From: Dylan Van Assche <me@dylanvanassche.be>
Date: Thu, 14 Apr 2022 20:43:42 +0200
Subject: [PATCH 13/26] bluetooth: support more AT+CIND? indicators
If ModemManager is available, report service status, roaming status and
signal strength as well when an AT+CIND? command is received.
---
src/modules/bluetooth/backend-native.c | 24 +++++++++++++++++-------
1 file changed, 17 insertions(+), 7 deletions(-)
diff --git a/src/modules/bluetooth/backend-native.c b/src/modules/bluetooth/backend-native.c
index 19ca497d5..fcbae5540 100644
--- a/src/modules/bluetooth/backend-native.c
+++ b/src/modules/bluetooth/backend-native.c
@@ -123,7 +123,9 @@ typedef enum pa_bluetooth_ag_to_hf_indicators {
CIND_CALL_HELD_INDICATOR = 3,
CIND_SERVICE_INDICATOR = 4,
CIND_BATT_CHG_INDICATOR = 5,
- CIND_INDICATOR_MAX = 6
+ CIND_ROAMING_INDICATOR = 6,
+ CIND_SIGNAL_STRENGTH_INDICATOR = 7,
+ CIND_INDICATOR_MAX = 8
} pa_bluetooth_ag_to_hf_indicators_t;
/* gateway features we support, which is as little as we can get away with */
@@ -822,14 +824,16 @@ 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=?")) {
- /* UPower backend available, declare support for more indicators */
- if (discovery->native_backend->upower) {
+ /* UPower and ModemManager backends available, declare support for more indicators */
+ if (discovery->native_backend->upower && discovery->native_backend->modemmanager && pa_modemmanager_has_modem(discovery->native_backend->modemmanager)) {
rfcomm_write_response(fd, "+CIND: "
MANDATORY_CALL_INDICATORS ","
"(\"service\",(0-1)),"
- "(\"battchg\",(0-5))");
+ "(\"battchg\",(0-5)),"
+ "(\"roam\",(0-1)),"
+ "(\"signal\",(0-5))");
- /* Minimal indicators supported without any additional backend */
+ /* Minimal indicators supported without any additional backends */
} else {
rfcomm_write_response(fd, "+CIND: "
MANDATORY_CALL_INDICATORS ","
@@ -845,8 +849,14 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
return true;
} else if (c->state >= 2 && pa_startswith(buf, "AT+CIND?")) {
- if (discovery->native_backend->upower)
- rfcomm_write_response(fd, "+CIND: 0,0,0,0,%u", pa_upower_get_battery_level(discovery->native_backend->upower));
+ if (discovery->native_backend->upower && discovery->native_backend->modemmanager && pa_modemmanager_has_modem(discovery->native_backend->modemmanager))
+ rfcomm_write_response(fd, "+CIND: %u,%u,0,%u,%u,%u,%u",
+ discovery->native_backend->cind_call_indicator,
+ discovery->native_backend->cind_call_setup_indicator,
+ pa_modemmanager_has_service(discovery->native_backend->modemmanager),
+ pa_upower_get_battery_level(discovery->native_backend->upower),
+ pa_modemmanager_is_roaming(discovery->native_backend->modemmanager),
+ pa_modemmanager_get_signal_strength(discovery->native_backend->modemmanager));
else
rfcomm_write_response(fd, "+CIND: 0,0,0,0");
--
2.35.1

View file

@ -1,60 +0,0 @@
From 07adf1b246ba1db7b4c5bb9555f1d392b856c049 Mon Sep 17 00:00:00 2001
From: Dylan Van Assche <me@dylanvanassche.be>
Date: Thu, 14 Apr 2022 20:52:36 +0200
Subject: [PATCH 14/26] bluetooth: support AT+CLIP
Enable or disable Call Line Indication reporting (+CLIP) when
a call is incoming to display the number that is calling on the HF's
screen.
---
src/modules/bluetooth/backend-native.c | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/src/modules/bluetooth/backend-native.c b/src/modules/bluetooth/backend-native.c
index fcbae5540..1eaa36b4a 100644
--- a/src/modules/bluetooth/backend-native.c
+++ b/src/modules/bluetooth/backend-native.c
@@ -62,6 +62,7 @@ struct pa_bluetooth_backend {
bool cmee_extended_error_reporting_enabled;
uint32_t cind_enabled_indicators;
pa_bluetooth_cops_t cops_format;
+ bool clip_call_line_reporting_enabled;
PA_LLIST_HEAD(pa_dbus_pending, pending);
};
@@ -751,6 +752,22 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
rfcomm_write_response(fd, "+CNUM: ,\"%s\",%d,,4", number, type);
return true;
+ } else if (sscanf(buf, "AT+CLIP=%d", &val) == 1) {
+ switch(val) {
+ /* Disable call line reporting */
+ case 0:
+ discovery->native_backend->clip_call_line_reporting_enabled = false;
+ return true;
+ /* Enable call line reporting */
+ case 1:
+ discovery->native_backend->clip_call_line_reporting_enabled = true;
+ return true;
+ default:
+ pa_log_warn("Unknown AT+CLIP value: %d", val);
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_AG_FAILURE);
+ return false;
+ }
+ pa_assert_not_reached();
}
/* first-time initialize selected codec to CVSD */
@@ -1553,6 +1570,9 @@ pa_bluetooth_backend *pa_bluetooth_native_backend_new(pa_core *c, pa_bluetooth_d
/* CMEE is disabled by default */
backend->cmee_extended_error_reporting_enabled = false;
+ /* CLIP is disabled by default */
+ backend->clip_call_line_reporting_enabled = false;
+
return backend;
}
--
2.35.1

View file

@ -1,48 +0,0 @@
From d10422fafd9eececfa6755617c41b05f895e5a5b Mon Sep 17 00:00:00 2001
From: Dylan Van Assche <me@dylanvanassche.be>
Date: Thu, 14 Apr 2022 21:03:37 +0200
Subject: [PATCH 15/26] bluetooth: support AT+CREG?
AT+CREG? is not officialy supported by the Bluetooth HFP 1.8 spec,
but some car multimedia systems use it. AT+CREG? reports if the phone
has network available or not with a +CREG response.
---
src/modules/bluetooth/backend-native.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/src/modules/bluetooth/backend-native.c b/src/modules/bluetooth/backend-native.c
index 1eaa36b4a..2fa769d07 100644
--- a/src/modules/bluetooth/backend-native.c
+++ b/src/modules/bluetooth/backend-native.c
@@ -770,6 +770,28 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
pa_assert_not_reached();
}
+ /*
+ * Out-of-spec Bluetooth HFP 1.8 AT commands.
+ * Some car multimedia systems implement the HFP spec,
+ * but also use out-of-spec AT commands which are normally supported
+ * by any 3GPP compliant modem such as:
+ * - AT+CREG?: Get service status
+ * - AT+CGMM: Get modem model
+ * - AT+CGMI: Get modem manufacturer
+ * - AT+CGMR: Get modem revision
+ * - AT+CGSN: Get modem IMEI
+ *
+ * These AT commands are additionally supported if ModemManager is available.
+ */
+ if (strstr(buf, "AT+CREG?")) {
+ if (discovery->native_backend->modemmanager && pa_modemmanager_has_modem(discovery->native_backend->modemmanager)) {
+ rfcomm_write_response(fd, "+CREG: 0,%d", pa_modemmanager_has_service(discovery->native_backend->modemmanager));
+ return true;
+ }
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_NO_CONNECTION_TO_PHONE);
+ return false;
+ }
+
/* first-time initialize selected codec to CVSD */
if (c->selected_codec == 0)
c->selected_codec = 1;
--
2.35.1

View file

@ -1,32 +0,0 @@
From ee52128fbb8bc953c43b8c53763443c715fa64c8 Mon Sep 17 00:00:00 2001
From: Dylan Van Assche <me@dylanvanassche.be>
Date: Fri, 15 Apr 2022 07:29:49 +0200
Subject: [PATCH 16/26] bluetooth: support AT+CGMM
Return the modem model if ModemManager is available when the HF
sends an out-of-spec AT+CGMM command.
---
src/modules/bluetooth/backend-native.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/modules/bluetooth/backend-native.c b/src/modules/bluetooth/backend-native.c
index 2fa769d07..2c39a090c 100644
--- a/src/modules/bluetooth/backend-native.c
+++ b/src/modules/bluetooth/backend-native.c
@@ -790,6 +790,13 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
}
rfcomm_write_error(discovery->native_backend, fd, CMEE_NO_CONNECTION_TO_PHONE);
return false;
+ } else if (strstr(buf, "AT+CGMM") && pa_modemmanager_has_modem(discovery->native_backend->modemmanager)) {
+ if (discovery->native_backend->modemmanager) {
+ rfcomm_write_response(fd, "%s", pa_modemmanager_get_modem_model(discovery->native_backend->modemmanager));
+ return true;
+ }
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_NO_CONNECTION_TO_PHONE);
+ return false;
}
/* first-time initialize selected codec to CVSD */
--
2.35.1

View file

@ -1,33 +0,0 @@
From 6e851563dbe4e90a37b0f8fef1604395acba47b4 Mon Sep 17 00:00:00 2001
From: Dylan Van Assche <me@dylanvanassche.be>
Date: Fri, 15 Apr 2022 07:31:43 +0200
Subject: [PATCH 17/26] bluetooth: support AT+CGMI
Return the modem manufacturer if ModemManager is available.
AT+CGMI is an out-of-spec AT command for Bluetooth HFP, but car
multimedia units use it since it is a standard 3GPP command.
---
src/modules/bluetooth/backend-native.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/modules/bluetooth/backend-native.c b/src/modules/bluetooth/backend-native.c
index 2c39a090c..3099b13fc 100644
--- a/src/modules/bluetooth/backend-native.c
+++ b/src/modules/bluetooth/backend-native.c
@@ -797,6 +797,13 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
}
rfcomm_write_error(discovery->native_backend, fd, CMEE_NO_CONNECTION_TO_PHONE);
return false;
+ } else if (strstr(buf, "AT+CGMI") && pa_modemmanager_has_modem(discovery->native_backend->modemmanager)) {
+ if (discovery->native_backend->modemmanager) {
+ rfcomm_write_response(fd, "%s", pa_modemmanager_get_modem_manufacturer(discovery->native_backend->modemmanager));
+ return true;
+ }
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_NO_CONNECTION_TO_PHONE);
+ return false;
}
/* first-time initialize selected codec to CVSD */
--
2.35.1

View file

@ -1,32 +0,0 @@
From 5fd316c23c3051be2394c16bf97ee189fc07ce23 Mon Sep 17 00:00:00 2001
From: Dylan Van Assche <me@dylanvanassche.be>
Date: Fri, 15 Apr 2022 07:33:19 +0200
Subject: [PATCH 18/26] bluetooth: support AT+CGMR
Return the modem revision when HF sends an out-of-spec AT+CGMR
command. This is a 3GPP standardized command so lets support it.
---
src/modules/bluetooth/backend-native.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/modules/bluetooth/backend-native.c b/src/modules/bluetooth/backend-native.c
index 3099b13fc..cf016a282 100644
--- a/src/modules/bluetooth/backend-native.c
+++ b/src/modules/bluetooth/backend-native.c
@@ -804,6 +804,13 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
}
rfcomm_write_error(discovery->native_backend, fd, CMEE_NO_CONNECTION_TO_PHONE);
return false;
+ } else if (strstr(buf, "AT+CGMR")) {
+ if (discovery->native_backend->modemmanager && pa_modemmanager_has_modem(discovery->native_backend->modemmanager)) {
+ rfcomm_write_response(fd, "%s", pa_modemmanager_get_modem_revision(discovery->native_backend->modemmanager));
+ return true;
+ }
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_NO_CONNECTION_TO_PHONE);
+ return false;
}
/* first-time initialize selected codec to CVSD */
--
2.35.1

View file

@ -1,33 +0,0 @@
From f39e071a558a9b3a301028c7f3506f024d60ceea Mon Sep 17 00:00:00 2001
From: Dylan Van Assche <me@dylanvanassche.be>
Date: Fri, 15 Apr 2022 07:35:11 +0200
Subject: [PATCH 19/26] bluetooth: support AT+CGSN
Return the modem's IMEI number when car multimedia systems (HF)
send an AT+CGSN 3GPP AT command, even though it is not supported by the
Bluetooth HFP 1.8 spec.
---
src/modules/bluetooth/backend-native.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/modules/bluetooth/backend-native.c b/src/modules/bluetooth/backend-native.c
index cf016a282..fec24a57b 100644
--- a/src/modules/bluetooth/backend-native.c
+++ b/src/modules/bluetooth/backend-native.c
@@ -811,6 +811,13 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
}
rfcomm_write_error(discovery->native_backend, fd, CMEE_NO_CONNECTION_TO_PHONE);
return false;
+ } else if (strstr(buf, "AT+CGSN") && pa_modemmanager_has_modem(discovery->native_backend->modemmanager)) {
+ if (discovery->native_backend->modemmanager) {
+ rfcomm_write_response(fd, "%s", pa_modemmanager_get_modem_imei(discovery->native_backend->modemmanager));
+ return true;
+ }
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_NO_CONNECTION_TO_PHONE);
+ return false;
}
/* first-time initialize selected codec to CVSD */
--
2.35.1

View file

@ -1,108 +0,0 @@
From 320d906f8e64b1713dc09c8e95316c4de515f70f Mon Sep 17 00:00:00 2001
From: Dylan Van Assche <me@dylanvanassche.be>
Date: Fri, 15 Apr 2022 08:18:48 +0200
Subject: [PATCH 20/26] bluetooth: support AT+CLCC
Report ongoing calls when the HF sends an AT+CLCC command.
Currently, only one ongoing call is supported, three-way calling is not
implemented. Any additional calls are ignored.
---
src/modules/bluetooth/backend-native.c | 61 ++++++++++++++++++++++++++
src/modules/bluetooth/bluez5-util.h | 9 ++++
2 files changed, 70 insertions(+)
diff --git a/src/modules/bluetooth/backend-native.c b/src/modules/bluetooth/backend-native.c
index fec24a57b..faefed9bd 100644
--- a/src/modules/bluetooth/backend-native.c
+++ b/src/modules/bluetooth/backend-native.c
@@ -768,6 +768,67 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
return false;
}
pa_assert_not_reached();
+ } else if (strstr(buf, "AT+CLCC")) {
+ pa_hashmap *calls;
+ call_status_t *call;
+ unsigned int type;
+ unsigned int clcc_status;
+
+ /* Return error if ModemManager is unavailable */
+ if (!discovery->native_backend->modemmanager || !pa_modemmanager_has_modem(discovery->native_backend->modemmanager)) {
+ pa_log_debug("ModemManager backend unavailable, cannot answer AT+CLCC");
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_NO_CONNECTION_TO_PHONE);
+ return false;
+ } else if (!pa_modemmanager_has_service(discovery->native_backend->modemmanager)) {
+ pa_log_debug("No network service, cannot answer AT+CLCC");
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_NO_NETWORK_SERVICE);
+ return false;
+ }
+
+ calls = pa_modemmanager_get_calls(discovery->native_backend->modemmanager);
+
+ /* Check if we have any ongoing calls, if not return OK */
+ if (pa_hashmap_isempty(calls))
+ return true;
+
+ /* FIXME: support three-way calling */
+ call = pa_hashmap_first(calls);
+
+ /* Map call status to CLCC status numbers */
+ if (call->status == PA_MODEMMANAGER_CALL_STATE_INVALID) {
+ pa_log_warn("Unable to answer AT+CLCC, call state is unknown");
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_AG_FAILURE);
+ return false;
+ } else if (call->is_incoming && call->status == PA_MODEMMANAGER_CALL_STATE_RINGING) {
+ clcc_status = CLCC_INCOMING;
+ } else if (!call->is_incoming && call->status == PA_MODEMMANAGER_CALL_STATE_RINGING) {
+ clcc_status = CLCC_ALERTING;
+ } else if (!call->is_incoming && call->status == PA_MODEMMANAGER_CALL_STATE_DIALING) {
+ clcc_status = CLCC_DIALING;
+ } else if (call->status == PA_MODEMMANAGER_CALL_STATE_ACTIVE) {
+ clcc_status = CLCC_ACTIVE;
+ } else if (call->status == PA_MODEMMANAGER_CALL_STATE_TERMINATED) {
+ pa_log_debug("Call terminated already, do not report it.");
+ return true;
+ /* FIXME: support three-way calling */
+ } else {
+ pa_assert_not_reached();
+ }
+
+ /* Check if call has a number, if not drop it since number and type are optional */
+ if (!call->number) {
+ rfcomm_write_response(fd, "+CLCC: %d,%d,%d,%d,%d", 1, call->is_incoming, clcc_status, 0, 0);
+ return true;
+ }
+
+ /* International numbers start with '+' */
+ if (strncmp("+", call->number, 1) == 0)
+ type = CLIP_INTERNATIONAL_NUMBER;
+ else
+ type = CLIP_NATIONAL_NUMBER;
+
+ rfcomm_write_response(fd, "+CLCC: %d,%d,%d,%d,%d,\"%s\",%d", 1, call->is_incoming, clcc_status, 0, 0, call->number, type);
+ return true;
}
/*
diff --git a/src/modules/bluetooth/bluez5-util.h b/src/modules/bluetooth/bluez5-util.h
index 638a793b7..fb88f00e2 100644
--- a/src/modules/bluetooth/bluez5-util.h
+++ b/src/modules/bluetooth/bluez5-util.h
@@ -233,6 +233,15 @@ typedef enum pa_bluetooth_clip {
CLIP_NATIONAL_NUMBER = 129
} pa_bluetooth_clip_t;
+/* CLCC call states, described in Bluetooth HFP 1.8 spec */
+/* FIXME: support three-way calling */
+typedef enum pa_bluetooth_clcc {
+ CLCC_ACTIVE = 0,
+ CLCC_DIALING = 2,
+ CLCC_ALERTING = 3,
+ CLCC_INCOMING = 4
+} pa_bluetooth_clcc_t;
+
#ifdef HAVE_BLUEZ_5_OFONO_HEADSET
pa_bluetooth_backend *pa_bluetooth_ofono_backend_new(pa_core *c, pa_bluetooth_discovery *y);
void pa_bluetooth_ofono_backend_free(pa_bluetooth_backend *b);
--
2.35.1

View file

@ -1,365 +0,0 @@
From 6861de91294c8bed206e8338d1074ebc57683ae4 Mon Sep 17 00:00:00 2001
From: Dylan Van Assche <me@dylanvanassche.be>
Date: Sat, 16 Apr 2022 08:46:13 +0200
Subject: [PATCH 21/26] bluetooth: support +CIEV, RING, and +CLIP URCs
Report changes of the CIND indicators and calls to the HF with URCs:
- +CIEV: report any change to the CIND indicators such as signal
strength, roaming status, service status, etc.
- RING: play a ringtone on the HF side for incoming calls.
- +CLIP: enhanced call status reports the subscriber number to the HF
for incoming calls.
---
src/modules/bluetooth/backend-native.c | 258 +++++++++++++++++++++++++
src/modules/bluetooth/bluez5-util.h | 12 ++
2 files changed, 270 insertions(+)
diff --git a/src/modules/bluetooth/backend-native.c b/src/modules/bluetooth/backend-native.c
index faefed9bd..e12b38d71 100644
--- a/src/modules/bluetooth/backend-native.c
+++ b/src/modules/bluetooth/backend-native.c
@@ -29,6 +29,8 @@
#include <pulsecore/core-util.h>
#include <pulsecore/dbus-shared.h>
#include <pulsecore/log.h>
+#include <pulse/timeval.h>
+#include <pulse/rtclock.h>
#include <errno.h>
#include <sys/types.h>
@@ -42,6 +44,7 @@
#include "upower.h"
#include "modemmanager.h"
+#define RING_WAIT_TIME ((pa_usec_t) (3 * PA_USEC_PER_SEC))
#define MANDATORY_CALL_INDICATORS \
"(\"call\",(0-1))," \
"(\"callsetup\",(0-3))," \
@@ -49,10 +52,17 @@
struct pa_bluetooth_backend {
pa_core *core;
+ pa_time_event *timer_ring_event;
pa_dbus_connection *connection;
pa_bluetooth_discovery *discovery;
pa_hook_slot *adapter_uuids_changed_slot;
pa_hook_slot *host_battery_level_changed_slot;
+ pa_hook_slot *host_operation_succeed_slot;
+ pa_hook_slot *host_operation_failed_slot;
+ pa_hook_slot *host_signal_strength_changed_slot;
+ pa_hook_slot *host_has_service_changed_slot;
+ pa_hook_slot *host_is_roaming_changed_slot;
+ pa_hook_slot *host_calls_changed_slot;
pa_upower_backend *upower;
pa_modemmanager_backend *modemmanager;
bool enable_shared_profiles;
@@ -63,6 +73,8 @@ struct pa_bluetooth_backend {
uint32_t cind_enabled_indicators;
pa_bluetooth_cops_t cops_format;
bool clip_call_line_reporting_enabled;
+ unsigned int cind_call_indicator;
+ unsigned int cind_call_setup_indicator;
PA_LLIST_HEAD(pa_dbus_pending, pending);
};
@@ -1172,6 +1184,206 @@ static pa_hook_result_t host_battery_level_changed_cb(pa_bluetooth_discovery *y,
return PA_HOOK_OK;
}
+static pa_hook_result_t host_operation_failed_cb(pa_bluetooth_discovery *y, const pa_modemmanager_backend *m, pa_bluetooth_backend *b) {
+ int rfcomm_fd;
+
+ pa_assert(y);
+ pa_assert(m);
+ 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 operation failure over RFCOMM */
+ rfcomm_write_error(b, rfcomm_fd, CMEE_AG_FAILURE);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t host_operation_succeed_cb(pa_bluetooth_discovery *y, const pa_modemmanager_backend *m, pa_bluetooth_backend *b) {
+ int rfcomm_fd;
+
+ pa_assert(y);
+ pa_assert(m);
+ 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 operation succeed over RFCOMM */
+ rfcomm_write_response(rfcomm_fd, "OK");
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t host_signal_strength_changed_cb(pa_bluetooth_discovery *y, const pa_modemmanager_backend *m, pa_bluetooth_backend *b) {
+ int rfcomm_fd;
+
+ pa_assert(y);
+ pa_assert(m);
+ 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 signal strength change */
+ if (b->cmer_indicator_reporting_enabled && (b->cind_enabled_indicators & (1 << CIND_SIGNAL_STRENGTH_INDICATOR))) {
+ rfcomm_write_response(rfcomm_fd, "+CIEV: %d,%d", CIND_SIGNAL_STRENGTH_INDICATOR, pa_modemmanager_get_signal_strength(m));
+ /* Skip notification if indicator is disabled or event reporting is completely disabled */
+ } else
+ pa_log_debug("Signal strength change indicator disabled, skipping notification");
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t host_has_service_changed_cb(pa_bluetooth_discovery *y, const pa_modemmanager_backend *m, pa_bluetooth_backend *b) {
+ int rfcomm_fd;
+
+ pa_assert(y);
+ pa_assert(m);
+ 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 cellular service status change */
+ if (b->cmer_indicator_reporting_enabled && (b->cind_enabled_indicators & (1 << CIND_SERVICE_INDICATOR))) {
+ rfcomm_write_response(rfcomm_fd, "+CIEV: %d,%d", CIND_SERVICE_INDICATOR, pa_modemmanager_has_service(m));
+ /* Skip notification if indicator is disabled or event reporting is completely disabled */
+ } else
+ pa_log_debug("Cellular service status change indicator disabled, skipping notification");
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t host_is_roaming_changed_cb(pa_bluetooth_discovery *y, const pa_modemmanager_backend *m, pa_bluetooth_backend *b) {
+ int rfcomm_fd;
+
+ pa_assert(y);
+ pa_assert(m);
+ 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 roaming status change */
+ if (b->cmer_indicator_reporting_enabled && (b->cind_enabled_indicators & (1 << CIND_ROAMING_INDICATOR))) {
+ rfcomm_write_response(rfcomm_fd, "+CIEV: %d,%d", CIND_ROAMING_INDICATOR, pa_modemmanager_is_roaming(m));
+ /* Skip notification if indicator is disabled or event reporting is completely disabled */
+ } else
+ pa_log_debug("Roaming status change indicator disabled, skipping notification");
+
+ return PA_HOOK_OK;
+}
+
+static void timer_ring_cb(pa_mainloop_api *a, pa_time_event *e, const struct timeval *t, void *userdata) {
+ int rfcomm_fd;
+ unsigned int type;
+ pa_bluetooth_discovery *y = userdata;
+ pa_hashmap *calls;
+ call_status_t *call;
+
+ /* Get RFCOMM channel if available */
+ rfcomm_fd = get_rfcomm_fd (y);
+ if (rfcomm_fd < 0)
+ return;
+
+ calls = pa_modemmanager_get_calls(y->native_backend->modemmanager);
+
+ /* Stop ringing if no calls are available */
+ if (pa_hashmap_isempty(calls))
+ return;
+
+ /* FIXME: support three-way calling */
+ call = pa_hashmap_first(calls);
+
+ /* Send RING indicator */
+ rfcomm_write_response(rfcomm_fd, "RING");
+
+ /* If enabled, send +CLIP indications to HF about the caller */
+ if (y->native_backend->clip_call_line_reporting_enabled && call->number) {
+ /* International numbers start with '+' */
+ if (strncmp("+", call->number, 1) == 0)
+ type = CLIP_INTERNATIONAL_NUMBER;
+ else
+ type = CLIP_NATIONAL_NUMBER;
+ rfcomm_write_response(rfcomm_fd, "+CLIP: \"%s\",%d", call->number, type);
+ }
+
+ if (y->native_backend->timer_ring_event)
+ pa_core_rttime_restart(y->native_backend->core, y->native_backend->timer_ring_event, pa_rtclock_now() + RING_WAIT_TIME);
+}
+
+static pa_hook_result_t host_calls_changed_cb(pa_bluetooth_discovery *y, const pa_modemmanager_backend *m, pa_bluetooth_backend *b) {
+ int rfcomm_fd;
+ unsigned int call_indicator;
+ unsigned int call_setup_indicator;
+ pa_hashmap *calls;
+ call_status_t *call;
+
+ pa_assert(y);
+ pa_assert(m);
+ pa_assert(b);
+
+ /* Get RFCOMM channel if available */
+ rfcomm_fd = get_rfcomm_fd (y);
+ if (rfcomm_fd < 0)
+ return PA_HOOK_OK;
+
+ calls = pa_modemmanager_get_calls(m);
+ call = pa_hashmap_isempty(calls)? NULL: pa_hashmap_first(calls);
+
+ /* Notify HF about call indicator state, if changed */
+ if (call && call->status == PA_MODEMMANAGER_CALL_STATE_ACTIVE)
+ call_indicator = CIND_CALL_AT_LEAST_ONE;
+ else
+ call_indicator = CIND_CALL_NONE;
+
+ if (b->cind_call_indicator != call_indicator) {
+ b->cind_call_indicator = call_indicator;
+ rfcomm_write_response(rfcomm_fd, "+CIEV: %d,%d", CIND_CALL_INDICATOR, b->cind_call_indicator);
+ }
+
+ /* Notify HF about call setup indicator state, if changed */
+ if (call && call->is_incoming && call->status == PA_MODEMMANAGER_CALL_STATE_RINGING)
+ call_setup_indicator = CIND_CALL_SETUP_INCOMING;
+ else if (call && !call->is_incoming && call->status == PA_MODEMMANAGER_CALL_STATE_DIALING)
+ call_setup_indicator = CIND_CALL_SETUP_OUTGOING_DIALING;
+ else if (call && !call->is_incoming && call->status == PA_MODEMMANAGER_CALL_STATE_RINGING)
+ call_setup_indicator = CIND_CALL_SETUP_OUTGOING_ALERTING;
+ else
+ call_setup_indicator = CIND_CALL_SETUP_NONE;
+
+ if (b->cind_call_setup_indicator != call_setup_indicator) {
+ b->cind_call_setup_indicator = call_setup_indicator;
+ rfcomm_write_response(rfcomm_fd, "+CIEV: %d,%d", CIND_CALL_SETUP_INDICATOR, b->cind_call_setup_indicator);
+ }
+
+ /* Start/stop ringing */
+ if (call && call->is_incoming && call->status == PA_MODEMMANAGER_CALL_STATE_RINGING) {
+ if (!b->timer_ring_event) {
+ pa_log_debug("RING indicator started");
+ b->timer_ring_event = pa_core_rttime_new(b->core, pa_rtclock_now(), timer_ring_cb, y);
+ }
+ } else if (b->timer_ring_event) {
+ pa_log_debug("RING indicator stopped");
+ b->core->mainloop->time_free(b->timer_ring_event);
+ b->timer_ring_event = NULL;
+ }
+
+ 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;
@@ -1658,6 +1870,30 @@ 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_HOST_BATTERY_LEVEL_CHANGED), PA_HOOK_NORMAL,
(pa_hook_cb_t) host_battery_level_changed_cb, backend);
+ backend->host_operation_failed_slot =
+ pa_hook_connect(pa_bluetooth_discovery_hook(y, PA_BLUETOOTH_HOOK_HOST_OPERATION_FAILED), PA_HOOK_NORMAL,
+ (pa_hook_cb_t) host_operation_failed_cb, backend);
+
+ backend->host_operation_succeed_slot =
+ pa_hook_connect(pa_bluetooth_discovery_hook(y, PA_BLUETOOTH_HOOK_HOST_OPERATION_SUCCEED), PA_HOOK_NORMAL,
+ (pa_hook_cb_t) host_operation_succeed_cb, backend);
+
+ backend->host_signal_strength_changed_slot =
+ pa_hook_connect(pa_bluetooth_discovery_hook(y, PA_BLUETOOTH_HOOK_HOST_SIGNAL_STRENGTH_CHANGED), PA_HOOK_NORMAL,
+ (pa_hook_cb_t) host_signal_strength_changed_cb, backend);
+
+ backend->host_has_service_changed_slot =
+ pa_hook_connect(pa_bluetooth_discovery_hook(y, PA_BLUETOOTH_HOOK_HOST_HAS_SERVICE_CHANGED), PA_HOOK_NORMAL,
+ (pa_hook_cb_t) host_has_service_changed_cb, backend);
+
+ backend->host_is_roaming_changed_slot =
+ pa_hook_connect(pa_bluetooth_discovery_hook(y, PA_BLUETOOTH_HOOK_HOST_IS_ROAMING_CHANGED), PA_HOOK_NORMAL,
+ (pa_hook_cb_t) host_is_roaming_changed_cb, backend);
+
+ backend->host_calls_changed_slot =
+ pa_hook_connect(pa_bluetooth_discovery_hook(y, PA_BLUETOOTH_HOOK_HOST_CALLS_CHANGED), PA_HOOK_NORMAL,
+ (pa_hook_cb_t) host_calls_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.");
@@ -1684,6 +1920,10 @@ pa_bluetooth_backend *pa_bluetooth_native_backend_new(pa_core *c, pa_bluetooth_d
/* CLIP is disabled by default */
backend->clip_call_line_reporting_enabled = false;
+ /* TODO: Initiate CIND call & callsetup indicators at startup */
+ backend->cind_call_indicator = CIND_CALL_NONE;
+ backend->cind_call_setup_indicator = CIND_CALL_SETUP_NONE;
+
return backend;
}
@@ -1698,6 +1938,24 @@ void pa_bluetooth_native_backend_free(pa_bluetooth_backend *backend) {
if (backend->host_battery_level_changed_slot)
pa_hook_slot_free(backend->host_battery_level_changed_slot);
+ if (backend->host_operation_failed_slot)
+ pa_hook_slot_free(backend->host_operation_failed_slot);
+
+ if (backend->host_operation_succeed_slot)
+ pa_hook_slot_free(backend->host_operation_succeed_slot);
+
+ if (backend->host_signal_strength_changed_slot)
+ pa_hook_slot_free(backend->host_signal_strength_changed_slot);
+
+ if (backend->host_has_service_changed_slot)
+ pa_hook_slot_free(backend->host_has_service_changed_slot);
+
+ if (backend->host_is_roaming_changed_slot)
+ pa_hook_slot_free(backend->host_is_roaming_changed_slot);
+
+ if (backend->host_calls_changed_slot)
+ pa_hook_slot_free(backend->host_calls_changed_slot);
+
if (backend->enable_shared_profiles)
native_backend_apply_profile_registration_change(backend, false);
diff --git a/src/modules/bluetooth/bluez5-util.h b/src/modules/bluetooth/bluez5-util.h
index fb88f00e2..35b3153b8 100644
--- a/src/modules/bluetooth/bluez5-util.h
+++ b/src/modules/bluetooth/bluez5-util.h
@@ -242,6 +242,18 @@ typedef enum pa_bluetooth_clcc {
CLCC_INCOMING = 4
} pa_bluetooth_clcc_t;
+typedef enum pa_bluetooth_cind_call {
+ CIND_CALL_NONE = 0,
+ CIND_CALL_AT_LEAST_ONE = 1,
+} pa_bluetooth_cind_call_t;
+
+typedef enum pa_bluetooth_cind_call_setup {
+ CIND_CALL_SETUP_NONE = 0,
+ CIND_CALL_SETUP_INCOMING = 1,
+ CIND_CALL_SETUP_OUTGOING_DIALING = 2,
+ CIND_CALL_SETUP_OUTGOING_ALERTING = 3
+} pa_bluetooth_cind_call_setup_t;
+
#ifdef HAVE_BLUEZ_5_OFONO_HEADSET
pa_bluetooth_backend *pa_bluetooth_ofono_backend_new(pa_core *c, pa_bluetooth_discovery *y);
void pa_bluetooth_ofono_backend_free(pa_bluetooth_backend *b);
--
2.35.1

View file

@ -1,71 +0,0 @@
From 8d2504c42a1910b4b6bf80a0474f4b5502c4f490 Mon Sep 17 00:00:00 2001
From: Dylan Van Assche <me@dylanvanassche.be>
Date: Sat, 16 Apr 2022 16:37:10 +0200
Subject: [PATCH 22/26] bluetooth: support AT+VTS
Send DTMF tones to the active call when the HF sends AT+VTS=$character.
Return an error if more than one character is given or ModemManager is
unavailable.
---
src/modules/bluetooth/backend-native.c | 32 ++++++++++++++++++++++++++
src/modules/bluetooth/bluez5-util.h | 1 +
2 files changed, 33 insertions(+)
diff --git a/src/modules/bluetooth/backend-native.c b/src/modules/bluetooth/backend-native.c
index e12b38d71..306de44a7 100644
--- a/src/modules/bluetooth/backend-native.c
+++ b/src/modules/bluetooth/backend-native.c
@@ -841,6 +841,38 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
rfcomm_write_response(fd, "+CLCC: %d,%d,%d,%d,%d,\"%s\",%d", 1, call->is_incoming, clcc_status, 0, 0, call->number, type);
return true;
+ } else if (strstr(buf, "AT+VTS=")) {
+ char *call_key = NULL;
+ char dtmf_char[2]; /* character and '\0' */
+
+ /* AT+VTS is only possible if ModemManager is availble */
+ if (!discovery->native_backend->modemmanager || !pa_modemmanager_has_modem(discovery->native_backend->modemmanager)) {
+ pa_log_debug("ModemManager backend unavailable, cannot send DTMF tone with AT+VTS");
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_NO_CONNECTION_TO_PHONE);
+ return false;
+ } else if (!pa_modemmanager_has_service(discovery->native_backend->modemmanager)) {
+ pa_log_debug("No network service, cannot send DTMF tone with AT+VTS");
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_NO_NETWORK_SERVICE);
+ return false;
+ }
+
+ /* Parse DTMF character, HFP spec allows 1 character. Return an error if parsing failed. */
+ val = sscanf(buf, "AT+VTS=%1s", dtmf_char);
+ if (val != 1) {
+ pa_log_warn("Cannot parse DTMF character from \"%s\"", buf);
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_INVALID_CHARACTERS_TEXT_STRING);
+ return false;
+ }
+ call_key = pa_modemmanager_get_active_call_key(discovery->native_backend->modemmanager);
+ if (call_key)
+ pa_modemmanager_send_dtmf(discovery->native_backend->modemmanager, call_key, dtmf_char);
+ else {
+ pa_log_warn("No active call, cannot send DTMF tone with AT+VTS");
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_OPERATION_NOT_ALLOWED);
+ }
+
+ /* AT response will be reported through PA_BLUETOOTH_HOST_OPERATION_{SUCCEED, FAILED} hook */
+ return false;
}
/*
diff --git a/src/modules/bluetooth/bluez5-util.h b/src/modules/bluetooth/bluez5-util.h
index 35b3153b8..4802cca69 100644
--- a/src/modules/bluetooth/bluez5-util.h
+++ b/src/modules/bluetooth/bluez5-util.h
@@ -217,6 +217,7 @@ typedef enum pa_bluetooth_cmee {
CMEE_NO_CONNECTION_TO_PHONE = 1,
CMEE_OPERATION_NOT_ALLOWED = 3,
CMEE_OPERATION_NOT_SUPPORTED = 4,
+ CMEE_INVALID_CHARACTERS_TEXT_STRING = 25,
CMEE_NO_NETWORK_SERVICE = 30
} pa_bluetooth_cmee_t;
--
2.35.1

View file

@ -1,57 +0,0 @@
From 48286245124ed08e57624b1e069cfa2ce4776568 Mon Sep 17 00:00:00 2001
From: Dylan Van Assche <me@dylanvanassche.be>
Date: Sat, 16 Apr 2022 19:37:04 +0200
Subject: [PATCH 23/26] bluetooth: support ATA
Accept incoming calls if the HF sends 'ATA'.
The first call in the call list is accepted,
multiparty calling is not supported (yet).
---
src/modules/bluetooth/backend-native.c | 31 ++++++++++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/src/modules/bluetooth/backend-native.c b/src/modules/bluetooth/backend-native.c
index 306de44a7..6f6241479 100644
--- a/src/modules/bluetooth/backend-native.c
+++ b/src/modules/bluetooth/backend-native.c
@@ -873,6 +873,37 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
/* AT response will be reported through PA_BLUETOOTH_HOST_OPERATION_{SUCCEED, FAILED} hook */
return false;
+ } else if (strstr(buf, "ATA")) {
+ pa_hashmap *calls;
+ char *call_key = NULL;
+ call_status_t *call = NULL;
+
+ /* ATA is only possible if ModemManager is availble */
+ if (!discovery->native_backend->modemmanager || !pa_modemmanager_has_modem(discovery->native_backend->modemmanager)) {
+ pa_log_debug("ModemManager backend unavailable, cannot reject call with ATA");
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_NO_CONNECTION_TO_PHONE);
+ return false;
+ } else if (!pa_modemmanager_has_service(discovery->native_backend->modemmanager)) {
+ pa_log_debug("No network service, cannot accept call with ATA");
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_NO_NETWORK_SERVICE);
+ return false;
+ }
+
+ /* Accept call is available */
+ calls = pa_modemmanager_get_calls(discovery->native_backend->modemmanager);
+ call_key = pa_modemmanager_get_active_call_key(discovery->native_backend->modemmanager);
+ if (call_key)
+ call = pa_hashmap_get(calls, call_key);
+
+ if (call && call->status == PA_MODEMMANAGER_CALL_STATE_RINGING && call->is_incoming)
+ pa_modemmanager_accept_call(discovery->native_backend->modemmanager, call_key);
+ else {
+ pa_log_warn("Cannot accept call, call must be incoming and ringing");
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_OPERATION_NOT_ALLOWED);
+ }
+
+ /* AT response will be reported through PA_BLUETOOTH_HOST_OPERATION_{SUCCEED, FAILED} hook */
+ return false;
}
/*
--
2.35.1

View file

@ -1,72 +0,0 @@
From 12610afa56b74e7db44086d81709faedda2b83e9 Mon Sep 17 00:00:00 2001
From: Dylan Van Assche <me@dylanvanassche.be>
Date: Sat, 16 Apr 2022 19:53:38 +0200
Subject: [PATCH 24/26] bluetooth: support AT+CHUP
Hangup or reject a call when the HF sends 'AT+CHUP'.
If the call is on going, it is ended. If the call is ringing and
incoming, it is rejected. The same interface in ModemManager is used for
this functionality. This AT command is only available if ModemManager is
present.
---
src/modules/bluetooth/backend-native.c | 37 ++++++++++++++++++++++++--
1 file changed, 35 insertions(+), 2 deletions(-)
diff --git a/src/modules/bluetooth/backend-native.c b/src/modules/bluetooth/backend-native.c
index 6f6241479..d502cff78 100644
--- a/src/modules/bluetooth/backend-native.c
+++ b/src/modules/bluetooth/backend-native.c
@@ -144,7 +144,7 @@ typedef enum pa_bluetooth_ag_to_hf_indicators {
/* gateway features we support, which is as little as we can get away with */
static uint32_t hfp_features =
/* HFP 1.6 requires this */
- (1 << HFP_AG_ESTATUS ) | (1 << HFP_AG_CODECS) | (1 << HFP_AG_INDICATORS) | (1 << HFP_AG_EERR);
+ (1 << HFP_AG_ESTATUS ) | (1 << HFP_AG_CODECS) | (1 << HFP_AG_INDICATORS) | (1 << HFP_AG_EERR) | (1 << HFP_AG_REJECT);
#define HSP_AG_PROFILE "/Profile/HSPAGProfile"
#define HFP_AG_PROFILE "/Profile/HFPAGProfile"
@@ -904,7 +904,40 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
/* AT response will be reported through PA_BLUETOOTH_HOST_OPERATION_{SUCCEED, FAILED} hook */
return false;
- }
+ } else if (strstr(buf, "AT+CHUP")) {
+ pa_hashmap *calls;
+ char *call_key = NULL;
+ call_status_t *call = NULL;
+
+ /* AT+CHUP is only possible if ModemManager is availble */
+ if (!discovery->native_backend->modemmanager || !pa_modemmanager_has_modem(discovery->native_backend->modemmanager)) {
+ pa_log_debug("ModemManager backend unavailable, cannot reject call with AT+CHUP");
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_NO_CONNECTION_TO_PHONE);
+ return false;
+ } else if (!pa_modemmanager_has_service(discovery->native_backend->modemmanager)) {
+ pa_log_debug("No network service, cannot reject call with ATD+CHUP");
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_NO_NETWORK_SERVICE);
+ return false;
+ }
+
+ /* Hangup/reject call is available */
+ calls = pa_modemmanager_get_calls(discovery->native_backend->modemmanager);
+ call_key = pa_modemmanager_get_active_call_key(discovery->native_backend->modemmanager);
+ if (call_key)
+ call = pa_hashmap_get(calls, call_key);
+
+ if (call && (call->status == PA_MODEMMANAGER_CALL_STATE_ACTIVE
+ || call->status == PA_MODEMMANAGER_CALL_STATE_RINGING
+ || call->status == PA_MODEMMANAGER_CALL_STATE_DIALING))
+ pa_modemmanager_end_call(discovery->native_backend->modemmanager, call_key);
+ else {
+ pa_log_warn("Cannot end call, call must be active, ringing or dialing");
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_OPERATION_NOT_ALLOWED);
+ }
+
+ /* AT response will be reported through PA_BLUETOOTH_HOST_OPERATION_{SUCCEED, FAILED} hook */
+ return false;
+ }
/*
* Out-of-spec Bluetooth HFP 1.8 AT commands.
--
2.35.1

View file

@ -1,104 +0,0 @@
From d2748e1952c282ce82e93daaeb2f25bb10ce46ab Mon Sep 17 00:00:00 2001
From: Dylan Van Assche <me@dylanvanassche.be>
Date: Sat, 16 Apr 2022 20:29:54 +0200
Subject: [PATCH 25/26] bluetooth: support ATD$number;
Start a call when HF sends 'ATD$number;'
This AT command is only available when ModemManager is present.
Number is filtered for any invalid characters, if an invalid character
is found, the call is aborted and an error is returned.
---
src/modules/bluetooth/backend-native.c | 57 ++++++++++++++++++++++++++
src/modules/bluetooth/bluez5-util.h | 1 +
2 files changed, 58 insertions(+)
diff --git a/src/modules/bluetooth/backend-native.c b/src/modules/bluetooth/backend-native.c
index d502cff78..b6991ae13 100644
--- a/src/modules/bluetooth/backend-native.c
+++ b/src/modules/bluetooth/backend-native.c
@@ -44,6 +44,9 @@
#include "upower.h"
#include "modemmanager.h"
+#define MAX_NUMBER_LENGTH 30
+#define STR_VALUE(x) STR(x)
+#define STR(x) #x
#define RING_WAIT_TIME ((pa_usec_t) (3 * PA_USEC_PER_SEC))
#define MANDATORY_CALL_INDICATORS \
"(\"call\",(0-1))," \
@@ -935,6 +938,60 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
rfcomm_write_error(discovery->native_backend, fd, CMEE_OPERATION_NOT_ALLOWED);
}
+ /* AT response will be reported through PA_BLUETOOTH_HOST_OPERATION_{SUCCEED, FAILED} hook */
+ return false;
+ } else if (strstr(buf, "ATD")) {
+ int k, j;
+ char number[MAX_NUMBER_LENGTH];
+ char number_filtered[MAX_NUMBER_LENGTH];
+
+ /* ATD$number; is only possible if ModemManager is availble */
+ if (!discovery->native_backend->modemmanager || !pa_modemmanager_has_modem(discovery->native_backend->modemmanager)) {
+ pa_log_debug("ModemManager backend unavailable, cannot create call with ATD$number;");
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_NO_CONNECTION_TO_PHONE);
+ return false;
+ } else if (!pa_modemmanager_has_service(discovery->native_backend->modemmanager)) {
+ pa_log_debug("No network service, cannot create call with ATD$number;");
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_NO_NETWORK_SERVICE);
+ return false;
+ }
+
+ /* Try to parse number from ATD command */
+ if (sscanf(buf, "ATD%" STR_VALUE(MAX_NUMBER_LENGTH) "s;", number) != 1) {
+ pa_log_debug("Cannot parse ATD$number: \"%s\"", buf);
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_AG_FAILURE);
+ return false;
+ }
+
+ /*
+ * Filter extracted number from invalid characters
+ * Allowed characters: 0-9, *, #, +, A-C
+ */
+ k=0;
+ memset(number_filtered, '\0', sizeof(char) * MAX_NUMBER_LENGTH);
+ for (j=0; j < MAX_NUMBER_LENGTH; j++) {
+ if ((number[j] >= '0' && number[j] <= '9')
+ || (number[j] == '*')
+ || (number[j] == '#')
+ || (number[j] == '+')
+ || (number[j] >= 'A' && number[j] <= 'C')) {
+ number_filtered[k] = number[j];
+ k++;
+ }
+ /* ATD commands ends with ';'. */
+ else if (number[j] == ';')
+ break;
+ /* Send error for invalid characters */
+ else {
+ pa_log_warn("Call creation canceled, invalid character found in dial string: %c", number[j]);
+ rfcomm_write_error(discovery->native_backend, fd, CMEE_INVALID_CHARACTERS_DIAL_STRING);
+ return false;
+ }
+ }
+
+ /* Create call for filtered number */
+ pa_modemmanager_start_call(discovery->native_backend->modemmanager, number_filtered);
+
/* AT response will be reported through PA_BLUETOOTH_HOST_OPERATION_{SUCCEED, FAILED} hook */
return false;
}
diff --git a/src/modules/bluetooth/bluez5-util.h b/src/modules/bluetooth/bluez5-util.h
index 4802cca69..ccbe6ba78 100644
--- a/src/modules/bluetooth/bluez5-util.h
+++ b/src/modules/bluetooth/bluez5-util.h
@@ -218,6 +218,7 @@ typedef enum pa_bluetooth_cmee {
CMEE_OPERATION_NOT_ALLOWED = 3,
CMEE_OPERATION_NOT_SUPPORTED = 4,
CMEE_INVALID_CHARACTERS_TEXT_STRING = 25,
+ CMEE_INVALID_CHARACTERS_DIAL_STRING = 27,
CMEE_NO_NETWORK_SERVICE = 30
} pa_bluetooth_cmee_t;
--
2.35.1

View file

@ -1,60 +0,0 @@
From aff9bf69960c3e2ac1d7c35176e9f3b34389f656 Mon Sep 17 00:00:00 2001
From: Dylan Van Assche <me@dylanvanassche.be>
Date: Tue, 19 Apr 2022 20:15:09 +0200
Subject: [PATCH 26/26] bluetooth: strip additional out-of-spec '\r\n' chars
Some HF devices do not comply strictly with the HFP specification
and send additional '\r\n' sequences besides the expected ones.
This breaks AT command parsing in PulseAudio.
To work around this problem, strip these characters before processing
the AT command and responses.
---
src/modules/bluetooth/backend-native.c | 22 ++++++++++++++++++----
1 file changed, 18 insertions(+), 4 deletions(-)
diff --git a/src/modules/bluetooth/backend-native.c b/src/modules/bluetooth/backend-native.c
index b6991ae13..8aa83f025 100644
--- a/src/modules/bluetooth/backend-native.c
+++ b/src/modules/bluetooth/backend-native.c
@@ -1556,19 +1556,33 @@ static void rfcomm_io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_i
}
if (events & PA_IO_EVENT_INPUT) {
- char buf[512];
+ char buf[512], raw_buf[512];
ssize_t len;
int gain, dummy;
bool do_reply = false;
int vendor, product, version, features;
- int num;
+ int num, i, j;
- len = pa_read(fd, buf, 511, NULL);
+ len = pa_read(fd, raw_buf, 511, NULL);
if (len < 0) {
pa_log_error("RFCOMM read error: %s", pa_cstrerror(errno));
goto fail;
}
- buf[len] = 0;
+ raw_buf[len] = 0;
+
+ /*
+ * Filter '\r' and '\n' characters out since some HF devices
+ * send too much \r\n sequences in their responses
+ */
+ memset(buf, '\0', sizeof(char) * 512);
+ j=0;
+ for (i=0; i<512; i++) {
+ if (raw_buf[i] == '\r' || raw_buf[i] == '\n')
+ continue;
+ buf[j] = raw_buf[i];
+ j++;
+ }
+ buf[j] = 0;
pa_log_debug("RFCOMM << %s", buf);
/* There are only four HSP AT commands:
--
2.35.1

View file

@ -1,285 +0,0 @@
# Forked from Alpine for enhanced Bluetooth HFP support
pkgname=pulseaudio
pkgver=9999_git20220621
_pkgver=16.1
pkgrel=2
pkgdesc="featureful, general-purpose sound server"
provider_priority=10
url="https://www.freedesktop.org/wiki/Software/PulseAudio/"
arch="all"
license="LGPL-2.1-or-later"
options="pmb:strict !check"
makedepends="
meson
tdb-dev
alsa-lib-dev
libasyncns-dev
dbus-dev
glib-dev
gtk+3.0-dev
orc-dev
orc-compiler
libsndfile-dev
soxr-dev
libx11-dev
libxcb-dev
libice-dev
libsm-dev
libxtst-dev
avahi-dev
sbc-dev
fftw-dev
jack-dev
openssl-dev>3
speexdsp-dev
eudev-dev
libcap-dev
bluez-dev
check-dev
libtool
perl
perl-xml-parser
m4
gstreamer-dev
gst-plugins-base-dev
modemmanager-dev
"
depends_openrc="alsa-utils alsa-utils-openrc"
subpackages="
$pkgname-dev
$pkgname-doc
$pkgname-bluez
libpulse-mainloop-glib:_libpulse_mainloop_glib
$pkgname-alsa
$pkgname-utils
$pkgname-jack
$pkgname-zeroconf
$pkgname-openrc
$pkgname-bash-completion
$pkgname-zsh-completion
$pkgname-lang
$pkgname-equalizer
libpulse:_libpulse
"
install="pulseaudio.post-install"
builddir="$srcdir/$pkgname-$_pkgver"
source="https://freedesktop.org/software/pulseaudio/releases/pulseaudio-$_pkgver.tar.xz
0001-bluez5-util-move-pa_bluetooth_discovery-to-header.patch
0002-bluetooth-add-AT-BIA-support.patch
0003-bluetooth-add-UPower-backend.patch
0004-bluetooth-hook-up-UPower-backend.patch
0005-bluetooth-add-ModemManager-backend.patch
0006-bluetooth-only-reply-OK-for-supported-AT-cmds.patch
0007-bluetooth-Always-reply-to-AT-CIND.patch
0008-bluetooth-support-AT-CMEE.patch
0009-bluetooth-support-AT-NREC.patch
0010-bluetooth-support-AT-BCC.patch
0011-bluetooth-support-AT-COPS.patch
0012-bluetooth-support-AT-CNUM.patch
0013-bluetooth-support-more-AT-CIND-indicators.patch
0014-bluetooth-support-AT-CLIP.patch
0015-bluetooth-support-AT-CREG.patch
0016-bluetooth-support-AT-CGMM.patch
0017-bluetooth-support-AT-CGMI.patch
0018-bluetooth-support-AT-CGMR.patch
0019-bluetooth-support-AT-CGSN.patch
0020-bluetooth-support-AT-CLCC.patch
0021-bluetooth-support-CIEV-RING-and-CLIP-URCs.patch
0022-bluetooth-support-AT-VTS.patch
0023-bluetooth-support-ATA.patch
0024-bluetooth-support-AT-CHUP.patch
0025-bluetooth-support-ATD-number.patch
0026-bluetooth-strip-additional-out-of-spec-r-n-chars.patch
link-libintl.patch
remove-once-test.patch
$pkgname.initd
$pkgname.confd
"
case "$CARCH" in
x86|x86_64|aarch64)
makedepends="$makedepends webrtc-audio-processing-dev"
_webrtc_aec="enabled"
;;
* )
_webrtc_aec="disabled" # webrtc-audio-processing not available
;;
esac
prepare() {
default_prepare
sed "s|sysconfdir, 'dbus-1'|datadir, 'dbus-1'|" \
-i src/daemon/meson.build
}
build() {
# See pma#2368
_lto_enable="false"
abuild-meson \
-Db_lto="$_lto_enable" \
-Dgcov=false \
-Dman=true \
-Dtests="$(want_check && echo true || echo false)" \
-Dsystem_user=pulse \
-Dsystem_group=pulse \
-Ddatabase=tdb \
-Dalsa=enabled \
-Dasyncns=enabled \
-Davahi=enabled \
-Dbluez5=enabled \
-Ddbus=enabled \
-Dfftw=enabled \
-Dglib=enabled \
-Dgsettings=enabled \
-Dgtk=enabled \
-Dhal-compat=false \
-Dipv6=true \
-Djack=enabled \
-Dlirc=disabled \
-Dopenssl=enabled \
-Dorc=enabled \
-Dsamplerate=disabled \
-Dsoxr=enabled \
-Dspeex=enabled \
-Dsystemd=disabled \
-Dudev=enabled \
-Dx11=enabled \
-Dudevrulesdir=/usr/lib/udev/rules.d \
-Dgstreamer=enabled \
-Dwebrtc-aec="$_webrtc_aec" \
-Ddoxygen=false \
-Dstream-restore-clear-old-devices=true \
. output
meson compile -C output
}
check() {
XDG_RUNTIME_DIR="$(mktemp -d)" \
meson test --no-rebuild --print-errorlogs -C output
}
package() {
DESTDIR="$pkgdir" meson install --no-rebuild -C output
install -Dm755 "$srcdir"/$pkgname.initd "$pkgdir"/etc/init.d/$pkgname
install -Dm644 "$srcdir"/$pkgname.confd "$pkgdir"/etc/conf.d/$pkgname
# Assumes that any volume adjustment is intended by the user, who can control
# each app's volume. Misbehaving clients can trigger earsplitting volume
# jumps. App volumes can diverge wildly and cause apps without their own
# volume control to fall below sink volume; a sink-only volume control will
# suddenly be unable to make such an app loud enough.
sed '/flat-volumes/iflat-volumes = no' -i "$pkgdir"/etc/pulse/daemon.conf
# Disable cork-request module, can result in e.g. media players unpausing
# when there's a Skype call incoming
sed 's|/usr/bin/pactl load-module module-x11-cork-request|#&|' \
-i "$pkgdir"/usr/bin/start-pulseaudio-x11
# Required by qpaeq
sed '/Load several protocols/aload-module module-dbus-protocol' \
-i "$pkgdir"/etc/pulse/default.pa
}
openrc() {
replaces="$pkgname-system" # Backward compatibility
default_openrc
}
_libpulse() {
pkgdesc="Pulseaudio libraries"
replaces="$pkgname-libs"
amove usr/lib/pulseaudio/libpulse*
amove usr/lib/libpulse.so.0*
amove usr/lib/libpulse-simple.so.0*
amove etc/pulse/client.conf
}
_libpulse_mainloop_glib() {
pkgdesc="Pulseaudio mainloop-glib library"
amove usr/lib/libpulse-mainloop-glib.so.*
}
bluez() {
pkgdesc="Pulseaudio Bluetooth support"
install_if="$pkgname=$pkgver-r$pkgrel bluez"
provider_priority=10 # highest (other provider is pipewire-pulse)
amove usr/lib/pulseaudio/modules/*bluez*.so
amove usr/lib/pulseaudio/modules/*bluetooth*.so
}
alsa() {
pkgdesc="Pulseaudio ALSA support"
install_if="$pkgname=$pkgver-r$pkgrel alsa-lib"
amove usr/lib/pulseaudio/modules/*alsa*.so
}
utils() {
pkgdesc="Pulseaudio utilities"
amove usr/bin/pa*
amove usr/bin/start-pulseaudio-x11
}
jack() {
pkgdesc="Pulseaudio JACK support"
amove usr/lib/pulseaudio/modules/*jack*.so
}
zeroconf() {
pkgdesc="Pulseaudio Zeroconf support"
depends="avahi"
amove usr/lib/pulseaudio/modules/*avahi*.so
amove usr/lib/pulseaudio/modules/*zeroconf*.so
amove usr/lib/pulseaudio/modules/*raop*.so
}
equalizer() {
pkgdesc="Equalizer for $pkgname"
depends="pulseaudio=$pkgver-r$pkgrel py3-qt5 py3-dbus"
amove usr/bin/qpaeq
amove usr/lib/pulseaudio/modules/module-equalizer-sink.so
}
sha512sums="
33b0b4292f05e0882f3ec822cf5720414bb34c523d80fe287b9740d6be219787c562e8024c9b0d9e2ee010771ca72c7ae4f27df1bbef9c9cb6bb6a23cbcc412f pulseaudio-16.1.tar.xz
c0a5a4854be7f8c5421671b7c5b890d9f097f8d0a1ac20009549786c28a66c9f28ac58863eea12f467ced87cbdb6093ea1a8444b9f1c1abda2aaa7e64c6c640a 0001-bluez5-util-move-pa_bluetooth_discovery-to-header.patch
c84e4e0591c21348be197fae4b54bdd437a8e39803daa8448fcaf03119a39723214b81b8c32b5a6b166fc4449b78a44c7633bd09a77902903d15b17299a58234 0002-bluetooth-add-AT-BIA-support.patch
a2ceac55f6cc8097569fec5e7a76f9e6ce3c79dacb27f5f19baed3a808e9be30e3c9a22b84b74e3d774d16f2d45c93e64a1248fc56b6d3cd897f620d46f581f9 0003-bluetooth-add-UPower-backend.patch
ffe399905f400628c0bcb732c6000ced30a3835884dfd0910bebf4af81ca63e743bb144e845978fda02ec4c81295e87ccf596b55d68e4c59ff827e5b1102a72e 0004-bluetooth-hook-up-UPower-backend.patch
58f7fc83c3c15e9df3b36cc192113d29fc6020823a36b30b68702373248a93c6e60a174a3c71c693547d33de55a9d20e72cf65f3957cea323b04c222d5d701ff 0005-bluetooth-add-ModemManager-backend.patch
98c0b80638603899b1639fedcc5edcde721f22cd3e7abed425015c933e65769b295a80b0d6a444fc5144cd1c6851dce41710dd8361fce2bfe5022ed913ad82ba 0006-bluetooth-only-reply-OK-for-supported-AT-cmds.patch
535b242b76fc494138a16b326708eaa4d410fbc5a779bda1471e25c56440d7e9a4c80b0bccd3ba5e0026f40c09e3b7b84bbb409e51c020deff8c9b0ead74fb71 0007-bluetooth-Always-reply-to-AT-CIND.patch
cdd7d3d47932c11ff7bd2bd4589b26dd3d92180fd4c156e70f57dcf5b560e05cd37d548a6e5280e47d258804f9dcaa3dd498d9dcdbb2e5c4e87f9f81dc7de73a 0008-bluetooth-support-AT-CMEE.patch
7b6a99b1d76781c36007b746bf2a47f163efeca843f36d75d4485c688d8f3a7fcafc9c36c1aa2ff02759184e20d387a89beea36ca0da64e23bb29dfc71feb55c 0009-bluetooth-support-AT-NREC.patch
b2bda75da4a4a5a2511fd3cd9ac8da8b3c59276a6396106e2249dd31053b68797f3f923177e6cd06fd7ae3cb3e2110e8bf8facb269d80a0e637a6f38cd8bb68c 0010-bluetooth-support-AT-BCC.patch
53bdf256fb2db1d6997204ba9504b584423946e177f8e1a819de0fafea29092a1925a0f7cde253635aa1f83c67e6b91bf3608fe543bc2185cd2ddf4aa5942054 0011-bluetooth-support-AT-COPS.patch
f3063daac100ef5d1fba792bb900a434be8cd0f9093a9be1ab68d84b8f94f39efc16c59d749f293e9b7556632866688757dc3d6e922096102f4583acb3f3cf5a 0012-bluetooth-support-AT-CNUM.patch
19880702e533bb46a67f09e8848bc2741a275c20b847dea96a0dba0424639715b0cd3d25a9168916cdc4ae4462b67855a3bb3b1d8384c12292fbacc032c2f0fa 0013-bluetooth-support-more-AT-CIND-indicators.patch
cd3d67672ff77d7908b01b4a377901d5e008b31b88d0e2c86918263c10a250aaf807cbf54cc3b095b1bf38ee533b3c0f409c5e1e2ced1df045fc64f421e02f78 0014-bluetooth-support-AT-CLIP.patch
967983f1e589945da4c0139d3d7d7be2c796f4a9ab52b344acd19723170c220dd7e00feafd2ad0a0481940f27c01176ccd54418758acedbc1c686d3426df9812 0015-bluetooth-support-AT-CREG.patch
61c400797405120cb065dea285e79a0056d716ff5ade3c8ea5a7e9f205c6895c0885f5ddb8c1abeec08ce3cf195b79bf7dc4d20281c5f7d5ee668accddab8cb9 0016-bluetooth-support-AT-CGMM.patch
42a3739a44bdcc01df0f3fa6fac5efb88f4fcf69686b529f8390d58b4becfd9b948d3ddea80aa5b8d007d96e22b4a87dedb5cbff610f008c317b31c8ae700774 0017-bluetooth-support-AT-CGMI.patch
6a4cd357126ebe99647bd3f4780ca109f2e823e0653851663d1d000b98e156ac17293058c8b94263371e303233f1c8e56073b523c91588d75989acb45b660ac2 0018-bluetooth-support-AT-CGMR.patch
b83d00a32c8e28c566e74ab8edd35e107313f31f4ff2ead21d6f9446a79f6ec520e7a737ef79844d46fbcceaad74fdaeb14ad37a865284c259f768156514f858 0019-bluetooth-support-AT-CGSN.patch
c58265d7c8f201765a254b2b26ff38f89ac2392acfb57024592e01096a997394322e726425fd02c37e21f6ab72638206d891354b2cd950102e7c13d87e026d40 0020-bluetooth-support-AT-CLCC.patch
c93be49e11055af356a01cc3545f984348dcff052f8777ef0127e2a9cab241fa83682550a5ffd69589d6603f1fb47ad3da248ff055b6c983e50ed817c63d1008 0021-bluetooth-support-CIEV-RING-and-CLIP-URCs.patch
d0663f8962f916d2149f44fe6d598ddea6df3cee542ee5ba9b418d05397c88a9c16368f9da2f96750e8bfb6a8e747e59fdc10f52fa59d1096ed6e59625c5dabd 0022-bluetooth-support-AT-VTS.patch
a39cbab500be77e88a7b8dd615ce7e1a2f8d678b19fe823552bb5d1279e2ae06aac3c353e03e10d601c53a5befe731ce00f8163c7b3a1e7b4b0c26a4fdde1730 0023-bluetooth-support-ATA.patch
9b93fd3dd2859b600d9eed9097f89b2a26220603eccdc61c6edb9b3b7488a97dbc5cee0dd4c40da3d1cbd8849023fd48f30ea1da0668c0bae189337bc8d1db9f 0024-bluetooth-support-AT-CHUP.patch
3eec77a1b39dbab93a4284de433946b70d6b9f972e62b02f076cb4a45b1f288e32ae05d8d557ed13ef21eded407951f9c0f48c78bdac9d47f318d2c7b288e10c 0025-bluetooth-support-ATD-number.patch
76b486095b1f2bb5d23837ed8f08d5ac7533e7c9621b2534c85b39a932663ffcf86b7fefaba1f2abc6aa7a95660c658cbcdeef20427b8ebb6e05cf532ee48731 0026-bluetooth-strip-additional-out-of-spec-r-n-chars.patch
2c31c5bc592e748248215f8f62f85687cfec230b05f65441e6dafa5fa77d4967e97636209b2011a4501ed1337ecd880b264baa175586fc85183a980846cb8146 link-libintl.patch
c1223cf42538d91f31dbcb1a167537a1d3b6e8a3bf1b454cdebf95c25ca6f3b444848e9d546f092988851f71fe2c9dd9c4a781a58795e2f57d98563963a4630a remove-once-test.patch
d3a95453361d4b12e2d4df73a72bbdc9343e2a46e8f5f3aac07b3cb1f9e016fd89816745f485468d144911900a2b38741a371f91cf08285c4b4988e4403d91a0 pulseaudio.initd
75b54581591519d63a3362b155c0f9b0501a60763ab394693a456c44d0216138cf3a40bdd0f7442028663bc045e9ffee286f8f8eaf2ee3bb17379b43615fee0e pulseaudio.confd
"

View file

@ -1,30 +0,0 @@
diff --git a/meson.build b/meson.build
index 658eeee..349752e 100644
--- a/meson.build
+++ b/meson.build
@@ -307,6 +307,12 @@ else
libintl_dep = cc.find_library('intl')
endif
+if cc.has_function('libintl_dgettext')
+ libintl_dep = []
+else
+ libintl_dep = cc.find_library('intl')
+endif
+
# Symbols
if cc.has_header_symbol('signal.h', 'SIGXCPU')
diff --git a/src/pulse/meson.build b/src/pulse/meson.build
index aaebff5..3f68ac3 100644
--- a/src/pulse/meson.build
+++ b/src/pulse/meson.build
@@ -84,7 +84,7 @@ libpulse = shared_library('pulse',
dependencies : [libm_dep, thread_dep, libpulsecommon_dep, dbus_dep, dl_dep, iconv_dep, libintl_dep],
implicit_include_directories : false)
-libpulse_dep = declare_dependency(link_with: libpulse)
+libpulse_dep = declare_dependency(link_with: libpulse, dependencies: libintl_dep)
install_headers(
libpulse_headers, 'simple.h',

View file

@ -1,7 +0,0 @@
# Config file for /etc/init.d/pulseaudio
# $Header: /var/cvsroot/gentoo-x86/media-sound/pulseaudio/files/pulseaudio.conf.d,v 1.6 2006/07/29 15:34:18 flameeyes Exp $
# For more see "pulseaudio -h".
# Startup options
PA_OPTS="--log-target=syslog --disallow-module-loading=1"

View file

@ -1,81 +0,0 @@
#!/sbin/openrc-run
# Copyright 1999-2011 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Header: /var/cvsroot/gentoo-x86/media-sound/pulseaudio/files/pulseaudio.init.d-5,v 1.1 2011/03/27 16:58:49 ssuominen Exp $
depend() {
need localmount
use net
local script="/etc/pulse/system.pa"
for opt in ${PA_OPTS}; do
case "$opt" in
--file=*) script="${opt#*=}" ;;
-F*) script="${opt#-F}" ;;
esac
done
config "$script"
local needs="$(get_options need)"
if [ -n "${needs}" ]; then
need ${needs}
return
fi
if grep -E -q '^[[:space:]]*load-module[[:space:]]+module-console-kit' "$script"; then
needs="${needs} consolekit"
fi
#ifdef UDEV
if grep -E -q '^[[:space:]]*load-module[[:space:]]+module-udev-detect' "$script"; then
needs="${needs} udev"
fi
#endif
#ifdef AVAHI
if grep -E -q '^[[:space:]]*load-module[[:space:]]+module-zeroconf-publish' "$script"; then
needs="${needs} avahi-daemon"
fi
#endif
#ifdef BLUETOOTH
if grep -E -q '^[[:space:]]*load-module[[:space:]]+module-bt-proximity' "$script"; then
needs="${needs} bluetooth"
fi
#endif
#ifdef ALSA
if grep -E -q '^[[:space:]]*load-module[[:space:]]+module-alsa-(sink|source)' "$script" ||
grep -E -q '^[[:space:]]*load-module[[:space:]]+module-(udev-)?detect' "$script" ||
grep -E -q '^[[:space:]]*add-autoload-source[[:space:]]+(input|output)[[:space:]]+module-alsa-(sink|source)' "$script"; then
needs="${needs} alsa" # in Alpine install alsa-utils to provide /etc/init.d/alsa (not alsasound as in Gentoo).
fi
#endif
need "${needs}"
save_options need "${needs}"
}
start() {
if [ -z "${PULSEAUDIO_SHOULD_NOT_GO_SYSTEMWIDE}" ]; then
eerror "Please don't use system wide PulseAudio unless you read the"
eerror "documentation available at http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/User/SystemWide/"
eerror ""
eerror "When you're done, please set the variable PULSEAUDIO_SHOULD_NOT_GO_SYSTEMWIDE in"
eerror "/etc/conf.d/pulseaudio . Please remember that upstream does not support this mode"
eerror "when used for standard desktop configurations."
return 1
fi
ebegin "Starting pulseaudio"
PA_ALL_OPTS="${PA_OPTS} --fail=1 --daemonize=1 --system"
start-stop-daemon --start --exec /usr/bin/pulseaudio -- ${PA_ALL_OPTS}
eend $?
}
stop() {
ebegin "Stopping pulseaudio"
start-stop-daemon --stop --quiet --exec /usr/bin/pulseaudio --pidfile /var/run/pulse/pid
eend $?
}

View file

@ -1,11 +0,0 @@
#!/bin/sh
# Alpine Linux post-install script for socklog
# Copyright 2019 Leo (thinkabit.ukim@gmail.com)
# Distributed under the terms of the GNU General Public License, v2 or later
addgroup -S pulse-access 2>/dev/null
addgroup -S pulse 2>/dev/null
adduser -S -D -H -h /var/run/pulse -g "PulseAudio daemon" -G pulse pulse 2>/dev/null
adduser pulse audio 2>/dev/null

View file

@ -1,16 +0,0 @@
--- a/src/tests/meson.build
+++ b/src/tests/meson.build
@@ -111,13 +111,6 @@
]
endif
- if host_machine.system() != 'darwin'
- default_tests += [
- [ 'once-test', 'once-test.c',
- [ check_dep, thread_dep, libpulse_dep, libpulsecommon_dep, libpulsecore_dep, libatomic_ops_dep ] ],
- ]
- endif
-
if alsa_dep.found()
default_tests += [
[ 'alsa-mixer-path-test', 'alsa-mixer-path-test.c',