Commit 4444ecad authored by James Bottomley's avatar James Bottomley Committed by PulseAudio Marge Bot
Browse files

bluetooth: add wideband audio codec negotiation to HFP



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's avatarJames Bottomley <James.Bottomley@HansenPartnership.com>
Part-of: <!507>
parent f22cfa8f
......@@ -35,6 +35,7 @@
#include <bluetooth/sco.h>
#include "bluez5-util.h"
#include "bt-codec-msbc.h"
#define HSP_MAX_GAIN 15
......@@ -59,6 +60,7 @@ struct transport_data {
struct hfp_config {
uint32_t capabilities;
int state;
bool support_msbc;
};
/*
......@@ -91,7 +93,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 HSP_AG_PROFILE "/Profile/HSPAGProfile"
#define HFP_AG_PROFILE "/Profile/HFPAGProfile"
......@@ -219,6 +221,20 @@ static void rfcomm_write_response(int fd, const char *fmt, ...)
va_end(ap);
}
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;
memset(&voice, 0, sizeof(voice));
voice.setting = BT_VOICE_TRANSPARENT;
if (setsockopt(fd, SOL_BLUETOOTH, BT_VOICE, &voice, sizeof(voice)) < 0) {
pa_log_error("sockopt(): %s", pa_cstrerror(errno));
return -1;
}
pa_log_info("Enabled BT_VOICE_TRANSPARENT connection for mSBC");
return 0;
}
static int sco_do_connect(pa_bluetooth_transport *t) {
pa_bluetooth_device *d = t->device;
struct sockaddr_sco addr;
......@@ -254,6 +270,9 @@ static int sco_do_connect(pa_bluetooth_transport *t) {
goto fail_close;
}
if (t->setsockopt && t->setsockopt(t, sock) < 0)
goto fail_close;
memset(&addr, 0, len);
addr.sco_family = AF_BLUETOOTH;
bacpy(&addr.sco_bdaddr, &dst);
......@@ -323,6 +342,11 @@ static int sco_acquire_cb(pa_bluetooth_transport *t, bool optional, size_t *imtu
}
}
/* read/decode machinery only works if we get at most one MSBC encoded packet at a time
* when it is fixed to process stream of packets, lift this assertion */
pa_assert(*imtu <= MSBC_PACKET_SIZE);
pa_assert(*omtu <= MSBC_PACKET_SIZE);
return sock;
fail:
......@@ -526,6 +550,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) {
......@@ -534,6 +559,13 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
rfcomm_write_response(fd, "+BRSF: %d", hfp_features);
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 */
......@@ -543,21 +575,52 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf
"(\"call\",(0-1)),"
"(\"callheld\",(0-2))");
c->state = 2;
return true;
} else if (c->state == 2 && pa_startswith(buf, "AT+CIND?")) {
rfcomm_write_response(fd, "+CIND: 0,0");
c->state = 3;
return true;
} else if ((c->state == 2 || c->state == 3) && pa_startswith(buf, "AT+CMER=")) {
rfcomm_write_response(fd, "OK");
c->state = 4;
t->bt_codec = pa_bluetooth_get_hf_codec("CVSD");
transport_put(t);
if (c->support_msbc) {
rfcomm_write_response(fd, "+BCS:2");
c->state = 4;
} else {
c->state = 5;
t->bt_codec = pa_bluetooth_get_hf_codec("CVSD");
t->setsockopt = NULL;
transport_put(t);
}
return false;
} else if (sscanf(buf, "AT+BCS=%d", &val)) {
if (val == 1) {
t->bt_codec = pa_bluetooth_get_hf_codec("CVSD");
t->setsockopt = NULL;
} else if (val == 2) {
t->bt_codec = pa_bluetooth_get_hf_codec("mSBC");
t->setsockopt = sco_setsockopt_enable_bt_voice;
} else
pa_assert_not_reached();
if (c->state == 4) {
c->state = 5;
pa_log_info("HFP negotiated codec %s", t->bt_codec->name);
transport_put(t);
}
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_response(fd, "ERROR");
......
......@@ -219,6 +219,7 @@ static int card_acquire(struct hf_audio_card *card) {
return -1;
}
card->transport->bt_codec = pa_bluetooth_get_hf_codec("CVSD");
card->transport->setsockopt = NULL;
card->fd = fd;
return 0;
}
......@@ -687,6 +688,7 @@ static DBusMessage *hf_audio_agent_new_connection(DBusConnection *c, DBusMessage
card->connecting = false;
card->fd = fd;
card->transport->bt_codec = pa_bluetooth_get_hf_codec("CVSD");
card->transport->setsockopt = NULL;
pa_bluetooth_transport_set_state(card->transport, PA_BLUETOOTH_TRANSPORT_STATE_PLAYING);
......
......@@ -89,6 +89,7 @@ typedef void (*pa_bluetooth_transport_release_cb)(pa_bluetooth_transport *t);
typedef void (*pa_bluetooth_transport_destroy_cb)(pa_bluetooth_transport *t);
typedef pa_volume_t (*pa_bluetooth_transport_set_volume_cb)(pa_bluetooth_transport *t, pa_volume_t volume);
typedef ssize_t (*pa_bluetooth_transport_write_cb)(pa_bluetooth_transport *t, int fd, const void* buffer, size_t size, size_t write_mtu);
typedef int (*pa_bluetooth_transport_setsockopt_cb)(pa_bluetooth_transport *t, int fd);
struct pa_bluetooth_transport {
pa_bluetooth_device *device;
......@@ -111,6 +112,7 @@ struct pa_bluetooth_transport {
pa_bluetooth_transport_acquire_cb acquire;
pa_bluetooth_transport_release_cb release;
pa_bluetooth_transport_write_cb write;
pa_bluetooth_transport_setsockopt_cb setsockopt;
pa_bluetooth_transport_destroy_cb destroy;
pa_bluetooth_transport_set_volume_cb set_sink_volume;
pa_bluetooth_transport_set_volume_cb set_source_volume;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment