broadband-modem-mbim: implement 3GPP USSD support
This commit is contained in:
committed by
Dan Williams
parent
eda46d05cf
commit
532ff24101
@@ -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);
|
||||
@@ -62,6 +64,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)
|
||||
@@ -79,6 +82,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 {
|
||||
@@ -108,6 +112,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;
|
||||
@@ -2520,6 +2527,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,
|
||||
@@ -2543,6 +2569,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;
|
||||
@@ -2557,14 +2586,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 */
|
||||
@@ -2732,16 +2762,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 ||
|
||||
@@ -2787,6 +2818,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 (
|
||||
@@ -3549,6 +3590,551 @@ modem_signal_load_values (MMIfaceModemSignal *self,
|
||||
task);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* 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) */
|
||||
|
||||
@@ -3955,6 +4541,32 @@ iface_modem_3gpp_init (MMIfaceModem3gpp *iface)
|
||||
iface->scan_networks_finish = modem_3gpp_scan_networks_finish;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user