summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--policy/org.freedesktop.modem-manager.policy.in9
-rw-r--r--src/Makefile.am8
-rw-r--r--src/mm-auth-provider.h1
-rw-r--r--src/mm-generic-gsm.c233
-rw-r--r--src/mm-generic-gsm.h3
-rw-r--r--src/mm-modem-gsm-ussd.c321
-rw-r--r--src/mm-modem-gsm-ussd.h65
-rw-r--r--test/ussd.py27
8 files changed, 665 insertions, 2 deletions
diff --git a/policy/org.freedesktop.modem-manager.policy.in b/policy/org.freedesktop.modem-manager.policy.in
index 87199363..ed98f636 100644
--- a/policy/org.freedesktop.modem-manager.policy.in
+++ b/policy/org.freedesktop.modem-manager.policy.in
@@ -54,4 +54,13 @@
</defaults>
</action>
+ <action id="org.freedesktop.ModemManager.USSD">
+ <_description>Query and utilize network information and services</_description>
+ <_message>System policy prevents querying or utilizing network information and services.</_message>
+ <defaults>
+ <allow_inactive>no</allow_inactive>
+ <allow_active>yes</allow_active>
+ </defaults>
+ </action>
+
</policyconfig>
diff --git a/src/Makefile.am b/src/Makefile.am
index 268b29df..cadc98ac 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -104,6 +104,8 @@ modem_manager_SOURCES = \
mm-modem-gsm-network.h \
mm-modem-gsm-sms.c \
mm-modem-gsm-sms.h \
+ mm-modem-gsm-ussd.c \
+ mm-modem-gsm-ussd.h \
mm-modem-simple.c \
mm-modem-simple.h \
mm-options.c \
@@ -138,6 +140,9 @@ mm-modem-gsm-network-glue.h: $(top_srcdir)/introspection/mm-modem-gsm-network.xm
mm-modem-gsm-sms-glue.h: $(top_srcdir)/introspection/mm-modem-gsm-sms.xml
$(AM_V_GEN) dbus-binding-tool --prefix=mm_modem_gsm_sms --mode=glib-server --output=$@ $<
+mm-modem-gsm-ussd-glue.h: $(top_srcdir)/introspection/mm-modem-gsm-ussd.xml
+ $(AM_V_GEN) dbus-binding-tool --prefix=mm_modem_gsm_ussd --mode=glib-server --output=$@ $<
+
BUILT_SOURCES = \
mm-manager-glue.h \
mm-modem-glue.h \
@@ -145,7 +150,8 @@ BUILT_SOURCES = \
mm-modem-cdma-glue.h \
mm-modem-gsm-card-glue.h \
mm-modem-gsm-network-glue.h \
- mm-modem-gsm-sms-glue.h
+ mm-modem-gsm-sms-glue.h \
+ mm-modem-gsm-ussd-glue.h
mm-modem-location-glue.h: $(top_srcdir)/introspection/mm-modem-location.xml
$(AM_V_GEN) dbus-binding-tool --prefix=mm_modem_location --mode=glib-server --output=$@ $<
diff --git a/src/mm-auth-provider.h b/src/mm-auth-provider.h
index 26ff340c..32082f09 100644
--- a/src/mm-auth-provider.h
+++ b/src/mm-auth-provider.h
@@ -26,6 +26,7 @@
#define MM_AUTHORIZATION_DEVICE_CONTROL "org.freedesktop.ModemManager.Device.Control"
#define MM_AUTHORIZATION_CONTACTS "org.freedesktop.ModemManager.Contacts"
#define MM_AUTHORIZATION_SMS "org.freedesktop.ModemManager.SMS"
+#define MM_AUTHORIZATION_USSD "org.freedesktop.ModemManager.USSD"
#define MM_AUTHORIZATION_LOCATION "org.freedesktop.ModemManager.Location"
/******************/
diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c
index 2ffa8cfc..20e26c25 100644
--- a/src/mm-generic-gsm.c
+++ b/src/mm-generic-gsm.c
@@ -25,6 +25,7 @@
#include "mm-modem-gsm-card.h"
#include "mm-modem-gsm-network.h"
#include "mm-modem-gsm-sms.h"
+#include "mm-modem-gsm-ussd.h"
#include "mm-modem-simple.h"
#include "mm-errors.h"
#include "mm-callback-info.h"
@@ -41,6 +42,7 @@ static void modem_init (MMModem *modem_class);
static void modem_gsm_card_init (MMModemGsmCard *gsm_card_class);
static void modem_gsm_network_init (MMModemGsmNetwork *gsm_network_class);
static void modem_gsm_sms_init (MMModemGsmSms *gsm_sms_class);
+static void modem_gsm_ussd_init (MMModemGsmUssd *gsm_ussd_class);
static void modem_simple_init (MMModemSimple *class);
static void modem_location_init (MMModemLocation *class);
@@ -50,6 +52,7 @@ G_DEFINE_TYPE_EXTENDED (MMGenericGsm, mm_generic_gsm, MM_TYPE_MODEM_BASE, 0,
G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_NETWORK, modem_gsm_network_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_SMS, modem_gsm_sms_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_LOCATION, modem_location_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_USSD, modem_gsm_ussd_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_SIMPLE, modem_simple_init))
#define MM_GENERIC_GSM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_GENERIC_GSM, MMGenericGsmPrivate))
@@ -110,6 +113,8 @@ typedef struct {
guint32 loc_caps;
gboolean loc_enabled;
gboolean loc_signal;
+
+ MMModemGsmUssdState ussd_state;
} MMGenericGsmPrivate;
static void get_registration_status (MMAtSerialPort *port, MMCallbackInfo *info);
@@ -3603,7 +3608,7 @@ sms_send_done (MMAtSerialPort *port,
}
static void
-sms_send (MMModemGsmSms *modem,
+sms_send (MMModemGsmSms *modem,
const char *number,
const char *text,
const char *smsc,
@@ -3669,6 +3674,180 @@ mm_generic_gsm_get_best_at_port (MMGenericGsm *self, GError **error)
}
/*****************************************************************************/
+/* MMModemGsmUssd interface */
+
+static void
+ussd_update_state (MMGenericGsm *self, MMModemGsmUssdState new_state)
+{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+
+ if (new_state != priv->ussd_state) {
+ priv->ussd_state = new_state;
+ g_object_notify (G_OBJECT (self), MM_MODEM_GSM_USSD_STATE);
+ }
+}
+
+static void
+ussd_initiate_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ MMGenericGsmPrivate *priv;
+ gint status;
+ gboolean parsed = FALSE;
+ MMModemGsmUssdState ussd_state = MM_MODEM_GSM_USSD_STATE_IDLE;
+ const char *str, *start = NULL, *end = NULL;
+ char *reply = NULL, *converted;
+
+ if (error) {
+ info->error = g_error_copy (error);
+ goto done;
+ }
+
+ priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
+ ussd_state = priv->ussd_state;
+
+ str = mm_strip_tag (response->str, "+CUSD:");
+ if (!str || !isdigit (*str))
+ goto done;
+
+ status = g_ascii_digit_value (*str);
+ switch (status) {
+ case 0: /* no further action required */
+ ussd_state = MM_MODEM_GSM_USSD_STATE_IDLE;
+ break;
+ case 1: /* Not an error but not yet implemented */
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Further action required.");
+ ussd_state = MM_MODEM_GSM_USSD_STATE_USER_RESPONSE;
+ break;
+ case 2:
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "USSD terminated by network.");
+ ussd_state = MM_MODEM_GSM_USSD_STATE_IDLE;
+ break;
+ case 4:
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Operiation not supported.");
+ ussd_state = MM_MODEM_GSM_USSD_STATE_IDLE;
+ break;
+ default:
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Unknown USSD reply %d", status);
+ ussd_state = MM_MODEM_GSM_USSD_STATE_IDLE;
+ break;
+ }
+ if (info->error)
+ goto done;
+
+ /* look for the reply */
+ if ((start = strchr (str, '"')) && (end = strrchr (str, '"')) && (start != end))
+ reply = g_strndup (start + 1, end - start -1);
+
+ if (reply) {
+ /* look for the reply data coding scheme */
+ if (mm_options_debug ()) {
+ if ((start = strrchr (end, ',')) != NULL)
+ g_debug ("USSD data coding scheme %d", atoi (start + 1));
+ }
+
+ converted = mm_modem_charset_hex_to_utf8 (reply, priv->cur_charset);
+ mm_callback_info_set_result (info, converted, g_free);
+ parsed = TRUE;
+ g_free (reply);
+ }
+
+done:
+ if (!parsed && !info->error) {
+ info->error = g_error_new (MM_MODEM_ERROR,
+ MM_MODEM_ERROR_GENERAL,
+ "Could not parse USSD reply '%s'",
+ response->str);
+ }
+ mm_callback_info_schedule (info);
+
+ if (info->modem)
+ ussd_update_state (MM_GENERIC_GSM (info->modem), ussd_state);
+}
+
+static void
+ussd_initiate (MMModemGsmUssd *modem,
+ const char *command,
+ MMModemStringFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+ char *atc_command;
+ char *hex;
+ GByteArray *ussd_command = g_byte_array_new();
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
+ MMAtSerialPort *port;
+
+ info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data);
+
+ port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ /* encode to cur_charset */
+ g_warn_if_fail (mm_modem_charset_byte_array_append (ussd_command, command, FALSE, priv->cur_charset));
+ /* convert to hex representation */
+ hex = utils_bin2hexstr (ussd_command->data, ussd_command->len);
+ g_byte_array_free (ussd_command, TRUE);
+ atc_command = g_strdup_printf ("+CUSD=1,\"%s\",15", hex);
+ g_free (hex);
+
+ mm_at_serial_port_queue_command (port, atc_command, 10, ussd_initiate_done, info);
+ g_free (atc_command);
+
+ ussd_update_state (MM_GENERIC_GSM (modem), MM_MODEM_GSM_USSD_STATE_ACTIVE);
+}
+
+static void
+ussd_cancel_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+
+ if (error)
+ info->error = g_error_copy (error);
+
+ mm_callback_info_schedule (info);
+
+ if (info->modem)
+ ussd_update_state (MM_GENERIC_GSM (info->modem), MM_MODEM_GSM_USSD_STATE_IDLE);
+}
+
+static void
+ussd_cancel (MMModemGsmUssd *modem,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+ MMAtSerialPort *port;
+
+ info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
+
+ port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (modem), &info->error);
+ if (!port) {
+ mm_callback_info_schedule (info);
+ return;
+ }
+
+ mm_at_serial_port_queue_command (port, "+CUSD=2", 10, ussd_cancel_done, info);
+}
+
+/*****************************************************************************/
/* MMModemSimple interface */
typedef enum {
@@ -4309,6 +4488,13 @@ modem_gsm_sms_init (MMModemGsmSms *class)
}
static void
+modem_gsm_ussd_init (MMModemGsmUssd *class)
+{
+ class->initiate = ussd_initiate;
+ class->cancel = ussd_cancel;
+}
+
+static void
modem_simple_init (MMModemSimple *class)
{
class->connect = simple_connect;
@@ -4354,6 +4540,21 @@ mm_generic_gsm_init (MMGenericGsm *self)
NULL,
MM_MODEM_LOCATION_DBUS_INTERFACE);
+ mm_properties_changed_signal_register_property (G_OBJECT (self),
+ MM_MODEM_GSM_USSD_STATE,
+ "State",
+ MM_MODEM_GSM_USSD_DBUS_INTERFACE);
+
+ mm_properties_changed_signal_register_property (G_OBJECT (self),
+ MM_MODEM_GSM_USSD_NETWORK_NOTIFICATION,
+ "NetworkNotification",
+ MM_MODEM_GSM_USSD_DBUS_INTERFACE);
+
+ mm_properties_changed_signal_register_property (G_OBJECT (self),
+ MM_MODEM_GSM_USSD_NETWORK_REQUEST,
+ "NetworkRequest",
+ MM_MODEM_GSM_USSD_DBUS_INTERFACE);
+
g_signal_connect (self, "notify::" MM_MODEM_STATE,
G_CALLBACK (modem_state_changed), NULL);
}
@@ -4377,6 +4578,9 @@ set_property (GObject *object, guint prop_id,
case MM_GENERIC_GSM_PROP_LOC_ENABLED:
case MM_GENERIC_GSM_PROP_LOC_SIGNAL:
case MM_GENERIC_GSM_PROP_LOC_LOCATION:
+ case MM_GENERIC_GSM_PROP_USSD_STATE:
+ case MM_GENERIC_GSM_PROP_USSD_NETWORK_REQUEST:
+ case MM_GENERIC_GSM_PROP_USSD_NETWORK_NOTIFICATION:
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -4384,6 +4588,24 @@ set_property (GObject *object, guint prop_id,
}
}
+static const char *
+ussd_state_to_string (MMModemGsmUssdState ussd_state)
+{
+ switch (ussd_state) {
+ case MM_MODEM_GSM_USSD_STATE_IDLE:
+ return "idle";
+ case MM_MODEM_GSM_USSD_STATE_ACTIVE:
+ return "active";
+ case MM_MODEM_GSM_USSD_STATE_USER_RESPONSE:
+ return "user-response";
+ default:
+ break;
+ }
+
+ g_warning ("Unknown GSM USSD state %d", ussd_state);
+ return "unknown";
+}
+
static void
get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
@@ -4457,6 +4679,15 @@ get_property (GObject *object, guint prop_id,
locations = g_hash_table_new (g_direct_hash, g_direct_equal);
g_value_take_boxed (value, locations);
break;
+ case MM_GENERIC_GSM_PROP_USSD_STATE:
+ g_value_set_string (value, ussd_state_to_string (priv->ussd_state));
+ break;
+ case MM_GENERIC_GSM_PROP_USSD_NETWORK_REQUEST:
+ g_value_set_string (value, "");
+ break;
+ case MM_GENERIC_GSM_PROP_USSD_NETWORK_NOTIFICATION:
+ g_value_set_string (value, "");
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
diff --git a/src/mm-generic-gsm.h b/src/mm-generic-gsm.h
index c6836efa..57126600 100644
--- a/src/mm-generic-gsm.h
+++ b/src/mm-generic-gsm.h
@@ -54,6 +54,9 @@ typedef enum {
MM_GENERIC_GSM_PROP_LOC_SIGNAL,
MM_GENERIC_GSM_PROP_LOC_LOCATION,
MM_GENERIC_GSM_PROP_SIM_IDENTIFIER,
+ MM_GENERIC_GSM_PROP_USSD_STATE,
+ MM_GENERIC_GSM_PROP_USSD_NETWORK_REQUEST,
+ MM_GENERIC_GSM_PROP_USSD_NETWORK_NOTIFICATION,
} MMGenericGsmProp;
typedef enum {
diff --git a/src/mm-modem-gsm-ussd.c b/src/mm-modem-gsm-ussd.c
new file mode 100644
index 00000000..def22134
--- /dev/null
+++ b/src/mm-modem-gsm-ussd.c
@@ -0,0 +1,321 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2010 Guido Guenther <agx@sigxcpu.org>
+ */
+
+#include <string.h>
+#include <dbus/dbus-glib.h>
+
+#include "mm-modem-gsm-ussd.h"
+#include "mm-errors.h"
+#include "mm-callback-info.h"
+#include "mm-marshal.h"
+
+static void impl_modem_gsm_ussd_initiate(MMModemGsmUssd *modem,
+ const char*command,
+ DBusGMethodInvocation *context);
+
+static void impl_modem_gsm_ussd_respond(MMModemGsmUssd *modem,
+ const char *response,
+ DBusGMethodInvocation *context);
+
+static void impl_modem_gsm_ussd_cancel(MMModemGsmUssd *modem,
+ DBusGMethodInvocation *context);
+
+#include "mm-modem-gsm-ussd-glue.h"
+
+
+/*****************************************************************************/
+
+static void
+str_call_done (MMModem *modem, const char *result, GError *error, gpointer user_data)
+{
+ DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data;
+
+ if (error)
+ dbus_g_method_return_error (context, error);
+ else
+ dbus_g_method_return (context, result);
+}
+
+static void
+str_call_not_supported (MMModemGsmUssd *self,
+ MMModemStringFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_string_new (MM_MODEM (self), callback, user_data);
+ info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED,
+ "Operation not supported");
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+async_call_done (MMModem *modem, GError *error, gpointer user_data)
+{
+ DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data;
+
+ if (error)
+ dbus_g_method_return_error (context, error);
+ else
+ dbus_g_method_return (context);
+}
+
+static void
+async_call_not_supported (MMModemGsmUssd *self,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_new (MM_MODEM (self), callback, user_data);
+ info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED,
+ "Operation not supported");
+ mm_callback_info_schedule (info);
+}
+
+/*****************************************************************************/
+
+void
+mm_modem_gsm_ussd_initiate (MMModemGsmUssd *self,
+ const char *command,
+ MMModemStringFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_GSM_USSD (self));
+ g_return_if_fail (command != NULL);
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_GSM_USSD_GET_INTERFACE (self)->initiate)
+ MM_MODEM_GSM_USSD_GET_INTERFACE (self)->initiate(self, command, callback, user_data);
+ else
+ str_call_not_supported (self, callback, user_data);
+
+}
+
+void
+mm_modem_gsm_ussd_cancel (MMModemGsmUssd *self,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_GSM_USSD (self));
+ g_return_if_fail (callback != NULL);
+
+ if (MM_MODEM_GSM_USSD_GET_INTERFACE (self)->cancel)
+ MM_MODEM_GSM_USSD_GET_INTERFACE (self)->cancel(self, callback, user_data);
+ else
+ async_call_not_supported (self, callback, user_data);
+
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ char *command;
+} UssdAuthInfo;
+
+static void
+ussd_auth_info_destroy (gpointer data)
+{
+ UssdAuthInfo *info = data;
+
+ g_free (info->command);
+ g_free (info);
+}
+
+static UssdAuthInfo *
+ussd_auth_info_new (const char* command)
+{
+ UssdAuthInfo *info;
+
+ info = g_malloc0 (sizeof (UssdAuthInfo));
+ info->command = g_strdup (command);
+
+ return info;
+}
+
+/*****************************************************************************/
+
+static void
+impl_modem_gsm_ussd_respond (MMModemGsmUssd *modem,
+ const char *responste,
+ DBusGMethodInvocation *context)
+{
+ async_call_not_supported (modem, async_call_done, context);
+}
+
+/*****************************************************************************/
+
+static void
+ussd_initiate_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemGsmUssd *self = MM_MODEM_GSM_USSD (owner);
+ UssdAuthInfo *info = user_data;
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise initiate the USSD */
+ if (!mm_modem_auth_finish (MM_MODEM (self), req, &error))
+ goto done;
+
+ if (!info->command) {
+ error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Missing USSD command");
+ }
+
+done:
+ if (error) {
+ str_call_done (MM_MODEM (self), NULL, error, context);
+ g_error_free (error);
+ } else
+ mm_modem_gsm_ussd_initiate (self, info->command, str_call_done, context);
+}
+
+static void
+impl_modem_gsm_ussd_initiate (MMModemGsmUssd *modem,
+ const char *command,
+ DBusGMethodInvocation *context)
+{
+ GError *error = NULL;
+ UssdAuthInfo *info;
+
+ info = ussd_auth_info_new (command);
+
+ /* Make sure the caller is authorized to initiate the USSD */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_USSD,
+ context,
+ ussd_initiate_auth_cb,
+ info,
+ ussd_auth_info_destroy,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+}
+
+static void
+ussd_cancel_auth_cb (MMAuthRequest *req,
+ GObject *owner,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ MMModemGsmUssd *self = MM_MODEM_GSM_USSD (owner);
+ GError *error = NULL;
+
+ /* Return any authorization error, otherwise cancel the USSD */
+ mm_modem_auth_finish (MM_MODEM (self), req, &error);
+
+ if (error) {
+ str_call_done (MM_MODEM (self), NULL, error, context);
+ g_error_free (error);
+ } else
+ mm_modem_gsm_ussd_cancel (self, async_call_done, context);
+}
+
+static void
+impl_modem_gsm_ussd_cancel (MMModemGsmUssd *modem,
+ DBusGMethodInvocation *context)
+{
+ GError *error = NULL;
+ UssdAuthInfo *info;
+
+ info = ussd_auth_info_new (NULL);
+
+ /* Make sure the caller is authorized to cancel USSD */
+ if (!mm_modem_auth_request (MM_MODEM (modem),
+ MM_AUTHORIZATION_USSD,
+ context,
+ ussd_cancel_auth_cb,
+ info,
+ ussd_auth_info_destroy,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+mm_modem_gsm_ussd_init (gpointer g_iface)
+{
+ static gboolean initialized = FALSE;
+
+ if (initialized)
+ return;
+
+ /* Properties */
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_string (MM_MODEM_GSM_USSD_STATE,
+ "State",
+ "Current state of USSD session",
+ NULL,
+ G_PARAM_READABLE));
+
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_string (MM_MODEM_GSM_USSD_NETWORK_NOTIFICATION,
+ "NetworkNotification",
+ "Network initiated request, no response required",
+ NULL,
+ G_PARAM_READABLE));
+
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_string (MM_MODEM_GSM_USSD_NETWORK_REQUEST,
+ "NetworkRequest",
+ "Network initiated request, reponse required",
+ NULL,
+ G_PARAM_READABLE));
+
+ initialized = TRUE;
+}
+
+GType
+mm_modem_gsm_ussd_get_type (void)
+{
+ static GType ussd_type = 0;
+
+ if (!G_UNLIKELY (ussd_type)) {
+ const GTypeInfo ussd_info = {
+ sizeof (MMModemGsmUssd), /* class_size */
+ mm_modem_gsm_ussd_init, /* base_init */
+ NULL, /* base_finalize */
+ NULL,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ 0,
+ 0, /* n_preallocs */
+ NULL
+ };
+
+ ussd_type = g_type_register_static (G_TYPE_INTERFACE,
+ "MMModemGsmUssd",
+ &ussd_info, 0);
+
+ g_type_interface_add_prerequisite (ussd_type, G_TYPE_OBJECT);
+ dbus_g_object_type_install_info (ussd_type, &dbus_glib_mm_modem_gsm_ussd_object_info);
+
+ dbus_g_object_type_register_shadow_property (ussd_type,
+ "State",
+ MM_MODEM_GSM_USSD_STATE);
+ }
+
+ return ussd_type;
+}
diff --git a/src/mm-modem-gsm-ussd.h b/src/mm-modem-gsm-ussd.h
new file mode 100644
index 00000000..d0da59be
--- /dev/null
+++ b/src/mm-modem-gsm-ussd.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2010 Guido Guenther <agx@sigxcpu.org>
+ */
+
+#ifndef MM_MODEM_GSM_USSD_H
+#define MM_MODEM_GSM_USSD_H
+
+#include <mm-modem.h>
+
+typedef enum {
+ MM_MODEM_GSM_USSD_STATE_IDLE = 0x00000000,
+ MM_MODEM_GSM_USSD_STATE_ACTIVE = 0x00000001,
+ MM_MODEM_GSM_USSD_STATE_USER_RESPONSE = 0x00000002,
+} MMModemGsmUssdState;
+
+#define MM_MODEM_GSM_USSD_STATE "ussd-state"
+#define MM_MODEM_GSM_USSD_NETWORK_NOTIFICATION "network-notification"
+#define MM_MODEM_GSM_USSD_NETWORK_REQUEST "network-request"
+
+#define MM_TYPE_MODEM_GSM_USSD (mm_modem_gsm_ussd_get_type ())
+#define MM_MODEM_GSM_USSD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_GSM_USSD, MMModemGsmUssd))
+#define MM_IS_MODEM_GSM_USSD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_GSM_USSD))
+#define MM_MODEM_GSM_USSD_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_MODEM_GSM_USSD, MMModemGsmUssd))
+
+#define MM_MODEM_GSM_USSD_DBUS_INTERFACE "org.freedesktop.ModemManager.Modem.Gsm.Ussd"
+
+typedef struct _MMModemGsmUssd MMModemGsmUssd;
+
+struct _MMModemGsmUssd {
+ GTypeInterface g_iface;
+
+ /* Methods */
+ void (*initiate) (MMModemGsmUssd *modem,
+ const char *command,
+ MMModemStringFn callback,
+ gpointer user_data);
+
+ void (*cancel) (MMModemGsmUssd *modem,
+ MMModemFn callback,
+ gpointer user_data);
+};
+
+GType mm_modem_gsm_ussd_get_type (void);
+
+void mm_modem_gsm_ussd_initiate (MMModemGsmUssd *self,
+ const char *command,
+ MMModemStringFn callback,
+ gpointer user_data);
+
+void mm_modem_gsm_ussd_cancel (MMModemGsmUssd *self,
+ MMModemFn callback,
+ gpointer user_data);
+
+#endif /* MM_MODEM_GSM_USSD_H */
diff --git a/test/ussd.py b/test/ussd.py
new file mode 100644
index 00000000..332cc172
--- /dev/null
+++ b/test/ussd.py
@@ -0,0 +1,27 @@
+#!/usr/bin/python
+# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details:
+#
+# Copyright (C) 2010 Guido Guenther <agx@sigxcpu.org>
+#
+# Usage: ./test/ussd.py /org/freedesktop/ModemManager/Modems/0 '*130#'
+
+import sys, dbus
+
+MM_DBUS_SERVICE='org.freedesktop.ModemManager'
+MM_DBUS_INTERFACE_USSD='org.freedesktop.ModemManager.Modem.Gsm.Ussd'
+
+bus = dbus.SystemBus()
+proxy = bus.get_object(MM_DBUS_SERVICE, sys.argv[1])
+modem = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_USSD)
+ret = modem.Initiate (sys.argv[2])
+print ret