summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuido Günther <agx@sigxcpu.org>2010-11-05 14:50:50 -0500
committerDan Williams <dcbw@redhat.com>2010-11-05 14:50:50 -0500
commit3c13d9aec86a310f7ac9ed2fef58500d5a3a720a (patch)
treecc8c71f77fc2fc4562915f1436aca4f15705520a
parent2a98b2ae2d2541e78fad4d931a84094f3b00773a (diff)
gsm: add preliminary USSD support (bgo #590798)
We currently convert to and from the modem's set charset and always pass '15' as the data coding scheme. Passing the correct data coding scheme as third argument to CUSD only upsets the network. This contradicts 3GPP TS 23.038. Other tools like gsm-ussd handle it the same way. Network responses that require further actions are not yet implemented. (some fixes and cleanups by Dan Williams)
-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