summaryrefslogtreecommitdiffstats
path: root/src/ussd.c
diff options
context:
space:
mode:
authorDenis Kenzior <denis.kenzior@intel.com>2009-05-05 21:13:14 -0700
committerMarcel Holtmann <marcel.holtmann@intel.com>2009-05-05 21:14:19 -0700
commita78b36290bed783fb636735d66257bf138f68123 (patch)
tree623c253319a84a11ec22317acddb05e3ae7f529f /src/ussd.c
parent838583f4988ba132cab57d5c3ddbac80072c5284 (diff)
downloadofono-a78b36290bed783fb636735d66257bf138f68123.tar.bz2
Add implementation of telephony daemon
Diffstat (limited to 'src/ussd.c')
-rw-r--r--src/ussd.c426
1 files changed, 426 insertions, 0 deletions
diff --git a/src/ussd.c b/src/ussd.c
new file mode 100644
index 00000000..2429507a
--- /dev/null
+++ b/src/ussd.c
@@ -0,0 +1,426 @@
+/*
+ *
+ * oFono - Open Source Telephony
+ *
+ * Copyright (C) 2008-2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdio.h>
+
+#include <dbus/dbus.h>
+#include <glib.h>
+#include <gdbus.h>
+
+#include "ofono.h"
+
+#include "dbus-gsm.h"
+#include "modem.h"
+#include "driver.h"
+#include "common.h"
+#include "ussd.h"
+
+#define SUPPLEMENTARY_SERVICES_INTERFACE "org.ofono.SupplementaryServices"
+
+#define USSD_FLAG_PENDING 0x1
+
+enum ussd_state {
+ USSD_STATE_IDLE = 0,
+ USSD_STATE_ACTIVE = 1,
+ USSD_STATE_USER_ACTION = 2
+};
+
+static struct ussd_data *ussd_create()
+{
+ struct ussd_data *r;
+
+ r = g_try_new0(struct ussd_data, 1);
+
+ return r;
+}
+
+static void ussd_destroy(gpointer data)
+{
+ struct ofono_modem *modem = data;
+ struct ussd_data *ussd = modem->ussd;
+
+ g_free(ussd);
+}
+
+struct ss_control_entry {
+ char *service;
+ ss_control_cb_t cb;
+};
+
+static struct ss_control_entry *ss_control_entry_create(const char *service,
+ ss_control_cb_t cb)
+{
+ struct ss_control_entry *r;
+
+ r = g_try_new0(struct ss_control_entry, 1);
+
+ if (!r)
+ return r;
+
+ r->service = g_strdup(service);
+ r->cb = cb;
+
+ return r;
+}
+
+static void ss_control_entry_destroy(struct ss_control_entry *ca)
+{
+ g_free(ca->service);
+ g_free(ca);
+}
+
+static gint ss_control_entry_compare(gconstpointer a, gconstpointer b)
+{
+ const struct ss_control_entry *ca = a;
+ const struct ss_control_entry *cb = b;
+ int ret;
+
+ ret = strcmp(ca->service, cb->service);
+
+ if (ret)
+ return ret;
+
+ if (ca->cb < cb->cb)
+ return -1;
+
+ if (ca->cb > cb->cb)
+ return 1;
+
+ return 0;
+}
+
+static gint ss_control_entry_find_by_service(gconstpointer a, gconstpointer b)
+{
+ const struct ss_control_entry *ca = a;
+ //const char *cb = b;
+
+ return strcmp(ca->service, b);
+}
+
+gboolean ss_control_register(struct ofono_modem *modem, const char *str,
+ ss_control_cb_t cb)
+{
+ //struct ussd_data *ussd = modem->ussd;
+ struct ss_control_entry *entry;
+
+ if (!modem)
+ return FALSE;
+
+ entry = ss_control_entry_create(str, cb);
+
+ if (!entry)
+ return FALSE;
+
+ modem->ss_control_list = g_slist_append(modem->ss_control_list, entry);
+
+ return TRUE;
+}
+
+void ss_control_unregister(struct ofono_modem *modem, const char *str,
+ ss_control_cb_t cb)
+{
+ //struct ussd_data *ussd = modem->ussd;
+ const struct ss_control_entry entry = { (char *)str, cb };
+ GSList *l;
+
+ if (!modem)
+ return;
+
+ l = g_slist_find_custom(modem->ss_control_list, &entry,
+ ss_control_entry_compare);
+
+ if (!l)
+ return;
+
+ ss_control_entry_destroy(l->data);
+ modem->ss_control_list = g_slist_remove(modem->ss_control_list,
+ l->data);
+}
+
+static gboolean recognized_control_string(struct ofono_modem *modem,
+ const char *ss_str,
+ DBusMessage *msg)
+{
+ //struct ussd_data *ussd = modem->ussd;
+ char *str = g_strdup(ss_str);
+ char *sc, *sia, *sib, *sic, *dn;
+ int type;
+ gboolean ret = FALSE;
+
+ ofono_debug("parsing control string");
+
+ if (parse_ss_control_string(str, &type, &sc, &sia, &sib, &sic, &dn)) {
+ GSList *l = modem->ss_control_list;
+
+ ofono_debug("Got parse result: %d, %s, %s, %s, %s, %s",
+ type, sc, sia, sib, sic, dn);
+
+ while ((l = g_slist_find_custom(l, sc,
+ ss_control_entry_find_by_service)) != NULL) {
+ struct ss_control_entry *entry = l->data;
+
+ if (entry->cb(modem, type, sc, sia, sib, sic, dn, msg)) {
+ ret = TRUE;
+ goto out;
+ }
+
+ l = l->next;
+ }
+ }
+
+ /* TODO: Handle all strings that control voice calls */
+
+ /* TODO: Handle Multiple subscriber profile DN*59#SEND and *59#SEND
+ */
+
+ /* Note: SIM PIN/PIN2 change and unblock and IMEI presentation
+ * procedures are not handled by the daemon since they are not followed
+ * by SEND and are not valid USSD requests.
+ */
+
+ /* TODO: Handle Password registration according to 22.030 Section 6.5.4
+ */
+
+out:
+ g_free(str);
+
+ return ret;
+}
+
+void ofono_ussd_notify(struct ofono_modem *modem, int status, const char *str)
+{
+ struct ussd_data *ussd = modem->ussd;
+ DBusConnection *conn = dbus_gsm_connection();
+ const char *ussdstr = "USSD";
+ const char sig[] = { DBUS_TYPE_STRING, 0 };
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter variant;
+
+ if (status == USSD_STATUS_NOT_SUPPORTED) {
+ ussd->state = USSD_STATE_IDLE;
+ reply = dbus_gsm_not_supported(ussd->pending);
+ goto out;
+ }
+
+ if (status == USSD_STATUS_TIMED_OUT) {
+ ussd->state = USSD_STATE_IDLE;
+ reply = dbus_gsm_timed_out(ussd->pending);
+ goto out;
+ }
+
+ /* TODO: Rework this in the Agent framework */
+ if (ussd->state == USSD_STATE_ACTIVE) {
+ if (status == USSD_STATUS_ACTION_REQUIRED) {
+ ofono_error("Unable to handle action required ussd");
+ return;
+ }
+
+ reply = dbus_message_new_method_return(ussd->pending);
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
+ &ussdstr);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, sig,
+ &variant);
+
+ dbus_message_iter_append_basic(&variant, DBUS_TYPE_STRING,
+ &str);
+
+ dbus_message_iter_close_container(&iter, &variant);
+
+ ussd->state = USSD_STATE_IDLE;
+ } else {
+ ofono_error("Received an unsolicited USSD, ignoring for now...");
+ ofono_debug("USSD is: status: %d, %s", status, str);
+
+ return;
+ }
+
+out:
+ g_dbus_send_message(conn, reply);
+
+ dbus_message_unref(ussd->pending);
+ ussd->pending = NULL;
+}
+
+static void ussd_callback(const struct ofono_error *error, void *data)
+{
+ struct ussd_data *ussd = data;
+ DBusConnection *conn = dbus_gsm_connection();
+ DBusMessage *reply;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
+ ofono_debug("ussd request failed with error: %s",
+ telephony_error_to_str(error));
+
+ ussd->flags &= ~USSD_FLAG_PENDING;
+
+ if (!ussd->pending)
+ return;
+
+ if (error->type == OFONO_ERROR_TYPE_NO_ERROR) {
+ ussd->state = USSD_STATE_ACTIVE;
+ return;
+ }
+
+ reply = dbus_gsm_failed(ussd->pending);
+
+ g_dbus_send_message(conn, reply);
+
+ dbus_message_unref(ussd->pending);
+ ussd->pending = NULL;
+}
+
+static DBusMessage *ussd_initiate(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct ofono_modem *modem = data;
+ struct ussd_data *ussd = modem->ussd;
+ const char *str;
+
+ if (ussd->flags & USSD_FLAG_PENDING)
+ return dbus_gsm_busy(msg);
+
+ if (ussd->state == USSD_STATE_ACTIVE)
+ return dbus_gsm_busy(msg);
+
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &str,
+ DBUS_TYPE_INVALID) == FALSE)
+ return dbus_gsm_invalid_args(msg);
+
+ if (strlen(str) == 0)
+ return dbus_gsm_invalid_format(msg);
+
+ ofono_debug("checking if this is a recognized control string");
+ if (recognized_control_string(modem, str, msg))
+ return NULL;
+
+ ofono_debug("No.., checking if this is a USSD string");
+ if (!valid_ussd_string(str))
+ return dbus_gsm_invalid_format(msg);
+
+ ofono_debug("OK, running USSD request");
+
+ if (!ussd->ops->request)
+ return dbus_gsm_not_implemented(msg);
+
+ ussd->flags |= USSD_FLAG_PENDING;
+ ussd->pending = dbus_message_ref(msg);
+
+ ussd->ops->request(modem, str, ussd_callback, ussd);
+
+ return NULL;
+}
+
+static void ussd_cancel_callback(const struct ofono_error *err, void *data)
+{
+ //struct ussd_data *ussd = data;
+}
+
+static DBusMessage *ussd_cancel(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct ofono_modem *modem = data;
+ struct ussd_data *ussd = modem->ussd;
+
+ if (ussd->flags & USSD_FLAG_PENDING)
+ return dbus_gsm_busy(msg);
+
+ if (ussd->state == USSD_STATE_IDLE)
+ return dbus_gsm_not_active(msg);
+
+ if (!ussd->ops->cancel)
+ return dbus_gsm_not_implemented(msg);
+
+ ussd->flags |= USSD_FLAG_PENDING;
+ ussd->pending = dbus_message_ref(msg);
+
+ ussd->ops->cancel(modem, ussd_cancel_callback, ussd);
+
+ return NULL;
+}
+
+static GDBusMethodTable ussd_methods[] = {
+ { "Initiate", "s", "sv", ussd_initiate,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "Cancel", "", "", ussd_cancel,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { }
+};
+
+static GDBusSignalTable ussd_signals[] = {
+ { }
+};
+
+int ofono_ussd_register(struct ofono_modem *modem, struct ofono_ussd_ops *ops)
+{
+ DBusConnection *conn = dbus_gsm_connection();
+
+ if (modem == NULL)
+ return -1;
+
+ if (ops == NULL)
+ return -1;
+
+ modem->ussd = ussd_create();
+
+ if (modem->ussd == NULL)
+ return -1;
+
+ modem->ussd->ops = ops;
+
+ if (!g_dbus_register_interface(conn, modem->path,
+ SUPPLEMENTARY_SERVICES_INTERFACE,
+ ussd_methods, ussd_signals, NULL,
+ modem, ussd_destroy)) {
+ ofono_error("Could not create %s interface",
+ SUPPLEMENTARY_SERVICES_INTERFACE);
+
+ ussd_destroy(modem->ussd);
+
+ return -1;
+ }
+
+ modem_add_interface(modem, SUPPLEMENTARY_SERVICES_INTERFACE);
+
+ return 0;
+}
+
+void ofono_ussd_unregister(struct ofono_modem *modem)
+{
+ DBusConnection *conn = dbus_gsm_connection();
+
+ if (modem->ussd == NULL)
+ return;
+
+ modem_remove_interface(modem, SUPPLEMENTARY_SERVICES_INTERFACE);
+ g_dbus_unregister_interface(conn, modem->path,
+ SUPPLEMENTARY_SERVICES_INTERFACE);
+}