From 20572a17894a162eee6191913f38b071e691fb41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Danis?= Date: Fri, 23 Sep 2022 16:26:07 +0200 Subject: [PATCH] bluez5: backend-native: Add AT+CLCC support This allows the HFP HF to retrieve the list of calls with their index, state and remote number (if available). This commit shared the list of calls between modemmanager.c and backend-native.c, and switches call state storage from ModemManager states to CLCC states --- spa/plugins/bluez5/backend-native.c | 22 ++++++++ spa/plugins/bluez5/modemmanager.c | 86 +++++++++++++++++++---------- spa/plugins/bluez5/modemmanager.h | 37 +++++++++++++ 3 files changed, 117 insertions(+), 28 deletions(-) diff --git a/spa/plugins/bluez5/backend-native.c b/spa/plugins/bluez5/backend-native.c index e96e807c6..29e7c0639 100644 --- a/spa/plugins/bluez5/backend-native.c +++ b/spa/plugins/bluez5/backend-native.c @@ -946,6 +946,28 @@ next_indicator: str++; } + rfcomm_send_reply(rfcomm, "OK"); + } else if (spa_strstartswith(buf, "AT+CLCC")) { + struct spa_list *calls; + struct call *call; + unsigned int type; + + if (backend->modemmanager) { + calls = mm_get_calls(backend->modemmanager); + spa_list_for_each(call, calls, link) { + if (!call->number) { + rfcomm_send_reply(rfcomm, "+CLCC: %u,%u,%u,0,%u", call->index, call->direction, call->state, call->multiparty); + } else { + if (spa_strstartswith(call->number, "+")) + type = INTERNATIONAL_NUMBER; + else + type = NATIONAL_NUMBER; + rfcomm_send_reply(rfcomm, "+CLCC: %u,%u,%u,0,%u,\"%s\",%d", call->index, call->direction, call->state, + call->multiparty, call->number, type); + } + } + } + rfcomm_send_reply(rfcomm, "OK"); } else if (sscanf(buf, "AT+CLIP=%u", &value) == 1) { if (value > 1) { diff --git a/spa/plugins/bluez5/modemmanager.c b/spa/plugins/bluez5/modemmanager.c index dc34510dd..f1d9fdfca 100644 --- a/spa/plugins/bluez5/modemmanager.c +++ b/spa/plugins/bluez5/modemmanager.c @@ -31,18 +31,6 @@ #define DBUS_INTERFACE_OBJECTMANAGER "org.freedesktop.DBus.ObjectManager" -struct call { - struct spa_list link; - struct impl *this; - DBusPendingCall *pending; - - char *path; - char *number; - bool call_indicator; - MMCallDirection direction; - MMCallState state; -}; - struct modem { char *path; bool network_has_service; @@ -96,6 +84,28 @@ static bool mm_dbus_connection_send_with_reply(struct impl *this, DBusMessage *m return true; } +static int mm_state_to_clcc(struct impl *this, MMCallState state) +{ + switch (state) { + case MM_CALL_STATE_DIALING: + return CLCC_DIALING; + case MM_CALL_STATE_RINGING_OUT: + return CLCC_ALERTING; + case MM_CALL_STATE_RINGING_IN: + return CLCC_INCOMING; + case MM_CALL_STATE_ACTIVE: + return CLCC_ACTIVE; + case MM_CALL_STATE_HELD: + return CLCC_HELD; + case MM_CALL_STATE_WAITING: + return CLCC_WAITING; + case MM_CALL_STATE_TERMINATED: + case MM_CALL_STATE_UNKNOWN: + default: + return -1; + } +} + static void mm_call_state_changed(struct impl *this) { struct call *call; @@ -103,13 +113,13 @@ static void mm_call_state_changed(struct impl *this) enum call_setup call_setup_indicator = CIND_CALLSETUP_NONE; spa_list_for_each(call, &this->call_list, link) { - call_indicator |= (call->state == MM_CALL_STATE_ACTIVE); + call_indicator |= (call->state == CLCC_ACTIVE); - if (call->state == MM_CALL_STATE_RINGING_IN && call_setup_indicator < CIND_CALLSETUP_INCOMING) + if (call->state == CLCC_INCOMING && call_setup_indicator < CIND_CALLSETUP_INCOMING) call_setup_indicator = CIND_CALLSETUP_INCOMING; - else if (call->state == MM_CALL_STATE_DIALING && call_setup_indicator < CIND_CALLSETUP_DIALING) + else if (call->state == CLCC_DIALING && call_setup_indicator < CIND_CALLSETUP_DIALING) call_setup_indicator = CIND_CALLSETUP_DIALING; - else if (call->state == MM_CALL_STATE_RINGING_OUT && call_setup_indicator < CIND_CALLSETUP_ALERTING) + else if (call->state == CLCC_ALERTING && call_setup_indicator < CIND_CALLSETUP_ALERTING) call_setup_indicator = CIND_CALLSETUP_ALERTING; } @@ -167,7 +177,7 @@ static void mm_get_call_properties_reply(DBusPendingCall *pending, void *user_da if (spa_streq(key, MM_CALL_PROPERTY_DIRECTION)) { dbus_message_iter_get_basic(&value_i, &direction); spa_log_debug(this->log, "Call direction: %u", direction); - call->direction = direction; + call->direction = (direction == MM_CALL_DIRECTION_INCOMING) ? CALL_INCOMING : CALL_OUTGOING; } else if (spa_streq(key, MM_CALL_PROPERTY_NUMBER)) { char *number; @@ -177,10 +187,17 @@ static void mm_get_call_properties_reply(DBusPendingCall *pending, void *user_da free(call->number); call->number = strdup(number); } else if (spa_streq(key, MM_CALL_PROPERTY_STATE)) { + int clcc_state; + dbus_message_iter_get_basic(&value_i, &state); spa_log_debug(this->log, "Call state: %u", state); - call->state = state; - mm_call_state_changed(this); + clcc_state = mm_state_to_clcc(this, state); + if (clcc_state < 0) { + spa_log_debug(this->log, "Unsupported modem state: %s, state=%d", call->path, call->state); + } else { + call->state = clcc_state; + mm_call_state_changed(this); + } } dbus_message_iter_next(&element_i); @@ -620,6 +637,7 @@ static DBusHandlerResult mm_filter_cb(DBusConnection *bus, DBusMessage *m, void MMCallState old, new; MMCallStateReason reason; struct call *call = NULL, *call_tmp; + int clcc_state; if (!dbus_message_iter_init(m, &iface_i) || !spa_streq(dbus_message_get_signature(m), "iiu")) { spa_log_error(this->log, "Invalid signature found in %s", MM_CALL_SIGNAL_STATECHANGED); @@ -648,8 +666,13 @@ static DBusHandlerResult mm_filter_cb(DBusConnection *bus, DBusMessage *m, void goto finish; } - call->state = new; - mm_call_state_changed(this); + clcc_state = mm_state_to_clcc(this, new); + if (clcc_state < 0) { + spa_log_debug(this->log, "Unsupported modem state: %s, state=%d", call->path, call->state); + } else { + call->state = clcc_state; + mm_call_state_changed(this); + } } finish: @@ -756,7 +779,7 @@ bool mm_is_available(void *modemmanager) unsigned int mm_supported_features() { - return SPA_BT_HFP_AG_FEATURE_REJECT_CALL; + return SPA_BT_HFP_AG_FEATURE_REJECT_CALL | SPA_BT_HFP_AG_FEATURE_ENHANCED_CALL_STATUS; } static void mm_get_call_simple_reply(DBusPendingCall *pending, void *data) @@ -802,7 +825,7 @@ bool mm_answer_call(void *modemmanager, void *user_data, enum cmee_error *error) call_object = NULL; spa_list_for_each(call_tmp, &this->call_list, link) { - if (call_tmp->state == MM_CALL_STATE_RINGING_IN) { + if (call_tmp->state == CLCC_INCOMING) { call_object = call_tmp; break; } @@ -850,16 +873,16 @@ bool mm_hangup_call(void *modemmanager, void *user_data, enum cmee_error *error) call_object = NULL; spa_list_for_each(call_tmp, &this->call_list, link) { - if (call_tmp->state == MM_CALL_STATE_ACTIVE) { + if (call_tmp->state == CLCC_ACTIVE) { call_object = call_tmp; break; } } if (!call_object) { spa_list_for_each(call_tmp, &this->call_list, link) { - if (call_tmp->state == MM_CALL_STATE_RINGING_OUT || - call_tmp->state == MM_CALL_STATE_RINGING_IN || - call_tmp->state == MM_CALL_STATE_DIALING) { + if (call_tmp->state == CLCC_DIALING || + call_tmp->state == CLCC_ALERTING || + call_tmp->state == CLCC_INCOMING) { call_object = call_tmp; break; } @@ -906,7 +929,7 @@ const char *mm_get_incoming_call_number(void *modemmanager) call_object = NULL; spa_list_for_each(call_tmp, &this->call_list, link) { - if (call_tmp->state == MM_CALL_STATE_RINGING_IN) { + if (call_tmp->state == CLCC_INCOMING) { call_object = call_tmp; break; } @@ -919,6 +942,13 @@ const char *mm_get_incoming_call_number(void *modemmanager) return call_object->number; } +struct spa_list *mm_get_calls(void *modemmanager) +{ + struct impl *this = modemmanager; + + return &this->call_list; +} + void *mm_register(struct spa_log *log, void *dbus_connection, const struct mm_ops *ops, void *user_data) { struct impl *this; diff --git a/spa/plugins/bluez5/modemmanager.h b/spa/plugins/bluez5/modemmanager.h index 182724a8e..78c37a278 100644 --- a/spa/plugins/bluez5/modemmanager.h +++ b/spa/plugins/bluez5/modemmanager.h @@ -25,6 +25,8 @@ #ifndef SPA_BLUEZ5_MODEMMANAGER_H_ #define SPA_BLUEZ5_MODEMMANAGER_H_ +#include + #include "defs.h" enum cmee_error { @@ -44,6 +46,35 @@ enum call_setup { CIND_CALLSETUP_ALERTING }; +enum call_direction { + CALL_OUTGOING, + CALL_INCOMING +}; + +enum call_state { + CLCC_ACTIVE, + CLCC_HELD, + CLCC_DIALING, + CLCC_ALERTING, + CLCC_INCOMING, + CLCC_WAITING, + CLCC_RESPONSE_AND_HOLD +}; + +struct call { + struct spa_list link; + unsigned int index; + struct impl *this; + DBusPendingCall *pending; + + char *path; + char *number; + bool call_indicator; + enum call_direction direction; + enum call_state state; + bool multiparty; +}; + struct mm_ops { void (*send_cmd_result)(bool success, enum cmee_error error, void *user_data); void (*set_modem_service)(bool available, void *user_data); @@ -62,6 +93,7 @@ unsigned int mm_supported_features(); bool mm_answer_call(void *modemmanager, void *user_data, enum cmee_error *error); bool mm_hangup_call(void *modemmanager, void *user_data, enum cmee_error *error); const char *mm_get_incoming_call_number(void *modemmanager); +struct spa_list *mm_get_calls(void *modemmanager); #else void *mm_register(struct spa_log *log, void *dbus_connection, const struct mm_ops *ops, void *user_data) { @@ -100,6 +132,11 @@ const char *mm_get_incoming_call_number(void *modemmanager) { return NULL; } + +struct spa_list *mm_get_calls(void *modemmanager) +{ + return NULL; +} #endif #endif