diff options
-rw-r--r-- | src/mm-broadband-modem-mbim.c | 622 |
1 files changed, 617 insertions, 5 deletions
diff --git a/src/mm-broadband-modem-mbim.c b/src/mm-broadband-modem-mbim.c index 0469dbf50..d5e27cbec 100644 --- a/src/mm-broadband-modem-mbim.c +++ b/src/mm-broadband-modem-mbim.c @@ -35,6 +35,7 @@ #include "mm-bearer-list.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" +#include "mm-iface-modem-3gpp-ussd.h" #include "mm-iface-modem-location.h" #include "mm-iface-modem-messaging.h" #include "mm-iface-modem-signal.h" @@ -47,6 +48,7 @@ static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface); +static void iface_modem_3gpp_ussd_init (MMIfaceModem3gppUssd *iface); static void iface_modem_location_init (MMIfaceModemLocation *iface); static void iface_modem_messaging_init (MMIfaceModemMessaging *iface); static void iface_modem_signal_init (MMIfaceModemSignal *iface); @@ -59,6 +61,7 @@ static MMIfaceModemSignal *iface_modem_signal_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemMbim, mm_broadband_modem_mbim, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP_USSD, iface_modem_3gpp_ussd_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_MESSAGING, iface_modem_messaging_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_SIGNAL, iface_modem_signal_init) @@ -76,6 +79,7 @@ typedef enum { PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO = 1 << 4, PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE = 1 << 5, PROCESS_NOTIFICATION_FLAG_PCO = 1 << 6, + PROCESS_NOTIFICATION_FLAG_USSD = 1 << 7, } ProcessNotificationFlag; struct _MMBroadbandModemMbimPrivate { @@ -103,6 +107,9 @@ struct _MMBroadbandModemMbimPrivate { gchar *current_operator_id; gchar *current_operator_name; + /* USSD helpers */ + GTask *pending_ussd_action; + /* Access technology updates */ MbimDataClass available_data_classes; MbimDataClass highest_available_data_class; @@ -2501,6 +2508,25 @@ basic_connect_extensions_notification (MMBroadbandModemMbim *self, } static void +process_ussd_notification (MMBroadbandModemMbim *self, + MbimMessage *notification); + +static void +ussd_notification (MMBroadbandModemMbim *self, + MbimMessage *notification) +{ + if (mbim_message_indicate_status_get_cid (notification) != MBIM_CID_USSD) { + mm_warn ("unexpected USSD notification (cid %u)", mbim_message_indicate_status_get_cid (notification)); + return; + } + + if (!(self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_USSD)) + return; + + process_ussd_notification (self, notification); +} + +static void device_notification_cb (MbimDevice *device, MbimMessage *notification, MMBroadbandModemMbim *self) @@ -2523,6 +2549,9 @@ device_notification_cb (MbimDevice *device, case MBIM_SERVICE_SMS: sms_notification (self, notification); break; + case MBIM_SERVICE_USSD: + ussd_notification (self, notification); + break; default: /* Ignore */ break; @@ -2537,14 +2566,15 @@ common_setup_cleanup_unsolicited_events_sync (MMBroadbandModemMbim *self, if (!device) return; - mm_dbg ("Supported notifications: signal (%s), registration (%s), sms (%s), connect (%s), subscriber (%s), packet (%s), pco (%s)", + mm_dbg ("Supported notifications: signal (%s), registration (%s), sms (%s), connect (%s), subscriber (%s), packet (%s), pco (%s), ussd (%s)", self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_SIGNAL_QUALITY ? "yes" : "no", self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_REGISTRATION_UPDATES ? "yes" : "no", self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_SMS_READ ? "yes" : "no", self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_CONNECT ? "yes" : "no", self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO ? "yes" : "no", self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE ? "yes" : "no", - self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_PCO ? "yes" : "no"); + self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_PCO ? "yes" : "no", + self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_USSD ? "yes" : "no"); if (setup) { /* Don't re-enable it if already there */ @@ -2712,16 +2742,17 @@ common_enable_disable_unsolicited_events (MMBroadbandModemMbim *self, if (!peek_device (self, &device, callback, user_data)) return; - mm_dbg ("Enabled notifications: signal (%s), registration (%s), sms (%s), connect (%s), subscriber (%s), packet (%s), pco (%s)", + mm_dbg ("Enabled notifications: signal (%s), registration (%s), sms (%s), connect (%s), subscriber (%s), packet (%s), pco (%s), ussd (%s)", self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_SIGNAL_QUALITY ? "yes" : "no", self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_REGISTRATION_UPDATES ? "yes" : "no", self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_SMS_READ ? "yes" : "no", self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_CONNECT ? "yes" : "no", self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO ? "yes" : "no", self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE ? "yes" : "no", - self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_PCO ? "yes" : "no"); + self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_PCO ? "yes" : "no", + self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_USSD ? "yes" : "no"); - entries = g_new0 (MbimEventEntry *, 4); + entries = g_new0 (MbimEventEntry *, 5); /* Basic connect service */ if (self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_SIGNAL_QUALITY || @@ -2767,6 +2798,16 @@ common_enable_disable_unsolicited_events (MMBroadbandModemMbim *self, n_entries++; } + /* USSD service */ + if (self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_USSD) { + entries[n_entries] = g_new (MbimEventEntry, 1); + memcpy (&(entries[n_entries]->device_service_id), MBIM_UUID_USSD, sizeof (MbimUuid)); + entries[n_entries]->cids_count = 1; + entries[n_entries]->cids = g_new0 (guint32, 1); + entries[n_entries]->cids[0] = MBIM_CID_USSD; + n_entries++; + } + task = g_task_new (self, NULL, callback, user_data); request = (mbim_message_device_service_subscribe_list_set_new ( @@ -3530,6 +3571,551 @@ modem_signal_load_values (MMIfaceModemSignal *self, } /*****************************************************************************/ +/* Check if USSD supported (3GPP/USSD interface) */ + +static gboolean +modem_3gpp_ussd_check_support_finish (MMIfaceModem3gppUssd *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void +modem_3gpp_ussd_check_support (MMIfaceModem3gppUssd *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + + task = g_task_new (self, NULL, callback, user_data); + g_task_return_boolean (task, MM_BROADBAND_MODEM_MBIM (self)->priv->is_ussd_supported); + g_object_unref (task); +} + +/*****************************************************************************/ +/* USSD encoding/deconding helpers + * + * Note: we don't care about subclassing the ussd_encode/decode methods in the + * interface, as we're going to use this methods just here. + */ + +static GByteArray * +ussd_encode (const gchar *command, + guint32 *scheme, + GError **error) +{ + GByteArray *array; + + if (mm_charset_can_convert_to (command, MM_MODEM_CHARSET_GSM)) { + guint8 *gsm; + guint8 *packed; + guint32 len = 0; + guint32 packed_len = 0; + + *scheme = MM_MODEM_GSM_USSD_SCHEME_7BIT; + gsm = mm_charset_utf8_to_unpacked_gsm (command, &len); + if (!gsm) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, + "Failed to encode USSD command in GSM7 charset"); + return NULL; + } + packed = mm_charset_gsm_pack (gsm, len, 0, &packed_len); + g_free (gsm); + + array = g_byte_array_new_take (packed, packed_len); + } else { + *scheme = MM_MODEM_GSM_USSD_SCHEME_UCS2; + array = g_byte_array_sized_new (strlen (command) * 2); + if (!mm_modem_charset_byte_array_append (array, command, FALSE, MM_MODEM_CHARSET_UCS2)) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, + "Failed to encode USSD command in UCS2 charset"); + g_byte_array_unref (array); + return NULL; + } + } + + if (array->len > 160) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, + "Failed to encode USSD command: encoded data too long (%u > 160)", array->len); + g_byte_array_unref (array); + return NULL; + } + + return array; +} + +static gchar * +ussd_decode (guint32 scheme, + GByteArray *data, + GError **error) +{ + gchar *decoded = NULL; + + if (scheme == MM_MODEM_GSM_USSD_SCHEME_7BIT) { + guint8 *unpacked; + guint32 unpacked_len; + + unpacked = mm_charset_gsm_unpack ((const guint8 *)data->data, (data->len * 8) / 7, 0, &unpacked_len); + decoded = (gchar *) mm_charset_gsm_unpacked_to_utf8 (unpacked, unpacked_len); + if (!decoded) + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, + "Error decoding USSD command in 0x%04x scheme (GSM7 charset)", + scheme); + } else if (scheme == MM_MODEM_GSM_USSD_SCHEME_UCS2) { + decoded = mm_modem_charset_byte_array_to_utf8 (data, MM_MODEM_CHARSET_UCS2); + if (!decoded) + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, + "Error decoding USSD command in 0x%04x scheme (UCS2 charset)", + scheme); + } else + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, + "Failed to decode USSD command in unsupported 0x%04x scheme", scheme); + + return decoded; +} + +/*****************************************************************************/ +/* USSD notifications */ + +static void +process_ussd_message (MMBroadbandModemMbim *self, + MbimUssdResponse ussd_response, + MbimUssdSessionState ussd_session_state, + guint32 scheme, + guint32 data_size, + const guint8 *data) +{ + GTask *task = NULL; + MMModem3gppUssdSessionState ussd_state = MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE; + GByteArray *bytearray = NULL; + gchar *converted = NULL; + GError *error = NULL; + + /* Steal task and balance out received reference */ + if (self->priv->pending_ussd_action) { + task = self->priv->pending_ussd_action; + self->priv->pending_ussd_action = NULL; + } + + if (data_size) + bytearray = g_byte_array_append (g_byte_array_new (), data, data_size); + + switch (ussd_response) { + case MBIM_USSD_RESPONSE_NO_ACTION_REQUIRED: + /* no further action required */ + converted = ussd_decode (scheme, bytearray, &error); + if (!converted) + break; + + /* Response to the user's request? */ + if (task) + break; + + /* Network-initiated USSD-Notify */ + mm_iface_modem_3gpp_ussd_update_network_notification (MM_IFACE_MODEM_3GPP_USSD (self), converted); + g_clear_pointer (&converted, g_free); + break; + + case MBIM_USSD_RESPONSE_ACTION_REQUIRED: + /* further action required */ + ussd_state = MM_MODEM_3GPP_USSD_SESSION_STATE_USER_RESPONSE; + + converted = ussd_decode (scheme, bytearray, &error); + if (!converted) + break; + /* Response to the user's request? */ + if (task) + break; + + /* Network-initiated USSD-Request */ + mm_iface_modem_3gpp_ussd_update_network_request (MM_IFACE_MODEM_3GPP_USSD (self), converted); + g_clear_pointer (&converted, g_free); + break; + + case MBIM_USSD_RESPONSE_TERMINATED_BY_NETWORK: + error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_CANCELLED, "USSD terminated by network"); + break; + + case MBIM_USSD_RESPONSE_OTHER_LOCAL_CLIENT: + error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_CANCELLED, "Another ongoing USSD operation is in progress"); + break; + + case MBIM_USSD_RESPONSE_OPERATION_NOT_SUPPORTED: + error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_CANCELLED, "Operation not supported"); + break; + + case MBIM_USSD_RESPONSE_NETWORK_TIMEOUT: + error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_CANCELLED, "Network timeout"); + break; + + default: + error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_CANCELLED, "Unknown USSD response (%u)", ussd_response); + break; + } + + mm_iface_modem_3gpp_ussd_update_state (MM_IFACE_MODEM_3GPP_USSD (self), ussd_state); + + if (bytearray) + g_byte_array_unref (bytearray); + + /* Complete the pending action */ + if (task) { + if (error) + g_task_return_error (task, error); + else if (converted) + g_task_return_pointer (task, converted, g_free); + else + g_assert_not_reached (); + return; + } + + /* If no pending task, just report the error */ + if (error) { + mm_dbg ("Network reported USSD message: %s", error->message); + g_error_free (error); + } + + g_assert (!converted); +} + +static void +process_ussd_notification (MMBroadbandModemMbim *self, + MbimMessage *notification) +{ + MbimUssdResponse ussd_response; + MbimUssdSessionState ussd_session_state; + guint32 scheme; + guint32 data_size; + const guint8 *data; + + if (mbim_message_ussd_notification_parse (notification, + &ussd_response, + &ussd_session_state, + &scheme, + &data_size, + &data, + NULL)) { + mm_dbg ("Received USSD indication: %s, session state: %s, scheme: 0x%x, data size: %u bytes", + mbim_ussd_response_get_string (ussd_response), + mbim_ussd_session_state_get_string (ussd_session_state), + scheme, + data_size); + process_ussd_message (self, ussd_response, ussd_session_state, scheme, data_size, data); + } +} + +/*****************************************************************************/ +/* Setup/Cleanup unsolicited result codes (3GPP/USSD interface) */ + +static gboolean +modem_3gpp_ussd_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gppUssd *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void +common_setup_flag_ussd_ready (MMBroadbandModemMbim *self, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + + if (!common_setup_cleanup_unsolicited_events_finish (self, res, &error)) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + g_task_return_boolean (task, TRUE); + g_object_unref (task); +} + +static void +common_setup_cleanup_unsolicited_ussd_events (MMBroadbandModemMbim *self, + gboolean setup, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + + task = g_task_new (self, NULL, callback, user_data); + g_task_set_task_data (task, GINT_TO_POINTER (setup), NULL); + + if (setup) + self->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_USSD; + else + self->priv->setup_flags &= ~PROCESS_NOTIFICATION_FLAG_USSD; + common_setup_cleanup_unsolicited_events (self, setup, (GAsyncReadyCallback)common_setup_flag_ussd_ready, task); +} + +static void +modem_3gpp_ussd_cleanup_unsolicited_events (MMIfaceModem3gppUssd *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + common_setup_cleanup_unsolicited_ussd_events (MM_BROADBAND_MODEM_MBIM (self), FALSE, callback, user_data); +} + +static void +modem_3gpp_ussd_setup_unsolicited_events (MMIfaceModem3gppUssd *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + common_setup_cleanup_unsolicited_ussd_events (MM_BROADBAND_MODEM_MBIM (self), TRUE, callback, user_data); +} + +/*****************************************************************************/ +/* Enable/Disable URCs (3GPP/USSD interface) */ + +static gboolean +modem_3gpp_ussd_enable_disable_unsolicited_events_finish (MMIfaceModem3gppUssd *self, + GAsyncResult *res, + GError **error) +{ + return common_enable_disable_unsolicited_events_finish (MM_BROADBAND_MODEM_MBIM (self), res, error); +} + +static void +modem_3gpp_ussd_disable_unsolicited_events (MMIfaceModem3gppUssd *_self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); + + self->priv->enable_flags &= ~PROCESS_NOTIFICATION_FLAG_USSD; + common_enable_disable_unsolicited_events (self, callback, user_data); +} + +static void +modem_3gpp_ussd_enable_unsolicited_events (MMIfaceModem3gppUssd *_self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self); + + self->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_USSD; + common_enable_disable_unsolicited_events (self, callback, user_data); +} + +/*****************************************************************************/ +/* Send command (3GPP/USSD interface) */ + +static gchar * +modem_3gpp_ussd_send_finish (MMIfaceModem3gppUssd *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_pointer (G_TASK (res), error); +} + +static void +ussd_send_ready (MbimDevice *device, + GAsyncResult *res, + MMBroadbandModemMbim *self) +{ + MbimMessage *response; + GError *error = NULL; + MbimUssdResponse ussd_response; + MbimUssdSessionState ussd_session_state; + guint32 scheme; + guint32 data_size; + const guint8 *data; + + /* Note: if there is a cached task, it is ALWAYS completed here */ + + response = mbim_device_command_finish (device, res, &error); + if (response && + mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) && + mbim_message_ussd_response_parse (response, + &ussd_response, + &ussd_session_state, + &scheme, + &data_size, + &data, + &error)) { + mm_dbg ("Received USSD response: %s, session state: %s, scheme: 0x%x, data size: %u bytes", + mbim_ussd_response_get_string (ussd_response), + mbim_ussd_session_state_get_string (ussd_session_state), + scheme, + data_size); + process_ussd_message (self, ussd_response, ussd_session_state, scheme, data_size, data); + } else { + /* Report error in the cached task, if any */ + if (self->priv->pending_ussd_action) { + GTask *task; + + task = self->priv->pending_ussd_action; + self->priv->pending_ussd_action = NULL; + g_task_return_error (task, error); + g_object_unref (task); + } else { + mm_dbg ("Failed to parse USSD response: %s", error->message); + g_clear_error (&error); + } + } + + if (response) + mbim_message_unref (response); + + /* Balance out received reference */ + g_object_unref (self); +} + +static void +modem_3gpp_ussd_send (MMIfaceModem3gppUssd *_self, + const gchar *command, + GAsyncReadyCallback callback, + gpointer user_data) +{ + MMBroadbandModemMbim *self; + MbimDevice *device; + GTask *task; + MbimUssdAction action; + MbimMessage *message; + GByteArray *encoded; + guint32 scheme = 0; + GError *error = NULL; + + self = MM_BROADBAND_MODEM_MBIM (_self); + if (!peek_device (self, &device, callback, user_data)) + return; + + task = g_task_new (self, NULL, callback, user_data); + + /* Fail if there is an ongoing operation already */ + if (self->priv->pending_ussd_action) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS, + "there is already an ongoing USSD operation"); + g_object_unref (task); + return; + } + + switch (mm_iface_modem_3gpp_ussd_get_state (MM_IFACE_MODEM_3GPP_USSD (self))) { + case MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE: + action = MBIM_USSD_ACTION_INITIATE; + break; + case MM_MODEM_3GPP_USSD_SESSION_STATE_USER_RESPONSE: + action = MBIM_USSD_ACTION_CONTINUE; + break; + default: + g_assert_not_reached (); + return; + } + + encoded = ussd_encode (command, &scheme, &error); + if (!encoded) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + message = mbim_message_ussd_set_new (action, scheme, encoded->len, encoded->data, &error); + if (!message) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + /* Cache the action, as it may be completed via URCs */ + self->priv->pending_ussd_action = task; + mm_iface_modem_3gpp_ussd_update_state (_self, MM_MODEM_3GPP_USSD_SESSION_STATE_ACTIVE); + + mbim_device_command (device, + message, + 100, + NULL, + (GAsyncReadyCallback)ussd_send_ready, + g_object_ref (self)); /* Full reference! */ + mbim_message_unref (message); +} + +/*****************************************************************************/ +/* Cancel USSD (3GPP/USSD interface) */ + +static gboolean +modem_3gpp_ussd_cancel_finish (MMIfaceModem3gppUssd *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void +ussd_cancel_ready (MbimDevice *device, + GAsyncResult *res, + GTask *task) +{ + MMBroadbandModemMbim *self; + MbimMessage *response; + GError *error = NULL; + + self = g_task_get_source_object (task); + + response = mbim_device_command_finish (device, res, &error); + if (response) + mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error); + + /* Complete the pending action, regardless of the operation result */ + if (self->priv->pending_ussd_action) { + GTask *task; + + task = self->priv->pending_ussd_action; + self->priv->pending_ussd_action = NULL; + + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_CANCELLED, + "USSD session was cancelled"); + g_object_unref (task); + } + + mm_iface_modem_3gpp_ussd_update_state (MM_IFACE_MODEM_3GPP_USSD (self), + MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE); + + if (error) + g_task_return_error (task, error); + else + g_task_return_boolean (task, TRUE); + g_object_unref (task); + + if (response) + mbim_message_unref (response); +} + +static void +modem_3gpp_ussd_cancel (MMIfaceModem3gppUssd *_self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + MMBroadbandModemMbim *self; + MbimDevice *device; + GTask *task; + MbimMessage *message; + GError *error = NULL; + + self = MM_BROADBAND_MODEM_MBIM (_self); + if (!peek_device (self, &device, callback, user_data)) + return; + + task = g_task_new (self, NULL, callback, user_data); + + message = mbim_message_ussd_set_new (MBIM_USSD_ACTION_CANCEL, 0, 0, NULL, &error); + if (!message) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + mbim_device_command (device, + message, + 10, + NULL, + (GAsyncReadyCallback)ussd_cancel_ready, + task); + mbim_message_unref (message); +} + +/*****************************************************************************/ /* Check support (Messaging interface) */ static gboolean @@ -3935,6 +4521,32 @@ iface_modem_3gpp_init (MMIfaceModem3gpp *iface) } static void +iface_modem_3gpp_ussd_init (MMIfaceModem3gppUssd *iface) +{ + /* Initialization steps */ + iface->check_support = modem_3gpp_ussd_check_support; + iface->check_support_finish = modem_3gpp_ussd_check_support_finish; + + /* Enabling steps */ + iface->setup_unsolicited_events = modem_3gpp_ussd_setup_unsolicited_events; + iface->setup_unsolicited_events_finish = modem_3gpp_ussd_setup_cleanup_unsolicited_events_finish; + iface->enable_unsolicited_events = modem_3gpp_ussd_enable_unsolicited_events; + iface->enable_unsolicited_events_finish = modem_3gpp_ussd_enable_disable_unsolicited_events_finish; + + /* Disabling steps */ + iface->cleanup_unsolicited_events_finish = modem_3gpp_ussd_setup_cleanup_unsolicited_events_finish; + iface->cleanup_unsolicited_events = modem_3gpp_ussd_cleanup_unsolicited_events; + iface->disable_unsolicited_events = modem_3gpp_ussd_disable_unsolicited_events; + iface->disable_unsolicited_events_finish = modem_3gpp_ussd_enable_disable_unsolicited_events_finish; + + /* Additional actions */ + iface->send = modem_3gpp_ussd_send; + iface->send_finish = modem_3gpp_ussd_send_finish; + iface->cancel = modem_3gpp_ussd_cancel; + iface->cancel_finish = modem_3gpp_ussd_cancel_finish; +} + +static void iface_modem_location_init (MMIfaceModemLocation *iface) { #if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED |