bluez5: backend-native: add CLCC support

Start call id at 1 as for the index calls in HFP, and move this id
to spa_bt_telephony_[ag|call] so they can be used by CLCC to retrieve
the related call.

if enhanced call status is supported, send AT+CLCC on +CIEV events to
get the calls information.
This commit is contained in:
Frédéric Danis 2024-10-15 12:21:27 +02:00 committed by Wim Taymans
parent a8363ff92b
commit f5e08677a2
3 changed files with 84 additions and 10 deletions

View file

@ -183,6 +183,7 @@ struct rfcomm {
unsigned int extended_error_reporting:1;
unsigned int clip_notify:1;
unsigned int hfp_hf_3way:1;
unsigned int hfp_hf_clcc:1;
unsigned int chld_supported:1;
enum hfp_hf_state hf_state;
enum hsp_hs_state hs_state;
@ -1774,6 +1775,7 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
(rfcomm->msbc_supported_by_hfp || rfcomm->lc3_supported_by_hfp))
rfcomm->codec_negotiation_supported = true;
rfcomm->hfp_hf_3way = (features & SPA_BT_HFP_AG_FEATURE_3WAY) != 0;
rfcomm->hfp_hf_clcc = (features & SPA_BT_HFP_AG_FEATURE_ENHANCED_CALL_STATUS) != 0;
} else if (sscanf(token, "+BCS:%u", &selected_codec) == 1 && rfcomm->codec_negotiation_supported) {
if (selected_codec != HFP_AUDIO_CODEC_CVSD && selected_codec != HFP_AUDIO_CODEC_MSBC &&
selected_codec != HFP_AUDIO_CODEC_LC3_SWB) {
@ -1920,6 +1922,9 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
}
}
}
if (rfcomm->hfp_hf_clcc)
rfcomm_send_cmd(rfcomm, "AT+CLCC");
} else if (spa_streq(rfcomm->hf_indicators[indicator], "call")) {
if (value == 0) {
struct spa_bt_telephony_call *call, *tcall;
@ -1941,6 +1946,9 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
}
}
}
if (rfcomm->hfp_hf_clcc)
rfcomm_send_cmd(rfcomm, "AT+CLCC");
} else if (spa_streq(rfcomm->hf_indicators[indicator], "callheld")) {
if (value == 0) { /* Reject waiting call or no held calls */
struct spa_bt_telephony_call *call, *tcall;
@ -1976,6 +1984,9 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
telephony_call_notify_updated_props(call);
}
}
if (rfcomm->hfp_hf_clcc)
rfcomm_send_cmd(rfcomm, "AT+CLCC");
}
}
} else if (sscanf(token, "+CLIP: \"%16[^\"]\",%u", number, &type) == 2) {
@ -1995,6 +2006,66 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
call = hfp_hf_add_call(rfcomm, rfcomm->telephony_ag, CALL_STATE_WAITING, number);
if (call == NULL)
spa_log_warn(backend->log, "failed to create waiting call");
} else if (spa_strstartswith(token, "+CLCC: ")) {
struct spa_bt_telephony_call *call;
int idx;
unsigned int status, mpty;
bool found = false;
token[strcspn(token, "\r")] = 0;
token[strcspn(token, "\n")] = 0;
token += strlen("+CLCC: ");
token[strcspn(token, ",")] = 0;
idx = atoi(token);
token += strcspn(token, "\0") + 1;
// Skip direction
token[strcspn(token, ",")] = 0;
token += strcspn(token, "\0") + 1;
token[strcspn(token, ",")] = 0;
status = atoi(token);
token += strcspn(token, "\0") + 1;
// Skip mode
token[strcspn(token, ",")] = 0;
token += strcspn(token, "\0") + 1;
token[strcspn(token, ",")] = 0;
mpty = atoi(token);
token += strcspn(token, "\0") + 1;
if (strlen(token) > 0) {
if (sscanf(token, "\"%16[^\"]\",%u", number, &type) != 2) {
spa_log_warn(backend->log, "Failed to parse number: %s", token);
number[0] = '\0';
}
}
spa_list_for_each(call, &rfcomm->telephony_ag->call_list, link) {
if (call->id == idx) {
bool changed = false;
found = true;
if (call->state != status) {
call->state =status;
changed = true;
}
if (call->multiparty != mpty) {
call->multiparty = mpty;
changed = true;
}
if (strlen(number) && !spa_streq(number, call->line_identification)) {
if (call->line_identification)
free(call->line_identification);
call->line_identification = strdup(number);
changed = true;
}
if (changed)
telephony_call_notify_updated_props(call);
}
}
if (!found) {
spa_log_warn(backend->log, "unknown call index: %u", idx);
}
} else if (spa_strstartswith(token, "OK")) {
switch(rfcomm->hf_state) {
case hfp_hf_brsf:
@ -3036,7 +3107,8 @@ static DBusHandlerResult profile_new_connection(DBusConnection *conn, DBusMessag
rfcomm->transport->path, handler);
} else if (profile == SPA_BT_PROFILE_HFP_AG) {
/* Start SLC connection */
unsigned int hf_features = SPA_BT_HFP_HF_FEATURE_CLIP | SPA_BT_HFP_HF_FEATURE_3WAY;
unsigned int hf_features = SPA_BT_HFP_HF_FEATURE_CLIP | SPA_BT_HFP_HF_FEATURE_3WAY |
SPA_BT_HFP_HF_FEATURE_ENHANCED_CALL_STATUS;
bool has_msbc = device_supports_codec(backend, rfcomm->device, HFP_AUDIO_CODEC_MSBC);
bool has_lc3 = device_supports_codec(backend, rfcomm->device, HFP_AUDIO_CODEC_LC3_SWB);

View file

@ -185,7 +185,6 @@ struct impl {
struct agimpl {
struct spa_bt_telephony_ag this;
struct spa_list link;
int id;
char *path;
struct spa_hook_list listener_list;
void *user_data;
@ -196,7 +195,6 @@ struct agimpl {
struct callimpl {
struct spa_bt_telephony_call this;
int id;
char *path;
struct spa_hook_list listener_list;
void *user_data;
@ -266,11 +264,11 @@ static const char *telephony_error_to_description (enum spa_bt_telephony_error e
#define find_free_object_id(list, obj_type, link) \
({ \
int id = 0; \
int id = 1; \
obj_type *object; \
spa_list_for_each(object, list, link) { \
if (object->id <= id) \
id = object->id + 1; \
if (object->this.id <= id) \
id = object->this.id + 1; \
} \
id; \
})
@ -759,7 +757,7 @@ telephony_ag_new(struct spa_bt_telephony *telephony, size_t user_data_size)
return NULL;
agimpl->this.telephony = telephony;
agimpl->id = find_free_object_id(&impl->ag_list, struct agimpl, link);
agimpl->this.id = find_free_object_id(&impl->ag_list, struct agimpl, link);
spa_list_init(&agimpl->this.call_list);
spa_hook_list_init(&agimpl->listener_list);
@ -803,7 +801,7 @@ int telephony_ag_register(struct spa_bt_telephony_ag *ag)
.message_function = ag_handler,
};
path = spa_aprintf (PW_TELEPHONY_OBJECT_PATH "/ag%d", agimpl->id);
path = spa_aprintf (PW_TELEPHONY_OBJECT_PATH "/ag%d", agimpl->this.id);
/* register object */
if (!dbus_connection_register_object_path(impl->conn, path, &vtable, agimpl)) {
@ -933,7 +931,7 @@ telephony_call_new(struct spa_bt_telephony_ag *ag, size_t user_data_size)
return NULL;
callimpl->this.ag = ag;
callimpl->id = find_free_object_id(&ag->call_list, struct callimpl, this.link);
callimpl->this.id = find_free_object_id(&ag->call_list, struct callimpl, this.link);
spa_hook_list_init(&callimpl->listener_list);
spa_list_append(&ag->call_list, &callimpl->this.link);
@ -1262,7 +1260,7 @@ int telephony_call_register(struct spa_bt_telephony_call *call)
.message_function = call_handler,
};
path = spa_aprintf ("%s/call%d", agimpl->path, callimpl->id);
path = spa_aprintf ("%s/call%d", agimpl->path, callimpl->this.id);
/* register object */
if (!dbus_connection_register_object_path(impl->conn, path, &vtable, callimpl)) {

View file

@ -32,12 +32,16 @@ struct spa_bt_telephony {
struct spa_bt_telephony_ag {
struct spa_bt_telephony *telephony;
struct spa_list call_list;
int id;
};
struct spa_bt_telephony_call {
struct spa_bt_telephony_ag *ag;
struct spa_list link; /* link in ag->call_list */
int id;
/* D-Bus properties */
char *line_identification;
char *incoming_line;