All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Frédéric Danis" <frederic.danis@collabora.com>
To: linux-bluetooth@vger.kernel.org
Subject: [PATCH BlueZ 2/3] audio/hfp-hf: Add simple call support
Date: Wed, 17 Dec 2025 17:48:19 +0100	[thread overview]
Message-ID: <20251217164820.277845-2-frederic.danis@collabora.com> (raw)
In-Reply-To: <20251217164820.277845-1-frederic.danis@collabora.com>

This allows to dial, hang-up, answer or reject a call.
---
 profiles/audio/hfp-hf.c | 184 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 184 insertions(+)

diff --git a/profiles/audio/hfp-hf.c b/profiles/audio/hfp-hf.c
index 384480c9e..81d5f4437 100644
--- a/profiles/audio/hfp-hf.c
+++ b/profiles/audio/hfp-hf.c
@@ -36,6 +36,7 @@
 #include "src/btd.h"
 #include "src/dbus-common.h"
 #include "src/device.h"
+#include "src/error.h"
 #include "src/log.h"
 #include "src/plugin.h"
 #include "src/profile.h"
@@ -52,6 +53,7 @@ struct hfp_device {
 	uint16_t		version;
 	GIOChannel		*io;
 	struct hfp_hf		*hf;
+	GSList			*calls;
 };
 
 static void hfp_hf_debug(const char *str, void *user_data)
@@ -59,6 +61,33 @@ static void hfp_hf_debug(const char *str, void *user_data)
 	DBG_IDX(0xffff, "%s", str);
 }
 
+static enum call_state hfp_call_status_to_call_state(
+						enum hfp_call_status status)
+{
+	switch (status) {
+	case CALL_STATUS_ACTIVE: return CALL_STATE_ACTIVE; break;
+	case CALL_STATUS_HELD: return CALL_STATE_HELD; break;
+	case CALL_STATUS_DIALING: return CALL_STATE_DIALING; break;
+	case CALL_STATUS_ALERTING: return CALL_STATE_ALERTING; break;
+	case CALL_STATUS_INCOMING: return CALL_STATE_INCOMING; break;
+	case CALL_STATUS_WAITING: return CALL_STATE_WAITING; break;
+	case CALL_STATUS_RESPONSE_AND_HOLD:
+		return CALL_STATE_RESPONSE_AND_HOLD; break;
+	default:
+		DBG("Unknown hfp_call_status: %u", status);
+	}
+
+	return CALL_STATE_DISCONNECTED;
+}
+
+static int call_id_cmp(gconstpointer a, gconstpointer b)
+{
+	struct call *call = (struct call *) a;
+	uint16_t id = GPOINTER_TO_UINT(b);
+
+	return call->idx == id ? 0 : -1;
+}
+
 static void device_destroy(struct hfp_device *dev)
 {
 	DBG("%s", telephony_get_path(dev->telephony));
@@ -115,6 +144,42 @@ static void hfp_hf_update_operator(const char *operator_name, void *user_data)
 	telephony_set_operator_name(dev->telephony, operator_name);
 }
 
+static void hfp_hf_call_added(uint id, enum hfp_call_status status,
+							void *user_data)
+{
+	struct hfp_device *dev = user_data;
+	struct call *call;
+
+	call = telephony_new_call(dev->telephony, id,
+					hfp_call_status_to_call_state(status),
+					NULL);
+	if (telephony_call_register_interface(call)) {
+		telephony_free_call(call);
+		return;
+	}
+
+	dev->calls = g_slist_append(dev->calls, call);
+}
+
+static void hfp_hf_call_removed(uint id, void *user_data)
+{
+	struct hfp_device *dev = user_data;
+	GSList *match;
+	struct call *call;
+
+	match = g_slist_find_custom(dev->calls, GINT_TO_POINTER(id),
+							call_id_cmp);
+	if (!match) {
+		DBG("Unknown call id: %u", id);
+		return;
+	}
+	call = match->data;
+
+	telephony_call_set_state(call, CALL_STATE_DISCONNECTED);
+	dev->calls = g_slist_remove(dev->calls, call);
+	telephony_call_unregister_interface(call);
+}
+
 static void hfp_hf_session_ready_cb(enum hfp_result res, enum hfp_error cme_err,
 							void *user_data)
 {
@@ -129,10 +194,51 @@ static void hfp_hf_session_ready_cb(enum hfp_result res, enum hfp_error cme_err,
 	telephony_set_state(dev->telephony, CONNECTED);
 }
 
+static void hfp_hf_call_status_updated(uint id, enum hfp_call_status status,
+							void *user_data)
+{
+	struct hfp_device *dev = user_data;
+	GSList *match;
+	struct call *call;
+
+	match = g_slist_find_custom(dev->calls, GINT_TO_POINTER(id),
+							call_id_cmp);
+	if (!match) {
+		DBG("Unknown call id: %u", id);
+		return;
+	}
+	call = match->data;
+
+	telephony_call_set_state(call, hfp_call_status_to_call_state(status));
+}
+
+static void hfp_hf_call_line_id_updated(uint id, const char *number,
+							unsigned int type,
+							void *user_data)
+{
+	struct hfp_device *dev = user_data;
+	GSList *match;
+	struct call *call;
+
+	match = g_slist_find_custom(dev->calls, GINT_TO_POINTER(id),
+							call_id_cmp);
+	if (!match) {
+		DBG("Unknown call id: %u", id);
+		return;
+	}
+	call = match->data;
+
+	telephony_call_set_line_id(call, number);
+}
+
 static struct hfp_hf_callbacks hf_session_callbacks = {
 	.session_ready = hfp_hf_session_ready_cb,
 	.update_indicator = hfp_hf_update_indicator,
 	.update_operator = hfp_hf_update_operator,
+	.call_added = hfp_hf_call_added,
+	.call_removed = hfp_hf_call_removed,
+	.call_status_updated = hfp_hf_call_status_updated,
+	.call_line_id_updated = hfp_hf_call_line_id_updated,
 };
 
 static void hfp_disconnect_watch(void *user_data)
@@ -184,7 +290,85 @@ failed:
 	device_destroy(dev);
 }
 
+static void cmd_complete(enum hfp_result res, enum hfp_error cme_err,
+							void *user_data)
+{
+	DBusMessage *msg = user_data;
+
+	if (res != HFP_RESULT_OK) {
+		DBusMessage *reply;
+		const char *name = dbus_message_get_member(msg);
+
+		error("Command %s error: %d", name, res);
+		reply = g_dbus_create_error(msg, ERROR_INTERFACE
+					".Failed",
+					"Command %s failed: %d", name, res);
+		g_dbus_send_message(btd_get_dbus_connection(), reply);
+		dbus_message_unref(msg);
+		return;
+	}
+
+	g_dbus_send_reply(btd_get_dbus_connection(), msg, DBUS_TYPE_INVALID);
+	dbus_message_unref(msg);
+}
+
+static DBusMessage *dial(DBusConnection *conn, DBusMessage *msg,
+				void *profile_data)
+{
+	struct hfp_device *dev = profile_data;
+	const char *number;
+	bool ret;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
+					DBUS_TYPE_INVALID)) {
+		return btd_error_invalid_args(msg);
+	}
+
+	if (strncmp(number, URI_PREFIX, strlen(URI_PREFIX)) != 0)
+		return btd_error_invalid_args(msg);
+
+	ret = hfp_hf_dial(dev->hf, number + strlen(URI_PREFIX), cmd_complete,
+					dbus_message_ref(msg));
+	if (!ret)
+		return btd_error_failed(msg, "Dial command failed");
+
+	return NULL;
+}
+
+static DBusMessage *call_answer(DBusConnection *conn, DBusMessage *msg,
+				void *call_data)
+{
+	struct call *call = call_data;
+	struct hfp_device *dev = telephony_get_profile_data(call->device);
+	bool ret;
+
+	ret = hfp_hf_call_answer(dev->hf, call->idx, cmd_complete,
+					dbus_message_ref(msg));
+	if (!ret)
+		return btd_error_failed(msg, "Answer call command failed");
+
+	return NULL;
+}
+
+static DBusMessage *call_hangup(DBusConnection *conn, DBusMessage *msg,
+				void *call_data)
+{
+	struct call *call = call_data;
+	struct hfp_device *dev = telephony_get_profile_data(call->device);
+	bool ret;
+
+	ret = hfp_hf_call_hangup(dev->hf, call->idx, cmd_complete,
+					dbus_message_ref(msg));
+	if (!ret)
+		return btd_error_failed(msg, "Hangup call command failed");
+
+	return NULL;
+}
+
 struct telephony_callbacks hfp_callbacks = {
+	.dial = dial,
+	.call_answer = call_answer,
+	.call_hangup = call_hangup,
 };
 
 static int hfp_connect(struct btd_service *service)
-- 
2.43.0


  reply	other threads:[~2025-12-17 16:48 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-12-17 16:48 [PATCH BlueZ 1/3] audio/hfp-hf: Add Operator name support Frédéric Danis
2025-12-17 16:48 ` Frédéric Danis [this message]
2025-12-18 19:07   ` [PATCH BlueZ 2/3] audio/hfp-hf: Add simple call support Luiz Augusto von Dentz
2025-12-17 16:48 ` [PATCH BlueZ 3/3] audio/hfp-hf: Add in-band ringtone flag support Frédéric Danis
2025-12-17 18:24 ` [BlueZ,1/3] audio/hfp-hf: Add Operator name support bluez.test.bot

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=20251217164820.277845-2-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.