* [PATCH BlueZ 1/2] shared/hfp: Add Enhanced Call Status support
@ 2025-11-10 7:38 Frédéric Danis
2025-11-10 7:38 ` [PATCH BlueZ 2/2] unit/test-hfp: Add Enhanced Call Status tests Frédéric Danis
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Frédéric Danis @ 2025-11-10 7:38 UTC (permalink / raw)
To: linux-bluetooth
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
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH BlueZ 2/2] unit/test-hfp: Add Enhanced Call Status tests
2025-11-10 7:38 [PATCH BlueZ 1/2] shared/hfp: Add Enhanced Call Status support Frédéric Danis
@ 2025-11-10 7:38 ` 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
2 siblings, 0 replies; 4+ messages in thread
From: Frédéric Danis @ 2025-11-10 7:38 UTC (permalink / raw)
To: linux-bluetooth
This add the following test:
- /HFP/HF/ECS/BV-01-C
Verify that the HF can request the status of current calls in the AG.
- /HFP/HF/ECS/BV-02-C
Verify that the AG can send the correct call status when an SLC is
initialized.
- /HFP/HF/ICA/BV-07-C-full
Verify that HF can connect to an AG that is receiving an in-coming
call.
---
unit/test-hfp.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 68 insertions(+), 1 deletion(-)
diff --git a/unit/test-hfp.c b/unit/test-hfp.c
index b25b67379..67a88a1cc 100644
--- a/unit/test-hfp.c
+++ b/unit/test-hfp.c
@@ -948,7 +948,8 @@ static void hf_call_added(uint id, enum hfp_call_status status,
} 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")) {
+ g_str_equal(test_name, "/HFP/HF/ICA/BV-04-C-full") ||
+ g_str_equal(test_name, "/HFP/HF/ICA/BV-07-C-full")) {
bool ret;
g_assert_cmpint(id, ==, 1);
@@ -1055,6 +1056,7 @@ static void hf_call_status_updated(uint id, enum hfp_call_status status,
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/ICA/BV-07-C-full") ||
g_str_equal(test_name, "/HFP/HF/TCA/BV-02-C")) {
const char *number;
@@ -1377,6 +1379,38 @@ int main(int argc, char *argv[])
',', '1', '2', '9', ',', ',', '\r', '\n'),
data_end());
+ /* Query list of current calls - HF */
+ define_hf_test("/HFP/HF/ECS/BV-01-C", test_hf_session,
+ NULL, test_hf_session_done,
+ FULL_SLC_SESSION('1', '0', '0', '0'),
+ frg_pdu('\r', '\n', '+', 'C', 'L', 'C', 'C', ':', '1',
+ ',', '1', ',', '0', ',', '0', ',', '0', ',',
+ '\"', '7', '6', '5', '4', '3', '2', '1', '\"',
+ ',', '1', '2', '9', ',', '\"', 'A', 'n', 'A',
+ 'c', 't', 'i', 'v', 'e', 'c', 'a', 'l', 'l',
+ '\"', '\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());
+
+ /* Receiving call status on SLC initialization - HF */
+ define_hf_test("/HFP/HF/ECS/BV-02-C", test_hf_session,
+ NULL, test_hf_session_done,
+ FULL_SLC_SESSION('1', '1', '0', '0'),
+ frg_pdu('\r', '\n', '+', 'C', 'L', 'C', 'C', ':', '1',
+ ',', '1', ',', '0', ',', '0', ',', '0', ',',
+ '\"', '7', '6', '5', '4', '3', '2', '1', '\"',
+ ',', '1', '2', '9', ',', '\"', 'A', 'n', 'A',
+ 'c', 't', 'i', 'v', 'e', 'c', 'a', 'l', 'l',
+ '\"', '\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());
+
/* Disable EC/NR on the AG - HF */
define_hf_test("/HFP/HF/ENO/BV-01-C", test_hf_session,
NULL, test_hf_session_done,
@@ -1622,6 +1656,39 @@ int main(int argc, char *argv[])
'2', ',', '0', '\r', '\n'),
data_end());
+ define_hf_test("/HFP/HF/ICA/BV-07-C-full", test_hf_session,
+ NULL, test_hf_session_done,
+ FULL_SLC_SESSION('1', '0', '1', '0'),
+ frg_pdu('\r', '\n', '+', 'C', 'L', 'C', 'C', ':', '1',
+ ',', '1', ',', '4', ',', '0', ',', '0', ',',
+ '\"', '1', '2', '3', '4', '5', '6', '7', '\"',
+ ',', '1', '2', '9', ',', '\"', 'A', 'i', 'n',
+ 'c', 'o', 'm', 'i', 'n', 'g', 'c', 'a', 'l',
+ 'l', '\"', '\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', '\"',
+ ',', '1', '2', '9', ',', ',', ',', '\"', 'A',
+ 'i', 'n', 'c', 'o', 'm', 'i', 'n', 'g', 'c',
+ 'a', 'l', 'l', '\"', '\r', '\n'),
+ raw_pdu('\r', '\n', 'O', 'K', '\r', '\n'),
+ frg_pdu('\r', '\n', '+', 'C', 'I', 'E', 'V', ':', ' ',
+ '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', ',', '\"', 'A', 'i', 'n',
+ 'c', 'o', 'm', 'i', 'n', 'g', 'c', 'a', 'l',
+ 'l', '\"', '\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());
+
/* Initiate rejection of incoming call - HF */
define_hf_test("/HFP/HF/ICR/BV-01-C", test_hf_session,
NULL, test_hf_session_done,
--
2.43.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* RE: [BlueZ,1/2] shared/hfp: Add Enhanced Call Status support
2025-11-10 7:38 [PATCH BlueZ 1/2] shared/hfp: Add Enhanced Call Status support Frédéric Danis
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.test.bot
2025-11-10 20:20 ` [PATCH BlueZ 1/2] " patchwork-bot+bluetooth
2 siblings, 0 replies; 4+ messages in thread
From: bluez.test.bot @ 2025-11-10 9:02 UTC (permalink / raw)
To: linux-bluetooth, frederic.danis
[-- Attachment #1: Type: text/plain, Size: 1262 bytes --]
This is automated email and please do not reply to this email!
Dear submitter,
Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1021420
---Test result---
Test Summary:
CheckPatch PENDING 0.26 seconds
GitLint PENDING 0.26 seconds
BuildEll PASS 20.19 seconds
BluezMake PASS 2691.38 seconds
MakeCheck PASS 20.25 seconds
MakeDistcheck PASS 187.06 seconds
CheckValgrind PASS 238.82 seconds
CheckSmatch PASS 312.11 seconds
bluezmakeextell PASS 130.20 seconds
IncrementalBuild PENDING 0.26 seconds
ScanBuild PASS 932.78 seconds
Details
##############################
Test: CheckPatch - PENDING
Desc: Run checkpatch.pl script
Output:
##############################
Test: GitLint - PENDING
Desc: Run gitlint
Output:
##############################
Test: IncrementalBuild - PENDING
Desc: Incremental build with the patches in the series
Output:
---
Regards,
Linux Bluetooth
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH BlueZ 1/2] shared/hfp: Add Enhanced Call Status support
2025-11-10 7:38 [PATCH BlueZ 1/2] shared/hfp: Add Enhanced Call Status support Frédéric Danis
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 ` patchwork-bot+bluetooth
2 siblings, 0 replies; 4+ messages in thread
From: patchwork-bot+bluetooth @ 2025-11-10 20:20 UTC (permalink / raw)
To: =?utf-8?b?RnLDqWTDqXJpYyBEYW5pcyA8ZnJlZGVyaWMuZGFuaXNAY29sbGFib3JhLmNvbT4=?=
Cc: linux-bluetooth
Hello:
This series was applied to bluetooth/bluez.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:
On Mon, 10 Nov 2025 08:38:03 +0100 you wrote:
> 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
>
> [...]
Here is the summary with links:
- [BlueZ,1/2] shared/hfp: Add Enhanced Call Status support
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=d33b9b7c9e7e
- [BlueZ,2/2] unit/test-hfp: Add Enhanced Call Status tests
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=173e9963df4b
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2025-11-10 20:20 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-10 7:38 [PATCH BlueZ 1/2] shared/hfp: Add Enhanced Call Status support Frédéric Danis
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
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.