From: "Frédéric Danis" <frederic.danis@collabora.com>
To: linux-bluetooth@vger.kernel.org
Subject: [PATCH BlueZ v2 12/12] audio/hfp-hf: Add codecs support
Date: Thu, 1 Oct 2026 23:10:36 +0200 [thread overview]
Message-ID: <20261001211036.1760396-13-frederic.danis@collabora.com> (raw)
In-Reply-To: <20261001211036.1760396-1-frederic.danis@collabora.com>
From: Frédéric Danis <frederic.danis.oss@gmail.com>
---
profiles/audio/hfp-hf.c | 171 +++++++++++++++++++++++++++++++++++-----
1 file changed, 152 insertions(+), 19 deletions(-)
diff --git a/profiles/audio/hfp-hf.c b/profiles/audio/hfp-hf.c
index 25dce517f..4e626653d 100644
--- a/profiles/audio/hfp-hf.c
+++ b/profiles/audio/hfp-hf.c
@@ -65,11 +65,19 @@
#define HFP_HF_SDP_FEATURES (HFP_HF_SDP_ECNR | HFP_HF_SDP_3WAY |\
HFP_HF_SDP_CLIP |\
- HFP_HF_SDP_REMOTE_VOLUME_CONTROL)
+ HFP_HF_SDP_REMOTE_VOLUME_CONTROL |\
+ HFP_HF_SDP_WIDE_BAND_SPEECH |\
+ HFP_HF_SDP_SUPER_WIDE_BAND_SPEECH)
#define URI "tel"
#define URI_PREFIX URI ":"
+struct connect_data {
+ struct hfp_device *dev;
+ void (*cb)(int status, void *data);
+ void *cb_user_data;
+};
+
struct hfp_device {
struct telephony *telephony;
uint16_t version;
@@ -81,6 +89,7 @@ struct hfp_device {
struct queue *calls;
uint8_t codec;
unsigned int resume_id;
+ struct connect_data *pending_connect;
hfp_hf_sco_closed_cb sco_closed_cb;
void *sco_closed_data;
};
@@ -169,6 +178,20 @@ static bool call_id_cmp(const void *data, const void *match_data)
return call->idx == id;
}
+static void sco_connect_complete(struct connect_data *connect_data, int status)
+{
+ if (!connect_data)
+ return;
+
+ if (connect_data->dev->pending_connect == connect_data)
+ connect_data->dev->pending_connect = NULL;
+
+ if (connect_data->cb)
+ connect_data->cb(status, connect_data->cb_user_data);
+
+ g_free(connect_data);
+}
+
static void device_destroy(struct hfp_device *dev)
{
struct hfp_server *server;
@@ -177,6 +200,9 @@ static void device_destroy(struct hfp_device *dev)
telephony_set_state(dev->telephony, DISCONNECTING);
+ if (dev->pending_connect)
+ sco_connect_complete(dev->pending_connect, -ENOTCONN);
+
if (dev->hf) {
hfp_hf_unref(dev->hf);
dev->hf = NULL;
@@ -329,6 +355,49 @@ static void hfp_hf_call_line_id_updated(uint id, const char *number,
telephony_call_set_line_id(call, number);
}
+static uint8_t hfp_hf_get_codecs(uint8_t *codecs, uint8_t max_codecs,
+ void *user_data)
+{
+ struct hfp_device *dev = user_data;
+ struct btd_adapter *adapter;
+ struct hfp_server *server;
+ GSList *l;
+ uint8_t i = 0;
+
+ adapter = device_get_adapter(telephony_get_device(dev->telephony));
+ server = find_server(servers, adapter);
+
+ for (l = server->endpoints; l; l = l->next) {
+ codecs[i++] = media_endpoint_get_codec(l->data);
+ if (i >= max_codecs)
+ break;
+ }
+
+ return i;
+}
+
+static bool hfp_hf_select_codec(uint8_t codec, void *user_data)
+{
+ struct hfp_device *dev = user_data;
+ struct btd_adapter *adapter;
+ struct hfp_server *server;
+ GSList *l;
+
+ DBG("Selecting codec: %u", codec);
+ adapter = device_get_adapter(telephony_get_device(dev->telephony));
+ server = find_server(servers, adapter);
+
+ for (l = server->endpoints; l; l = l->next) {
+ if (media_endpoint_get_codec(l->data) == codec) {
+ dev->codec = codec;
+ return true;
+ }
+ }
+
+ DBG("Unsupported codec: %u", codec);
+ return false;
+}
+
static struct hfp_hf_callbacks hf_session_callbacks = {
.session_ready = hfp_hf_session_ready_cb,
.update_indicator = hfp_hf_update_indicator,
@@ -338,6 +407,8 @@ static struct hfp_hf_callbacks hf_session_callbacks = {
.call_removed = hfp_hf_call_removed,
.call_status_updated = hfp_hf_call_status_updated,
.call_line_id_updated = hfp_hf_call_line_id_updated,
+ .get_codecs = hfp_hf_get_codecs,
+ .select_codec = hfp_hf_select_codec,
};
static void hfp_disconnect_watch(void *user_data)
@@ -991,23 +1062,6 @@ static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond, void *data)
return FALSE;
}
-struct connect_data {
- struct hfp_device *dev;
- void (*cb)(int status, void *data);
- void *cb_user_data;
-};
-
-static void sco_connect_complete(struct connect_data *connect_data, int status)
-{
- if (!connect_data || !connect_data->cb)
- goto done;
-
- connect_data->cb(status, connect_data->cb_user_data);
-
-done:
- g_free(connect_data);
-}
-
static void sco_connect_cb(GIOChannel *io, GError *err, gpointer user_data)
{
struct connect_data *connect_data = user_data;
@@ -1109,6 +1163,62 @@ static void sco_connect_cb(GIOChannel *io, GError *err, gpointer user_data)
sco_connect_complete(connect_data, 0);
}
+static void confirm_cb(GIOChannel *io, gpointer user_data)
+{
+ bdaddr_t src, dst;
+ char address[18];
+ struct btd_device *device;
+ struct btd_service *service;
+ struct hfp_device *dev = NULL;
+ int voice;
+ GError *err = NULL;
+
+ if (!bt_io_get(io, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &src,
+ BT_IO_OPT_DEST_BDADDR, &dst,
+ BT_IO_OPT_DEST, address,
+ BT_IO_OPT_INVALID)) {
+ error("Unable to get destination address: %s", err->message);
+ g_clear_error(&err);
+ goto drop;
+ }
+
+ DBG("Incoming SCO connection from %s", address);
+
+ device = btd_adapter_find_device(adapter_find(&src), &dst,
+ BDADDR_BREDR);
+ if (!device)
+ goto drop;
+
+ service = btd_device_get_service(device, HFP_AG_UUID);
+ if (!service)
+ goto drop;
+
+ dev = btd_service_get_user_data(service);
+ voice = dev->codec == 1 ? BT_VOICE_CVSD_16BIT : BT_VOICE_TRANSPARENT;
+
+ if (!bt_io_set(io, &err, BT_IO_OPT_VOICE, voice, BT_IO_OPT_INVALID)) {
+ error("Could not set voice settings on SCO IO: %s",
+ err->message);
+ g_error_free(err);
+ goto drop;
+ }
+
+ if (!bt_io_accept(io, sco_connect_cb, dev->pending_connect, NULL,
+ &err)) {
+ error("bt_io_accept() failed: %s", err->message);
+ g_error_free(err);
+ goto drop;
+ }
+
+ return;
+
+drop:
+ if (dev && dev->pending_connect)
+ sco_connect_complete(dev->pending_connect, -EIO);
+ g_io_channel_shutdown(io, TRUE, NULL);
+}
+
bool hfp_hf_sco_listen(struct btd_adapter *adapter, void *endpoint)
{
struct hfp_server *server;
@@ -1126,7 +1236,7 @@ bool hfp_hf_sco_listen(struct btd_adapter *adapter, void *endpoint)
if (server->sco_io)
return true;
- server->sco_io = bt_io_listen(sco_connect_cb, NULL, NULL, NULL,
+ server->sco_io = bt_io_listen(NULL, confirm_cb, NULL, NULL,
&err,
BT_IO_OPT_SOURCE_BDADDR,
btd_adapter_get_address(server->adapter),
@@ -1190,6 +1300,16 @@ uint16_t hfp_hf_device_get_omtu(struct hfp_device *dev)
return dev->omtu;
}
+static void req_codec_connection_complete(enum hfp_result res,
+ enum hfp_error cme_err,
+ void *user_data)
+{
+ struct connect_data *connect_data = user_data;
+
+ if (res != HFP_RESULT_OK)
+ sco_connect_complete(connect_data, -EIO);
+}
+
static gboolean sco_start_cb(gpointer data)
{
struct connect_data *connect_data = data;
@@ -1211,6 +1331,11 @@ unsigned int hfp_hf_sco_start(struct hfp_device *dev, void *cb, void *user_data)
DBG("codec %u", dev->codec);
+ if (dev->pending_connect) {
+ error("SCO connect already in progress");
+ return 0;
+ }
+
connect_data = g_new0(struct connect_data, 1);
connect_data->dev = dev;
connect_data->cb = cb;
@@ -1219,6 +1344,13 @@ unsigned int hfp_hf_sco_start(struct hfp_device *dev, void *cb, void *user_data)
src = telephony_get_src(dev->telephony);
dst = telephony_get_dst(dev->telephony);
if (!dev->sco_io) {
+ if (hfp_hf_request_codec_connection(dev->hf,
+ req_codec_connection_complete,
+ connect_data)) {
+ dev->pending_connect = connect_data;
+ goto done;
+ }
+
io = bt_io_connect(sco_connect_cb, connect_data, NULL, &err,
BT_IO_OPT_SOURCE_BDADDR, &src,
BT_IO_OPT_DEST_BDADDR, &dst,
@@ -1234,6 +1366,7 @@ unsigned int hfp_hf_sco_start(struct hfp_device *dev, void *cb, void *user_data)
g_idle_add(sco_start_cb, connect_data);
}
+done:
return (++dev->resume_id);
}
--
2.43.0
next prev parent reply other threads:[~2026-10-01 21:10 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-01 21:10 [PATCH BlueZ v2 00/12] hfp-hf: Enhance HFP Hands-Free profile support Frédéric Danis
2026-10-01 21:10 ` [PATCH BlueZ v2 01/12] audio/hfp-hf: Add HFP HF server and SDP record Frédéric Danis
2026-10-02 0:53 ` hfp-hf: Enhance HFP Hands-Free profile support bluez.test.bot
2026-10-01 21:10 ` [PATCH BlueZ v2 02/12] audio/hfp-hf: Add MediaEndpoint for HFP codecs Frédéric Danis
2026-10-01 21:10 ` [PATCH BlueZ v2 03/12] client/player: Add MediaEndpoints for HFP HF codecs Frédéric Danis
2026-10-01 21:10 ` [PATCH BlueZ v2 04/12] shared/hfp: Add hangup all calls support Frédéric Danis
2026-10-01 21:10 ` [PATCH BlueZ v2 05/12] audio/hfp-hf: " Frédéric Danis
2026-10-01 21:10 ` [PATCH BlueZ v2 06/12] shared/hfp: Add send tones support Frédéric Danis
2026-10-01 21:10 ` [PATCH BlueZ v2 07/12] audio/hfp-hf: Add Send Tones support Frédéric Danis
2026-10-01 21:10 ` [PATCH BlueZ v2 08/12] client/telephony: Add Send Tones menu entry Frédéric Danis
2026-10-01 21:10 ` [PATCH BlueZ v2 09/12] audio/hfp-hf: Add multi calls support Frédéric Danis
2026-10-01 21:10 ` [PATCH BlueZ v2 10/12] client/telephony: " Frédéric Danis
2026-10-01 21:10 ` [PATCH BlueZ v2 11/12] shared/hfp: Add codecs support Frédéric Danis
2026-10-01 21:10 ` Frédéric Danis [this message]
2026-10-05 20:20 ` [PATCH BlueZ v2 00/12] hfp-hf: Enhance HFP Hands-Free profile support patchwork-bot+bluetooth
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261001211036.1760396-13-frederic.danis@collabora.com \
--to=frederic.danis@collabora.com \
--cc=linux-bluetooth@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.