aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Bottomley <James.Bottomley@HansenPartnership.com>2018-01-09 08:30:04 -0800
committerJames Bottomley <James.Bottomley@HansenPartnership.com>2018-07-10 17:20:34 -0700
commitcd52585aca1b9c4936de107b7b45905bcb360d8e (patch)
tree0856c05af3379662e27b6e0e7e6578a19d845d3a
parent6cc4d9fa77aa5d9b3f17da435f3f35c021ebb4be (diff)
downloadpulseaudio-jejb-v12.tar.gz
bluetooth: add wideband audio codec negotiation to HFPjejb-v12
The HFP protocol supports the ability to negotiate codecs if that is supported by both AG and HF. This patch adds advertising of codec negotiation support and the ability to negotiate a codec change. The only currently supported extra codec (as of HF 1.7.1) is mSBC. mSBC requires that the transmission be done over an eSCO link with Transparent Data. The linux kernel ensures the former, but we have to manually set the socket to transparent data. Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
-rw-r--r--src/modules/bluetooth/backend-native.c53
1 files changed, 49 insertions, 4 deletions
diff --git a/src/modules/bluetooth/backend-native.c b/src/modules/bluetooth/backend-native.c
index bf158ef6..bc84be40 100644
--- a/src/modules/bluetooth/backend-native.c
+++ b/src/modules/bluetooth/backend-native.c
@@ -56,6 +56,7 @@ struct transport_data {
struct hfp_config {
uint32_t capabilities;
int state;
+ bool support_msbc;
};
/*
@@ -88,7 +89,7 @@ enum hfp_ag_features {
/* 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_ESTATUS ) | (1 << HFP_AG_CODECS);
#define BLUEZ_SERVICE "org.bluez"
#define BLUEZ_MEDIA_TRANSPORT_INTERFACE BLUEZ_SERVICE ".MediaTransport1"
@@ -202,6 +203,18 @@ static int sco_do_connect(pa_bluetooth_transport *t) {
goto fail_close;
}
+ if (t->codec == PA_BLUETOOTH_CODEC_MSBC) {
+ /* the mSBC codec requires a special transparent eSCO connection */
+ struct bt_voice voice;
+
+ memset(&voice, 0, sizeof(voice));
+ voice.setting = BT_VOICE_TRANSPARENT;
+ if (setsockopt(sock, SOL_BLUETOOTH, BT_VOICE, &voice, sizeof(voice)) < 0) {
+ pa_log_error("sockopt(): %s", pa_cstrerror(errno));
+ goto fail_close;
+ }
+ pa_log_info("Enabled BT_VOICE_TRANSPARENT connection for mSBC");
+ }
memset(&addr, 0, len);
addr.sco_family = AF_BLUETOOTH;
bacpy(&addr.sco_bdaddr, &dst);
@@ -421,6 +434,7 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
{
struct hfp_config *c = t->config;
int val;
+ char str[5];
/* stateful negotiation */
if (c->state == 0 && sscanf(buf, "AT+BRSF=%d", &val) == 1) {
@@ -429,6 +443,12 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
hfp_send_features(fd);
c->state = 1;
return true;
+ } else if (c->state == 1 && sscanf(buf, "AT+BAC=%3s", str) == 1) {
+ if (strncmp(str, "1,2", 3) == 0)
+ c->support_msbc = true;
+ else
+ c->support_msbc = false;
+ return true;
} else if (c->state == 1 && pa_startswith(buf, "AT+CIND=?")) {
/* we declare minimal no indicators */
rfcomm_write(fd, "+CIND: "
@@ -444,13 +464,38 @@ 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(fd, "\r\nOK\r\n");
- c->state = 4;
- transport_put(t);
+ if (c->support_msbc) {
+ rfcomm_write(fd, "+BCS:2");
+ c->state = 4;
+ } else {
+ c->state = 5;
+ transport_put(t);
+ }
+
return false;
+ } else if (sscanf(buf, "AT+BCS=%d", &val)) {
+ if (val == 1)
+ t->codec = PA_BLUETOOTH_CODEC_CVSD;
+ else if (val == 2)
+ t->codec = PA_BLUETOOTH_CODEC_MSBC;
+ else
+ pa_assert_not_reached();
+
+ if (c->state == 4) {
+ c->state = 5;
+ transport_put(t);
+ pa_log_info("HFP negotiated codec %s",
+ t->codec == PA_BLUETOOTH_CODEC_MSBC ? "mSBC" : "CVSD");
+ }
+ return true;
+ } if (c->state == 4) {
+ /* the ack for the codec setting may take a while. we need
+ * to reply OK to everything else until then */
+ return true;
}
/* if we get here, negotiation should be complete */
- if (c->state != 4) {
+ if (c->state != 5) {
pa_log_error("HFP negotiation failed in state %d with inbound %s\n",
c->state, buf);
rfcomm_write(fd, "ERROR");