diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/mm-broadband-modem-qmi.c | 233 | ||||
-rw-r--r-- | src/mm-broadband-modem.c | 57 | ||||
-rw-r--r-- | src/mm-iface-modem-voice.c | 56 | ||||
-rw-r--r-- | src/mm-iface-modem-voice.h | 15 |
4 files changed, 356 insertions, 5 deletions
diff --git a/src/mm-broadband-modem-qmi.c b/src/mm-broadband-modem-qmi.c index 9d508216..9a4c932f 100644 --- a/src/mm-broadband-modem-qmi.c +++ b/src/mm-broadband-modem-qmi.c @@ -65,6 +65,7 @@ static void shared_qmi_init (MMSharedQmi *iface); static MMIfaceModemLocation *iface_modem_location_parent; static MMIfaceModemMessaging *iface_modem_messaging_parent; +static MMIfaceModemVoice *iface_modem_voice_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemQmi, mm_broadband_modem_qmi, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) @@ -8772,9 +8773,14 @@ modem_voice_check_support (MMIfaceModemVoice *self, mm_obj_dbg (self, "Voice capabilities not supported"); g_task_return_boolean (task, FALSE); } else { - /* In case of QMI, we don't need polling as call list will be dynamically updated by All Call Status indication */ + /* + * In case of QMI, we don't need polling as call list + * will be dynamically updated by All Call Status indication. + * If an AT URC is received, reload the call list through QMI. + */ g_object_set (self, MM_IFACE_MODEM_VOICE_PERIODIC_CALL_LIST_CHECK_DISABLED, TRUE, + MM_IFACE_MODEM_VOICE_INDICATION_CALL_LIST_RELOAD_ENABLED, TRUE, NULL); mm_obj_dbg (self, "Voice capabilities supported"); g_task_return_boolean (task, TRUE); @@ -8927,6 +8933,38 @@ supplementary_service_indication_cb (QmiClientVoice /*****************************************************************************/ /* Setup/cleanup unsolicited events */ +static void +parent_voice_setup_unsolicited_events_ready (MMIfaceModemVoice *self, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + + if (!iface_modem_voice_parent->setup_unsolicited_events_finish (self, res, &error)) { + mm_obj_warn (self, "setting up parent voice unsolicited events failed: %s", error->message); + g_clear_error (&error); + } + + g_task_return_boolean (task, TRUE); + g_object_unref (task); +} + +static void +parent_voice_cleanup_unsolicited_events_ready (MMIfaceModemVoice *self, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + + if (!iface_modem_voice_parent->cleanup_unsolicited_events_finish (self, res, &error)) { + mm_obj_warn (self, "cleaning up parent voice unsolicited events failed: %s", error->message); + g_clear_error (&error); + } + + g_task_return_boolean (task, TRUE); + g_object_unref (task); +} + static gboolean common_voice_setup_cleanup_unsolicited_events_finish (MMIfaceModemVoice *self, GAsyncResult *res, @@ -8952,7 +8990,7 @@ common_voice_setup_cleanup_unsolicited_events (MMBroadbandModemQmi *self, task = g_task_new (self, NULL, callback, user_data); if (setup == self->priv->all_call_status_unsolicited_events_setup) { - mm_obj_dbg (self, "All Call Status unsolicited events already %s; skipping", + mm_obj_dbg (self, "voice unsolicited events already %s; skipping", setup ? "setup" : "cleanup"); g_task_return_boolean (task, TRUE); g_object_unref (task); @@ -8961,16 +8999,37 @@ common_voice_setup_cleanup_unsolicited_events (MMBroadbandModemQmi *self, self->priv->all_call_status_unsolicited_events_setup = setup; if (setup) { + /* Connect QMI indications signals for calls */ g_assert (self->priv->all_call_status_indication_id == 0); self->priv->all_call_status_indication_id = g_signal_connect (client, "all-call-status", G_CALLBACK (all_call_status_indication_cb), self); + + /* Setup AT URCs as fall back for calls */ + if (iface_modem_voice_parent->setup_unsolicited_events) { + iface_modem_voice_parent->setup_unsolicited_events ( + MM_IFACE_MODEM_VOICE (self), + (GAsyncReadyCallback) parent_voice_setup_unsolicited_events_ready, + task); + return; + } + } else { + /* Disconnect QMI indications signals for calls */ g_assert (self->priv->all_call_status_indication_id != 0); g_signal_handler_disconnect (client, self->priv->all_call_status_indication_id); self->priv->all_call_status_indication_id = 0; + + /* Cleanup AT URCs as fall back for calls */ + if (iface_modem_voice_parent->cleanup_unsolicited_events) { + iface_modem_voice_parent->cleanup_unsolicited_events ( + MM_IFACE_MODEM_VOICE (self), + (GAsyncReadyCallback) parent_voice_cleanup_unsolicited_events_ready, + task); + return; + } } g_task_return_boolean (task, TRUE); @@ -9147,6 +9206,172 @@ modem_voice_cleanup_in_call_unsolicited_events (MMIfaceModemVoice *self, } /*****************************************************************************/ +/* Load full list of calls (Voice interface) */ + +static gboolean +modem_voice_load_call_list_finish (MMIfaceModemVoice *self, + GAsyncResult *res, + GList **out_call_info_list, + GError **error) +{ + GList *call_info_list; + GError *inner_error = NULL; + + call_info_list = g_task_propagate_pointer (G_TASK (res), &inner_error); + if (inner_error) { + g_assert (!call_info_list); + g_propagate_error (error, inner_error); + return FALSE; + } + + *out_call_info_list = call_info_list; + return TRUE; +} + +static void +process_get_all_call_info (QmiClientVoice *client, + QmiMessageVoiceGetAllCallInfoOutput *output, + GTask *task) +{ + GArray *qmi_remote_party_number_list = NULL; + GArray *qmi_call_information_list = NULL; + GList *call_info_list = NULL; + guint i; + guint j; + + qmi_message_voice_get_all_call_info_output_get_remote_party_number (output, &qmi_remote_party_number_list, NULL); + qmi_message_voice_get_all_call_info_output_get_call_information (output, &qmi_call_information_list, NULL); + + if (!qmi_remote_party_number_list || !qmi_call_information_list) { + mm_obj_dbg (client, "Ignoring Get All Call Status message. Remote party number or call information not available"); + return; + } + + for (i = 0; i < qmi_call_information_list->len; i++) { + QmiMessageVoiceGetAllCallInfoOutputCallInformationCall qmi_call_information; + + qmi_call_information = g_array_index (qmi_call_information_list, + QmiMessageVoiceGetAllCallInfoOutputCallInformationCall, + i); + for (j = 0; j < qmi_remote_party_number_list->len; j++) { + QmiMessageVoiceGetAllCallInfoOutputRemotePartyNumberCall qmi_remote_party_number; + + qmi_remote_party_number = g_array_index (qmi_remote_party_number_list, + QmiMessageVoiceGetAllCallInfoOutputRemotePartyNumberCall, + j); + if (qmi_call_information.id == qmi_remote_party_number.id) { + MMCallInfo *call_info; + + call_info = g_slice_new0 (MMCallInfo); + call_info->index = qmi_call_information.id; + call_info->number = g_strdup (qmi_remote_party_number.type); + + switch (qmi_call_information.state) { + case QMI_VOICE_CALL_STATE_UNKNOWN: + call_info->state = MM_CALL_STATE_UNKNOWN; + break; + case QMI_VOICE_CALL_STATE_ORIGINATION: + case QMI_VOICE_CALL_STATE_CC_IN_PROGRESS: + call_info->state = MM_CALL_STATE_DIALING; + break; + case QMI_VOICE_CALL_STATE_ALERTING: + call_info->state = MM_CALL_STATE_RINGING_OUT; + break; + case QMI_VOICE_CALL_STATE_SETUP: + case QMI_VOICE_CALL_STATE_INCOMING: + call_info->state = MM_CALL_STATE_RINGING_IN; + break; + case QMI_VOICE_CALL_STATE_CONVERSATION: + call_info->state = MM_CALL_STATE_ACTIVE; + break; + case QMI_VOICE_CALL_STATE_HOLD: + call_info->state = MM_CALL_STATE_HELD; + break; + case QMI_VOICE_CALL_STATE_WAITING: + call_info->state = MM_CALL_STATE_WAITING; + break; + case QMI_VOICE_CALL_STATE_DISCONNECTING: + case QMI_VOICE_CALL_STATE_END: + call_info->state = MM_CALL_STATE_TERMINATED; + break; + default: + call_info->state = MM_CALL_STATE_UNKNOWN; + break; + } + + switch (qmi_call_information.direction) { + case QMI_VOICE_CALL_DIRECTION_UNKNOWN: + call_info->direction = MM_CALL_DIRECTION_UNKNOWN; + break; + case QMI_VOICE_CALL_DIRECTION_MO: + call_info->direction = MM_CALL_DIRECTION_OUTGOING; + break; + case QMI_VOICE_CALL_DIRECTION_MT: + call_info->direction = MM_CALL_DIRECTION_INCOMING; + break; + default: + call_info->direction = MM_CALL_DIRECTION_UNKNOWN; + break; + } + + call_info_list = g_list_append (call_info_list, call_info); + } + } + } + + g_task_return_pointer (task, call_info_list, (GDestroyNotify)mm_3gpp_call_info_list_free); +} + +static void +modem_voice_load_call_list_ready (QmiClientVoice *client, + GAsyncResult *res, + GTask *task) +{ + g_autoptr(QmiMessageVoiceGetAllCallInfoOutput) output = NULL; + GError *error = NULL; + + /* Parse QMI message */ + output = qmi_client_voice_get_all_call_info_finish (client, res, &error); + + if (!output) { + g_prefix_error (&error, "QMI operation failed: "); + g_task_return_error (task, error); + } else if (!qmi_message_voice_get_all_call_info_output_get_result (output, &error)) { + g_prefix_error (&error, "Couldn't process Get All Call Info action: "); + g_task_return_error (task, error); + } else { + process_get_all_call_info (client, output, task); + } + + /* We're done, call list already returned */ + g_object_unref (task); +} + +static void +modem_voice_load_call_list (MMIfaceModemVoice *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + QmiClient *client = NULL; + GTask *task; + + if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), + QMI_SERVICE_VOICE, &client, + callback, user_data)) + return; + + task = g_task_new (self, NULL, callback, user_data); + + /* Update call list through QMI instead of AT+CLCC */ + qmi_client_voice_get_all_call_info (QMI_CLIENT_VOICE (client), + NULL, /* no input data */ + 10, + NULL, + (GAsyncReadyCallback) modem_voice_load_call_list_ready, + task); +} + +/*****************************************************************************/ /* Create CALL (Voice interface) */ static MMBaseCall * @@ -12098,6 +12323,8 @@ iface_modem_3gpp_ussd_init (MMIfaceModem3gppUssd *iface) static void iface_modem_voice_init (MMIfaceModemVoice *iface) { + iface_modem_voice_parent = g_type_interface_peek_parent (iface); + iface->check_support = modem_voice_check_support; iface->check_support_finish = modem_voice_check_support_finish; iface->setup_unsolicited_events = modem_voice_setup_unsolicited_events; @@ -12115,6 +12342,8 @@ iface_modem_voice_init (MMIfaceModemVoice *iface) iface->cleanup_in_call_unsolicited_events_finish = common_voice_setup_cleanup_in_call_unsolicited_events_finish; iface->create_call = modem_voice_create_call; + iface->load_call_list = modem_voice_load_call_list; + iface->load_call_list_finish = modem_voice_load_call_list_finish; iface->hold_and_accept = modem_voice_hold_and_accept; iface->hold_and_accept_finish = modem_voice_hold_and_accept_finish; iface->hangup_and_accept = modem_voice_hangup_and_accept; diff --git a/src/mm-broadband-modem.c b/src/mm-broadband-modem.c index ab166cc9..dad19948 100644 --- a/src/mm-broadband-modem.c +++ b/src/mm-broadband-modem.c @@ -129,6 +129,7 @@ enum { PROP_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED, PROP_MODEM_PERIODIC_ACCESS_TECH_CHECK_DISABLED, PROP_MODEM_PERIODIC_CALL_LIST_CHECK_DISABLED, + PROP_MODEM_INDICATION_CALL_LIST_RELOAD_ENABLED, PROP_MODEM_CARRIER_CONFIG_MAPPING, PROP_MODEM_FIRMWARE_IGNORE_CARRIER, PROP_FLOW_CONTROL, @@ -246,6 +247,7 @@ struct _MMBroadbandModemPrivate { GObject *modem_voice_dbus_skeleton; MMCallList *modem_voice_call_list; gboolean periodic_call_list_check_disabled; + gboolean indication_call_list_reload_enabled; gboolean clcc_supported; /*<--- Modem Time interface --->*/ @@ -7852,6 +7854,17 @@ ccwa_received (MMPortSerialAt *port, MMBroadbandModem *self) { MMCallInfo call_info; + gboolean indication_call_list_reload_enabled = FALSE; + + g_object_get (self, + MM_IFACE_MODEM_VOICE_INDICATION_CALL_LIST_RELOAD_ENABLED, &indication_call_list_reload_enabled, + NULL); + + if (indication_call_list_reload_enabled) { + mm_obj_dbg (self, "call waiting, refreshing call list"); + mm_iface_modem_voice_reload_all_calls (MM_IFACE_MODEM_VOICE (self), NULL, NULL); + return; + } call_info.index = 0; call_info.direction = MM_CALL_DIRECTION_INCOMING; @@ -7870,6 +7883,17 @@ ring_received (MMPortSerialAt *port, MMBroadbandModem *self) { MMCallInfo call_info; + gboolean indication_call_list_reload_enabled = FALSE; + + g_object_get (self, + MM_IFACE_MODEM_VOICE_INDICATION_CALL_LIST_RELOAD_ENABLED, &indication_call_list_reload_enabled, + NULL); + + if (indication_call_list_reload_enabled) { + mm_obj_dbg (self, "ringing, refreshing call list"); + mm_iface_modem_voice_reload_all_calls (MM_IFACE_MODEM_VOICE (self), NULL, NULL); + return; + } call_info.index = 0; call_info.direction = MM_CALL_DIRECTION_INCOMING; @@ -7887,6 +7911,17 @@ cring_received (MMPortSerialAt *port, { MMCallInfo call_info; gchar *str; + gboolean indication_call_list_reload_enabled = FALSE; + + g_object_get (self, + MM_IFACE_MODEM_VOICE_INDICATION_CALL_LIST_RELOAD_ENABLED, &indication_call_list_reload_enabled, + NULL); + + if (indication_call_list_reload_enabled) { + mm_obj_dbg (self, "ringing, refreshing call list"); + mm_iface_modem_voice_reload_all_calls (MM_IFACE_MODEM_VOICE (self), NULL, NULL); + return; + } /* We could have "VOICE" or "DATA". Now consider only "VOICE" */ str = mm_get_string_unquoted_from_match_info (info, 1); @@ -7907,6 +7942,17 @@ clip_received (MMPortSerialAt *port, MMBroadbandModem *self) { MMCallInfo call_info; + gboolean indication_call_list_reload_enabled = FALSE; + + g_object_get (self, + MM_IFACE_MODEM_VOICE_INDICATION_CALL_LIST_RELOAD_ENABLED, &indication_call_list_reload_enabled, + NULL); + + if (indication_call_list_reload_enabled) { + mm_obj_dbg (self, "ringing, refreshing call list"); + mm_iface_modem_voice_reload_all_calls (MM_IFACE_MODEM_VOICE (self), NULL, NULL); + return; + } call_info.index = 0; call_info.direction = MM_CALL_DIRECTION_INCOMING; @@ -12942,6 +12988,9 @@ set_property (GObject *object, case PROP_MODEM_PERIODIC_CALL_LIST_CHECK_DISABLED: self->priv->periodic_call_list_check_disabled = g_value_get_boolean (value); break; + case PROP_MODEM_INDICATION_CALL_LIST_RELOAD_ENABLED: + self->priv->indication_call_list_reload_enabled = g_value_get_boolean (value); + break; case PROP_MODEM_CARRIER_CONFIG_MAPPING: self->priv->carrier_config_mapping = g_value_dup_string (value); break; @@ -13086,6 +13135,9 @@ get_property (GObject *object, case PROP_MODEM_PERIODIC_CALL_LIST_CHECK_DISABLED: g_value_set_boolean (value, self->priv->periodic_call_list_check_disabled); break; + case PROP_MODEM_INDICATION_CALL_LIST_RELOAD_ENABLED: + g_value_set_boolean (value, self->priv->indication_call_list_reload_enabled); + break; case PROP_MODEM_CARRIER_CONFIG_MAPPING: g_value_set_string (value, self->priv->carrier_config_mapping); break; @@ -13131,6 +13183,7 @@ mm_broadband_modem_init (MMBroadbandModem *self) self->priv->periodic_signal_check_disabled = FALSE; self->priv->periodic_access_tech_check_disabled = FALSE; self->priv->periodic_call_list_check_disabled = FALSE; + self->priv->indication_call_list_reload_enabled = FALSE; self->priv->modem_cmer_enable_mode = MM_3GPP_CMER_MODE_NONE; self->priv->modem_cmer_disable_mode = MM_3GPP_CMER_MODE_NONE; self->priv->modem_cmer_ind = MM_3GPP_CMER_IND_NONE; @@ -13721,6 +13774,10 @@ mm_broadband_modem_class_init (MMBroadbandModemClass *klass) MM_IFACE_MODEM_VOICE_PERIODIC_CALL_LIST_CHECK_DISABLED); g_object_class_override_property (object_class, + PROP_MODEM_INDICATION_CALL_LIST_RELOAD_ENABLED, + MM_IFACE_MODEM_VOICE_INDICATION_CALL_LIST_RELOAD_ENABLED); + + g_object_class_override_property (object_class, PROP_MODEM_CARRIER_CONFIG_MAPPING, MM_IFACE_MODEM_CARRIER_CONFIG_MAPPING); diff --git a/src/mm-iface-modem-voice.c b/src/mm-iface-modem-voice.c index 03aaeb0a..3b918259 100644 --- a/src/mm-iface-modem-voice.c +++ b/src/mm-iface-modem-voice.c @@ -2503,6 +2503,54 @@ setup_call_list_polling (MMCallList *call_list, } /*****************************************************************************/ +/* Call list reload */ + +gboolean +mm_iface_modem_voice_reload_all_calls_finish (MMIfaceModemVoice *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void +reload_all_calls_ready (MMIfaceModemVoice *self, + GAsyncResult *res, + GTask *task) +{ + GList *call_info_list = NULL; + GError *error = NULL; + + g_assert (MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->load_call_list_finish); + if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->load_call_list_finish (self, res, &call_info_list, &error)) { + mm_obj_warn (self, "couldn't reload call list: %s", error->message); + + g_task_return_error (task, error); + } else { + /* Always report the list even if NULL (it would mean no ongoing calls) */ + mm_iface_modem_voice_report_all_calls (self, call_info_list); + mm_3gpp_call_info_list_free (call_info_list); + + g_task_return_boolean (task, TRUE); + } + + g_object_unref (task); +} + +void +mm_iface_modem_voice_reload_all_calls (MMIfaceModemVoice *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + + task = g_task_new (self, NULL, callback, user_data); + MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->load_call_list (self, + reload_all_calls_ready, + task); +} + +/*****************************************************************************/ static void update_call_list (MmGdbusModemVoice *skeleton, @@ -3134,6 +3182,14 @@ iface_modem_voice_init (gpointer g_iface) FALSE, G_PARAM_READWRITE)); + g_object_interface_install_property + (g_iface, + g_param_spec_boolean (MM_IFACE_MODEM_VOICE_INDICATION_CALL_LIST_RELOAD_ENABLED, + "Reload call list on call update", + "Ignore call updates and forcefully reload all calls.", + FALSE, + G_PARAM_READWRITE)); + initialized = TRUE; } diff --git a/src/mm-iface-modem-voice.h b/src/mm-iface-modem-voice.h index fe2ae243..f3164d64 100644 --- a/src/mm-iface-modem-voice.h +++ b/src/mm-iface-modem-voice.h @@ -30,9 +30,10 @@ #define MM_IS_IFACE_MODEM_VOICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_IFACE_MODEM_VOICE)) #define MM_IFACE_MODEM_VOICE_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_IFACE_MODEM_VOICE, MMIfaceModemVoice)) -#define MM_IFACE_MODEM_VOICE_DBUS_SKELETON "iface-modem-voice-dbus-skeleton" -#define MM_IFACE_MODEM_VOICE_CALL_LIST "iface-modem-voice-call-list" -#define MM_IFACE_MODEM_VOICE_PERIODIC_CALL_LIST_CHECK_DISABLED "iface-modem-voice-periodic-call-list-check-disabled" +#define MM_IFACE_MODEM_VOICE_DBUS_SKELETON "iface-modem-voice-dbus-skeleton" +#define MM_IFACE_MODEM_VOICE_CALL_LIST "iface-modem-voice-call-list" +#define MM_IFACE_MODEM_VOICE_PERIODIC_CALL_LIST_CHECK_DISABLED "iface-modem-voice-periodic-call-list-check-disabled" +#define MM_IFACE_MODEM_VOICE_INDICATION_CALL_LIST_RELOAD_ENABLED "iface-modem-voice-indication-call-list-reload-enabled" typedef struct _MMIfaceModemVoice MMIfaceModemVoice; @@ -239,6 +240,14 @@ void mm_iface_modem_voice_report_call (MMIfaceModemVoice *self, void mm_iface_modem_voice_report_all_calls (MMIfaceModemVoice *self, GList *call_info_list); +/* Full reload of call list (async) */ +void mm_iface_modem_voice_reload_all_calls (MMIfaceModemVoice *self, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean mm_iface_modem_voice_reload_all_calls_finish (MMIfaceModemVoice *self, + GAsyncResult *res, + GError **error); + /* Report an incoming DTMF received */ void mm_iface_modem_voice_received_dtmf (MMIfaceModemVoice *self, guint index, |