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 1/2] shared/hfp: Add Enhanced Call Status support
Date: Mon, 10 Nov 2025 08:38:03 +0100	[thread overview]
Message-ID: <20251110073804.49651-1-frederic.danis@collabora.com> (raw)

In case remote device supports the Enhanced Call Status the calls are
created, updated or removed based on results of AT+CLCC commands.
This implies to send an AT+CLCC command on reception of +CIEV events
for <call>, <callsetup> or <callheld> indicators instead of managing
calls directly.

This updates the tests using FULL_SLC_SESSION:
- /HFP/HF/ENO/BV-01-C
- /HFP/HF/ICA/BV-01-C
- /HFP/HF/ICA/BV-02-C
- /HFP/HF/ICA/BV-03-C
- /HFP/HF/ICA/BV-04-C-full
---
 src/shared/hfp.c | 193 ++++++++++++++++++++++++++++++++++++++++++++++-
 src/shared/hfp.h |   1 +
 unit/test-hfp.c  |  82 ++++++++++++++++++--
 3 files changed, 264 insertions(+), 12 deletions(-)

diff --git a/src/shared/hfp.c b/src/shared/hfp.c
index c2028ac2b..b5e84bf2b 100644
--- a/src/shared/hfp.c
+++ b/src/shared/hfp.c
@@ -33,6 +33,7 @@
 #define HFP_HF_FEATURES	( \
 	HFP_HF_FEAT_ECNR | \
 	HFP_HF_FEAT_CLIP | \
+	HFP_HF_FEAT_ENHANCED_CALL_STATUS | \
 	HFP_HF_FEAT_ESCO_S4_T2 \
 )
 
@@ -105,7 +106,11 @@ struct hfp_hf {
 	bool roaming;
 	uint8_t battchg;
 
+	bool session;
+	bool clcc_in_progress;
+
 	struct queue *calls;
+	struct queue *updated_calls;
 	char *dialing_number;
 };
 
@@ -140,6 +145,7 @@ struct hf_call {
 	enum hfp_call_status status;
 	char *line_id;
 	uint type;
+	bool mpty;
 
 	struct hfp_hf *hfp;
 };
@@ -1320,6 +1326,7 @@ struct hfp_hf *hfp_hf_new(int fd)
 	hfp->event_handlers = queue_new();
 	hfp->cmd_queue = queue_new();
 	hfp->calls = queue_new();
+	hfp->updated_calls = queue_new();
 	hfp->writer_active = false;
 
 	if (!io_set_read_handler(hfp->io, hf_can_read_data, hfp,
@@ -1393,6 +1400,9 @@ void hfp_hf_unref(struct hfp_hf *hfp)
 	queue_destroy(hfp->calls, remove_call_cb);
 	hfp->calls = NULL;
 
+	queue_destroy(hfp->updated_calls, NULL);
+	hfp->updated_calls = NULL;
+
 	if (hfp->dialing_number) {
 		free(hfp->dialing_number);
 		hfp->dialing_number = NULL;
@@ -1625,7 +1635,8 @@ static uint next_call_index(struct hfp_hf *hfp)
 
 static struct hf_call *call_new(struct hfp_hf *hfp, unsigned int id,
 						enum hfp_call_status status,
-						char *number)
+						char *number, unsigned int type,
+						bool mpty)
 {
 	struct hf_call *call;
 
@@ -1634,6 +1645,8 @@ static struct hf_call *call_new(struct hfp_hf *hfp, unsigned int id,
 	call->status = status;
 	if (number)
 		call->line_id = strdup(number);
+	call->type = type;
+	call->mpty = mpty;
 	call->hfp = hfp;
 	queue_push_tail(hfp->calls, call);
 
@@ -1662,6 +1675,84 @@ static void ciev_service_cb(uint8_t val, void *user_data)
 							hfp->callbacks_data);
 }
 
+static void clcc_resp(enum hfp_result result, enum hfp_error cme_err,
+	void *user_data)
+{
+	struct hfp_hf *hfp = user_data;
+	const struct queue_entry *call_entry, *id_entry;
+	struct hf_call *call;
+	uint id;
+	bool found;
+	struct queue *to_remove;
+
+	DBG(hfp, "");
+
+	hfp->clcc_in_progress = false;
+
+	if (result != HFP_RESULT_OK) {
+		DBG(hfp, "hf: CLCC error: %d", result);
+		goto failed;
+	}
+
+	/* Removed disconnected calls */
+	to_remove = queue_new();
+	for (call_entry = queue_get_entries(hfp->calls); call_entry;
+		call_entry = call_entry->next) {
+		call = call_entry->data;
+		found = false;
+
+		for (id_entry = queue_get_entries(hfp->updated_calls);
+			id_entry; id_entry = id_entry->next) {
+			id = PTR_TO_UINT(id_entry->data);
+			if (call->id == id) {
+				found = true;
+				break;
+			}
+		}
+		DBG(hfp, "hf: call %d -> %s", call->id,
+			found ? "updated" : "disconnected");
+
+		if (!found)
+			queue_push_tail(to_remove, UINT_TO_PTR(call->id));
+	}
+
+	for (id_entry = queue_get_entries(to_remove);
+		id_entry; id_entry = id_entry->next) {
+		id = PTR_TO_UINT(id_entry->data);
+		call = queue_find(hfp->calls, call_id_match, UINT_TO_PTR(id));
+		if (!call) {
+			DBG(hfp, "hf: Unknown call to remove: %u", id);
+			continue;
+		}
+		queue_remove(hfp->calls, call);
+		remove_call_cb(call);
+	}
+
+	queue_remove_all(hfp->updated_calls, NULL, NULL, NULL);
+	queue_destroy(to_remove, NULL);
+	return;
+
+failed:
+	if (!hfp->session && hfp->callbacks->session_ready)
+		hfp->callbacks->session_ready(result, cme_err,
+						hfp->callbacks_data);
+}
+
+static bool send_clcc(struct hfp_hf *hfp)
+{
+	if (!hfp->session || hfp->clcc_in_progress)
+		return true;
+
+	if (!hfp_hf_send_command(hfp, clcc_resp, hfp, "AT+CLCC")) {
+		DBG(hfp, "hf: Could not send AT+CLCC");
+		return false;
+	}
+
+	hfp->clcc_in_progress = true;
+
+	return true;
+}
+
 static bool update_call_to_active(struct hfp_hf *hfp)
 {
 	const struct queue_entry *entry;
@@ -1695,6 +1786,11 @@ static void ciev_call_cb(uint8_t val, void *user_data)
 
 	DBG(hfp, "%u", val);
 
+	if (hfp->features & HFP_AG_FEAT_ENHANCED_CALL_STATUS) {
+		send_clcc(hfp);
+		return;
+	}
+
 	if (val < hfp->ag_ind[HFP_INDICATOR_CALL].min ||
 			val > hfp->ag_ind[HFP_INDICATOR_CALL].max) {
 		DBG(hfp, "hf: Incorrect call state: %u", val);
@@ -1720,7 +1816,7 @@ static void ciev_call_cb(uint8_t val, void *user_data)
 				DBG(hfp, "hf: No new call index available");
 				return;
 			}
-			call_new(hfp, id, CALL_STATUS_ACTIVE, NULL);
+			call_new(hfp, id, CALL_STATUS_ACTIVE, NULL, 0, false);
 		}
 		break;
 	default:
@@ -1779,6 +1875,11 @@ static void ciev_callsetup_cb(uint8_t val, void *user_data)
 
 	DBG(hfp, "%u", val);
 
+	if (hfp->features & HFP_AG_FEAT_ENHANCED_CALL_STATUS) {
+		send_clcc(hfp);
+		return;
+	}
+
 	if (val < hfp->ag_ind[HFP_INDICATOR_CALLSETUP].min ||
 			val > hfp->ag_ind[HFP_INDICATOR_CALLSETUP].max) {
 		DBG(hfp, "hf: Incorrect call setup state: %u", val);
@@ -1802,7 +1903,7 @@ static void ciev_callsetup_cb(uint8_t val, void *user_data)
 			DBG(hfp, "hf: No new call index available");
 			return;
 		}
-		call_new(hfp, id, CALL_STATUS_INCOMING, NULL);
+		call_new(hfp, id, CALL_STATUS_INCOMING, NULL, 0, false);
 		break;
 	case CIND_CALLSETUP_DIALING:
 	case CIND_CALLSETUP_ALERTING:
@@ -1832,7 +1933,7 @@ static void ciev_callsetup_cb(uint8_t val, void *user_data)
 			DBG(hfp, "hf: No new call index available");
 			return;
 		}
-		call_new(hfp, id, status, hfp->dialing_number);
+		call_new(hfp, id, status, hfp->dialing_number, 0, false);
 		if (hfp->dialing_number) {
 			free(hfp->dialing_number);
 			hfp->dialing_number = NULL;
@@ -1847,6 +1948,11 @@ static void ciev_callheld_cb(uint8_t val, void *user_data)
 
 	DBG(hfp, "%u", val);
 
+	if (hfp->features & HFP_AG_FEAT_ENHANCED_CALL_STATUS) {
+		send_clcc(hfp);
+		return;
+	}
+
 	if (val < hfp->ag_ind[HFP_INDICATOR_CALLHELD].min ||
 			val > hfp->ag_ind[HFP_INDICATOR_CALLHELD].max) {
 		DBG(hfp, "hf: Incorrect call held state: %u", val);
@@ -1962,6 +2068,76 @@ static void cops_cb(struct hfp_context *context, void *user_data)
 		hfp->callbacks->update_operator(name, hfp->callbacks_data);
 }
 
+static void clcc_cb(struct hfp_context *context, void *user_data)
+{
+	struct hfp_hf *hfp = user_data;
+	unsigned int id, status, mpty, type;
+	char number[255];
+	struct hf_call *call;
+
+	DBG(hfp, "");
+
+	if (!hfp_context_get_number(context, &id))
+		return;
+
+	/* Skip direction */
+	hfp_context_skip_field(context);
+
+	if (!hfp_context_get_number(context, &status))
+		return;
+
+	/* Skip mode */
+	hfp_context_skip_field(context);
+
+	if (!hfp_context_get_number(context, &mpty))
+		return;
+
+	if (!hfp_context_get_string(context, number, sizeof(number))) {
+		DBG(hfp, "hf: Could not get string");
+		return;
+	}
+
+	if (!hfp_context_get_number(context, &type))
+		return;
+
+	queue_push_tail(hfp->updated_calls, UINT_TO_PTR(id));
+
+	call = queue_find(hfp->calls, call_id_match, UINT_TO_PTR(id));
+	if (!call) {
+		call_new(hfp, id, status, number, type, !!mpty);
+		return;
+	}
+
+	if (call->status != status) {
+		call->status = status;
+		if (hfp->callbacks && hfp->callbacks->call_status_updated)
+			hfp->callbacks->call_status_updated(call->id,
+					call->status, hfp->callbacks_data);
+	}
+
+	if (call->mpty != mpty) {
+		call->mpty = mpty;
+		if (hfp->callbacks && hfp->callbacks->call_mpty_updated)
+			hfp->callbacks->call_mpty_updated(call->id,
+					call->mpty, hfp->callbacks_data);
+	}
+
+	if (call->line_id && strcmp(call->line_id, number) == 0 &&
+		call->type == type)
+		return;
+
+	if (call->line_id)
+		free(call->line_id);
+	call->line_id = strdup(number);
+	call->type = type;
+
+	if (hfp->callbacks && hfp->callbacks->call_line_id_updated)
+		hfp->callbacks->call_line_id_updated(call->id,
+						call->line_id,
+						call->type,
+						hfp->callbacks_data);
+}
+
 static void clip_cb(struct hfp_context *context, void *user_data)
 {
 	struct hfp_hf *hfp = user_data;
@@ -2012,10 +2188,18 @@ static void nrec_resp(enum hfp_result result, enum hfp_error cme_err,
 		goto failed;
 	}
 
+	hfp->session = true;
 	if (hfp->callbacks->session_ready)
 		hfp->callbacks->session_ready(HFP_RESULT_OK, 0,
 						hfp->callbacks_data);
 
+	if (hfp->features & HFP_AG_FEAT_ENHANCED_CALL_STATUS) {
+		if (!send_clcc(hfp)) {
+			result = HFP_RESULT_ERROR;
+			goto failed;
+		}
+	}
+
 	return;
 
 failed:
@@ -2168,6 +2352,7 @@ static void slc_cmer_resp(enum hfp_result result, enum hfp_error cme_err,
 	if (hfp->features & HFP_AG_FEAT_IN_BAND_RING_TONE)
 		hfp_hf_register(hfp, bsir_cb, "+BSIR", hfp, NULL);
 	hfp_hf_register(hfp, ciev_cb, "+CIEV", hfp, NULL);
+	hfp_hf_register(hfp, clcc_cb, "+CLCC", hfp, NULL);
 	hfp_hf_register(hfp, clip_cb, "+CLIP", hfp, NULL);
 	hfp_hf_register(hfp, cops_cb, "+COPS", hfp, NULL);
 
diff --git a/src/shared/hfp.h b/src/shared/hfp.h
index 27315bfa0..6e3d4c213 100644
--- a/src/shared/hfp.h
+++ b/src/shared/hfp.h
@@ -210,6 +210,7 @@ struct hfp_hf_callbacks {
 							void *user_data);
 	void (*call_line_id_updated)(uint id, const char *number, uint type,
 							void *user_data);
+	void (*call_mpty_updated)(uint id, bool mpty, void *user_data);
 };
 
 struct hfp_hf *hfp_hf_new(int fd);
diff --git a/unit/test-hfp.c b/unit/test-hfp.c
index c3f9ac4d0..b25b67379 100644
--- a/unit/test-hfp.c
+++ b/unit/test-hfp.c
@@ -937,11 +937,7 @@ static void hf_call_added(uint id, enum hfp_call_status status,
 
 	if (g_str_equal(test_name, "/HFP/HF/CIT/BV-01-C") ||
 		g_str_equal(test_name, "/HFP/HF/CLI/BV-01-C") ||
-		g_str_equal(test_name, "/HFP/HF/ICA/BV-01-C") ||
-		g_str_equal(test_name, "/HFP/HF/ICA/BV-02-C") ||
-		g_str_equal(test_name, "/HFP/HF/ICA/BV-03-C") ||
 		g_str_equal(test_name, "/HFP/HF/ICA/BV-04-C") ||
-		g_str_equal(test_name, "/HFP/HF/ICA/BV-04-C-full") ||
 		g_str_equal(test_name, "/HFP/HF/ICA/BV-06-C") ||
 		g_str_equal(test_name, "/HFP/HF/ICA/BV-07-C") ||
 		g_str_equal(test_name, "/HFP/HF/ICR/BV-01-C") ||
@@ -949,6 +945,19 @@ static void hf_call_added(uint id, enum hfp_call_status status,
 		g_str_equal(test_name, "/HFP/HF/TCA/BV-02-C")) {
 		g_assert_cmpint(id, ==, 1);
 		g_assert_cmpint(status, ==, CALL_STATUS_INCOMING);
+	} else if (g_str_equal(test_name, "/HFP/HF/ICA/BV-01-C") ||
+		g_str_equal(test_name, "/HFP/HF/ICA/BV-02-C") ||
+		g_str_equal(test_name, "/HFP/HF/ICA/BV-03-C") ||
+		g_str_equal(test_name, "/HFP/HF/ICA/BV-04-C-full")) {
+		bool ret;
+
+		g_assert_cmpint(id, ==, 1);
+		g_assert_cmpint(status, ==, CALL_STATUS_INCOMING);
+		if (tester_use_debug())
+			tester_debug("call %d: answering call", id);
+		ret = hfp_hf_call_answer(context->hfp_hf, id, hf_cmd_complete,
+								context);
+		g_assert(ret);
 	} else if (g_str_equal(test_name, "/HFP/HF/OCL/BV-01-C")) {
 		const char *number;
 
@@ -1002,11 +1011,7 @@ static void hf_call_line_id_updated(uint id, const char *number,
 	g_assert_cmpstr(number, ==, str);
 
 	if (g_str_equal(test_name, "/HFP/HF/ENO/BV-01-C") ||
-		g_str_equal(test_name, "/HFP/HF/ICA/BV-01-C") ||
-		g_str_equal(test_name, "/HFP/HF/ICA/BV-02-C") ||
-		g_str_equal(test_name, "/HFP/HF/ICA/BV-03-C") ||
 		g_str_equal(test_name, "/HFP/HF/ICA/BV-04-C") ||
-		g_str_equal(test_name, "/HFP/HF/ICA/BV-04-C-full") ||
 		g_str_equal(test_name, "/HFP/HF/ICA/BV-07-C") ||
 		g_str_equal(test_name, "/HFP/HF/TCA/BV-01-C") ||
 		g_str_equal(test_name, "/HFP/HF/TCA/BV-02-C")) {
@@ -1376,6 +1381,7 @@ int main(int argc, char *argv[])
 	define_hf_test("/HFP/HF/ENO/BV-01-C", test_hf_session,
 			NULL, test_hf_session_done,
 			FULL_SLC_SESSION('1', '0', '0', '0'),
+			raw_pdu('\r', '\n', 'O', 'K', '\r', '\n'),
 			frg_pdu('\r', '\n', '+', 'C', 'I', 'E', 'V', ':', ' ',
 				'3', ',', '1', '\r', '\n'),
 			frg_pdu('\r', '\n', 'R', 'I', 'N', 'G', '\r', '\n'),
@@ -1387,16 +1393,23 @@ int main(int argc, char *argv[])
 				'2', ',', '1', '\r', '\n'),
 			frg_pdu('\r', '\n', '+', 'C', 'I', 'E', 'V', ':', ' ',
 				'3', ',', '0', '\r', '\n'),
+			raw_pdu('\r', '\n', 'O', 'K', '\r', '\n'),
 			data_end());
 
 	/* Incoming call, in-band ring - HF */
 	define_hf_test("/HFP/HF/ICA/BV-01-C", test_hf_session,
 			NULL, test_hf_session_done,
 			FULL_SLC_SESSION('1', '0', '0', '0'),
+			raw_pdu('\r', '\n', 'O', 'K', '\r', '\n'),
 			frg_pdu('\r', '\n', '+', 'B', 'S', 'I', 'R', ':', ' ',
 				'1', '\r', '\n'),
 			frg_pdu('\r', '\n', '+', 'C', 'I', 'E', 'V', ':', ' ',
 				'3', ',', '1', '\r', '\n'),
+			frg_pdu('\r', '\n', '+', 'C', 'L', 'C', 'C', ':', '1',
+				',', '1', ',', '4', ',', '0', ',', '0', ',',
+				'\"', '1', '2', '3', '4', '5', '6', '7', '\"',
+				',', '1', '2', '9',  '\"', '\r', '\n'),
+			raw_pdu('\r', '\n', 'O', 'K', '\r', '\n'),
 			frg_pdu('\r', '\n', 'R', 'I', 'N', 'G', '\r', '\n'),
 			frg_pdu('\r', '\n', '+', 'C', 'L', 'I', 'P', ':',
 				'\"', '1', '2', '3', '4', '5', '6', '7', '\"',
@@ -1406,18 +1419,30 @@ int main(int argc, char *argv[])
 				'2', ',', '1', '\r', '\n'),
 			frg_pdu('\r', '\n', '+', 'C', 'I', 'E', 'V', ':', ' ',
 				'3', ',', '0', '\r', '\n'),
+			frg_pdu('\r', '\n', '+', 'C', 'L', 'C', 'C', ':', '1',
+				',', '1', ',', '0', ',', '0', ',', '0', ',',
+				'\"', '1', '2', '3', '4', '5', '6', '7', '\"',
+				',', '1', '2', '9',  '\"', '\r', '\n'),
+			raw_pdu('\r', '\n', 'O', 'K', '\r', '\n'),
 			frg_pdu('\r', '\n', '+', 'C', 'I', 'E', 'V', ':', ' ',
 				'2', ',', '0', '\r', '\n'),
+			raw_pdu('\r', '\n', 'O', 'K', '\r', '\n'),
 			data_end());
 
 	/* Answer incoming call and accept in-band setting change - HF */
 	define_hf_test("/HFP/HF/ICA/BV-02-C", test_hf_session,
 			NULL, test_hf_session_done,
 			FULL_SLC_SESSION('1', '0', '0', '0'),
+			raw_pdu('\r', '\n', 'O', 'K', '\r', '\n'),
 			frg_pdu('\r', '\n', '+', 'B', 'S', 'I', 'R', ':', ' ',
 				'0', '\r', '\n'),
 			frg_pdu('\r', '\n', '+', 'C', 'I', 'E', 'V', ':', ' ',
 				'3', ',', '1', '\r', '\n'),
+			frg_pdu('\r', '\n', '+', 'C', 'L', 'C', 'C', ':', '1',
+				',', '1', ',', '4', ',', '0', ',', '0', ',',
+				'\"', '1', '2', '3', '4', '5', '6', '7', '\"',
+				',', '1', '2', '9',  '\"', '\r', '\n'),
+			raw_pdu('\r', '\n', 'O', 'K', '\r', '\n'),
 			frg_pdu('\r', '\n', 'R', 'I', 'N', 'G', '\r', '\n'),
 			frg_pdu('\r', '\n', '+', 'C', 'L', 'I', 'P', ':',
 				'\"', '1', '2', '3', '4', '5', '6', '7', '\"',
@@ -1427,13 +1452,24 @@ int main(int argc, char *argv[])
 				'2', ',', '1', '\r', '\n'),
 			frg_pdu('\r', '\n', '+', 'C', 'I', 'E', 'V', ':', ' ',
 				'3', ',', '0', '\r', '\n'),
+			frg_pdu('\r', '\n', '+', 'C', 'L', 'C', 'C', ':', '1',
+				',', '1', ',', '0', ',', '0', ',', '0', ',',
+				'\"', '1', '2', '3', '4', '5', '6', '7', '\"',
+				',', '1', '2', '9',  '\"', '\r', '\n'),
+			raw_pdu('\r', '\n', 'O', 'K', '\r', '\n'),
 			raw_pdu('\r', '\n', 'O', 'K', '\r', '\n'),
 			frg_pdu('\r', '\n', '+', 'C', 'I', 'E', 'V', ':', ' ',
 				'2', ',', '0', '\r', '\n'),
+			raw_pdu('\r', '\n', 'O', 'K', '\r', '\n'),
 			frg_pdu('\r', '\n', '+', 'B', 'S', 'I', 'R', ':', ' ',
 				'1', '\r', '\n'),
 			frg_pdu('\r', '\n', '+', 'C', 'I', 'E', 'V', ':', ' ',
 				'3', ',', '1', '\r', '\n'),
+			frg_pdu('\r', '\n', '+', 'C', 'L', 'C', 'C', ':', '1',
+				',', '1', ',', '4', ',', '0', ',', '0', ',',
+				'\"', '1', '2', '3', '4', '5', '6', '7', '\"',
+				',', '1', '2', '9',  '\"', '\r', '\n'),
+			raw_pdu('\r', '\n', 'O', 'K', '\r', '\n'),
 			frg_pdu('\r', '\n', 'R', 'I', 'N', 'G', '\r', '\n'),
 			frg_pdu('\r', '\n', '+', 'C', 'L', 'I', 'P', ':',
 				'\"', '1', '2', '3', '4', '5', '6', '7', '\"',
@@ -1443,20 +1479,32 @@ int main(int argc, char *argv[])
 				'2', ',', '1', '\r', '\n'),
 			frg_pdu('\r', '\n', '+', 'C', 'I', 'E', 'V', ':', ' ',
 				'3', ',', '0', '\r', '\n'),
+			frg_pdu('\r', '\n', '+', 'C', 'L', 'C', 'C', ':', '1',
+				',', '1', ',', '0', ',', '0', ',', '0', ',',
+				'\"', '1', '2', '3', '4', '5', '6', '7', '\"',
+				',', '1', '2', '9',  '\"', '\r', '\n'),
+			raw_pdu('\r', '\n', 'O', 'K', '\r', '\n'),
 			frg_pdu('\r', '\n', '+', 'C', 'I', 'E', 'V', ':', ' ',
 				'2', ',', '0', '\r', '\n'),
+			raw_pdu('\r', '\n', 'O', 'K', '\r', '\n'),
 			data_end());
 
 	/* Answer incoming call on HF with ring muting - HF */
 	define_hf_test("/HFP/HF/ICA/BV-03-C", test_hf_session,
 			NULL, test_hf_session_done,
 			FULL_SLC_SESSION('1', '0', '0', '0'),
+			raw_pdu('\r', '\n', 'O', 'K', '\r', '\n'),
 			frg_pdu('\r', '\n', '+', 'B', 'S', 'I', 'R', ':', ' ',
 				'0', '\r', '\n'),
 			frg_pdu('\r', '\n', '+', 'B', 'S', 'I', 'R', ':', ' ',
 				'1', '\r', '\n'),
 			frg_pdu('\r', '\n', '+', 'C', 'I', 'E', 'V', ':', ' ',
 				'3', ',', '1', '\r', '\n'),
+			frg_pdu('\r', '\n', '+', 'C', 'L', 'C', 'C', ':', '1',
+				',', '1', ',', '4', ',', '0', ',', '0', ',',
+				'\"', '1', '2', '3', '4', '5', '6', '7', '\"',
+				',', '1', '2', '9',  '\"', '\r', '\n'),
+			raw_pdu('\r', '\n', 'O', 'K', '\r', '\n'),
 			frg_pdu('\r', '\n', 'R', 'I', 'N', 'G', '\r', '\n'),
 			frg_pdu('\r', '\n', '+', 'C', 'L', 'I', 'P', ':',
 				'\"', '1', '2', '3', '4', '5', '6', '7', '\"',
@@ -1466,8 +1514,14 @@ int main(int argc, char *argv[])
 				'2', ',', '1', '\r', '\n'),
 			frg_pdu('\r', '\n', '+', 'C', 'I', 'E', 'V', ':', ' ',
 				'3', ',', '0', '\r', '\n'),
+			frg_pdu('\r', '\n', '+', 'C', 'L', 'C', 'C', ':', '1',
+				',', '1', ',', '0', ',', '0', ',', '0', ',',
+				'\"', '1', '2', '3', '4', '5', '6', '7', '\"',
+				',', '1', '2', '9',  '\"', '\r', '\n'),
+			raw_pdu('\r', '\n', 'O', 'K', '\r', '\n'),
 			frg_pdu('\r', '\n', '+', 'C', 'I', 'E', 'V', ':', ' ',
 				'2', ',', '0', '\r', '\n'),
+			raw_pdu('\r', '\n', 'O', 'K', '\r', '\n'),
 			data_end());
 
 	/* Answer Incoming call on HF, no in-band ring - HF */
@@ -1496,10 +1550,16 @@ int main(int argc, char *argv[])
 	define_hf_test("/HFP/HF/ICA/BV-04-C-full", test_hf_session,
 			NULL, test_hf_session_done,
 			FULL_SLC_SESSION('1', '0', '0', '0'),
+			raw_pdu('\r', '\n', 'O', 'K', '\r', '\n'),
 			frg_pdu('\r', '\n', '+', 'B', 'S', 'I', 'R', ':', ' ',
 				'0', '\r', '\n'),
 			frg_pdu('\r', '\n', '+', 'C', 'I', 'E', 'V', ':', ' ',
 				'3', ',', '1', '\r', '\n'),
+			frg_pdu('\r', '\n', '+', 'C', 'L', 'C', 'C', ':', '1',
+				',', '1', ',', '4', ',', '0', ',', '0', ',',
+				'\"', '1', '2', '3', '4', '5', '6', '7', '\"',
+				',', '1', '2', '9',  '\"', '\r', '\n'),
+			raw_pdu('\r', '\n', 'O', 'K', '\r', '\n'),
 			frg_pdu('\r', '\n', 'R', 'I', 'N', 'G', '\r', '\n'),
 			frg_pdu('\r', '\n', '+', 'C', 'L', 'I', 'P', ':',
 				'\"', '1', '2', '3', '4', '5', '6', '7', '\"',
@@ -1509,8 +1569,14 @@ int main(int argc, char *argv[])
 				'2', ',', '1', '\r', '\n'),
 			frg_pdu('\r', '\n', '+', 'C', 'I', 'E', 'V', ':', ' ',
 				'3', ',', '0', '\r', '\n'),
+			frg_pdu('\r', '\n', '+', 'C', 'L', 'C', 'C', ':', '1',
+				',', '1', ',', '0', ',', '0', ',', '0', ',',
+				'\"', '1', '2', '3', '4', '5', '6', '7', '\"',
+				',', '1', '2', '9',  '\"', '\r', '\n'),
+			raw_pdu('\r', '\n', 'O', 'K', '\r', '\n'),
 			frg_pdu('\r', '\n', '+', 'C', 'I', 'E', 'V', ':', ' ',
 				'2', ',', '0', '\r', '\n'),
+			raw_pdu('\r', '\n', 'O', 'K', '\r', '\n'),
 			data_end());
 
 	/* Answer Incoming call on AG, no in-band ring - HF */
-- 
2.43.0


             reply	other threads:[~2025-11-10  7:38 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-11-10  7:38 Frédéric Danis [this message]
2025-11-10  7:38 ` [PATCH BlueZ 2/2] unit/test-hfp: Add Enhanced Call Status tests Frédéric Danis
2025-11-10  9:02 ` [BlueZ,1/2] shared/hfp: Add Enhanced Call Status support bluez.test.bot
2025-11-10 20:20 ` [PATCH BlueZ 1/2] " 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=20251110073804.49651-1-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.