pulseaudio/0003-bluetooth-add-correct-HFP-rfcomm-negotiation.patch
Takashi Iwai 37ac29471d Accepting request 877714 from home:tiwai:branches:multimedia:libs
- Upstream fixes for supporting HFP in native backend (bsc#1167940):
  0001-bluetooth-use-consistent-profile-names.patch
  0002-bluetooth-separate-HSP-and-HFP.patch
  0003-bluetooth-add-correct-HFP-rfcomm-negotiation.patch
  0004-bluetooth-make-native-the-default-backend.patch
  0005-bluetooth-enable-module-bluez5-discover-argument-ena.patch
  0006-bluetooth-fix-headset-auto-ofono-handover.patch
  0007-bluetooth-prefer-headset-HFP-HF-connection-with-nati.patch
  0008-bluetooth-complete-bluetooth-profile-separation.patch
  0009-bluetooth-use-device-flag-to-prevent-assertion-failu.patch
  0010-bluetooth-rename-enable_hs_role-to-enable_shared_pro.patch
  0011-bluetooth-clean-up-rfcomm_write-usage.patch

OBS-URL: https://build.opensuse.org/request/show/877714
OBS-URL: https://build.opensuse.org/package/show/multimedia:libs/pulseaudio?expand=0&rev=234
2021-03-08 12:50:38 +00:00

229 lines
7.1 KiB
Diff

From 485a64642e8da895f1b71ccc3d8aee78f5645639 Mon Sep 17 00:00:00 2001
From: James Bottomley <James.Bottomley@HansenPartnership.com>
Date: Sat, 20 Aug 2016 10:56:14 -0700
Subject: [PATCH 03/11] bluetooth: add correct HFP rfcomm negotiation
HFP 1.6 requires a stateful negotiation of AT commands. The prior
version got away with initialising HFP simply by replying 'OK' to
every negotiation attempt. This one actually tries to parse the state
and make sure the negotiation occurs correctly
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
---
v4:
- Update for PA 11.0
- Finally sort out CIND negotiaton for complex headsets
v3:
- remove internal debugging
- added comment for t->config being not null for hfp
- removed unused returns from hfp_rfcomm_handle()
- remove rfcomm comment
- use pa_startswith
- simplify negotiation
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/491>
---
src/modules/bluetooth/backend-native.c | 124 +++++++++++++++++++++++++++++++--
src/modules/bluetooth/bluez5-util.c | 5 +
src/modules/bluetooth/bluez5-util.h | 2
3 files changed, 125 insertions(+), 6 deletions(-)
--- a/src/modules/bluetooth/backend-native.c
+++ b/src/modules/bluetooth/backend-native.c
@@ -61,6 +61,43 @@ struct transport_data {
#define BLUEZ_PROFILE_MANAGER_INTERFACE BLUEZ_SERVICE ".ProfileManager1"
#define BLUEZ_PROFILE_INTERFACE BLUEZ_SERVICE ".Profile1"
+struct hfp_config {
+ uint32_t capabilities;
+ int state;
+};
+
+/*
+ * the separate hansfree headset (HF) and Audio Gateway (AG) features
+ */
+enum hfp_hf_features {
+ HFP_HF_EC_NR = 0,
+ HFP_HF_CALL_WAITING = 1,
+ HFP_HF_CLI = 2,
+ HFP_HF_VR = 3,
+ HFP_HF_RVOL = 4,
+ HFP_HF_ESTATUS = 5,
+ HFP_HF_ECALL = 6,
+ HFP_HF_CODECS = 7,
+};
+
+enum hfp_ag_features {
+ HFP_AG_THREE_WAY = 0,
+ HFP_AG_EC_NR = 1,
+ HFP_AG_VR = 2,
+ HFP_AG_RING = 3,
+ HFP_AG_NUM_TAG = 4,
+ HFP_AG_REJECT = 5,
+ HFP_AG_ESTATUS = 6,
+ HFP_AG_ECALL = 7,
+ HFP_AG_EERR = 8,
+ HFP_AG_CODECS = 9,
+};
+
+/* 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 );
+
#define HSP_AG_PROFILE "/Profile/HSPAGProfile"
#define HFP_AG_PROFILE "/Profile/HFPAGProfile"
#define HSP_HS_PROFILE "/Profile/HSPHSProfile"
@@ -109,6 +146,27 @@ static pa_dbus_pending* send_and_add_to_
return p;
}
+static void rfcomm_write(int fd, const char *str)
+{
+ size_t len;
+ char buf[512];
+
+ pa_log_debug("RFCOMM >> %s", str);
+ sprintf(buf, "\r\n%s\r\n", str);
+ len = write(fd, buf, strlen(buf));
+
+ if (len != strlen(buf))
+ pa_log_error("RFCOMM write error: %s", pa_cstrerror(errno));
+}
+
+static void hfp_send_features(int fd)
+{
+ char buf[512];
+
+ sprintf(buf, "+BRSF: %d", hfp_features);
+ rfcomm_write(fd, buf);
+}
+
static int sco_do_connect(pa_bluetooth_transport *t) {
pa_bluetooth_device *d = t->device;
struct sockaddr_sco addr;
@@ -352,6 +410,61 @@ static void register_profile(pa_bluetoot
send_and_add_to_pending(b, m, register_profile_reply, pa_xstrdup(profile));
}
+static void transport_put(pa_bluetooth_transport *t)
+{
+ pa_bluetooth_transport_put(t);
+
+ pa_log_debug("Transport %s available for profile %s", t->path, pa_bluetooth_profile_to_string(t->profile));
+}
+
+static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf)
+{
+ struct hfp_config *c = t->config;
+ int val;
+
+ /* stateful negotiation */
+ if (c->state == 0 && sscanf(buf, "AT+BRSF=%d", &val) == 1) {
+ c->capabilities = val;
+ pa_log_info("HFP capabilities returns 0x%x", val);
+ hfp_send_features(fd);
+ c->state = 1;
+ return true;
+ } else if (c->state == 1 && pa_startswith(buf, "AT+CIND=?")) {
+ /* we declare minimal no indicators */
+ rfcomm_write(fd, "+CIND: "
+ /* many indicators can be supported, only call and
+ * callheld are mandatory, so that's all we repy */
+ "(\"call\",(0-1)),"
+ "(\"callheld\",(0-2))");
+ c->state = 2;
+ return true;
+ } else if (c->state == 2 && pa_startswith(buf, "AT+CIND?")) {
+ rfcomm_write(fd, "+CIND: 0,0");
+ c->state = 3;
+ return true;
+ } else if ((c->state == 2 || c->state == 3) && pa_startswith(buf, "AT+CMER=")) {
+ rfcomm_write(fd, "\r\nOK\r\n");
+ c->state = 4;
+ transport_put(t);
+ return false;
+ }
+
+ /* if we get here, negotiation should be complete */
+ if (c->state != 4) {
+ pa_log_error("HFP negotiation failed in state %d with inbound %s\n",
+ c->state, buf);
+ rfcomm_write(fd, "ERROR");
+ 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;
+}
+
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;
@@ -398,6 +511,8 @@ static void rfcomm_io_callback(pa_mainlo
do_reply = true;
} else if (sscanf(buf, "AT+CKPD=%d", &dummy) == 1) {
do_reply = true;
+ } else if (t->config) { /* t->config is only non-null for hfp profile */
+ do_reply = hfp_rfcomm_handle(fd, t, buf);
} else {
do_reply = false;
}
@@ -540,7 +655,9 @@ static DBusMessage *profile_new_connecti
sender = dbus_message_get_sender(m);
pathfd = pa_sprintf_malloc ("%s/fd%d", path, fd);
- t = pa_bluetooth_transport_new(d, sender, pathfd, p, NULL, 0);
+ t = pa_bluetooth_transport_new(d, sender, pathfd, p, NULL,
+ p == PA_BLUETOOTH_PROFILE_HFP_HF ?
+ sizeof(struct hfp_config) : 0);
pa_xfree(pathfd);
t->acquire = sco_acquire_cb;
@@ -558,9 +675,8 @@ static DBusMessage *profile_new_connecti
sco_listen(t);
- pa_bluetooth_transport_put(t);
-
- pa_log_debug("Transport %s available for profile %s", t->path, pa_bluetooth_profile_to_string(t->profile));
+ if (p != PA_BLUETOOTH_PROFILE_HFP_HF)
+ transport_put(t);
pa_assert_se(r = dbus_message_new_method_return(m));
--- a/src/modules/bluetooth/bluez5-util.c
+++ b/src/modules/bluetooth/bluez5-util.c
@@ -152,7 +152,10 @@ pa_bluetooth_transport *pa_bluetooth_tra
if (size > 0) {
t->config = pa_xnew(uint8_t, size);
- memcpy(t->config, config, size);
+ if (config)
+ memcpy(t->config, config, size);
+ else
+ memset(t->config, 0, size);
}
return t;
--- a/src/modules/bluetooth/bluez5-util.h
+++ b/src/modules/bluetooth/bluez5-util.h
@@ -84,7 +84,7 @@ struct pa_bluetooth_transport {
pa_bluetooth_profile_t profile;
uint8_t codec;
- uint8_t *config;
+ void *config;
size_t config_size;
const pa_a2dp_codec *a2dp_codec;