diff options
author | James Bottomley <James.Bottomley@HansenPartnership.com> | 2018-01-09 08:30:04 -0800 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2018-07-10 17:20:34 -0700 |
commit | cd52585aca1b9c4936de107b7b45905bcb360d8e (patch) | |
tree | 0856c05af3379662e27b6e0e7e6578a19d845d3a | |
parent | 6cc4d9fa77aa5d9b3f17da435f3f35c021ebb4be (diff) | |
download | pulseaudio-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.c | 53 |
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"); |