summaryrefslogtreecommitdiffstats
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
parent838583f4988ba132cab57d5c3ddbac80072c5284 (diff)
downloadofono-a78b36290bed783fb636735d66257bf138f68123.tar.bz2
Add implementation of telephony daemon
-rw-r--r--src/Makefile.am6
-rw-r--r--src/call-forwarding.c1431
-rw-r--r--src/call-meter.c764
-rw-r--r--src/call-settings.c900
-rw-r--r--src/call-waiting.c648
-rw-r--r--src/common.c576
-rw-r--r--src/common.h164
-rw-r--r--src/dbus-gsm.c261
-rw-r--r--src/dbus-gsm.h131
-rw-r--r--src/driver.h332
-rw-r--r--src/main.c13
-rw-r--r--src/manager.c210
-rw-r--r--src/modem.c432
-rw-r--r--src/modem.h49
-rw-r--r--src/network.c1062
-rw-r--r--src/ofono.h3
-rw-r--r--src/ussd.c426
-rw-r--r--src/ussd.h39
-rw-r--r--src/util.c693
-rw-r--r--src/util.h58
-rw-r--r--src/voicecall.c1684
21 files changed, 9881 insertions, 1 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 9394aa98..0c0c8a5b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -7,7 +7,11 @@ endif
sbin_PROGRAMS = ofonod
-ofonod_SOURCES = main.c ofono.h log.c plugin.c
+ofonod_SOURCES = main.c ofono.h log.c plugin.c \
+ driver.h modem.h modem.c common.h common.c \
+ manager.c dbus-gsm.h dbus-gsm.c util.h util.c \
+ network.c voicecall.c ussd.h ussd.c \
+ call-settings.c call-waiting.c call-forwarding.c call-meter.c
ofonod_LDADD = @GDBUS_LIBS@ @GLIB_LIBS@ @GTHREAD_LIBS@ -ldl
diff --git a/src/call-forwarding.c b/src/call-forwarding.c
new file mode 100644
index 00000000..b7b100e1
--- /dev/null
+++ b/src/call-forwarding.c
@@ -0,0 +1,1431 @@
+/*
+ *
+ * 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
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.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 CALL_FORWARDING_INTERFACE "org.ofono.CallForwarding"
+
+#define CALL_FORWARDING_FLAG_CACHED 0x1
+
+/* According to 27.007 Spec */
+#define DEFAULT_NO_REPLY_TIMEOUT 20
+
+struct call_forwarding_data {
+ struct ofono_call_forwarding_ops *ops;
+ GSList *cf_conditions[4];
+ int flags;
+ DBusMessage *pending;
+ struct cf_ss_request *ss_req;
+};
+
+static void cf_busy_callback(const struct ofono_error *error, int total,
+ const struct ofono_cf_condition *list, void *data);
+static void cf_unconditional_callback(const struct ofono_error *error, int total,
+ const struct ofono_cf_condition *list, void *data);
+
+static void cf_register_ss_controls(struct ofono_modem *modem);
+static void cf_unregister_ss_controls(struct ofono_modem *modem);
+
+struct set_cf_request {
+ struct ofono_modem *modem;
+ int type;
+ int cls;
+ char number[OFONO_MAX_PHONE_NUMBER_LENGTH + 1];
+ int number_type;
+ int timeout;
+};
+
+struct cf_ss_request {
+ int ss_type;
+ int cf_type;
+ int cls;
+ GSList *cf_list[4];
+};
+
+static gint cf_condition_compare(gconstpointer a, gconstpointer b)
+{
+ const struct ofono_cf_condition *ca = a;
+ const struct ofono_cf_condition *cb = b;
+
+ if (ca->cls < cb->cls)
+ return -1;
+
+ if (ca->cls > cb->cls)
+ return 1;
+
+ return 0;
+}
+
+static gint cf_condition_find_with_cls(gconstpointer a, gconstpointer b)
+{
+ const struct ofono_cf_condition *c = a;
+ int cls = GPOINTER_TO_INT(b);
+
+ if (c->cls < cls)
+ return -1;
+
+ if (c->cls > cls)
+ return 1;
+
+ return 0;
+}
+
+static int cf_find_timeout(GSList *cf_list, int cls)
+{
+ GSList *l;
+ struct ofono_cf_condition *c;
+
+ l = g_slist_find_custom(cf_list, GINT_TO_POINTER(cls),
+ cf_condition_find_with_cls);
+
+ if (!l)
+ return DEFAULT_NO_REPLY_TIMEOUT;
+
+ c = l->data;
+
+ return c->time;
+}
+
+static void cf_cond_list_print(GSList *list)
+{
+ GSList *l;
+ struct ofono_cf_condition *cond;
+
+ for (l = list; l; l = l->next) {
+ cond = l->data;
+
+ ofono_debug("CF Condition status: %d, class: %d, number: %s,"
+ " number_type: %d, time: %d",
+ cond->status, cond->cls, cond->phone_number,
+ cond->number_type, cond->time);
+ }
+}
+
+static GSList *cf_cond_list_create(int total,
+ const struct ofono_cf_condition *list)
+{
+ GSList *l = NULL;
+ int i;
+ int j;
+ struct ofono_cf_condition *cond;
+
+ /* Specification is not really clear how the results are reported,
+ * so assume both multiple list items & compound values of class
+ * are possible
+ */
+ for (i = 0; i < total; i++) {
+ for (j = 1; j <= BEARER_CLASS_PAD; j = j << 1) {
+ if (!(list[i].cls & j))
+ continue;
+
+ if (list[i].status == 0)
+ continue;
+
+ cond = g_try_new0(struct ofono_cf_condition, 1);
+ if (!cond)
+ continue;
+
+ memcpy(cond, &list[i], sizeof(struct ofono_cf_condition));
+ cond->cls = j;
+
+ l = g_slist_insert_sorted(l, cond,
+ cf_condition_compare);
+ }
+ }
+
+ return l;
+}
+
+static inline void cf_list_clear(GSList *cf_list)
+{
+ GSList *l;
+
+ for (l = cf_list; l; l = l->next)
+ g_free(l->data);
+
+ g_slist_free(cf_list);
+}
+
+static inline void cf_clear_all(struct call_forwarding_data *cf)
+{
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ cf_list_clear(cf->cf_conditions[i]);
+ cf->cf_conditions[i] = NULL;
+ }
+}
+
+static struct call_forwarding_data *call_forwarding_create()
+{
+ struct call_forwarding_data *r;
+
+ r = g_try_new0(struct call_forwarding_data, 1);
+
+ if (!r)
+ return r;
+
+ return r;
+}
+
+static void call_forwarding_destroy(gpointer data)
+{
+ struct ofono_modem *modem = data;
+ struct call_forwarding_data *cf = modem->call_forwarding;
+
+ cf_clear_all(cf);
+
+ cf_unregister_ss_controls(modem);
+
+ g_free(cf);
+}
+
+static const char *cf_type_lut[] = {
+ "Unconditional",
+ "Busy",
+ "NoReply",
+ "NotReachable",
+ "All",
+ "AllConditional"
+};
+
+static void set_new_cond_list(struct ofono_modem *modem, int type, GSList *list)
+{
+ struct call_forwarding_data *cf = modem->call_forwarding;
+ GSList *old = cf->cf_conditions[type];
+ DBusConnection *conn = dbus_gsm_connection();
+ GSList *l;
+ GSList *o;
+ struct ofono_cf_condition *lc;
+ struct ofono_cf_condition *oc;
+ const char *number;
+ dbus_uint16_t timeout;
+ char attr[64];
+ char tattr[64];
+
+ for (l = list; l; l = l->next) {
+ lc = l->data;
+
+ /* New condition lists might have attributes we don't care about
+ * triggered by e.g. ss control magic strings just skip them
+ * here
+ */
+ if (lc->cls > BEARER_CLASS_SMS)
+ continue;
+
+ timeout = lc->time;
+ number = phone_number_to_string(lc->phone_number,
+ lc->number_type);
+
+ sprintf(attr, "%s%s", bearer_class_to_string(lc->cls),
+ cf_type_lut[type]);
+
+ if (type == CALL_FORWARDING_TYPE_NO_REPLY)
+ sprintf(tattr, "%sTimeout", attr);
+
+ o = g_slist_find_custom(old, GINT_TO_POINTER(lc->cls),
+ cf_condition_find_with_cls);
+
+ if (o) { /* On the old list, must be active */
+ oc = o->data;
+
+ if (oc->number_type != lc->number_type ||
+ strcmp(oc->phone_number, lc->phone_number))
+ dbus_gsm_signal_property_changed(conn,
+ modem->path,
+ CALL_FORWARDING_INTERFACE,
+ attr, DBUS_TYPE_STRING,
+ &number);
+
+ if (type == CALL_FORWARDING_TYPE_NO_REPLY &&
+ oc->time != lc->time)
+ dbus_gsm_signal_property_changed(conn,
+ modem->path,
+ CALL_FORWARDING_INTERFACE,
+ tattr, DBUS_TYPE_UINT16,
+ &timeout);
+
+ /* Remove from the old list */
+ g_free(o->data);
+ old = g_slist_remove(old, o->data);
+ } else {
+ number = phone_number_to_string(lc->phone_number,
+ lc->number_type);
+
+ dbus_gsm_signal_property_changed(conn, modem->path,
+ CALL_FORWARDING_INTERFACE,
+ attr, DBUS_TYPE_STRING,
+ &number);
+
+ if (type == CALL_FORWARDING_TYPE_NO_REPLY &&
+ lc->time != DEFAULT_NO_REPLY_TIMEOUT)
+ dbus_gsm_signal_property_changed(conn,
+ modem->path,
+ CALL_FORWARDING_INTERFACE,
+ tattr, DBUS_TYPE_UINT16,
+ &timeout);
+ }
+ }
+
+ timeout = DEFAULT_NO_REPLY_TIMEOUT;
+ number = "";
+
+ for (o = old; o; o = o->next) {
+ oc = o->data;
+
+ sprintf(attr, "%s%s", bearer_class_to_string(oc->cls),
+ cf_type_lut[type]);
+
+ if (type == CALL_FORWARDING_TYPE_NO_REPLY)
+ sprintf(tattr, "%sTimeout", attr);
+
+ dbus_gsm_signal_property_changed(conn, modem->path,
+ CALL_FORWARDING_INTERFACE, attr,
+ DBUS_TYPE_STRING, &number);
+
+ if (type == CALL_FORWARDING_TYPE_NO_REPLY &&
+ oc->time != DEFAULT_NO_REPLY_TIMEOUT)
+ dbus_gsm_signal_property_changed(conn, modem->path,
+ CALL_FORWARDING_INTERFACE,
+ tattr, DBUS_TYPE_UINT16,
+ &timeout);
+ }
+
+ cf_list_clear(old);
+ cf->cf_conditions[type] = list;
+}
+
+static inline void property_append_cf_condition(DBusMessageIter *dict, int cls,
+ const char *postfix,
+ const char *value,
+ dbus_uint16_t timeout)
+{
+ char attr[64];
+ char tattr[64];
+ int addt = !strcmp(postfix, "NoReply");
+
+ sprintf(attr, "%s%s", bearer_class_to_string(cls), postfix);
+
+ if (addt)
+ sprintf(tattr, "%s%sTimeout", bearer_class_to_string(cls),
+ postfix);
+
+ dbus_gsm_dict_append(dict, attr, DBUS_TYPE_STRING, &value);
+
+ if (addt)
+ dbus_gsm_dict_append(dict, tattr, DBUS_TYPE_UINT16, &timeout);
+}
+
+static void property_append_cf_conditions(DBusMessageIter *dict,
+ GSList *cf_list, int mask,
+ const char *postfix)
+{
+ GSList *l;
+ int i;
+ struct ofono_cf_condition *cf;
+ const char *number;
+
+ for (i = 1, l = cf_list; i <= BEARER_CLASS_PAD; i = i << 1) {
+ if (!(mask & i))
+ continue;
+
+ while (l && (cf = l->data) && (cf->cls < i))
+ l = l->next;
+
+ if (!l || cf->cls != i) {
+ property_append_cf_condition(dict, i, postfix, "",
+ DEFAULT_NO_REPLY_TIMEOUT);
+ continue;
+ }
+
+ number = phone_number_to_string(cf->phone_number,
+ cf->number_type);
+
+ property_append_cf_condition(dict, i, postfix, number,
+ cf->time);
+ }
+}
+
+static DBusMessage *cf_get_properties_reply(DBusMessage *msg,
+ struct call_forwarding_data *cf)
+{
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ int i;
+
+ reply = dbus_message_new_method_return(msg);
+
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ PROPERTIES_ARRAY_SIGNATURE,
+ &dict);
+
+ for (i = 0; i < 4; i++)
+ property_append_cf_conditions(&dict,
+ cf->cf_conditions[i],
+ BEARER_CLASS_DEFAULT | BEARER_CLASS_SMS,
+ cf_type_lut[i]);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static DBusMessage *cf_get_properties(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_forwarding_data *cf = modem->call_forwarding;
+
+ if (cf->flags & CALL_FORWARDING_FLAG_CACHED)
+ return cf_get_properties_reply(msg, cf);
+
+ /* We kicked off the query during interface creation, wait for it */
+ cf->pending = dbus_message_ref(msg);
+
+ return NULL;
+}
+
+static gboolean cf_condition_enabled_property(struct call_forwarding_data *cf,
+ const char *property, int *out_type, int *out_cls)
+{
+ int i;
+ int j;
+ int len;
+ const char *prefix;
+
+ /* We check the 4 bearer classes here, e.g. voice, data, fax, sms */
+ for (i = 0; i < 4; i++) {
+ prefix = bearer_class_to_string(1 << i);
+
+ len = strlen(prefix);
+
+ if (strncmp(property, prefix, len))
+ continue;
+
+ /* We check the 4 call forwarding types, e.g.
+ * unconditional, busy, no reply, not reachable
+ */
+ for (j = 0; j < 4; j++)
+ if (!strcmp(property+len, cf_type_lut[j])) {
+ *out_type = j;
+ *out_cls = 1 << i;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean cf_condition_timeout_property(const char *property,
+ int *out_cls)
+{
+ int i;
+ int len;
+ const char *prefix;
+
+ for (i = 0; i < 4; i++) {
+ prefix = bearer_class_to_string(1 << i);
+
+ len = strlen(prefix);
+
+ if (strncmp(property, prefix, len))
+ continue;
+
+ if (!strcmp(property+len, "NoReplyTimeout")) {
+ *out_cls = 1 << i;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static void cf_condition_manual_set(struct set_cf_request *req)
+{
+ struct ofono_modem *modem = req->modem;
+ struct call_forwarding_data *cf = modem->call_forwarding;
+ DBusConnection *conn = dbus_gsm_connection();
+ int status = req->number[0] == '\0' ? 0 : 1;
+ GSList *l;
+ struct ofono_cf_condition *c;
+ char attr[64];
+ char tattr[64];
+ const char *number = "";
+ dbus_uint16_t timeout;
+
+ l = g_slist_find_custom(cf->cf_conditions[req->type],
+ GINT_TO_POINTER(req->cls),
+ cf_condition_find_with_cls);
+
+ ofono_debug("L is: %p, status is: %d", l, status);
+
+ if (!l && !status)
+ return;
+
+ sprintf(attr, "%s%s", bearer_class_to_string(req->cls),
+ cf_type_lut[req->type]);
+
+ if (req->type == CALL_FORWARDING_TYPE_NO_REPLY)
+ sprintf(tattr, "%sTimeout", attr);
+
+ if (l && !status) {
+ c = l->data;
+ timeout = DEFAULT_NO_REPLY_TIMEOUT;
+
+ dbus_gsm_signal_property_changed(conn, modem->path,
+ CALL_FORWARDING_INTERFACE,
+ attr, DBUS_TYPE_STRING, &number);
+
+ if (req->type == CALL_FORWARDING_TYPE_NO_REPLY &&
+ c->time != DEFAULT_NO_REPLY_TIMEOUT)
+ dbus_gsm_signal_property_changed(conn, modem->path,
+ CALL_FORWARDING_INTERFACE, tattr,
+ DBUS_TYPE_UINT16, &timeout);
+
+ ofono_debug("Removing condition");
+
+ g_free(c);
+ cf->cf_conditions[req->type] =
+ g_slist_remove(cf->cf_conditions[req->type], c);
+
+ return;
+ }
+
+ if (l)
+ c = l->data;
+ else {
+ c = g_try_new0(struct ofono_cf_condition, 1);
+
+ if (!c)
+ return;
+
+ c->status = 1;
+ c->cls = req->cls;
+ c->phone_number[0] = '\0';
+ c->number_type = 129;
+ c->time = DEFAULT_NO_REPLY_TIMEOUT;
+
+ ofono_debug("Inserting condition");
+ cf->cf_conditions[req->type] =
+ g_slist_insert_sorted(cf->cf_conditions[req->type],
+ c, cf_condition_compare);
+ }
+
+ if (c->number_type != req->number_type ||
+ strcmp(req->number, c->phone_number)) {
+ strcpy(c->phone_number, req->number);
+ c->number_type = req->number_type;
+
+ number = phone_number_to_string(req->number, req->number_type);
+
+ dbus_gsm_signal_property_changed(conn, modem->path,
+ CALL_FORWARDING_INTERFACE,
+ attr, DBUS_TYPE_STRING, &number);
+ }
+
+ if (req->type == CALL_FORWARDING_TYPE_NO_REPLY &&
+ c->time != req->timeout) {
+ c->time = req->timeout;
+
+ dbus_gsm_signal_property_changed(conn, modem->path,
+ CALL_FORWARDING_INTERFACE,
+ tattr, DBUS_TYPE_UINT16, &req->timeout);
+ }
+}
+
+static void pending_msg_error(struct call_forwarding_data *cf,
+ const struct ofono_error *error)
+{
+ DBusMessage *reply;
+ DBusConnection *conn = dbus_gsm_connection();
+
+ reply = dbus_gsm_failed(cf->pending);
+ g_dbus_send_message(conn, reply);
+
+ dbus_message_unref(cf->pending);
+ cf->pending = NULL;
+}
+
+static void property_set_query_callback(const struct ofono_error *error, int total,
+ const struct ofono_cf_condition *list,
+ void *data)
+{
+ struct set_cf_request *req = data;
+ struct ofono_modem *modem = req->modem;
+ struct call_forwarding_data *cf = modem->call_forwarding;
+ //DBusConnection *conn = dbus_gsm_connection();
+ DBusMessage *reply;
+ GSList *new_cf_list;
+
+ reply = dbus_message_new_method_return(cf->pending);
+ dbus_gsm_pending_reply(&cf->pending, reply);
+
+ /* Strange, set succeeded but query failed, fallback to direct method */
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ ofono_debug("Error occurred during query");
+ cf_condition_manual_set(req);
+ goto out;
+ }
+
+ new_cf_list = cf_cond_list_create(total, list);
+
+ ofono_debug("Query ran successfully");
+ cf_cond_list_print(new_cf_list);
+
+ set_new_cond_list(modem, req->type, new_cf_list);
+
+out:
+ g_free(req);
+}
+
+static void set_property_callback(const struct ofono_error *error, void *data)
+{
+ struct set_cf_request *req = data;
+ struct ofono_modem *modem = req->modem;
+ struct call_forwarding_data *cf = modem->call_forwarding;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ ofono_debug("Error occurred during set/erasure");
+
+ pending_msg_error(cf, error);
+ g_free(req);
+
+ return;
+ }
+
+ /* Successfully set, query the entire set just in case */
+ cf->ops->query(modem, req->type,
+ BEARER_CLASS_DEFAULT | BEARER_CLASS_SMS,
+ property_set_query_callback, req);
+}
+
+static DBusMessage *set_property_request(struct ofono_modem *modem,
+ DBusMessage *msg,
+ int type, int cls,
+ const char *number,
+ int number_type, int timeout)
+{
+ struct call_forwarding_data *cf = modem->call_forwarding;
+ struct set_cf_request *req;
+
+ if (number[0] != '\0' && cf->ops->registration == NULL)
+ return dbus_gsm_not_implemented(msg);
+
+ if (number[0] == '\0' && cf->ops->erasure == NULL)
+ return dbus_gsm_not_implemented(msg);
+
+ req = g_try_new0(struct set_cf_request, 1);
+
+ if (!req)
+ return dbus_gsm_failed(msg);
+
+ req->modem = modem;
+ req->type = type;
+ req->cls = cls;
+ strcpy(req->number, number);
+ req->number_type = number_type;
+ req->timeout = timeout;
+
+ cf->pending = dbus_message_ref(msg);
+
+ ofono_debug("Farming off request, will be erasure: %d", number[0] == 0);
+
+ if (number[0] != '\0')
+ cf->ops->registration(modem, type, cls, number, number_type,
+ timeout, set_property_callback, req);
+ else
+ cf->ops->erasure(modem, type, cls, set_property_callback, req);
+
+ return NULL;
+}
+
+static DBusMessage *cf_set_property(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_forwarding_data *cf = modem->call_forwarding;
+ DBusMessageIter iter;
+ DBusMessageIter var;
+ const char *property;
+ int cls;
+ int type;
+
+ if (cf->pending)
+ return dbus_gsm_busy(msg);
+
+ if (!dbus_message_iter_init(msg, &iter))
+ return dbus_gsm_invalid_args(msg);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return dbus_gsm_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&iter, &property);
+ dbus_message_iter_next(&iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+ return dbus_gsm_invalid_args(msg);
+
+ dbus_message_iter_recurse(&iter, &var);
+
+ if (cf_condition_timeout_property(property, &cls)) {
+ dbus_uint16_t timeout;
+ GSList *l;
+ struct ofono_cf_condition *c;
+
+ type = CALL_FORWARDING_TYPE_NO_REPLY;
+
+ if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_UINT16)
+ return dbus_gsm_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&var, &timeout);
+
+ if (timeout < 1 || timeout > 30)
+ return dbus_gsm_invalid_format(msg);
+
+ l = g_slist_find_custom(cf->cf_conditions[type],
+ GINT_TO_POINTER(cls),
+ cf_condition_find_with_cls);
+
+ if (!l)
+ return dbus_gsm_failed(msg);
+
+ c = l->data;
+
+ return set_property_request(modem, msg, type, cls,
+ c->phone_number,
+ c->number_type, timeout);
+ } else if (cf_condition_enabled_property(cf, property, &type, &cls)) {
+ const char *number;
+ int number_type;
+ int timeout;
+
+ if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+ return dbus_gsm_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&var, &number);
+
+ if (strlen(number) > 0 && !valid_phone_number_format(number))
+ return dbus_gsm_invalid_format(msg);
+
+ if (number[0] != '\0')
+ string_to_phone_number(number, &number_type, &number);
+ else
+ number_type = 129;
+
+ timeout = cf_find_timeout(cf->cf_conditions[type], cls);
+
+ return set_property_request(modem, msg, type, cls, number,
+ number_type, timeout);
+ }
+
+ return dbus_gsm_invalid_args(msg);
+}
+
+static void disable_conditional_callback(const struct ofono_error *error,
+ void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_forwarding_data *cf = modem->call_forwarding;
+ //DBusConnection *conn = dbus_gsm_connection();
+ DBusMessage *reply;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ ofono_debug("Error occurred during conditional erasure");
+
+ pending_msg_error(cf, error);
+
+ return;
+ }
+
+ reply = dbus_message_new_method_return(cf->pending);
+ dbus_gsm_pending_reply(&cf->pending, reply);
+
+ set_new_cond_list(modem, CALL_FORWARDING_TYPE_NO_REPLY, NULL);
+ set_new_cond_list(modem, CALL_FORWARDING_TYPE_NOT_REACHABLE, NULL);
+ set_new_cond_list(modem, CALL_FORWARDING_TYPE_BUSY, NULL);
+
+ cf->ops->query(modem, CALL_FORWARDING_TYPE_BUSY,
+ BEARER_CLASS_DEFAULT | BEARER_CLASS_SMS,
+ cf_busy_callback, modem);
+}
+
+static void disable_all_callback(const struct ofono_error *error, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_forwarding_data *cf = modem->call_forwarding;
+ //DBusConnection *conn = dbus_gsm_connection();
+ DBusMessage *reply;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ ofono_debug("Error occurred during erasure of all");
+
+ pending_msg_error(cf, error);
+
+ return;
+ }
+
+ reply = dbus_message_new_method_return(cf->pending);
+ dbus_gsm_pending_reply(&cf->pending, reply);
+
+ set_new_cond_list(modem, CALL_FORWARDING_TYPE_UNCONDITIONAL, NULL);
+ set_new_cond_list(modem, CALL_FORWARDING_TYPE_NO_REPLY, NULL);
+ set_new_cond_list(modem, CALL_FORWARDING_TYPE_NOT_REACHABLE, NULL);
+ set_new_cond_list(modem, CALL_FORWARDING_TYPE_BUSY, NULL);
+
+ cf->ops->query(modem, CALL_FORWARDING_TYPE_UNCONDITIONAL,
+ BEARER_CLASS_DEFAULT | BEARER_CLASS_SMS,
+ cf_unconditional_callback, modem);
+}
+
+static DBusMessage *cf_disable_all(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_forwarding_data *cf = modem->call_forwarding;
+ const char *strtype;
+ int type;
+
+ if (cf->pending)
+ return dbus_gsm_busy(msg);
+
+ if (!cf->ops->erasure)
+ return dbus_gsm_not_implemented(msg);
+
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &strtype,
+ DBUS_TYPE_INVALID) == FALSE)
+ return dbus_gsm_invalid_args(msg);
+
+ if (!strcmp(strtype, "all") || !strcmp(strtype, ""))
+ type = CALL_FORWARDING_TYPE_ALL;
+ else if (!strcmp(strtype, "conditional"))
+ type = CALL_FORWARDING_TYPE_ALL_CONDITIONAL;
+ else
+ return dbus_gsm_invalid_format(msg);
+
+ cf->pending = dbus_message_ref(msg);
+
+ if (type == CALL_FORWARDING_TYPE_ALL)
+ cf->ops->erasure(modem, type,
+ BEARER_CLASS_DEFAULT | BEARER_CLASS_SMS,
+ disable_all_callback, modem);
+ else
+ cf->ops->erasure(modem, type,
+ BEARER_CLASS_DEFAULT | BEARER_CLASS_SMS,
+ disable_conditional_callback, modem);
+
+ return NULL;
+}
+
+static GDBusMethodTable cf_methods[] = {
+ { "GetProperties", "", "a{sv}", cf_get_properties },
+ { "SetProperty", "sv", "", cf_set_property,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "DisableAll", "s", "", cf_disable_all,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { }
+};
+
+static GDBusSignalTable cf_signals[] = {
+ { "PropertyChanged", "sv" },
+ { }
+};
+
+static void cf_ss_control_reply(struct ofono_modem *modem,
+ struct cf_ss_request *req)
+{
+ struct call_forwarding_data *cf = modem->call_forwarding;
+ const char *context = "CallForwarding";
+ const char *sig = "(ssa{sv})";
+ const char *ss_type = ss_control_type_to_string(req->ss_type);
+ const char *cf_type = cf_type_lut[req->cf_type];
+ DBusConnection *conn = dbus_gsm_connection();
+ DBusMessageIter iter;
+ DBusMessageIter variant;
+ DBusMessageIter vstruct;
+ DBusMessageIter dict;
+ DBusMessage *reply;
+
+ reply = dbus_message_new_method_return(cf->pending);
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &context);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, sig,
+ &variant);
+
+ dbus_message_iter_open_container(&variant, DBUS_TYPE_STRUCT, NULL,
+ &vstruct);
+
+ dbus_message_iter_append_basic(&vstruct, DBUS_TYPE_STRING,
+ &ss_type);
+
+ dbus_message_iter_append_basic(&vstruct, DBUS_TYPE_STRING,
+ &cf_type);
+
+ dbus_message_iter_open_container(&vstruct, DBUS_TYPE_ARRAY,
+ PROPERTIES_ARRAY_SIGNATURE, &dict);
+
+ if (req->cf_type == CALL_FORWARDING_TYPE_UNCONDITIONAL ||
+ req->cf_type == CALL_FORWARDING_TYPE_ALL)
+ property_append_cf_conditions(&dict,
+ req->cf_list[CALL_FORWARDING_TYPE_UNCONDITIONAL],
+ req->cls,
+ cf_type_lut[CALL_FORWARDING_TYPE_UNCONDITIONAL]);
+
+ if (req->cf_type == CALL_FORWARDING_TYPE_NO_REPLY ||
+ req->cf_type == CALL_FORWARDING_TYPE_ALL ||
+ req->cf_type == CALL_FORWARDING_TYPE_ALL_CONDITIONAL)
+ property_append_cf_conditions(&dict,
+ req->cf_list[CALL_FORWARDING_TYPE_NO_REPLY],
+ req->cls, cf_type_lut[CALL_FORWARDING_TYPE_NO_REPLY]);
+
+ if (req->cf_type == CALL_FORWARDING_TYPE_NOT_REACHABLE ||
+ req->cf_type == CALL_FORWARDING_TYPE_ALL ||
+ req->cf_type == CALL_FORWARDING_TYPE_ALL_CONDITIONAL)
+ property_append_cf_conditions(&dict,
+ req->cf_list[CALL_FORWARDING_TYPE_NOT_REACHABLE],
+ req->cls,
+ cf_type_lut[CALL_FORWARDING_TYPE_NOT_REACHABLE]);
+
+ if (req->cf_type == CALL_FORWARDING_TYPE_BUSY ||
+ req->cf_type == CALL_FORWARDING_TYPE_ALL ||
+ req->cf_type == CALL_FORWARDING_TYPE_ALL_CONDITIONAL)
+ property_append_cf_conditions(&dict,
+ req->cf_list[CALL_FORWARDING_TYPE_BUSY],
+ req->cls, cf_type_lut[CALL_FORWARDING_TYPE_BUSY]);
+
+ dbus_message_iter_close_container(&vstruct, &dict);
+
+ dbus_message_iter_close_container(&variant, &vstruct);
+
+ dbus_message_iter_close_container(&iter, &variant);
+
+ g_dbus_send_message(conn, reply);
+}
+
+static void cf_ss_control_query_callback(const struct ofono_error *error,
+ int total,
+ const struct ofono_cf_condition *list,
+ void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_forwarding_data *cf = modem->call_forwarding;
+ //DBusConnection *conn = dbus_gsm_connection();
+ //DBusMessage *reply;
+ GSList *new_cf_list;
+
+ /* Strange, set succeeded but query failed, fallback to direct method */
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ ofono_debug("Error occurred during cf ss query");
+
+ pending_msg_error(cf, error);
+
+ return;
+ }
+
+ new_cf_list = cf_cond_list_create(total, list);
+
+ ofono_debug("Query ran successfully");
+ cf_cond_list_print(new_cf_list);
+
+ cf->ss_req->cf_list[cf->ss_req->cf_type] = new_cf_list;
+
+ set_new_cond_list(modem, cf->ss_req->cf_type, new_cf_list);
+
+ cf_ss_control_reply(modem, cf->ss_req);
+
+ dbus_message_unref(cf->pending);
+ cf->pending = NULL;
+
+ g_free(cf->ss_req);
+ cf->ss_req = NULL;
+}
+
+static void cf_ss_control_callback(const struct ofono_error *error, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_forwarding_data *cf = modem->call_forwarding;
+ int cls;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ ofono_debug("Error occurred during cf ss control set/erasure");
+
+ pending_msg_error(cf, error);
+
+ return;
+ }
+
+ cls = BEARER_CLASS_DEFAULT | BEARER_CLASS_SMS | cf->ss_req->cls;
+
+ /* Successfully set, query the entire set just in case */
+ if (cf->ss_req->cf_type == CALL_FORWARDING_TYPE_ALL)
+ cf->ops->query(modem, CALL_FORWARDING_TYPE_UNCONDITIONAL,
+ cls, cf_unconditional_callback, modem);
+ else if (cf->ss_req->cf_type == CALL_FORWARDING_TYPE_ALL_CONDITIONAL)
+ cf->ops->query(modem, CALL_FORWARDING_TYPE_BUSY,
+ cls, cf_busy_callback, modem);
+ else
+ cf->ops->query(modem, cf->ss_req->cf_type, cls,
+ cf_ss_control_query_callback, modem);
+}
+
+static gboolean cf_ss_control(struct ofono_modem *modem, int type, const char *sc,
+ const char *sia, const char *sib,
+ const char *sic, const char *dn,
+ DBusMessage *msg)
+{
+ struct call_forwarding_data *cf = modem->call_forwarding;
+ DBusConnection *conn = dbus_gsm_connection();
+ int cls = BEARER_CLASS_DEFAULT;
+ int timeout = DEFAULT_NO_REPLY_TIMEOUT;
+ int cf_type;
+ DBusMessage *reply;
+ const char *number;
+ int number_type;
+ void *operation;
+
+ /* Before we do anything, make sure we're actually initialized */
+ if (!cf)
+ return FALSE;
+
+ if (cf->pending) {
+ reply = dbus_gsm_busy(msg);
+ g_dbus_send_message(conn, reply);
+
+ return TRUE;
+ }
+
+ ofono_debug("Received call forwarding ss control request");
+
+ ofono_debug("type: %d, sc: %s, sia: %s, sib: %s, sic: %s, dn: %s",
+ type, sc, sia, sib, sic, dn);
+
+ if (!strcmp(sc, "21"))
+ cf_type = CALL_FORWARDING_TYPE_UNCONDITIONAL;
+ else if (!strcmp(sc, "67"))
+ cf_type = CALL_FORWARDING_TYPE_BUSY;
+ else if (!strcmp(sc, "61"))
+ cf_type = CALL_FORWARDING_TYPE_NO_REPLY;
+ else if (!strcmp(sc, "62"))
+ cf_type = CALL_FORWARDING_TYPE_NOT_REACHABLE;
+ else if (!strcmp(sc, "002"))
+ cf_type = CALL_FORWARDING_TYPE_ALL;
+ else if (!strcmp(sc, "004"))
+ cf_type = CALL_FORWARDING_TYPE_ALL_CONDITIONAL;
+ else
+ return FALSE;
+
+ if (strlen(sia) &&
+ (type == SS_CONTROL_TYPE_QUERY ||
+ type == SS_CONTROL_TYPE_ERASURE ||
+ type == SS_CONTROL_TYPE_DEACTIVATION))
+ goto error;
+
+ /* Activation / Registration is figured context specific according to
+ * 22.030 Section 6.5.2 "The UE shall determine from the context
+ * whether, an entry of a single *, activation or registration
+ * was intended."
+ */
+ if (type == SS_CONTROL_TYPE_ACTIVATION && strlen(sia) > 0)
+ type = SS_CONTROL_TYPE_REGISTRATION;
+
+ if (type == SS_CONTROL_TYPE_REGISTRATION &&
+ !valid_phone_number_format(sia))
+ goto error;
+
+ if (strlen(sib) > 0) {
+ long service_code;
+ char *end;
+
+ service_code = strtoul(sib, &end, 10);
+
+ if (end == sib || *end != '\0')
+ goto error;
+
+ cls = mmi_service_code_to_bearer_class(service_code);
+
+ if (cls == 0)
+ goto error;
+ }
+
+ if (strlen(sic) > 0) {
+ char *end;
+
+ if (type != SS_CONTROL_TYPE_REGISTRATION)
+ goto error;
+
+ if (cf_type != CALL_FORWARDING_TYPE_ALL &&
+ cf_type != CALL_FORWARDING_TYPE_ALL_CONDITIONAL &&
+ cf_type != CALL_FORWARDING_TYPE_NO_REPLY)
+ goto error;
+
+ timeout = strtoul(sic, &end, 10);
+
+ if (end == sic || *end != '\0')
+ goto error;
+
+ if (timeout < 1 || timeout > 30)
+ goto error;
+ }
+
+ switch (type) {
+ case SS_CONTROL_TYPE_REGISTRATION:
+ operation = cf->ops->registration;
+ break;
+ case SS_CONTROL_TYPE_ACTIVATION:
+ operation = cf->ops->activation;
+ break;
+ case SS_CONTROL_TYPE_DEACTIVATION:
+ operation = cf->ops->deactivation;
+ break;
+ case SS_CONTROL_TYPE_ERASURE:
+ operation = cf->ops->erasure;
+ break;
+ case SS_CONTROL_TYPE_QUERY:
+ operation = cf->ops->query;
+ break;
+ }
+
+ if (!operation) {
+ reply = dbus_gsm_not_implemented(msg);
+ g_dbus_send_message(conn, reply);
+
+ return TRUE;
+ }
+
+ cf->ss_req = g_try_new0(struct cf_ss_request, 1);
+
+ if (!cf->ss_req) {
+ reply = dbus_gsm_failed(msg);
+ g_dbus_send_message(conn, reply);
+
+ return TRUE;
+ }
+
+ cf->ss_req->ss_type = type;
+ cf->ss_req->cf_type = cf_type;
+ cf->ss_req->cls = cls;
+
+ cf->pending = dbus_message_ref(msg);
+
+ switch (cf->ss_req->ss_type) {
+ case SS_CONTROL_TYPE_REGISTRATION:
+ string_to_phone_number(sia, &number_type, &number);
+ cf->ops->registration(modem, cf_type, cls, number, number_type,
+ timeout, cf_ss_control_callback,
+ modem);
+ break;
+ case SS_CONTROL_TYPE_ACTIVATION:
+ cf->ops->activation(modem, cf_type, cls, cf_ss_control_callback,
+ modem);
+ break;
+ case SS_CONTROL_TYPE_DEACTIVATION:
+ cf->ops->deactivation(modem, cf_type, cls,
+ cf_ss_control_callback, modem);
+ break;
+ case SS_CONTROL_TYPE_ERASURE:
+ cf->ops->erasure(modem, cf_type, cls, cf_ss_control_callback,
+ modem);
+ break;
+ case SS_CONTROL_TYPE_QUERY:
+ cls |= BEARER_CLASS_DEFAULT | BEARER_CLASS_SMS;
+ if (cf_type == CALL_FORWARDING_TYPE_ALL)
+ cf->ops->query(modem,
+ CALL_FORWARDING_TYPE_UNCONDITIONAL,
+ cls, cf_unconditional_callback, modem);
+ else if (cf_type == CALL_FORWARDING_TYPE_ALL_CONDITIONAL)
+ cf->ops->query(modem, CALL_FORWARDING_TYPE_BUSY,
+ cls, cf_busy_callback, modem);
+ else
+ cf->ops->query(modem, cf_type, cls,
+ cf_ss_control_query_callback, modem);
+ break;
+ }
+
+ return TRUE;
+
+error:
+ reply = dbus_gsm_invalid_format(msg);
+ g_dbus_send_message(conn, reply);
+ return TRUE;
+}
+
+static void cf_register_ss_controls(struct ofono_modem *modem)
+{
+ ss_control_register(modem, "21", cf_ss_control);
+ ss_control_register(modem, "67", cf_ss_control);
+ ss_control_register(modem, "61", cf_ss_control);
+ ss_control_register(modem, "62", cf_ss_control);
+
+ ss_control_register(modem, "002", cf_ss_control);
+ ss_control_register(modem, "004", cf_ss_control);
+}
+
+static void cf_unregister_ss_controls(struct ofono_modem *modem)
+{
+ ss_control_unregister(modem, "21", cf_ss_control);
+ ss_control_unregister(modem, "67", cf_ss_control);
+ ss_control_unregister(modem, "61", cf_ss_control);
+ ss_control_unregister(modem, "62", cf_ss_control);
+
+ ss_control_unregister(modem, "002", cf_ss_control);
+ ss_control_unregister(modem, "004", cf_ss_control);
+}
+
+static void cf_not_reachable_callback(const struct ofono_error *error, int total,
+ const struct ofono_cf_condition *list, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_forwarding_data *cf = modem->call_forwarding;
+ GSList *l = NULL;
+ //DBusConnection *conn = dbus_gsm_connection();
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ ofono_debug("Error during not reachable CF query");
+ goto out;
+ }
+
+ l = cf_cond_list_create(total, list);
+
+ set_new_cond_list(modem, CALL_FORWARDING_TYPE_NOT_REACHABLE, l);
+
+ ofono_debug("Not Reachable conditions:");
+ cf_cond_list_print(l);
+
+out:
+
+ cf->flags |= CALL_FORWARDING_FLAG_CACHED;
+
+ if (cf->pending) {
+ if (cf->ss_req) {
+ cf->ss_req->cf_list[CALL_FORWARDING_TYPE_NOT_REACHABLE] = l;
+ cf_ss_control_reply(modem, cf->ss_req);
+ g_free(cf->ss_req);
+ cf->ss_req = NULL;
+ } else {
+ DBusConnection *conn = dbus_gsm_connection();
+ DBusMessage *reply =
+ cf_get_properties_reply(cf->pending, cf);
+
+ g_dbus_send_message(conn, reply);
+ }
+
+ dbus_message_unref(cf->pending);
+ cf->pending = NULL;
+ }
+}
+
+static void cf_no_reply_callback(const struct ofono_error *error, int total,
+ const struct ofono_cf_condition *list, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_forwarding_data *cf = modem->call_forwarding;
+ GSList *l = NULL;
+ int cls = BEARER_CLASS_DEFAULT | BEARER_CLASS_SMS;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ ofono_debug("Error during no reply CF query");
+ goto out;
+ }
+
+ l = cf_cond_list_create(total, list);
+
+ set_new_cond_list(modem, CALL_FORWARDING_TYPE_NO_REPLY, l);
+
+ ofono_debug("No Reply conditions:");
+ cf_cond_list_print(l);
+
+out:
+ if (cf->ss_req) {
+ cls |= cf->ss_req->cls;
+ cf->ss_req->cf_list[CALL_FORWARDING_TYPE_NO_REPLY] = l;
+ }
+
+ cf->ops->query(modem, CALL_FORWARDING_TYPE_NOT_REACHABLE,
+ cls, cf_not_reachable_callback, modem);
+}
+
+static void cf_busy_callback(const struct ofono_error *error, int total,
+ const struct ofono_cf_condition *list, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_forwarding_data *cf = modem->call_forwarding;
+ GSList *l = NULL;
+ int cls = BEARER_CLASS_DEFAULT | BEARER_CLASS_SMS;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ ofono_debug("Error during busy CF query");
+ goto out;
+ }
+
+ l = cf_cond_list_create(total, list);
+
+ set_new_cond_list(modem, CALL_FORWARDING_TYPE_BUSY, l);
+
+ ofono_debug("On Busy conditions:");
+ cf_cond_list_print(l);
+
+out:
+ if (cf->ss_req) {
+ cls |= cf->ss_req->cls;
+ cf->ss_req->cf_list[CALL_FORWARDING_TYPE_BUSY] = l;
+ }
+
+ cf->ops->query(modem, CALL_FORWARDING_TYPE_NO_REPLY,
+ cls, cf_no_reply_callback, modem);
+}
+
+static void cf_unconditional_callback(const struct ofono_error *error, int total,
+ const struct ofono_cf_condition *list,
+ void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_forwarding_data *cf = modem->call_forwarding;
+ GSList *l = NULL;
+ int cls = BEARER_CLASS_DEFAULT | BEARER_CLASS_SMS;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ ofono_debug("Error during unconditional CF query");
+ goto out;
+ }
+
+ l = cf_cond_list_create(total, list);
+
+ set_new_cond_list(modem, CALL_FORWARDING_TYPE_UNCONDITIONAL, l);
+
+ ofono_debug("Unconditional conditions:");
+ cf_cond_list_print(l);
+
+out:
+ if (cf->ss_req) {
+ cls |= cf->ss_req->cls;
+ cf->ss_req->cf_list[CALL_FORWARDING_TYPE_UNCONDITIONAL] = l;
+ }
+
+ cf->ops->query(modem, CALL_FORWARDING_TYPE_BUSY,
+ cls, cf_busy_callback, modem);
+}
+
+static gboolean initiate_settings_request(void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_forwarding_data *call_forwarding = modem->call_forwarding;
+
+ /* We can't get all settings at the same time according to 22.004:
+ * "Interrogation of groups of Supplementary Services is not supported."
+ * so we do it piecemeal, unconditional, busy, no reply, not reachable
+ */
+
+ if (call_forwarding->ops->query)
+ call_forwarding->ops->query(modem,
+ CALL_FORWARDING_TYPE_UNCONDITIONAL,
+ BEARER_CLASS_DEFAULT | BEARER_CLASS_SMS,
+ cf_unconditional_callback, modem);
+
+ return FALSE;
+}
+
+static void request_settings(struct ofono_modem *modem)
+{
+ g_timeout_add(0, initiate_settings_request, modem);
+}
+
+int ofono_call_forwarding_register(struct ofono_modem *modem,
+ struct ofono_call_forwarding_ops *ops)
+{
+ DBusConnection *conn = dbus_gsm_connection();
+
+ if (modem == NULL)
+ return -1;
+
+ if (ops == NULL)
+ return -1;
+
+ if (ops->query == NULL)
+ return -1;
+
+ modem->call_forwarding = call_forwarding_create();
+
+ if (modem->call_forwarding == NULL)
+ return -1;
+
+ modem->call_forwarding->ops = ops;
+
+ if (!g_dbus_register_interface(conn, modem->path,
+ CALL_FORWARDING_INTERFACE,
+ cf_methods, cf_signals, NULL,
+ modem, call_forwarding_destroy)) {
+ ofono_error("Could not register CallForwarding %s", modem->path);
+ call_forwarding_destroy(modem);
+
+ return -1;
+ }
+
+ ofono_debug("Registered call forwarding interface");
+
+ cf_register_ss_controls(modem);
+
+ modem_add_interface(modem, CALL_FORWARDING_INTERFACE);
+
+ request_settings(modem);
+
+ return 0;
+}
+
+void ofono_call_forwarding_unregister(struct ofono_modem *modem)
+{
+ struct call_forwarding_data *cf = modem->call_forwarding;
+ DBusConnection *conn = dbus_gsm_connection();
+
+ if (!cf)
+ return;
+
+ modem_remove_interface(modem, CALL_FORWARDING_INTERFACE);
+ g_dbus_unregister_interface(conn, modem->path,
+ CALL_FORWARDING_INTERFACE);
+
+ modem->call_forwarding = NULL;
+}
diff --git a/src/call-meter.c b/src/call-meter.c
new file mode 100644
index 00000000..8a965ed2
--- /dev/null
+++ b/src/call-meter.c
@@ -0,0 +1,764 @@
+/*
+ *
+ * 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
+
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+
+#include <dbus/dbus.h>
+#include <glib.h>
+#include <gdbus.h>
+
+#include "ofono.h"
+
+#include "driver.h"
+#include "common.h"
+#include "dbus-gsm.h"
+#include "modem.h"
+
+#define CALL_METER_INTERFACE "org.ofono.CallMeter"
+
+#define CALL_METER_FLAG_CACHED 0x1
+#define CALL_METER_FLAG_HAVE_PUCT 0x2
+
+struct call_meter_data {
+ struct ofono_call_meter_ops *ops;
+ int flags;
+ DBusMessage *pending;
+
+ int call_meter;
+ int acm;
+ int acm_max;
+ double ppu;
+ char currency[4];
+ char *passwd;
+};
+
+static struct call_meter_data *call_meter_create(void)
+{
+ struct call_meter_data *cm = g_try_new0(struct call_meter_data, 1);
+
+ return cm;
+}
+
+static void call_meter_destroy(gpointer userdata)
+{
+ struct ofono_modem *modem = userdata;
+ struct call_meter_data *cm = modem->call_meter;
+
+ g_free(cm);
+
+ modem->call_meter = NULL;
+}
+
+static void set_call_meter(struct ofono_modem *modem, int value)
+{
+ struct call_meter_data *cm = modem->call_meter;
+
+ if (cm->call_meter != value) {
+ DBusConnection *conn = dbus_gsm_connection();
+
+ cm->call_meter = value;
+
+ dbus_gsm_signal_property_changed(conn, modem->path,
+ CALL_METER_INTERFACE,
+ "CallMeter",
+ DBUS_TYPE_UINT32,
+ &cm->call_meter);
+ }
+}
+
+static void set_acm(struct ofono_modem *modem, int value)
+{
+ struct call_meter_data *cm = modem->call_meter;
+
+ if (cm->acm != value) {
+ DBusConnection *conn = dbus_gsm_connection();
+
+ cm->acm = value;
+
+ dbus_gsm_signal_property_changed(conn, modem->path,
+ CALL_METER_INTERFACE,
+ "AccumulatedCallMeter",
+ DBUS_TYPE_UINT32,
+ &cm->acm);
+ }
+}
+
+static void set_acm_max(struct ofono_modem *modem, int value)
+{
+ struct call_meter_data *cm = modem->call_meter;
+
+ if (cm->acm_max != value) {
+ DBusConnection *conn = dbus_gsm_connection();
+
+ cm->acm_max = value;
+
+ dbus_gsm_signal_property_changed(conn, modem->path,
+ CALL_METER_INTERFACE,
+ "AccumulatedCallMeterMaximum",
+ DBUS_TYPE_UINT32,
+ &cm->acm_max);
+ }
+}
+
+static void set_ppu(struct ofono_modem *modem, double value)
+{
+ struct call_meter_data *cm = modem->call_meter;
+
+ if (cm->ppu != value) {
+ DBusConnection *conn = dbus_gsm_connection();
+
+ cm->ppu = value;
+
+ dbus_gsm_signal_property_changed(conn, modem->path,
+ CALL_METER_INTERFACE,
+ "PricePerUnit",
+ DBUS_TYPE_DOUBLE,
+ &cm->ppu);
+ }
+}
+
+static void set_currency(struct ofono_modem *modem, const char *value)
+{
+ struct call_meter_data *cm = modem->call_meter;
+
+ if (strlen(value) > 3) {
+ ofono_error("Currency reported with size > 3: %s", value);
+ return;
+ }
+
+ if (strcmp(cm->currency, value)) {
+ DBusConnection *conn = dbus_gsm_connection();
+ const char *dbusval = cm->currency;
+
+ strncpy(cm->currency, value, 3);
+ cm->currency[3] = '\0';
+
+ dbus_gsm_signal_property_changed(conn, modem->path,
+ CALL_METER_INTERFACE,
+ "Currency",
+ DBUS_TYPE_STRING,
+ &dbusval);
+ }
+}
+
+static void cm_get_properties_reply(struct ofono_modem *modem)
+{
+ struct call_meter_data *cm = modem->call_meter;
+ //struct call_meter_property *property;
+ DBusMessage *reply;
+ DBusMessageIter iter, dict;
+ const char *currency = cm->currency;
+
+ reply = dbus_message_new_method_return(cm->pending);
+ if (!reply)
+ return;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ PROPERTIES_ARRAY_SIGNATURE, &dict);
+
+ dbus_gsm_dict_append(&dict, "CallMeter", DBUS_TYPE_UINT32,
+ &cm->call_meter);
+
+ dbus_gsm_dict_append(&dict, "AccumulatedCallMeter", DBUS_TYPE_UINT32,
+ &cm->acm);
+
+ dbus_gsm_dict_append(&dict, "AccumulatedCallMeterMaximum",
+ DBUS_TYPE_UINT32, &cm->acm_max);
+
+ dbus_gsm_dict_append(&dict, "PricePerUnit", DBUS_TYPE_DOUBLE, &cm->ppu);
+
+ dbus_gsm_dict_append(&dict, "Currency", DBUS_TYPE_STRING, &currency);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ dbus_gsm_pending_reply(&cm->pending, reply);
+}
+
+static void query_call_meter_callback(const struct ofono_error *error, int value,
+ void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_meter_data *cm = modem->call_meter;
+
+ if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+ set_call_meter(modem, value);
+
+ if (cm->pending)
+ cm_get_properties_reply(modem);
+}
+
+static gboolean query_call_meter(gpointer user)
+{
+ struct ofono_modem *modem = user;
+ struct call_meter_data *cm = modem->call_meter;
+
+ if (!cm->ops->call_meter_query) {
+ if (cm->pending)
+ cm_get_properties_reply(modem);
+
+ return FALSE;
+ }
+
+ cm->ops->call_meter_query(modem, query_call_meter_callback, modem);
+
+ return FALSE;
+}
+
+static void query_acm_callback(const struct ofono_error *error, int value,
+ void *data)
+{
+ struct ofono_modem *modem = data;
+ //struct call_meter_data *cm = modem->call_meter;
+
+ if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+ set_acm(modem, value);
+
+ g_timeout_add(0, query_call_meter, modem);
+}
+
+static gboolean query_acm(gpointer user)
+{
+ struct ofono_modem *modem = user;
+ struct call_meter_data *cm = modem->call_meter;
+
+ if (!cm->ops->acm_query) {
+ query_call_meter(modem);
+ return FALSE;
+ }
+
+ cm->ops->acm_query(modem, query_acm_callback, modem);
+
+ return FALSE;
+}
+
+static void query_acm_max_callback(const struct ofono_error *error, int value,
+ void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_meter_data *cm = modem->call_meter;
+
+ if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+ set_acm_max(modem, value);
+
+ cm->flags |= CALL_METER_FLAG_CACHED;
+
+ g_timeout_add(0, query_acm, modem);
+}
+
+static gboolean query_acm_max(gpointer user)
+{
+ struct ofono_modem *modem = user;
+ struct call_meter_data *cm = modem->call_meter;
+
+ if (!cm->ops->acm_max_query) {
+ cm->flags |= CALL_METER_FLAG_CACHED;
+
+ query_acm(modem);
+ return FALSE;
+ }
+
+ cm->ops->acm_max_query(modem, query_acm_max_callback, modem);
+
+ return FALSE;
+}
+
+static void query_puct_callback(const struct ofono_error *error,
+ const char *currency, double ppu, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_meter_data *cm = modem->call_meter;
+
+ if (error->type == OFONO_ERROR_TYPE_NO_ERROR) {
+ cm->flags |= CALL_METER_FLAG_HAVE_PUCT;
+ set_currency(modem, currency);
+ set_ppu(modem, ppu);
+ }
+
+ g_timeout_add(0, query_acm_max, modem);
+}
+
+static gboolean query_puct(gpointer user)
+{
+ struct ofono_modem *modem = user;
+ struct call_meter_data *cm = modem->call_meter;
+
+ if (!cm->ops->puct_query) {
+ query_acm_max(modem);
+ return FALSE;
+ }
+
+ cm->ops->puct_query(modem, query_puct_callback, modem);
+
+ return FALSE;
+}
+
+static DBusMessage *cm_get_properties(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_meter_data *cm = modem->call_meter;
+
+ if (cm->pending)
+ return dbus_gsm_busy(msg);
+
+ cm->pending = dbus_message_ref(msg);
+
+ /* We don't need to query ppu, currency & acm_max every time
+ * Not sure if we have to query acm & call_meter every time
+ * so lets play on the safe side and query them. They should be
+ * fast to query anyway
+ */
+ if (cm->flags & CALL_METER_FLAG_CACHED)
+ query_acm(modem);
+ else
+ query_puct(modem);
+
+ return NULL;
+}
+
+static void set_acm_max_query_callback(const struct ofono_error *error, int value,
+ void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_meter_data *cm = modem->call_meter;
+ DBusMessage *reply;
+
+ if (!cm->pending)
+ return;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ ofono_error("Setting acm_max successful, but query was not");
+
+ cm->flags &= ~CALL_METER_FLAG_CACHED;
+
+ dbus_gsm_pending_reply(&cm->pending,
+ dbus_gsm_failed(cm->pending));
+ return;
+ }
+
+ reply = dbus_message_new_method_return(cm->pending);
+ dbus_gsm_pending_reply(&cm->pending, reply);
+
+ set_acm_max(modem, value);
+}
+
+static void set_acm_max_callback(const struct ofono_error *error, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_meter_data *cm = modem->call_meter;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ ofono_debug("Setting acm_max failed");
+ dbus_gsm_pending_reply(&cm->pending,
+ dbus_gsm_failed(cm->pending));
+ return;
+ }
+
+ /* Assume if we have acm_reset, we have acm_query */
+ cm->ops->acm_max_query(modem, set_acm_max_query_callback, modem);
+}
+
+static DBusMessage *prop_set_acm_max(DBusMessage *msg, struct ofono_modem *modem,
+ DBusMessageIter *dbus_value,
+ const char *pin2)
+{
+ struct call_meter_data *cm = modem->call_meter;
+ dbus_uint32_t value;
+
+ if (!cm->ops->acm_max_set)
+ return dbus_gsm_not_implemented(msg);
+
+ dbus_message_iter_get_basic(dbus_value, &value);
+
+ cm->pending = dbus_message_ref(msg);
+
+ cm->ops->acm_max_set(modem, value, pin2, set_acm_max_callback, modem);
+
+ return NULL;
+}
+
+static void set_puct_query_callback(const struct ofono_error *error,
+ const char *currency, double ppu,
+ void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_meter_data *cm = modem->call_meter;
+ DBusMessage *reply;
+
+ if (!cm->pending)
+ return;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ ofono_error("Setting PUCT successful, but query was not");
+
+ cm->flags &= ~CALL_METER_FLAG_CACHED;
+
+ dbus_gsm_pending_reply(&cm->pending,
+ dbus_gsm_failed(cm->pending));
+ return;
+ }
+
+ reply = dbus_message_new_method_return(cm->pending);
+ dbus_gsm_pending_reply(&cm->pending, reply);
+
+ set_currency(modem, currency);
+ set_ppu(modem, ppu);
+}
+
+static void set_puct_callback(const struct ofono_error *error, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_meter_data *cm = modem->call_meter;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ ofono_debug("setting puct failed");
+ dbus_gsm_pending_reply(&cm->pending,
+ dbus_gsm_failed(cm->pending));
+ return;
+ }
+
+ /* Assume if we have puct_set, we have puct_query */
+ cm->ops->puct_query(modem, set_puct_query_callback, modem);
+}
+
+/* This function is for the really bizarre case of someone trying to call
+ * SetProperty before GetProperties. But we must handle it...
+ */
+static void set_puct_initial_query_callback(const struct ofono_error *error,
+ const char *currency,
+ double ppu, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_meter_data *cm = modem->call_meter;
+ DBusMessageIter iter;
+ DBusMessageIter var;
+ const char *name;
+ const char *pin2;
+
+ if (!cm->pending)
+ return;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ dbus_gsm_pending_reply(&cm->pending,
+ dbus_gsm_failed(cm->pending));
+ return;
+ }
+
+ set_currency(modem, currency);
+ set_ppu(modem, ppu);
+
+ cm->flags |= CALL_METER_FLAG_HAVE_PUCT;
+
+ dbus_message_iter_init(cm->pending, &iter);
+ dbus_message_iter_get_basic(&iter, &name);
+ dbus_message_iter_next(&iter);
+ dbus_message_iter_recurse(&iter, &var);
+ dbus_message_iter_next(&iter);
+ dbus_message_iter_get_basic(&iter, &pin2);
+
+ if (!strcmp(name, "PricePerUnit"))
+ dbus_message_iter_get_basic(&var, &ppu);
+ else
+ dbus_message_iter_get_basic(&var, &currency);
+
+ cm->ops->puct_set(modem, currency, ppu, pin2,
+ set_puct_callback, modem);
+}
+
+static DBusMessage *prop_set_ppu(DBusMessage *msg, struct ofono_modem *modem,
+ DBusMessageIter *var, const char *pin2)
+{
+ struct call_meter_data *cm = modem->call_meter;
+ double ppu;
+
+ if (!cm->ops->puct_set || !cm->ops->puct_query)
+ return dbus_gsm_not_implemented(msg);
+
+ dbus_message_iter_get_basic(var, &ppu);
+
+ if (ppu < 0.0)
+ return dbus_gsm_invalid_format(msg);
+
+ cm->pending = dbus_message_ref(msg);
+
+ if (cm->flags & CALL_METER_FLAG_HAVE_PUCT)
+ cm->ops->puct_set(modem, cm->currency, ppu, pin2,
+ set_puct_callback, modem);
+ else
+ cm->ops->puct_query(modem, set_puct_initial_query_callback,
+ modem);
+
+ return NULL;
+}
+
+static DBusMessage *prop_set_cur(DBusMessage *msg, struct ofono_modem *modem,
+ DBusMessageIter *var, const char *pin2)
+{
+ struct call_meter_data *cm = modem->call_meter;
+ const char *value;
+
+ if (!cm->ops->puct_set || !cm->ops->puct_query)
+ return dbus_gsm_not_implemented(msg);
+
+ dbus_message_iter_get_basic(var, &value);
+
+ if (strlen(value) > 3)
+ return dbus_gsm_invalid_format(msg);
+
+ cm->pending = dbus_message_ref(msg);
+
+ if (cm->flags & CALL_METER_FLAG_HAVE_PUCT)
+ cm->ops->puct_set(modem, value, cm->ppu, pin2,
+ set_puct_callback, modem);
+ else
+ cm->ops->puct_query(modem, set_puct_initial_query_callback,
+ modem);
+
+ return NULL;
+}
+
+struct call_meter_property {
+ const char *name;
+ int type;
+ DBusMessage* (*set)(DBusMessage *msg, struct ofono_modem *modem,
+ DBusMessageIter *var, const char *pin2);
+};
+
+static struct call_meter_property cm_properties[] = {
+ { "AccumulatedCallMeterMaximum",DBUS_TYPE_UINT32, prop_set_acm_max },
+ { "PricePerUnit", DBUS_TYPE_DOUBLE, prop_set_ppu },
+ { "Currency", DBUS_TYPE_STRING, prop_set_cur },
+ { NULL, 0, 0 },
+};
+
+static DBusMessage *cm_set_property(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_meter_data *cm = modem->call_meter;
+ DBusMessageIter iter;
+ DBusMessageIter var;
+ const char *name, *passwd = "";
+ struct call_meter_property *property;
+
+ if (cm->pending)
+ return dbus_gsm_busy(msg);
+
+ if (!dbus_message_iter_init(msg, &iter))
+ return dbus_gsm_invalid_args(msg);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return dbus_gsm_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&iter, &name);
+
+ dbus_message_iter_next(&iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+ return dbus_gsm_invalid_args(msg);
+
+ dbus_message_iter_recurse(&iter, &var);
+
+ if (!dbus_message_iter_next(&iter))
+ return dbus_gsm_invalid_args(msg);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return dbus_gsm_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&iter, &passwd);
+
+ if (!is_valid_pin(passwd))
+ return dbus_gsm_invalid_format(msg);
+
+ for (property = cm_properties; property->name; property++) {
+ if (strcmp(name, property->name))
+ continue;
+
+ if (dbus_message_iter_get_arg_type(&var) != property->type)
+ return dbus_gsm_invalid_format(msg);
+
+ return property->set(msg, modem, &var, passwd);
+ }
+
+ return dbus_gsm_invalid_args(msg);
+}
+
+static void reset_acm_query_callback(const struct ofono_error *error, int value,
+ void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_meter_data *cm = modem->call_meter;
+ DBusMessage *reply;
+
+ if (!cm->pending)
+ return;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ ofono_error("Reseting ACM successful, but query was not");
+
+ cm->flags &= ~CALL_METER_FLAG_CACHED;
+
+ dbus_gsm_pending_reply(&cm->pending,
+ dbus_gsm_failed(cm->pending));
+ return;
+ }
+
+ reply = dbus_message_new_method_return(cm->pending);
+ dbus_gsm_pending_reply(&cm->pending, reply);
+
+ set_acm(modem, value);
+}
+
+static void acm_reset_callback(const struct ofono_error *error, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_meter_data *cm = modem->call_meter;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ ofono_debug("reseting acm failed");
+ dbus_gsm_pending_reply(&cm->pending,
+ dbus_gsm_failed(cm->pending));
+ return;
+ }
+
+ /* Assume if we have acm_reset, we have acm_query */
+ cm->ops->acm_query(modem, reset_acm_query_callback, modem);
+}
+
+static DBusMessage *cm_acm_reset(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_meter_data *cm = modem->call_meter;
+ DBusMessageIter iter;
+ const char *pin2;
+
+ if (cm->pending)
+ return dbus_gsm_busy(msg);
+
+ if (!dbus_message_iter_init(msg, &iter))
+ return dbus_gsm_invalid_args(msg);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return dbus_gsm_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&iter, &pin2);
+
+ if (!is_valid_pin(pin2))
+ return dbus_gsm_invalid_format(msg);
+
+ if (!cm->ops->acm_reset)
+ return dbus_gsm_not_implemented(msg);
+
+ cm->pending = dbus_message_ref(msg);
+
+ cm->ops->acm_reset(modem, pin2, acm_reset_callback, modem);
+
+ return NULL;
+}
+
+static GDBusMethodTable cm_methods[] = {
+ { "GetProperties", "", "a{sv}", cm_get_properties,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "SetProperty", "svs", "", cm_set_property,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "Reset", "s", "", cm_acm_reset,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { }
+};
+
+static GDBusSignalTable cm_signals[] = {
+ { "PropertyChanged", "sv" },
+ { "NearMaximumWarning", "" },
+ { }
+};
+
+void ofono_call_meter_changed_notify(struct ofono_modem *modem, int new_value)
+{
+ set_call_meter(modem, new_value);
+}
+
+void ofono_call_meter_maximum_notify(struct ofono_modem *modem)
+{
+ DBusConnection *conn = dbus_gsm_connection();
+ DBusMessage *signal;
+
+ signal = dbus_message_new_signal(modem->path,
+ CALL_METER_INTERFACE, "NearMaximumWarning");
+ if (!signal) {
+ ofono_error("Unable to allocate new %s.NearMaximumWarning "
+ "signal", CALL_METER_INTERFACE);
+ return;
+ }
+
+ g_dbus_send_message(conn, signal);
+}
+
+int ofono_call_meter_register(struct ofono_modem *modem,
+ struct ofono_call_meter_ops *ops)
+{
+ DBusConnection *conn = dbus_gsm_connection();
+
+ if (!modem || !ops)
+ return -1;
+
+ modem->call_meter = call_meter_create();
+
+ if (!modem->call_meter)
+ return -1;
+
+ modem->call_meter->ops = ops;
+
+ if (!g_dbus_register_interface(conn, modem->path, CALL_METER_INTERFACE,
+ cm_methods, cm_signals, NULL, modem,
+ call_meter_destroy)) {
+ ofono_error("Could not create %s interface",
+ CALL_METER_INTERFACE);
+ call_meter_destroy(modem);
+
+ return -1;
+ }
+
+ modem_add_interface(modem, CALL_METER_INTERFACE);
+
+ return 0;
+}
+
+void ofono_call_meter_unregister(struct ofono_modem *modem)
+{
+ DBusConnection *conn = dbus_gsm_connection();
+
+ if (!modem->call_meter)
+ return;
+
+ modem_remove_interface(modem, CALL_METER_INTERFACE);
+ g_dbus_unregister_interface(conn, modem->path, CALL_METER_INTERFACE);
+
+ modem->call_meter = NULL;
+}
diff --git a/src/call-settings.c b/src/call-settings.c
new file mode 100644
index 00000000..197400b2
--- /dev/null
+++ b/src/call-settings.c
@@ -0,0 +1,900 @@
+/*
+ *
+ * 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
+
+#include <string.h>
+#include <stdio.h>
+
+#include <dbus/dbus.h>
+#include <glib.h>
+#include <gdbus.h>
+
+#include "ofono.h"
+
+#include "driver.h"
+#include "common.h"
+#include "dbus-gsm.h"
+#include "modem.h"
+#include "ussd.h"
+
+#define CALL_SETTINGS_INTERFACE "org.ofono.CallSettings"
+
+#define CALL_SETTINGS_FLAG_CACHED 0x1
+
+struct call_settings_data {
+ struct ofono_call_settings_ops *ops;
+ int clir;
+ int colr;
+ int clip;
+ int colp;
+ int clir_setting;
+ int flags;
+ DBusMessage *pending;
+ int ss_req_type;
+ int call_setting_type;
+};
+
+enum call_setting_type {
+ CALL_SETTING_TYPE_CLIP = 0,
+ CALL_SETTING_TYPE_COLP,
+ CALL_SETTING_TYPE_COLR,
+ CALL_SETTING_TYPE_CLIR
+};
+
+static void cs_register_ss_controls(struct ofono_modem *modem);
+static void cs_unregister_ss_controls(struct ofono_modem *modem);
+
+static const char *clip_status_to_string(int status)
+{
+ switch (status) {
+ case CLIP_STATUS_NOT_PROVISIONED:
+ return "disabled";
+ case CLIP_STATUS_PROVISIONED:
+ return "enabled";
+ default:
+ return "unknown";
+ }
+}
+
+static const char *colp_status_to_string(int status)
+{
+ switch (status) {
+ case COLP_STATUS_NOT_PROVISIONED:
+ return "disabled";
+ case COLP_STATUS_PROVISIONED:
+ return "enabled";
+ default:
+ return "unknown";
+ }
+}
+
+static const char *colr_status_to_string(int status)
+{
+ switch (status) {
+ case COLR_STATUS_NOT_PROVISIONED:
+ return "disabled";
+ case COLR_STATUS_PROVISIONED:
+ return "enabled";
+ default:
+ return "unknown";
+ }
+}
+
+static const char *hide_callerid_to_string(int status)
+{
+ switch (status) {
+ case OFONO_CLIR_OPTION_DEFAULT:
+ return "default";
+ case OFONO_CLIR_OPTION_INVOCATION:
+ return "enabled";
+ case OFONO_CLIR_OPTION_SUPPRESSION:
+ return "disabled";
+ default:
+ return "default";
+ }
+}
+
+static const char *clir_status_to_string(int status)
+{
+ switch (status) {
+ case CLIR_STATUS_NOT_PROVISIONED:
+ return "disabled";
+ case CLIR_STATUS_PROVISIONED_PERMANENT:
+ return "permanent";
+ case CLIR_STATUS_TEMPORARY_RESTRICTED:
+ return "on";
+ case CLIR_STATUS_TEMPORARY_ALLOWED:
+ return "off";
+ default:
+ return "unknown";
+ }
+}
+
+static void set_clir_network(struct ofono_modem *modem, int clir)
+{
+ struct call_settings_data *cs = modem->call_settings;
+
+ if (cs->clir != clir) {
+ DBusConnection *conn = dbus_gsm_connection();
+ const char *str = clir_status_to_string(clir);
+
+ cs->clir = clir;
+
+ dbus_gsm_signal_property_changed(conn, modem->path,
+ CALL_SETTINGS_INTERFACE,
+ "CallingLineRestriction",
+ DBUS_TYPE_STRING, &str);
+ }
+}
+
+static void set_clir_override(struct ofono_modem *modem, int override)
+{
+ struct call_settings_data *cs = modem->call_settings;
+
+ if (cs->clir_setting != override) {
+ DBusConnection *conn = dbus_gsm_connection();
+ const char *str = hide_callerid_to_string(override);
+
+ cs->clir_setting = override;
+
+ dbus_gsm_signal_property_changed(conn, modem->path,
+ CALL_SETTINGS_INTERFACE,
+ "HideCallerId", DBUS_TYPE_STRING, &str);
+ }
+}
+
+static void set_clip(struct ofono_modem *modem, int clip)
+{
+ struct call_settings_data *cs = modem->call_settings;
+
+ if (cs->clip != clip) {
+ DBusConnection *conn = dbus_gsm_connection();
+ const char *str = clip_status_to_string(clip);
+
+ cs->clip = clip;
+
+ dbus_gsm_signal_property_changed(conn, modem->path,
+ CALL_SETTINGS_INTERFACE,
+ "CallingLinePresentation",
+ DBUS_TYPE_STRING, &str);
+ }
+}
+
+static void set_colp(struct ofono_modem *modem, int colp)
+{
+ struct call_settings_data *cs = modem->call_settings;
+
+ if (cs->colp != colp) {
+ DBusConnection *conn = dbus_gsm_connection();
+ const char *str = colp_status_to_string(colp);
+
+ cs->colp = colp;
+
+ dbus_gsm_signal_property_changed(conn, modem->path,
+ CALL_SETTINGS_INTERFACE,
+ "CalledLinePresentation",
+ DBUS_TYPE_STRING, &str);
+ }
+}
+
+static void set_colr(struct ofono_modem *modem, int colr)
+{
+ struct call_settings_data *cs = modem->call_settings;
+
+ if (cs->colr != colr) {
+ DBusConnection *conn = dbus_gsm_connection();
+ const char *str = colr_status_to_string(colr);
+
+ cs->colr = colr;
+
+ dbus_gsm_signal_property_changed(conn, modem->path,
+ CALL_SETTINGS_INTERFACE,
+ "CalledLineRestriction",
+ DBUS_TYPE_STRING, &str);
+ }
+}
+
+static struct call_settings_data *call_settings_create()
+{
+ struct call_settings_data *r;
+
+ r = g_try_new0(struct call_settings_data, 1);
+
+ if (!r)
+ return r;
+
+ /* Set all the settings to unknown state */
+ r->clip = 2;
+ r->clir = 2;
+ r->colp = 2;
+ r->colr = 2;
+
+ return r;
+}
+
+static void call_settings_destroy(gpointer data)
+{
+ struct ofono_modem *modem = data;
+ struct call_settings_data *cs = modem->call_settings;
+
+ cs_unregister_ss_controls(modem);
+
+ g_free(cs);
+}
+
+static void generate_ss_query_reply(struct ofono_modem *modem,
+ const char *context, const char *value)
+{
+ struct call_settings_data *cs = modem->call_settings;
+ const char *sig = "(ss)";
+ const char *ss_type = ss_control_type_to_string(cs->ss_req_type);
+ DBusMessageIter iter;
+ DBusMessageIter var;
+ DBusMessageIter vstruct;
+ DBusMessage *reply;
+
+ reply = dbus_message_new_method_return(cs->pending);
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &context);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, sig, &var);
+
+ dbus_message_iter_open_container(&var, DBUS_TYPE_STRUCT, NULL,
+ &vstruct);
+
+ dbus_message_iter_append_basic(&vstruct, DBUS_TYPE_STRING,
+ &ss_type);
+
+ dbus_message_iter_append_basic(&vstruct, DBUS_TYPE_STRING, &value);
+
+ dbus_message_iter_close_container(&var, &vstruct);
+
+ dbus_message_iter_close_container(&iter, &var);
+
+ dbus_gsm_pending_reply(&cs->pending, reply);
+}
+
+static void clip_colp_colr_ss_query_cb(const struct ofono_error *error,
+ int status, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_settings_data *cs = modem->call_settings;
+ const char *context;
+ const char *value;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ ofono_debug("Error occurred during ss control query");
+ dbus_gsm_pending_reply(&cs->pending,
+ dbus_gsm_failed(cs->pending));
+
+ return;
+ }
+
+ switch (cs->call_setting_type) {
+ case CALL_SETTING_TYPE_CLIP:
+ set_clip(modem, status);
+ value = clip_status_to_string(status);
+ context = "CallingLinePresentation";
+ break;
+
+ case CALL_SETTING_TYPE_COLP:
+ set_colp(modem, status);
+ value = colp_status_to_string(status);
+ context = "CalledLinePresentation";
+ break;
+
+ case CALL_SETTING_TYPE_COLR:
+ set_colr(modem, status);
+ value = colr_status_to_string(status);
+ context = "CallingLineRestriction";
+ break;
+
+ default:
+ dbus_gsm_pending_reply(&cs->pending,
+ dbus_gsm_failed(cs->pending));
+ ofono_error("Unknown type during COLR/COLP/CLIP ss");
+ return;
+ };
+
+ generate_ss_query_reply(modem, context, value);
+}
+
+static gboolean clip_colp_colr_ss(struct ofono_modem *modem, int type,
+ const char *sc, const char *sia,
+ const char *sib, const char *sic,
+ const char *dn, DBusMessage *msg)
+{
+ struct call_settings_data *cs = modem->call_settings;
+ DBusConnection *conn = dbus_gsm_connection();
+ void (*query_op)(struct ofono_modem *modem, ofono_call_setting_status_cb_t cb,
+ void *data);
+
+ if (!cs)
+ return FALSE;
+
+ if (cs->pending) {
+ DBusMessage *reply = dbus_gsm_busy(msg);
+ g_dbus_send_message(conn, reply);
+
+ return TRUE;
+ }
+
+ if (!strcmp(sc, "30")) {
+ cs->call_setting_type = CALL_SETTING_TYPE_CLIP;
+ query_op = cs->ops->clip_query;
+ } else if (!strcmp(sc, "76")) {
+ cs->call_setting_type = CALL_SETTING_TYPE_COLP;
+ query_op = cs->ops->colp_query;
+ } else if (!strcmp(sc, "77")) {
+ cs->call_setting_type = CALL_SETTING_TYPE_COLR;
+ query_op = cs->ops->colr_query;
+ } else
+ return FALSE;
+
+ if (type != SS_CONTROL_TYPE_QUERY || strlen(sia) || strlen(sib) ||
+ strlen(sic) || strlen(dn)) {
+ DBusMessage *reply = dbus_gsm_invalid_format(msg);
+ g_dbus_send_message(conn, reply);
+
+ return TRUE;
+ }
+
+ if (!query_op) {
+ DBusMessage *reply = dbus_gsm_not_implemented(msg);
+ g_dbus_send_message(conn, reply);
+
+ return TRUE;
+ }
+
+ ofono_debug("Received CLIP/COLR/COLP query ss control");
+
+ cs->pending = dbus_message_ref(msg);
+
+ query_op(modem, clip_colp_colr_ss_query_cb, modem);
+
+ return TRUE;
+}
+
+static void clir_ss_query_callback(const struct ofono_error *error,
+ int override, int network, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_settings_data *cs = modem->call_settings;
+ const char *value;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ ofono_debug("setting clir via SS failed");
+ dbus_gsm_pending_reply(&cs->pending,
+ dbus_gsm_failed(cs->pending));
+
+ return;
+ }
+
+ switch (network) {
+ case CLIR_STATUS_UNKNOWN:
+ value = "uknown";
+ break;
+
+ case CLIR_STATUS_PROVISIONED_PERMANENT:
+ value = "enabled";
+ break;
+
+ case CLIR_STATUS_NOT_PROVISIONED:
+ value = "disabled";
+ break;
+
+ case CLIR_STATUS_TEMPORARY_RESTRICTED:
+ if (override == OFONO_CLIR_OPTION_SUPPRESSION)
+ value = "enabled";
+ else
+ value = "disabled";
+ break;
+
+ case CLIR_STATUS_TEMPORARY_ALLOWED:
+ if (override == OFONO_CLIR_OPTION_INVOCATION)
+ value = "enabled";
+ else
+ value = "disabled";
+ break;
+ };
+
+ generate_ss_query_reply(modem, "CallingLineRestriction", value);
+
+ set_clir_network(modem, network);
+ set_clir_override(modem, override);
+}
+
+static void clir_ss_set_callback(const struct ofono_error *error, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_settings_data *cs = modem->call_settings;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ ofono_debug("setting clir via SS failed");
+ dbus_gsm_pending_reply(&cs->pending,
+ dbus_gsm_failed(cs->pending));
+
+ return;
+ }
+
+ cs->ops->clir_query(modem, clir_ss_query_callback, modem);
+}
+
+static gboolean clir_ss_control(struct ofono_modem *modem, int type,
+ const char *sc, const char *sia,
+ const char *sib, const char *sic,
+ const char *dn, DBusMessage *msg)
+{
+ struct call_settings_data *cs = modem->call_settings;
+ DBusConnection *conn = dbus_gsm_connection();
+ //void *op;
+
+ if (!cs)
+ return FALSE;
+
+ if (strcmp(sc, "31"))
+ return FALSE;
+
+ if (cs->pending) {
+ DBusMessage *reply = dbus_gsm_busy(msg);
+ g_dbus_send_message(conn, reply);
+
+ return TRUE;
+ }
+
+ /* This is the temporary form of CLIR, handled in voicecalls */
+ if (!strlen(sia) && !strlen(sib) & !strlen(sic) &&
+ strlen(dn) && type != SS_CONTROL_TYPE_QUERY)
+ return FALSE;
+
+ if (strlen(sia) || strlen(sib) || strlen(sic) || strlen(dn)) {
+ DBusMessage *reply = dbus_gsm_invalid_format(msg);
+ g_dbus_send_message(conn, reply);
+
+ return TRUE;
+ }
+
+ if ((type == SS_CONTROL_TYPE_QUERY && !cs->ops->clir_query) ||
+ (type != SS_CONTROL_TYPE_QUERY && !cs->ops->clir_set)) {
+ DBusMessage *reply = dbus_gsm_not_implemented(msg);
+ g_dbus_send_message(conn, reply);
+
+ return TRUE;
+ }
+
+ cs->call_setting_type = CALL_SETTING_TYPE_CLIR;
+ cs->pending = dbus_message_ref(msg);
+
+ switch (type) {
+ case SS_CONTROL_TYPE_REGISTRATION:
+ case SS_CONTROL_TYPE_ACTIVATION:
+ cs->ss_req_type = SS_CONTROL_TYPE_ACTIVATION;
+ cs->ops->clir_set(modem, OFONO_CLIR_OPTION_INVOCATION,
+ clir_ss_set_callback, modem);
+ break;
+
+ case SS_CONTROL_TYPE_QUERY:
+ cs->ss_req_type = SS_CONTROL_TYPE_QUERY;
+ cs->ops->clir_query(modem, clir_ss_query_callback,
+ modem);
+ break;
+
+ case SS_CONTROL_TYPE_DEACTIVATION:
+ case SS_CONTROL_TYPE_ERASURE:
+ cs->ss_req_type = SS_CONTROL_TYPE_DEACTIVATION;
+ cs->ops->clir_set(modem, OFONO_CLIR_OPTION_SUPPRESSION,
+ clir_ss_set_callback, modem);
+ break;
+ };
+
+ return TRUE;
+}
+
+static void cs_register_ss_controls(struct ofono_modem *modem)
+{
+ struct call_settings_data *cs = modem->call_settings;
+
+ ss_control_register(modem, "30", clip_colp_colr_ss);
+ ss_control_register(modem, "31", clir_ss_control);
+ ss_control_register(modem, "76", clip_colp_colr_ss);
+
+ if (cs->ops->colr_query)
+ ss_control_register(modem, "77", clip_colp_colr_ss);
+}
+
+static void cs_unregister_ss_controls(struct ofono_modem *modem)
+{
+ struct call_settings_data *cs = modem->call_settings;
+
+ ss_control_unregister(modem, "30", clip_colp_colr_ss);
+ ss_control_unregister(modem, "31", clir_ss_control);
+ ss_control_unregister(modem, "76", clip_colp_colr_ss);
+
+ if (cs->ops->colr_query)
+ ss_control_unregister(modem, "77", clip_colp_colr_ss);
+}
+
+static DBusMessage *generate_get_properties_reply(struct ofono_modem *modem,
+ DBusMessage *msg)
+{
+ struct call_settings_data *cs = modem->call_settings;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ const char *str;
+
+ reply = dbus_message_new_method_return(msg);
+
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ PROPERTIES_ARRAY_SIGNATURE,
+ &dict);
+
+ str = clip_status_to_string(cs->clip);
+ dbus_gsm_dict_append(&dict, "CallingLinePresentation",
+ DBUS_TYPE_STRING, &str);
+
+ str = colp_status_to_string(cs->colp);
+ dbus_gsm_dict_append(&dict, "CalledLinePresentation",
+ DBUS_TYPE_STRING, &str);
+
+ str = colr_status_to_string(cs->colr);
+ dbus_gsm_dict_append(&dict, "CalledLineRestriction",
+ DBUS_TYPE_STRING, &str);
+
+ str = clir_status_to_string(cs->clir);
+ dbus_gsm_dict_append(&dict, "CallingLineRestriction",
+ DBUS_TYPE_STRING, &str);
+
+ str = hide_callerid_to_string(cs->clir_setting);
+ dbus_gsm_dict_append(&dict, "HideCallerId", DBUS_TYPE_STRING, &str);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static void cs_clir_callback(const struct ofono_error *error,
+ int override_setting, int network_setting,
+ void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_settings_data *cs = modem->call_settings;
+ //DBusConnection *conn = dbus_gsm_connection();
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
+ goto out;
+
+ set_clir_network(modem, network_setting);
+ set_clir_override(modem, override_setting);
+
+ cs->flags |= CALL_SETTINGS_FLAG_CACHED;
+
+out:
+ if (cs->pending) {
+ DBusMessage *reply = generate_get_properties_reply(modem,
+ cs->pending);
+ dbus_gsm_pending_reply(&cs->pending, reply);
+ }
+}
+
+static gboolean query_clir(gpointer user)
+{
+ struct ofono_modem *modem = user;
+ struct call_settings_data *cs = modem->call_settings;
+
+ if (!cs->ops->clir_query) {
+ if (cs->pending) {
+ DBusMessage *reply =
+ generate_get_properties_reply(modem,
+ cs->pending);
+ dbus_gsm_pending_reply(&cs->pending, reply);
+ }
+
+ return FALSE;
+ }
+
+ cs->ops->clir_query(modem, cs_clir_callback, modem);
+
+ return FALSE;
+}
+
+static void cs_clip_callback(const struct ofono_error *error,
+ int state, void *data)
+{
+ struct ofono_modem *modem = data;
+
+ if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+ set_clip(modem, state);
+
+ g_timeout_add(0, query_clir, modem);
+}
+
+static gboolean query_clip(gpointer user)
+{
+ struct ofono_modem *modem = user;
+ struct call_settings_data *cs = modem->call_settings;
+
+ if (!cs->ops->clip_query) {
+ query_clir(modem);
+ return FALSE;
+ }
+
+ cs->ops->clip_query(modem, cs_clip_callback, modem);
+
+ return FALSE;
+}
+
+static void cs_colp_callback(const struct ofono_error *error,
+ int state, void *data)
+{
+ struct ofono_modem *modem = data;
+
+ if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+ set_colp(modem, state);
+
+ g_timeout_add(0, query_clip, modem);
+}
+
+static gboolean query_colp(gpointer user)
+{
+ struct ofono_modem *modem = user;
+ struct call_settings_data *cs = modem->call_settings;
+
+ if (!cs->ops->colp_query) {
+ query_clip(modem);
+ return FALSE;
+ }
+
+ cs->ops->colp_query(modem, cs_colp_callback, modem);
+
+ return FALSE;
+}
+
+static void cs_colr_callback(const struct ofono_error *error,
+ int state, void *data)
+{
+ struct ofono_modem *modem = data;
+
+ if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+ set_colr(modem, state);
+
+ g_timeout_add(0, query_colp, modem);
+}
+
+static gboolean query_colr(gpointer user)
+{
+ struct ofono_modem *modem = user;
+ struct call_settings_data *cs = modem->call_settings;
+
+ if (!cs->ops->colr_query) {
+ query_colp(modem);
+ return FALSE;
+ }
+
+ cs->ops->colr_query(modem, cs_colr_callback, modem);
+
+ return FALSE;
+}
+
+static DBusMessage *cs_get_properties(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_settings_data *cs = modem->call_settings;
+
+ if (cs->pending)
+ return dbus_gsm_busy(msg);
+
+ if (cs->flags & CALL_SETTINGS_FLAG_CACHED)
+ return generate_get_properties_reply(modem, msg);
+
+ /* Query the settings and report back */
+ cs->pending = dbus_message_ref(msg);
+
+ query_colr(modem);
+
+ return NULL;
+}
+
+static void clir_set_query_callback(const struct ofono_error *error,
+ int override_setting,
+ int network_setting, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_settings_data *cs = modem->call_settings;
+ DBusMessage *reply;
+
+ if (!cs->pending)
+ return;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ ofono_error("setting clir was successful, but the query was not");
+
+ cs->flags &= ~CALL_SETTINGS_FLAG_CACHED;
+
+ reply = dbus_gsm_failed(cs->pending);
+ dbus_gsm_pending_reply(&cs->pending, reply);
+ return;
+ }
+
+ reply = dbus_message_new_method_return(cs->pending);
+ dbus_gsm_pending_reply(&cs->pending, reply);
+
+ set_clir_override(modem, override_setting);
+ set_clir_network(modem, network_setting);
+}
+
+static void clir_set_callback(const struct ofono_error *error, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_settings_data *cs = modem->call_settings;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ ofono_debug("setting clir failed");
+ dbus_gsm_pending_reply(&cs->pending,
+ dbus_gsm_failed(cs->pending));
+
+ return;
+ }
+
+ /* Assume that if we have clir_set, we have clir_query */
+ cs->ops->clir_query(modem, clir_set_query_callback, modem);
+}
+
+static DBusMessage *set_clir(DBusMessage *msg, struct ofono_modem *modem,
+ const char *setting)
+{
+ struct call_settings_data *cs = modem->call_settings;
+ int clir = -1;
+
+ if (cs->ops->clir_set == NULL)
+ return dbus_gsm_not_implemented(msg);
+
+ if (!strcmp(setting, "default"))
+ clir = 0;
+ else if (!strcmp(setting, "enabled"))
+ clir = 1;
+ else if (!strcmp(setting, "disabled"))
+ clir = 2;
+
+ if (clir == -1)
+ return dbus_gsm_invalid_format(msg);
+
+ cs->pending = dbus_message_ref(msg);
+
+ cs->ops->clir_set(modem, clir, clir_set_callback, modem);
+
+ return NULL;
+}
+
+static DBusMessage *cs_set_property(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_settings_data *cs = modem->call_settings;
+ DBusMessageIter iter;
+ DBusMessageIter var;
+ const char *property;
+
+ if (cs->pending)
+ return dbus_gsm_busy(msg);
+
+ if (!dbus_message_iter_init(msg, &iter))
+ return dbus_gsm_invalid_args(msg);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return dbus_gsm_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&iter, &property);
+ dbus_message_iter_next(&iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+ return dbus_gsm_invalid_args(msg);
+
+ dbus_message_iter_recurse(&iter, &var);
+
+ if (!strcmp(property, "HideCallerId")) {
+ const char *setting;
+
+ if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+ return dbus_gsm_invalid_format(msg);
+
+ dbus_message_iter_get_basic(&var, &setting);
+
+ return set_clir(msg, modem, setting);
+ }
+
+ return dbus_gsm_invalid_args(msg);
+}
+
+static GDBusMethodTable cs_methods[] = {
+ { "GetProperties", "", "a{sv}", cs_get_properties,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "SetProperty", "sv", "", cs_set_property,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { }
+};
+
+static GDBusSignalTable cs_signals[] = {
+ { "PropertyChanged", "sv" },
+ { }
+};
+
+int ofono_call_settings_register(struct ofono_modem *modem,
+ struct ofono_call_settings_ops *ops)
+{
+ DBusConnection *conn = dbus_gsm_connection();
+
+ if (modem == NULL)
+ return -1;
+
+ if (ops == NULL)
+ return -1;
+
+ modem->call_settings = call_settings_create();
+
+ if (!modem->call_settings)
+ return -1;
+
+ modem->call_settings->ops = ops;
+
+ if (!g_dbus_register_interface(conn, modem->path,
+ CALL_SETTINGS_INTERFACE,
+ cs_methods, cs_signals, NULL,
+ modem, call_settings_destroy)) {
+ ofono_error("Could not register CallSettings %s", modem->path);
+ call_settings_destroy(modem);
+
+ return -1;
+ }
+
+ ofono_debug("Registered call settings interface");
+
+ cs_register_ss_controls(modem);
+
+ modem_add_interface(modem, CALL_SETTINGS_INTERFACE);
+ return 0;
+}
+
+void ofono_call_settings_unregister(struct ofono_modem *modem)
+{
+ struct call_settings_data *cs = modem->call_settings;
+ DBusConnection *conn = dbus_gsm_connection();
+
+ if (!cs)
+ return;
+
+ modem_remove_interface(modem, CALL_SETTINGS_INTERFACE);
+ g_dbus_unregister_interface(conn, modem->path,
+ CALL_SETTINGS_INTERFACE);
+
+ modem->call_settings = NULL;
+}
diff --git a/src/call-waiting.c b/src/call-waiting.c
new file mode 100644
index 00000000..2b3e5d9b
--- /dev/null
+++ b/src/call-waiting.c
@@ -0,0 +1,648 @@
+/*
+ *
+ * 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
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <dbus/dbus.h>
+#include <glib.h>
+#include <gdbus.h>
+
+#include "ofono.h"
+
+#include "driver.h"
+#include "common.h"
+#include "dbus-gsm.h"
+#include "modem.h"
+#include "ussd.h"
+
+#define CALL_WAITING_INTERFACE "org.ofono.CallWaiting"
+
+#define CALL_WAITING_FLAG_CACHED 0x1
+
+struct call_waiting_data {
+ struct ofono_call_waiting_ops *ops;
+ int flags;
+ DBusMessage *pending;
+ GSList *cw_list;
+ int ss_req_type;
+ int ss_req_cls;
+};
+
+static const char *enabled = "enabled";
+static const char *disabled = "disabled";
+
+static void cw_register_ss_controls(struct ofono_modem *modem);
+static void cw_unregister_ss_controls(struct ofono_modem *modem);
+
+static gint cw_condition_compare(gconstpointer a, gconstpointer b)
+{
+ const struct ofono_cw_condition *ca = a;
+ const struct ofono_cw_condition *cb = b;
+
+ if (ca->cls < cb->cls)
+ return -1;
+
+ if (ca->cls > cb->cls)
+ return 1;
+
+ return 0;
+}
+
+static gint cw_condition_find_with_cls(gconstpointer a, gconstpointer b)
+{
+ const struct ofono_cw_condition *c = a;
+ int cls = GPOINTER_TO_INT(b);
+
+ if (c->cls < cls)
+ return -1;
+
+ if (c->cls > cls)
+ return 1;
+
+ return 0;
+}
+
+static struct call_waiting_data *call_waiting_create()
+{
+ struct call_waiting_data *r;
+
+ r = g_try_new0(struct call_waiting_data, 1);
+
+ if (!r)
+ return r;
+
+ return r;
+}
+
+static void call_waiting_destroy(gpointer data)
+{
+ struct ofono_modem *modem = data;
+ struct call_waiting_data *cw = modem->call_waiting;
+
+ cw_unregister_ss_controls(modem);
+
+ g_slist_foreach(cw->cw_list, (GFunc)g_free, NULL);
+ g_slist_free(cw->cw_list);
+
+ g_free(cw);
+}
+
+static void cw_cond_list_print(GSList *list)
+{
+ GSList *l;
+ struct ofono_cw_condition *cond;
+
+ for (l = list; l; l = l->next) {
+ cond = l->data;
+
+ ofono_debug("CW condition status: %d, class: %d",
+ cond->status, cond->cls);
+ }
+}
+
+static GSList *cw_cond_list_create(int total,
+ const struct ofono_cw_condition *list)
+{
+ GSList *l = NULL;
+ int i;
+ int j;
+ struct ofono_cw_condition *cond;
+
+ /* Specification is not really clear on how the results are reported,
+ * most modems report it as multiple list items, one for each class
+ * however, specification does leave room for a single compound value
+ * to be reported
+ */
+ for (i = 0; i < total; i++) {
+ for (j = 1; j <= BEARER_CLASS_PAD; j = j << 1) {
+ if (!(list[i].cls & j))
+ continue;
+
+ if (list[i].status == 0)
+ continue;
+
+ cond = g_new0(struct ofono_cw_condition, 1);
+
+ memcpy(cond, &list[i], sizeof(struct ofono_cw_condition));
+ cond->cls = j;
+
+ l = g_slist_insert_sorted(l, cond,
+ cw_condition_compare);
+ }
+ }
+
+ return l;
+}
+
+static void set_new_cond_list(struct ofono_modem *modem, GSList *new_cw_list)
+{
+ struct call_waiting_data *cw = modem->call_waiting;
+ DBusConnection *conn = dbus_gsm_connection();
+ GSList *n;
+ GSList *o;
+ struct ofono_cw_condition *nc;
+ struct ofono_cw_condition *oc;
+ char buf[64];
+
+ for (n = new_cw_list; n; n = n->next) {
+ nc = n->data;
+
+ if (nc->cls > BEARER_CLASS_FAX)
+ continue;
+
+ sprintf(buf, "%s", bearer_class_to_string(nc->cls));
+
+ o = g_slist_find_custom(cw->cw_list, GINT_TO_POINTER(nc->cls),
+ cw_condition_find_with_cls);
+
+ if (o) {
+ g_free(o->data);
+ cw->cw_list = g_slist_remove(cw->cw_list, o->data);
+ } else {
+ dbus_gsm_signal_property_changed(conn, modem->path,
+ CALL_WAITING_INTERFACE,
+ buf, DBUS_TYPE_STRING,
+ &enabled);
+ }
+ }
+
+ for (o = cw->cw_list; o; o = o->next) {
+ oc = o->data;
+
+ sprintf(buf, "%s", bearer_class_to_string(oc->cls));
+
+ dbus_gsm_signal_property_changed(conn, modem->path,
+ CALL_WAITING_INTERFACE,
+ buf, DBUS_TYPE_STRING,
+ &disabled);
+ }
+
+ g_slist_foreach(cw->cw_list, (GFunc)g_free, NULL);
+ g_slist_free(cw->cw_list);
+
+ cw->cw_list = new_cw_list;
+}
+
+static void property_append_cw_conditions(DBusMessageIter *dict,
+ GSList *cw_list, int mask)
+{
+ GSList *l;
+ int i;
+ struct ofono_cw_condition *cw;
+ const char *prop;
+
+ for (i = 1, l = cw_list; i <= BEARER_CLASS_PAD; i = i << 1) {
+ if (!(mask & i))
+ continue;
+
+ prop = bearer_class_to_string(i);
+
+ while (l && (cw = l->data) && (cw->cls < i))
+ l = l->next;
+
+ if (!l || cw->cls != i) {
+ dbus_gsm_dict_append(dict, prop, DBUS_TYPE_STRING,
+ &disabled);
+ continue;
+ }
+
+ dbus_gsm_dict_append(dict, prop, DBUS_TYPE_STRING, &enabled);
+ }
+}
+
+static void generate_ss_query_reply(struct ofono_modem *modem)
+{
+ struct call_waiting_data *cw = modem->call_waiting;
+ const char *sig = "(sa{sv})";
+ const char *ss_type = ss_control_type_to_string(cw->ss_req_type);
+ const char *context = "CallWaiting";
+ DBusMessageIter iter;
+ DBusMessageIter var;
+ DBusMessageIter vstruct;
+ DBusMessageIter dict;
+ DBusMessage *reply;
+
+ reply = dbus_message_new_method_return(cw->pending);
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &context);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, sig, &var);
+
+ dbus_message_iter_open_container(&var, DBUS_TYPE_STRUCT, NULL,
+ &vstruct);
+
+ dbus_message_iter_append_basic(&vstruct, DBUS_TYPE_STRING,
+ &ss_type);
+
+ dbus_message_iter_open_container(&vstruct, DBUS_TYPE_ARRAY,
+ PROPERTIES_ARRAY_SIGNATURE, &dict);
+
+ property_append_cw_conditions(&dict, cw->cw_list, cw->ss_req_cls);
+
+ dbus_message_iter_close_container(&vstruct, &dict);
+
+ dbus_message_iter_close_container(&var, &vstruct);
+
+ dbus_message_iter_close_container(&iter, &var);
+
+ dbus_gsm_pending_reply(&cw->pending, reply);
+}
+
+static void cw_ss_query_callback(const struct ofono_error *error, int num,
+ struct ofono_cw_condition *cond_list,
+ void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_waiting_data *cw = modem->call_waiting;
+ GSList *l;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ ofono_debug("setting CW via SS failed");
+
+ cw->flags &= ~CALL_WAITING_FLAG_CACHED;
+ dbus_gsm_pending_reply(&cw->pending,
+ dbus_gsm_failed(cw->pending));
+
+ return;
+ }
+
+ l = cw_cond_list_create(num, cond_list);
+
+ cw_cond_list_print(l);
+
+ set_new_cond_list(modem, l);
+ cw->flags |= CALL_WAITING_FLAG_CACHED;
+
+ generate_ss_query_reply(modem);
+}
+
+static void cw_ss_set_callback(const struct ofono_error *error, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_waiting_data *cw = modem->call_waiting;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ ofono_debug("setting CW via SS failed");
+ dbus_gsm_pending_reply(&cw->pending,
+ dbus_gsm_failed(cw->pending));
+
+ return;
+ }
+
+ cw->ops->query(modem, cw->ss_req_cls, cw_ss_query_callback, modem);
+}
+
+static gboolean cw_ss_control(struct ofono_modem *modem, int type,
+ const char *sc, const char *sia,
+ const char *sib, const char *sic,
+ const char *dn, DBusMessage *msg)
+{
+ struct call_waiting_data *cw = modem->call_waiting;
+ DBusConnection *conn = dbus_gsm_connection();
+ int cls = BEARER_CLASS_DEFAULT;
+ DBusMessage *reply;
+ //void *op;
+
+ if (!cw)
+ return FALSE;
+
+ if (strcmp(sc, "43"))
+ return FALSE;
+
+ if (cw->pending) {
+ reply = dbus_gsm_busy(msg);
+ goto error;
+ }
+
+ if (strlen(sib) || strlen(sib) || strlen(dn))
+ goto bad_format;
+
+ if ((type == SS_CONTROL_TYPE_QUERY && !cw->ops->query) ||
+ (type != SS_CONTROL_TYPE_QUERY && !cw->ops->set)) {
+ reply = dbus_gsm_not_implemented(msg);
+ goto error;
+ }
+
+ if (strlen(sia) > 0) {
+ long service_code;
+ char *end;
+
+ service_code = strtoul(sia, &end, 10);
+
+ if (end == sia || *end != '\0')
+ goto bad_format;
+
+ cls = mmi_service_code_to_bearer_class(service_code);
+ if (cls == 0)
+ goto bad_format;
+ }
+
+ cw->ss_req_cls = cls;
+ cw->pending = dbus_message_ref(msg);
+
+ switch (type) {
+ case SS_CONTROL_TYPE_REGISTRATION:
+ case SS_CONTROL_TYPE_ACTIVATION:
+ cw->ss_req_type = SS_CONTROL_TYPE_ACTIVATION;
+ cw->ops->set(modem, 1, cls, cw_ss_set_callback, modem);
+ break;
+
+ case SS_CONTROL_TYPE_QUERY:
+ cw->ss_req_type = SS_CONTROL_TYPE_QUERY;
+ cw->ops->query(modem, cls, cw_ss_query_callback, modem);
+ break;
+
+ case SS_CONTROL_TYPE_DEACTIVATION:
+ case SS_CONTROL_TYPE_ERASURE:
+ cw->ss_req_type = SS_CONTROL_TYPE_DEACTIVATION;
+ cw->ops->set(modem, 0, cls, cw_ss_set_callback, modem);
+ break;
+ }
+
+ return TRUE;
+
+bad_format:
+ reply = dbus_gsm_invalid_format(msg);
+error:
+ g_dbus_send_message(conn, reply);
+ return TRUE;
+}
+
+static void cw_register_ss_controls(struct ofono_modem *modem)
+{
+ ss_control_register(modem, "43", cw_ss_control);
+}
+
+static void cw_unregister_ss_controls(struct ofono_modem *modem)
+{
+ ss_control_unregister(modem, "43", cw_ss_control);
+}
+
+static DBusMessage *generate_get_properties_reply(struct ofono_modem *modem,
+ DBusMessage *msg)
+{
+ struct call_waiting_data *cw = modem->call_waiting;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ //int i;
+ //GSList *l;
+
+ reply = dbus_message_new_method_return(msg);
+
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ PROPERTIES_ARRAY_SIGNATURE,
+ &dict);
+
+ property_append_cw_conditions(&dict, cw->cw_list, BEARER_CLASS_DEFAULT);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static void cw_query_callback(const struct ofono_error *error, int num,
+ struct ofono_cw_condition *cond_list, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_waiting_data *cw = modem->call_waiting;
+ GSList *l = NULL;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ ofono_debug("Error during cw query");
+ goto out;
+ }
+
+ l = cw_cond_list_create(num, cond_list);
+
+ cw_cond_list_print(l);
+
+ set_new_cond_list(modem, l);
+ cw->flags |= CALL_WAITING_FLAG_CACHED;
+
+out:
+ if (cw->pending) {
+ DBusMessage *reply;
+
+ reply = generate_get_properties_reply(modem, cw->pending);
+ dbus_gsm_pending_reply(&cw->pending, reply);
+ }
+}
+
+static DBusMessage *cw_get_properties(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_waiting_data *cw = modem->call_waiting;
+
+ if (cw->pending)
+ return dbus_gsm_busy(msg);
+
+ if (!cw->ops->query)
+ return dbus_gsm_not_implemented(msg);
+
+ if (cw->flags & CALL_WAITING_FLAG_CACHED)
+ return generate_get_properties_reply(modem, msg);
+
+ cw->pending = dbus_message_ref(msg);
+
+ cw->ops->query(modem, BEARER_CLASS_DEFAULT, cw_query_callback, modem);
+
+ return NULL;
+}
+
+static void set_query_callback(const struct ofono_error *error, int num,
+ struct ofono_cw_condition *cond_list, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_waiting_data *cw = modem->call_waiting;
+ GSList *l = NULL;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ ofono_error("CW set succeeded, but query failed!");
+ cw->flags &= ~CALL_WAITING_FLAG_CACHED;
+
+ dbus_gsm_pending_reply(&cw->pending,
+ dbus_gsm_failed(cw->pending));
+ return;
+ }
+
+ dbus_gsm_pending_reply(&cw->pending,
+ dbus_message_new_method_return(cw->pending));
+
+ l = cw_cond_list_create(num, cond_list);
+
+ cw_cond_list_print(l);
+
+ set_new_cond_list(modem, l);
+}
+
+static void set_callback(const struct ofono_error *error, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_waiting_data *cw = modem->call_waiting;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ ofono_debug("Error occurred during CW set");
+
+ dbus_gsm_pending_reply(&cw->pending,
+ dbus_gsm_failed(cw->pending));
+
+ return;
+ }
+
+ cw->ops->query(modem, BEARER_CLASS_DEFAULT, set_query_callback, modem);
+}
+
+static DBusMessage *cw_set_property(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct ofono_modem *modem = data;
+ struct call_waiting_data *cw = modem->call_waiting;
+ DBusMessageIter iter;
+ DBusMessageIter var;
+ const char *property;
+ int i;
+
+ if (cw->pending)
+ return dbus_gsm_busy(msg);
+
+ if (!cw->ops->set)
+ return dbus_gsm_not_implemented(msg);
+
+ if (!dbus_message_iter_init(msg, &iter))
+ return dbus_gsm_invalid_args(msg);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return dbus_gsm_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&iter, &property);
+ dbus_message_iter_next(&iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+ return dbus_gsm_invalid_args(msg);
+
+ dbus_message_iter_recurse(&iter, &var);
+
+ for (i = 1; i < BEARER_CLASS_SMS; i = i << 1)
+ if (!strcmp(property, bearer_class_to_string(i)))
+ break;
+
+ if (i < BEARER_CLASS_SMS) {
+ const char *value;
+ int status;
+
+ if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+ return dbus_gsm_invalid_format(msg);
+
+ dbus_message_iter_get_basic(&var, &value);
+
+ if (!strcmp(value, "enabled"))
+ status = 1;
+ else if (!strcmp(value, "disabled"))
+ status = 0;
+ else
+ return dbus_gsm_invalid_format(msg);
+
+ cw->pending = dbus_message_ref(msg);
+
+ cw->ops->set(modem, status, i, set_callback, modem);
+ }
+
+ return dbus_gsm_invalid_args(msg);
+}
+
+static GDBusMethodTable cw_methods[] = {
+ { "GetProperties", "", "a{sv}", cw_get_properties,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "SetProperty", "sv", "", cw_set_property,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { }
+};
+
+static GDBusSignalTable cw_signals[] = {
+ { "PropertyChanged", "sv" },
+ { }
+};
+
+int ofono_call_waiting_register(struct ofono_modem *modem,
+ struct ofono_call_waiting_ops *ops)
+{
+ DBusConnection *conn = dbus_gsm_connection();
+
+ if (modem == NULL)
+ return -1;
+
+ if (ops == NULL)
+ return -1;
+
+ modem->call_waiting = call_waiting_create();
+
+ if (!modem->call_waiting)
+ return -1;
+
+ modem->call_waiting->ops = ops;
+
+ if (!g_dbus_register_interface(conn, modem->path,
+ CALL_WAITING_INTERFACE,
+ cw_methods, cw_signals, NULL,
+ modem, call_waiting_destroy)) {
+ ofono_error("Could not register CallWaiting %s", modem->path);
+ call_waiting_destroy(modem);
+
+ return -1;
+ }
+
+ ofono_debug("Registered call waiting interface");
+
+ cw_register_ss_controls(modem);
+
+ modem_add_interface(modem, CALL_WAITING_INTERFACE);
+ return 0;
+}
+
+void ofono_call_waiting_unregister(struct ofono_modem *modem)
+{
+ struct call_waiting_data *cw = modem->call_waiting;
+ DBusConnection *conn = dbus_gsm_connection();
+
+ if (!cw)
+ return;
+
+ modem_remove_interface(modem, CALL_WAITING_INTERFACE);
+ g_dbus_unregister_interface(conn, modem->path,
+ CALL_WAITING_INTERFACE);
+
+ modem->call_waiting = NULL;
+}
diff --git a/src/common.c b/src/common.c
new file mode 100644
index 00000000..f3ae3f63
--- /dev/null
+++ b/src/common.c
@@ -0,0 +1,576 @@
+/*
+ *
+ * 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 <ctype.h>
+
+#include <glib.h>
+
+#include "driver.h"
+#include "common.h"
+
+struct error_entry {
+ int error;
+ const char *str;
+};
+
+/* 0-127 from 24.011 Annex E2
+ * 127-255 23.040 Section 9.2.3.22
+ * Rest are from 27.005 Section 3.2.5
+ */
+struct error_entry cms_errors[] = {
+ { 1, "Unassigned number" },
+ { 8, "Operator determined barring" },
+ { 10, "Call barred" },
+ { 21, "Short message transfer rejected" },
+ { 27, "Destination out of service" },
+ { 28, "Unindentified subscriber" },
+ { 29, "Facility rejected" },
+ { 30, "Unknown subscriber" },
+ { 38, "Network out of order" },
+ { 41, "Temporary failure" },
+ { 42, "Congestion" },
+ { 47, "Recources unavailable" },
+ { 50, "Requested facility not subscribed" },
+ { 69, "Requested facility not implemented" },
+ { 81, "Invalid short message transfer reference value" },
+ { 95, "Invalid message, unspecified" },
+ { 96, "Invalid mandatory information" },
+ { 97, "Message type non existent or not implemented" },
+ { 98, "Message not compatible with short message protocol state" },
+ { 99, "Information element non-existent or not implemented" },
+ { 111, "Protocol error, unspecified" },
+ { 127, "Internetworking error, unspecified" },
+ { 128, "Telematic internetworking not supported" },
+ { 129, "Short message type 0 not supported" },
+ { 130, "Cannot replace short message" },
+ { 143, "Unspecified TP-PID error" },
+ { 144, "Data code scheme not supported" },
+ { 145, "Message class not supported" },
+ { 159, "Unspecified TP-DCS error" },
+ { 160, "Command cannot be actioned" },
+ { 161, "Command unsupported" },
+ { 175, "Unspecified TP-Command error" },
+ { 176, "TPDU not supported" },
+ { 192, "SC busy" },
+ { 193, "No SC subscription" },
+ { 194, "SC System failure" },
+ { 195, "Invalid SME address" },
+ { 196, "Destination SME barred" },
+ { 197, "SM Rejected-Duplicate SM" },
+ { 198, "TP-VPF not supported" },
+ { 199, "TP-VP not supported" },
+ { 208, "(U)SIM SMS Storage full" },
+ { 209, "No SMS Storage capability in SIM" },
+ { 210, "Error in MS" },
+ { 211, "Memory capacity exceeded" },
+ { 212, "Sim application toolkit busy" },
+ { 213, "SIM data download error" },
+ { 255, "Unspecified error cause" },
+ { 300, "ME Failure" },
+ { 301, "SMS service of ME reserved" },
+ { 302, "Operation not allowed" },
+ { 303, "Operation not supported" },
+ { 304, "Invalid PDU mode parameter" },
+ { 305, "Invalid Text mode parameter" },
+ { 310, "(U)SIM not inserted" },
+ { 311, "(U)SIM PIN required" },
+ { 312, "PH-(U)SIM PIN required" },
+ { 313, "(U)SIM failure" },
+ { 314, "(U)SIM busy" },
+ { 315, "(U)SIM wrong" },
+ { 316, "(U)SIM PUK required" },
+ { 317, "(U)SIM PIN2 required" },
+ { 318, "(U)SIM PUK2 required" },
+ { 320, "Memory failure" },
+ { 321, "Invalid memory index" },
+ { 322, "Memory full" },
+ { 330, "SMSC address unknown" },
+ { 331, "No network service" },
+ { 332, "Network timeout" },
+ { 340, "No +CNMA expected" },
+ { 500, "Unknown error" },
+};
+
+/* 27.007, Section 9 */
+struct error_entry cme_errors[] = {
+ { 0, "Phone failure" },
+ { 1, "No connection to phone" },
+ { 2, "Phone adapter link reserved" },
+ { 3, "Operation not allowed" },
+ { 4, "Operation not supported" },
+ { 5, "PH_SIM PIN required" },
+ { 6, "PH_FSIM PIN required" },
+ { 7, "PH_FSIM PUK required" },
+ { 10, "SIM not inserted" },
+ { 11, "SIM PIN required" },
+ { 12, "SIM PUK required" },
+ { 13, "SIM failure" },
+ { 14, "SIM busy" },
+ { 15, "SIM wrong" },
+ { 16, "Incorrect password" },
+ { 17, "SIM PIN2 required" },
+ { 18, "SIM PUK2 required" },
+ { 20, "Memory full" },
+ { 21, "Invalid index" },
+ { 22, "Not found" },
+ { 23, "Memory failure" },
+ { 24, "Text string too long" },
+ { 25, "Invalid characters in text string" },
+ { 26, "Dial string too long" },
+ { 27, "Invalid characters in dial string" },
+ { 30, "No network service" },
+ { 31, "Network timeout" },
+ { 32, "Network not allowed, emergency calls only" },
+ { 40, "Network personalization PIN required" },
+ { 41, "Network personalization PUK required" },
+ { 42, "Network subset personalization PIN required" },
+ { 43, "Network subset personalization PUK required" },
+ { 44, "Service provider personalization PIN required" },
+ { 45, "Service provider personalization PUK required" },
+ { 46, "Corporate personalization PIN required" },
+ { 47, "Corporate personalization PUK required" },
+ { 48, "PH-SIM PUK required" },
+ { 100, "Unknown error" },
+ { 103, "Illegal MS" },
+ { 106, "Illegal ME" },
+ { 107, "GPRS services not allowed" },
+ { 111, "PLMN not allowed" },
+ { 112, "Location area not allowed" },
+ { 113, "Roaming not allowed in this location area" },
+ { 126, "Operation temporary not allowed" },
+ { 132, "Service operation not supported" },
+ { 133, "Requested service option not subscribed" },
+ { 134, "Service option temporary out of order" },
+ { 148, "Unspecified GPRS error" },
+ { 149, "PDP authentication failure" },
+ { 150, "Invalid mobile class" },
+ { 256, "Operation temporarily not allowed" },
+ { 257, "Call barred" },
+ { 258, "Phone is busy" },
+ { 259, "User abort" },
+ { 260, "Invalid dial string" },
+ { 261, "SS not executed" },
+ { 262, "SIM Blocked" },
+ { 263, "Invalid block" },
+ { 772, "SIM powered down" },
+};
+
+/* 24.008 Annex H */
+struct error_entry ceer_errors[] = {
+ { 1, "Unassigned number" },
+ { 3, "No route to destination" },
+ { 6, "Channel unacceptable" },
+ { 8, "Operator determined barring" },
+ { 16, "Normal call clearing" },
+ { 17, "User busy" },
+ { 18, "No user responding" },
+ { 19, "User alerting, no answer" },
+ { 21, "Call rejected" },
+ { 22, "Number changed" },
+ { 25, "Pre-emption" },
+ { 26, "Non-selected user clearing" },
+ { 27, "Destination out of order" },
+ { 28, "Invalid number format (incomplete number)" },
+ { 29, "Facility rejected" },
+ { 30, "Response to STATUS ENQUIRY" },
+ { 31, "Normal, unspecified" },
+ { 34, "No circuit/channel available" },
+ { 38, "Network out of order" },
+ { 41, "Temporary failure" },
+ { 42, "Switching equipment congestion" },
+ { 43, "Access information discared" },
+ { 44, "Requested circuit/channel not available" },
+ { 47, "Resource unavailable (unspecified)" },
+ { 49, "Quality of service unavailable" },
+ { 50, "Requested facility not subscribed" },
+ { 55, "Incoming calls barred within the CUG" },
+ { 57, "Bearer capability not authorized" },
+ { 58, "Bearar capability not presently available" },
+ { 63, "Service or option not available, unspecified" },
+ { 65, "Bearer service not implemented" },
+ { 68, "ACM equal to or greater than ACMmax" },
+ { 69, "Requested facility not implemented" },
+ { 70, "Only restricted digital information bearer capability is available" },
+ { 79, "Service or option not implemented, unspecified" },
+ { 81, "Invalid transaction identifier value" },
+ { 87, "User not member of CUG" },
+ { 88, "Incompatible destination" },
+ { 91, "Invalid transit network selection" },
+ { 95, "Semantically incorrect message" },
+ { 96, "Invalid mandatory information"},
+ { 97, "Message type non-existent or not implemented" },
+ { 98, "Message type not compatible with protocol state" },
+ { 99, "Information element non-existent or not implemented" },
+ { 100, "Conditional IE error" },
+ { 101, "Message not compatible with protocol state" },
+ { 102, "Recovery on timer expirty" },
+ { 111, "Protocol error, unspecified" },
+ { 127, "Interworking, unspecified" },
+};
+
+gboolean valid_phone_number_format(const char *number)
+{
+ int len = strlen(number);
+ int begin = 0;
+ int i;
+
+ if (!len)
+ return FALSE;
+
+ if (len > OFONO_MAX_PHONE_NUMBER_LENGTH)
+ return FALSE;
+
+ if (number[0] == '+')
+ begin = 1;
+
+ for (i = begin; i < len; i++) {
+ if (number[i] >= '0' && number[i] <= '9')
+ continue;
+
+ if (number[i] == '*' || number[i] == '#')
+ continue;
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+const char *telephony_error_to_str(const struct ofono_error *error)
+{
+ struct error_entry *e;
+ int maxentries;
+ int i;
+
+ switch (error->type) {
+ case OFONO_ERROR_TYPE_CME:
+ e = cme_errors;
+ maxentries = sizeof(cme_errors) / sizeof(struct error_entry);
+ break;
+ case OFONO_ERROR_TYPE_CMS:
+ e = cms_errors;
+ maxentries = sizeof(cme_errors) / sizeof(struct error_entry);
+ break;
+ case OFONO_ERROR_TYPE_CEER:
+ e = ceer_errors;
+ maxentries = sizeof(ceer_errors) / sizeof(struct error_entry);
+ break;
+ default:
+ return 0;
+ }
+
+ for (i = 0; i < maxentries; i++)
+ if (e[i].error == error->error)
+ return e[i].str;
+
+ return 0;
+}
+
+int mmi_service_code_to_bearer_class(int code)
+{
+ int cls = 0;
+
+ switch (code) {
+ case 10:
+ cls = BEARER_CLASS_DEFAULT | BEARER_CLASS_SMS;
+ break;
+ case 11:
+ cls = BEARER_CLASS_VOICE;
+ break;
+ case 12:
+ cls = BEARER_CLASS_DATA;
+ break;
+ case 13:
+ cls = BEARER_CLASS_FAX;
+ break;
+ case 16:
+ cls = BEARER_CLASS_SMS;
+ break;
+ /* TODO: Voice Group Call & Broadcast VGCS & VBS */
+ case 17:
+ case 18:
+ break;
+ case 19:
+ cls = BEARER_CLASS_DEFAULT;
+ break;
+
+ /* Funny, according to 22.030, 20 implies BS 7-11 */
+ /* 22.004 only defines BS 7 (Data Sync) & BS 8 (Data Async) */
+ case 20:
+ cls = BEARER_CLASS_DATA_SYNC | BEARER_CLASS_DATA_ASYNC;
+ break;
+ /* According to 22.030: All Async */
+ case 21:
+ /* According to 22.030: All Data Async */
+ case 25:
+ cls = BEARER_CLASS_DATA_ASYNC;
+ break;
+ /* According to 22.030: All Sync */
+ case 22:
+ /* According to 22.030: All Data Sync */
+ case 24:
+ cls = BEARER_CLASS_DATA_SYNC;
+ break;
+ /* According to 22.030: Telephony & All Sync services */
+ case 26:
+ cls = BEARER_CLASS_VOICE | BEARER_CLASS_DATA_SYNC;
+ break;
+ default:
+ break;
+ }
+
+ return cls;
+}
+
+const char *phone_number_to_string(const char *number, int type)
+{
+ static char buffer[64];
+
+ if (type == 145 && (strlen(number) > 0) && number[0] != '+') {
+ buffer[0] = '+';
+ strncpy(buffer + 1, number, 62);
+ buffer[63] = '\0';
+ } else {
+ strncpy(buffer, number, 63);
+ buffer[63] = '\0';
+ }
+
+ return buffer;
+}
+
+void string_to_phone_number(const char *str, int *type, const char **number)
+{
+ if (strlen(str) && str[0] == '+') {
+ *number = &str[1];
+ *type = 145; /* International */
+ } else {
+ *number = &str[0];
+ *type = 129; /* Local */
+ }
+}
+
+int valid_ussd_string(const char *str)
+{
+ int len = strlen(str);
+
+ if (!len)
+ return FALSE;
+
+ /* It is hard to understand exactly what constitutes a valid USSD string
+ * According to 22.090:
+ * Case a - 1, 2 or 3 digits from the set (*, #) followed by 1X(Y),
+ * where X=any number 0‑4, Y=any number 0‑9, then, optionally "*
+ * followed by any number of any characters", and concluding with #SEND
+ *
+ * Case b - 1, 2 or 3 digits from the set (*, #) followed by 1X(Y),
+ * where X=any number 5‑9, Y=any number 0‑9, then, optionally "*
+ * followed by any number of any characters", and concluding with #SEND
+ *
+ * Case c - 7(Y) SEND, where Y=any number 0‑9
+ *
+ * Case d - All other formats
+ *
+ * According to 22.030 Figure 3.5.3.2 USSD strings can be:
+ *
+ * Supplementary service control
+ * SIM control
+ * Manufacturer defined
+ * Terminated by '#'
+ * Short String - This can be any 2 digit short string. If the string
+ * starts with a '1' and no calls are in progress then
+ * this string is treated as a call setup request
+ *
+ * Everything else is not a valid USSD string
+ */
+
+ if (len != 2 && str[len-1] != '#')
+ return FALSE;
+
+ return TRUE;
+}
+
+const char *ss_control_type_to_string(enum ss_control_type type)
+{
+ switch (type) {
+ case SS_CONTROL_TYPE_ACTIVATION:
+ return "acivation";
+ case SS_CONTROL_TYPE_REGISTRATION:
+ return "registration";
+ case SS_CONTROL_TYPE_QUERY:
+ return "interrogation";
+ case SS_CONTROL_TYPE_DEACTIVATION:
+ return "deactivation";
+ case SS_CONTROL_TYPE_ERASURE:
+ return "erasure";
+ }
+
+ return NULL;
+}
+
+#define NEXT_FIELD(str, dest) \
+ do { \
+ dest = str; \
+ \
+ str = strchrnul(str, '*'); \
+ if (*str) { \
+ *str = '\0'; \
+ str += 1; \
+ } \
+ } while (0) \
+
+/* Note: The str will be modified, so in case of error you should
+ * throw it away and start over
+ */
+gboolean parse_ss_control_string(char *str, int *ss_type,
+ char **sc, char **sia,
+ char **sib, char **sic,
+ char **dn)
+{
+ int len = strlen(str);
+ int cur = 0;
+ char *c;
+ unsigned int i;
+ gboolean ret = FALSE;
+
+ /* Minimum is {*,#}SC# */
+ if (len < 4)
+ goto out;
+
+ if (str[0] != '*' && str[0] != '#')
+ goto out;
+
+ cur = 1;
+
+ if (str[1] != '*' && str[1] != '#' && str[1] > '9' && str[1] < '0')
+ goto out;
+
+ if (str[0] == '#' && str[1] == '*')
+ goto out;
+
+ if (str[1] == '#' || str[1] == '*')
+ cur = 2;
+
+ if (str[0] == '*' && str[1] == '*')
+ *ss_type = SS_CONTROL_TYPE_REGISTRATION;
+ else if (str[0] == '#' && str[1] == '#')
+ *ss_type = SS_CONTROL_TYPE_ERASURE;
+ else if (str[0] == '*' && str[1] == '#')
+ *ss_type = SS_CONTROL_TYPE_QUERY;
+ else if (str[0] == '*')
+ *ss_type = SS_CONTROL_TYPE_ACTIVATION;
+ else
+ *ss_type = SS_CONTROL_TYPE_DEACTIVATION;
+
+ /* Must have at least one other '#' */
+ c = strrchr(str+cur, '#');
+
+ if (!c)
+ goto out;
+
+ *dn = c+1;
+ *c = '\0';
+
+ if (strlen(*dn) > 0 && !valid_phone_number_format(*dn))
+ goto out;
+
+ c = str+cur;
+
+ NEXT_FIELD(c, *sc);
+
+ /* According to 22.030 SC is 2 or 3 digits, there can be
+ * an optional digit 'n' if this is a call setup string,
+ * however 22.030 does not define any SC of length 3
+ * with an 'n' present
+ */
+ if (strlen(*sc) < 2 || strlen(*sc) > 3)
+ goto out;
+
+ for (i = 0; i < strlen(*sc); i++)
+ if (!isdigit((*sc)[i]))
+ goto out;
+
+ NEXT_FIELD(c, *sia);
+ NEXT_FIELD(c, *sib);
+ NEXT_FIELD(c, *sic);
+
+ if (*c == '\0')
+ ret = TRUE;
+
+out:
+ return ret;
+}
+
+static const char *bearer_class_lut[] = {
+ "Voice",
+ "Data",
+ "Fax",
+ "Sms",
+ "DataSync",
+ "DataAsync",
+ "DataPad",
+ "DataPacket"
+};
+
+const char *bearer_class_to_string(enum bearer_class cls)
+{
+ switch (cls) {
+ case BEARER_CLASS_VOICE:
+ return bearer_class_lut[0];
+ case BEARER_CLASS_DATA:
+ return bearer_class_lut[1];
+ case BEARER_CLASS_FAX:
+ return bearer_class_lut[2];
+ case BEARER_CLASS_SMS:
+ return bearer_class_lut[3];
+ case BEARER_CLASS_DATA_SYNC:
+ return bearer_class_lut[4];
+ case BEARER_CLASS_DATA_ASYNC:
+ return bearer_class_lut[5];
+ case BEARER_CLASS_PACKET:
+ return bearer_class_lut[6];
+ case BEARER_CLASS_PAD:
+ return bearer_class_lut[7];
+ case BEARER_CLASS_DEFAULT:
+ break;
+ };
+
+ return NULL;
+}
+
+gboolean is_valid_pin(const char *pin)
+{
+ unsigned int i;
+
+ for (i = 0; i < strlen(pin); i++)
+ if (pin[i] < '0' || pin[i] > '9')
+ return FALSE;
+
+ if (i > 8)
+ return FALSE;
+
+ return TRUE;
+}
diff --git a/src/common.h b/src/common.h
new file mode 100644
index 00000000..d865eafe
--- /dev/null
+++ b/src/common.h
@@ -0,0 +1,164 @@
+/*
+ *
+ * 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
+ *
+ */
+
+/* 27.007 Section 7.3 <stat> */
+enum operator_status {
+ OPERATOR_STATUS_UNKNOWN = 0,
+ OPERATOR_STATUS_AVAILABLE = 1,
+ OPERATOR_STATUS_CURRENT = 2,
+ OPERATOR_STATUS_FORBIDDEN = 3
+};
+
+/* 27.007 Section 7.3 <AcT> */
+enum access_technology {
+ ACCESS_TECHNOLOGY_GSM = 0,
+ ACCESS_TECHNOLOGY_GSM_COMPACT = 1,
+ ACCESS_TECHNOLOGY_UTRAN = 2,
+ ACCESS_TECHNOLOGY_GSM_EGPRS = 3,
+ ACCESS_TECHNOLOGY_UTRAN_HSDPA = 4,
+ ACCESS_TECHNOLOGY_UTRAN_HSUPA = 5,
+ ACCESS_TECHNOLOGY_UTRAN_HSDPA_HSUPA = 6
+};
+
+/* 27.007 Section 7.2 <stat> */
+enum network_registration_status {
+ NETWORK_REGISTRATION_STATUS_NOT_REGISTERED = 0,
+ NETWORK_REGISTRATION_STATUS_REGISTERED = 1,
+ NETWORK_REGISTRATION_STATUS_SEARCHING = 2,
+ NETWORK_REGISTRATION_STATUS_DENIED = 3,
+ NETWORK_REGISTRATION_STATUS_UNKNOWN = 4,
+ NETWORK_REGISTRATION_STATUS_ROAMING = 5
+};
+
+/* 27.007 Section 7.7 */
+enum clir_status {
+ CLIR_STATUS_NOT_PROVISIONED = 0,
+ CLIR_STATUS_PROVISIONED_PERMANENT,
+ CLIR_STATUS_UNKNOWN,
+ CLIR_STATUS_TEMPORARY_RESTRICTED,
+ CLIR_STATUS_TEMPORARY_ALLOWED
+};
+
+/* 27.007 Section 7.6 */
+enum clip_status {
+ CLIP_STATUS_NOT_PROVISIONED = 0,
+ CLIP_STATUS_PROVISIONED,
+ CLIP_STATUS_UNKNOWN
+};
+
+/* 27.007 Section 7.6 */
+enum clip_validity {
+ CLIP_VALIDITY_VALID = 0,
+ CLIP_VALIDITY_WITHHELD = 1,
+ CLIP_VALIDITY_NOT_AVAILABLE = 2
+};
+
+/* 27.007 Section 7.8 */
+enum colp_status {
+ COLP_STATUS_NOT_PROVISIONED = 0,
+ COLP_STATUS_PROVISIONED = 1,
+ COLP_STATUS_UNKNOWN = 2
+};
+
+/* This is not defined in 27.007, but presumably the same as CLIP/COLP */
+enum colr_status {
+ COLR_STATUS_NOT_PROVISIONED = 0,
+ COLR_STATUS_PROVISIONED = 1,
+ COLR_STATUS_UNKNOWN = 2
+};
+
+/* 27.007 Section 7.18 */
+enum call_status {
+ CALL_STATUS_ACTIVE = 0,
+ CALL_STATUS_HELD = 1,
+ CALL_STATUS_DIALING = 2,
+ CALL_STATUS_ALERTING = 3,
+ CALL_STATUS_INCOMING = 4,
+ CALL_STATUS_WAITING = 5,
+ CALL_STATUS_DISCONNECTED
+};
+
+/* 27.007 Section 7.18 */
+enum call_direction {
+ CALL_DIRECTION_MOBILE_ORIGINATED = 0,
+ CALL_DIRECTION_MOBILE_TERMINATED = 1
+};
+
+/* 27.007 Section 7.11 */
+enum bearer_class {
+ BEARER_CLASS_VOICE = 1,
+ BEARER_CLASS_DATA = 2,
+ BEARER_CLASS_FAX = 4,
+ BEARER_CLASS_DEFAULT = 7,
+ BEARER_CLASS_SMS = 8,
+ BEARER_CLASS_DATA_SYNC = 16,
+ BEARER_CLASS_DATA_ASYNC = 32,
+ BEARER_CLASS_PACKET = 64,
+ BEARER_CLASS_PAD = 128
+};
+
+enum call_forwarding_type {
+ CALL_FORWARDING_TYPE_UNCONDITIONAL = 0,
+ CALL_FORWARDING_TYPE_BUSY = 1,
+ CALL_FORWARDING_TYPE_NO_REPLY = 2,
+ CALL_FORWARDING_TYPE_NOT_REACHABLE = 3,
+ CALL_FORWARDING_TYPE_ALL = 4,
+ CALL_FORWARDING_TYPE_ALL_CONDITIONAL = 5
+};
+
+enum ussd_status {
+ USSD_STATUS_NOTIFY = 0,
+ USSD_STATUS_ACTION_REQUIRED = 1,
+ USSD_STATUS_TERMINATED = 2,
+ USSD_STATUS_LOCAL_CLIENT_RESPONDED = 3,
+ USSD_STATUS_NOT_SUPPORTED = 4,
+ USSD_STATUS_TIMED_OUT = 5,
+};
+
+/* 22.030 Section 6.5.2 */
+enum ss_control_type {
+ SS_CONTROL_TYPE_ACTIVATION,
+ SS_CONTROL_TYPE_DEACTIVATION,
+ SS_CONTROL_TYPE_QUERY,
+ SS_CONTROL_TYPE_REGISTRATION,
+ SS_CONTROL_TYPE_ERASURE,
+};
+
+const char *telephony_error_to_str(const struct ofono_error *error);
+
+gboolean valid_phone_number_format(const char *number);
+const char *phone_number_to_string(const char *number, int type);
+void string_to_phone_number(const char *str, int *type, const char **number);
+
+int mmi_service_code_to_bearer_class(int code);
+
+gboolean valid_ussd_string(const char *str);
+
+gboolean parse_ss_control_string(char *str, int *ss_type,
+ char **sc, char **sia,
+ char **sib, char **sic,
+ char **dn);
+
+const char *ss_control_type_to_string(enum ss_control_type type);
+
+const char *bearer_class_to_string(enum bearer_class cls);
+
+gboolean is_valid_pin(const char *pin);
diff --git a/src/dbus-gsm.c b/src/dbus-gsm.c
new file mode 100644
index 00000000..f191032d
--- /dev/null
+++ b/src/dbus-gsm.c
@@ -0,0 +1,261 @@
+/*
+ *
+ * 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
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "ofono.h"
+
+#include "dbus-gsm.h"
+
+#define SERVICE_NAME "org.ofono"
+
+#define RECONNECT_RETRY_TIMEOUT 2000
+
+static DBusConnection *g_connection;
+
+void dbus_gsm_free_string_array(char **array)
+{
+ int i;
+
+ if (!array)
+ return;
+
+ for (i = 0; array[i]; i++)
+ g_free(array[i]);
+
+ g_free(array);
+}
+
+void dbus_gsm_append_variant(DBusMessageIter *iter,
+ int type, void *value)
+{
+ char sig[2];
+ DBusMessageIter valueiter;
+
+ sig[0] = type;
+ sig[1] = 0;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+ sig, &valueiter);
+
+ dbus_message_iter_append_basic(&valueiter, type, value);
+
+ dbus_message_iter_close_container(iter, &valueiter);
+}
+
+void dbus_gsm_dict_append(DBusMessageIter *dict,
+ const char *key, int type, void *value)
+{
+ DBusMessageIter keyiter;
+
+ if (type == DBUS_TYPE_STRING) {
+ const char *str = *((const char **) value);
+ if (str == NULL)
+ return;
+ }
+
+ dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+ NULL, &keyiter);
+
+ dbus_message_iter_append_basic(&keyiter, DBUS_TYPE_STRING, &key);
+
+ dbus_gsm_append_variant(&keyiter, type, value);
+
+ dbus_message_iter_close_container(dict, &keyiter);
+}
+
+void dbus_gsm_append_array_variant(DBusMessageIter *iter, int type, void *val)
+{
+ DBusMessageIter variant, array;
+ char typesig[2];
+ char arraysig[3];
+ const char **str_array = *(const char ***)val;
+ int i;
+
+ arraysig[0] = DBUS_TYPE_ARRAY;
+ arraysig[1] = typesig[0] = type;
+ arraysig[2] = typesig[1] = '\0';
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+ arraysig, &variant);
+
+ dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
+ typesig, &array);
+
+ for (i = 0; str_array[i]; i++)
+ dbus_message_iter_append_basic(&array, type,
+ &(str_array[i]));
+
+ dbus_message_iter_close_container(&variant, &array);
+
+ dbus_message_iter_close_container(iter, &variant);
+}
+
+void dbus_gsm_dict_append_array(DBusMessageIter *dict, const char *key,
+ int type, void *val)
+{
+ DBusMessageIter entry;
+
+ dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+ NULL, &entry);
+
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+ dbus_gsm_append_array_variant(&entry, type, val);
+
+ dbus_message_iter_close_container(dict, &entry);
+}
+
+int dbus_gsm_signal_property_changed(DBusConnection *conn,
+ const char *path,
+ const char *interface,
+ const char *name,
+ int type, void *value)
+{
+ DBusMessage *signal;
+ DBusMessageIter iter;
+
+ signal = dbus_message_new_signal(path, interface, "PropertyChanged");
+
+ if (!signal) {
+ ofono_error("Unable to allocate new %s.PropertyChanged signal",
+ interface);
+ return -1;
+ }
+
+ dbus_message_iter_init_append(signal, &iter);
+
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);
+
+ dbus_gsm_append_variant(&iter, type, value);
+
+ return g_dbus_send_message(conn, signal);
+}
+
+int dbus_gsm_signal_array_property_changed(DBusConnection *conn,
+ const char *path,
+ const char *interface,
+ const char *name,
+ int type, void *value)
+
+{
+ DBusMessage *signal;
+ DBusMessageIter iter;
+
+ signal = dbus_message_new_signal(path, interface, "PropertyChanged");
+
+ if (!signal) {
+ ofono_error("Unable to allocate new %s.PropertyChanged signal",
+ interface);
+ return -1;
+ }
+
+ dbus_message_iter_init_append(signal, &iter);
+
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);
+
+ dbus_gsm_append_array_variant(&iter, type, value);
+
+ return g_dbus_send_message(conn, signal);
+}
+
+DBusConnection *dbus_gsm_connection()
+{
+ return g_connection;
+}
+
+void dbus_gsm_set_connection(DBusConnection *conn)
+{
+ if (conn && g_connection != NULL)
+ ofono_error("Setting a connection when it is not NULL");
+
+ g_connection = conn;
+}
+
+static gboolean system_bus_reconnect(void *user_data)
+{
+ DBusConnection *conn = dbus_gsm_connection();
+
+ if (!conn && (dbus_gsm_init() < 0))
+ return TRUE;
+
+ conn = dbus_gsm_connection();
+
+ if (conn && dbus_connection_get_is_connected(conn))
+ return FALSE;
+
+ ofono_error("While attempting to reconnect, conn != NULL,"
+ " but not connected");
+
+ return TRUE;
+}
+
+static void system_bus_disconnected(DBusConnection *conn, void *user_data)
+{
+ ofono_error("System bus has disconnected!");
+
+ dbus_gsm_set_connection(NULL);
+
+ g_timeout_add(RECONNECT_RETRY_TIMEOUT,
+ system_bus_reconnect, NULL);
+}
+
+int dbus_gsm_init()
+{
+ DBusConnection *conn;
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, SERVICE_NAME, &error);
+ if (!conn) {
+ ofono_error("Unable to hop onto D-Bus: %s", error.message);
+ return -1;
+ }
+
+ if (g_dbus_set_disconnect_function(conn, system_bus_disconnected,
+ NULL, NULL) == FALSE) {
+ dbus_connection_unref(conn);
+ return -1;
+ }
+
+ dbus_gsm_set_connection(conn);
+
+ return 0;
+}
+
+void dbus_gsm_exit()
+{
+ DBusConnection *conn = dbus_gsm_connection();
+
+ if (!conn || !dbus_connection_get_is_connected(conn))
+ return;
+
+ dbus_gsm_set_connection(NULL);
+
+ dbus_connection_unref(conn);
+}
diff --git a/src/dbus-gsm.h b/src/dbus-gsm.h
new file mode 100644
index 00000000..8396c4b4
--- /dev/null
+++ b/src/dbus-gsm.h
@@ -0,0 +1,131 @@
+/*
+ *
+ * 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
+ *
+ */
+
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+DBusConnection *dbus_gsm_connection();
+void dbus_gsm_set_connection(DBusConnection *conn);
+
+int dbus_gsm_init();
+void dbus_gsm_exit();
+
+#define MAX_DBUS_PATH_LEN 64
+
+void dbus_gsm_free_string_array(char **array);
+
+/* Essentially a{sv} */
+#define PROPERTIES_ARRAY_SIGNATURE DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING \
+ DBUS_TYPE_STRING_AS_STRING \
+ DBUS_TYPE_VARIANT_AS_STRING \
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+
+void dbus_gsm_dict_append(DBusMessageIter *dict, const char *key, int type,
+ void *value);
+
+void dbus_gsm_append_variant(DBusMessageIter *iter, int type, void *value);
+
+void dbus_gsm_append_array_variant(DBusMessageIter *iter, int type, void *val);
+
+void dbus_gsm_dict_append_array(DBusMessageIter *dict, const char *key,
+ int type, void *val);
+
+
+int dbus_gsm_signal_property_changed(DBusConnection *conn, const char *path,
+ const char *interface, const char *name,
+ int type, void *value);
+
+int dbus_gsm_signal_array_property_changed(DBusConnection *conn,
+ const char *path,
+ const char *interface,
+ const char *name, int type,
+ void *value);
+
+#define DBUS_GSM_ERROR_INTERFACE "org.ofono.Error"
+
+static inline DBusMessage *dbus_gsm_invalid_args(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, DBUS_GSM_ERROR_INTERFACE
+ ".InvalidArguments",
+ "Invalid arguments in method call");
+}
+
+static inline DBusMessage *dbus_gsm_invalid_format(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, DBUS_GSM_ERROR_INTERFACE
+ ".InvalidFormat",
+ "Argument format is not recognized");
+}
+
+static inline DBusMessage *dbus_gsm_not_implemented(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, DBUS_GSM_ERROR_INTERFACE
+ ".NotImplemented",
+ "Implementation not provided");
+}
+
+static inline DBusMessage *dbus_gsm_failed(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, DBUS_GSM_ERROR_INTERFACE ".Failed",
+ "Operation failed");
+}
+
+static inline DBusMessage *dbus_gsm_busy(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, DBUS_GSM_ERROR_INTERFACE ".InProgress",
+ "Operation already in progress");
+}
+
+static inline DBusMessage *dbus_gsm_not_found(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, DBUS_GSM_ERROR_INTERFACE ".NotFound",
+ "Object is not found or not valid for this operation");
+}
+
+static inline DBusMessage *dbus_gsm_not_active(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, DBUS_GSM_ERROR_INTERFACE ".NotActive",
+ "Operation is not active or in progress");
+}
+
+static inline DBusMessage *dbus_gsm_not_supported(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, DBUS_GSM_ERROR_INTERFACE
+ ".NotSupported",
+ "Operation is not supported by the"
+ " network / modem");
+}
+
+static inline DBusMessage *dbus_gsm_timed_out(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, DBUS_GSM_ERROR_INTERFACE ".Timedout",
+ "Operation failure due to timeout");
+}
+
+static inline void dbus_gsm_pending_reply(DBusMessage **msg, DBusMessage *reply)
+{
+ DBusConnection *conn = dbus_gsm_connection();
+
+ g_dbus_send_message(conn, reply);
+
+ dbus_message_unref(*msg);
+ *msg = NULL;
+}
diff --git a/src/driver.h b/src/driver.h
new file mode 100644
index 00000000..543fc771
--- /dev/null
+++ b/src/driver.h
@@ -0,0 +1,332 @@
+/*
+ *
+ * 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
+ *
+ */
+
+struct ofono_modem;
+
+/* 27.007 Section 6.2 */
+enum ofono_clir_option {
+ OFONO_CLIR_OPTION_DEFAULT = 0,
+ OFONO_CLIR_OPTION_INVOCATION,
+ OFONO_CLIR_OPTION_SUPPRESSION
+};
+
+/* 27.007 Section 6.2 */
+enum ofono_cug_option {
+ OFONO_CUG_OPTION_DEFAULT = 0,
+ OFONO_CUG_OPTION_INVOCATION = 1,
+};
+
+enum ofono_error_type {
+ OFONO_ERROR_TYPE_NO_ERROR = 0,
+ OFONO_ERROR_TYPE_CME,
+ OFONO_ERROR_TYPE_CMS,
+ OFONO_ERROR_TYPE_CEER,
+ OFONO_ERROR_TYPE_FAILURE
+};
+
+struct ofono_error {
+ enum ofono_error_type type;
+ int error;
+};
+
+enum ofono_disconnect_reason {
+ OFONO_DISCONNECT_REASON_UNKNOWN = 0,
+ OFONO_DISCONNECT_REASON_LOCAL_HANGUP,
+ OFONO_DISCONNECT_REASON_REMOTE_HANGUP,
+ OFONO_DISCONNECT_REASON_ERROR,
+};
+
+#define OFONO_MAX_PHONE_NUMBER_LENGTH 20
+
+struct ofono_call {
+ unsigned id;
+ int type;
+ int direction;
+ int status;
+ char phone_number[OFONO_MAX_PHONE_NUMBER_LENGTH + 1];
+ int number_type;
+ int clip_validity;
+};
+
+/* Theoretical limit is 16, but each GSM char can be encoded into
+ * * 3 UTF8 characters resulting in 16*3=48 chars
+ * */
+#define OFONO_MAX_OPERATOR_NAME_LENGTH 63
+
+struct ofono_network_operator {
+ char name[OFONO_MAX_OPERATOR_NAME_LENGTH + 1];
+ short mcc;
+ short mnc;
+ int status;
+ int tech;
+};
+
+/* 27.007 Section 7.11 Call Forwarding */
+struct ofono_cf_condition {
+ int status;
+ int cls;
+ char phone_number[OFONO_MAX_PHONE_NUMBER_LENGTH + 1];
+ int number_type;
+ int time;
+};
+
+/* 27.007 Section 7.12 Call Waiting */
+struct ofono_cw_condition {
+ int status;
+ int cls;
+};
+
+/* Notification functions, the integer values here should map to
+ * values obtained from the modem. The enumerations are the same
+ * as the values for the fields found in 3GPP TS 27.007
+ *
+ * Pass in the integer value -1 if the value is not known
+ * Pass in NULL string value if the value is not known
+ */
+typedef void (*ofono_generic_cb_t)(const struct ofono_error *error,
+ void *data);
+
+typedef void (*ofono_call_list_cb_t)(const struct ofono_error *error,
+ int numcalls,
+ const struct ofono_call *call_list,
+ void *data);
+
+typedef void (*ofono_current_operator_cb_t)(const struct ofono_error *error,
+ const struct ofono_network_operator *op,
+ void *data);
+
+typedef void (*ofono_operator_list_cb_t)(const struct ofono_error *error,
+ int total,
+ const struct ofono_network_operator *list,
+ void *data);
+
+typedef void (*ofono_registration_status_cb_t)(const struct ofono_error *error,
+ int status, int lac, int ci, int tech,
+ void *data);
+
+typedef void (*ofono_signal_strength_cb_t)(const struct ofono_error *error,
+ int strength, void *data);
+
+typedef void (*ofono_call_forwarding_query_cb_t)(const struct ofono_error *error,
+ int total,
+ const struct ofono_cf_condition *list,
+ void *data);
+
+typedef void (*ofono_modem_attribute_query_cb_t)(const struct ofono_error *error,
+ const char *attribute, void *data);
+
+typedef void (*ofono_call_setting_status_cb_t)(const struct ofono_error *error,
+ int status, void *data);
+
+typedef void (*ofono_clir_setting_cb_t)(const struct ofono_error *error,
+ int override, int network, void *data);
+
+typedef void (*ofono_call_waiting_status_cb_t)(const struct ofono_error *error,
+ int num, struct ofono_cw_condition *cond,
+ void *data);
+
+typedef void (*ofono_call_meter_query_cb_t)(const struct ofono_error *error,
+ int value, void *data);
+
+typedef void (*ofono_call_meter_puct_query_cb_t)(const struct ofono_error *error,
+ const char *currency, double ppu,
+ void *data);
+
+struct ofono_modem_attribute_ops {
+ void (*query_manufacturer)(struct ofono_modem *modem,
+ ofono_modem_attribute_query_cb_t cb, void *data);
+ void (*query_serial)(struct ofono_modem *modem,
+ ofono_modem_attribute_query_cb_t cb, void *data);
+ void (*query_model)(struct ofono_modem *modem,
+ ofono_modem_attribute_query_cb_t cb, void *data);
+ void (*query_revision)(struct ofono_modem *modem,
+ ofono_modem_attribute_query_cb_t cb, void *data);
+};
+
+struct ofono_modem *ofono_modem_register(struct ofono_modem_attribute_ops *ops);
+int ofono_modem_unregister(struct ofono_modem *modem);
+
+void ofono_modem_set_userdata(struct ofono_modem *modem, void *data);
+void *ofono_modem_userdata(struct ofono_modem *modem);
+
+/* Network related functions, including registration status, operator selection
+ * and signal strength indicators.
+ *
+ * It is up to the plugin to implement CSQ polling if the modem does not support
+ * vendor extensions for signal strength notification.
+ */
+struct ofono_network_registration_ops {
+ void (*registration_status)(struct ofono_modem *modem,
+ ofono_registration_status_cb_t cb, void *data);
+ void (*current_operator)(struct ofono_modem *modem,
+ ofono_current_operator_cb_t cb, void *data);
+ void (*list_operators)(struct ofono_modem *modem,
+ ofono_operator_list_cb_t cb, void *data);
+ void (*register_auto)(struct ofono_modem *modem,
+ ofono_generic_cb_t cb, void *data);
+ void (*register_manual)(struct ofono_modem *modem,
+ const struct ofono_network_operator *oper,
+ ofono_generic_cb_t cb, void *data);
+ void (*deregister)(struct ofono_modem *modem,
+ ofono_generic_cb_t cb, void *data);
+ void (*signal_strength)(struct ofono_modem *modem,
+ ofono_signal_strength_cb_t, void *data);
+};
+
+void ofono_signal_strength_notify(struct ofono_modem *modem, int strength);
+void ofono_network_registration_notify(struct ofono_modem *modem, int status,
+ int lac, int ci, int tech);
+int ofono_network_registration_register(struct ofono_modem *modem,
+ struct ofono_network_registration_ops *ops);
+void ofono_network_registration_unregister(struct ofono_modem *modem);
+
+/* Voice call related functionality, including ATD, ATA, +CHLD, CTFR, CLCC,
+ * SSN notifications (CSSI and CSSU) and VTS.
+ *
+ * It is up to the plugin to implement polling of CLCC if the modem does
+ * not support vendor extensions for call progress indication.
+ */
+struct ofono_voicecall_ops {
+ void (*dial)(struct ofono_modem *modem, const char *number,
+ int number_type, enum ofono_clir_option clir,
+ enum ofono_cug_option cug, ofono_generic_cb_t cb,
+ void *data);
+ void (*answer)(struct ofono_modem *modem,
+ ofono_generic_cb_t cb, void *data);
+ void (*hangup)(struct ofono_modem *modem,
+ ofono_generic_cb_t cb, void *data);
+ void (*list_calls)(struct ofono_modem *modem,
+ ofono_call_list_cb_t cb, void *data);
+ void (*hold_all_active)(struct ofono_modem *modem,
+ ofono_generic_cb_t cb, void *data);
+ void (*release_all_held)(struct ofono_modem *modem,
+ ofono_generic_cb_t cb, void *data);
+ void (*set_udub)(struct ofono_modem *modem,
+ ofono_generic_cb_t cb, void *data);
+ void (*release_all_active)(struct ofono_modem *modem,
+ ofono_generic_cb_t cb, void *data);
+ void (*release_specific)(struct ofono_modem *modem, int id,
+ ofono_generic_cb_t cb, void *data);
+ void (*private_chat)(struct ofono_modem *modem, int id,
+ ofono_generic_cb_t cb, void *data);
+ void (*create_multiparty)(struct ofono_modem *modem,
+ ofono_generic_cb_t cb, void *data);
+ void (*transfer)(struct ofono_modem *modem,
+ ofono_generic_cb_t cb, void *data);
+ void (*deflect)(struct ofono_modem *modem, const char *number,
+ int number_type, ofono_generic_cb_t cb, void *data);
+ void (*swap_without_accept)(struct ofono_modem *modem,
+ ofono_generic_cb_t cb, void *data);
+ void (*send_tones)(struct ofono_modem *modem, const char *tones,
+ ofono_generic_cb_t cb, void *data);
+};
+
+void ofono_voicecall_notify(struct ofono_modem *modem, const struct ofono_call *call);
+void ofono_voicecall_disconnected(struct ofono_modem *modem, int id,
+ enum ofono_disconnect_reason reason,
+ const struct ofono_error *error);
+void ofono_voicecall_cssi(struct ofono_modem *modem, int code, int index);
+void ofono_voicecall_cssu(struct ofono_modem *modem, int code, int index,
+ const char *number, int number_type);
+
+int ofono_voicecall_register(struct ofono_modem *modem, struct ofono_voicecall_ops *ops);
+void ofono_voicecall_unregister(struct ofono_modem *modem);
+
+struct ofono_call_forwarding_ops {
+ void (*activation)(struct ofono_modem *modem, int type, int cls,
+ ofono_generic_cb_t cb, void *data);
+ void (*registration)(struct ofono_modem *modem, int type, int cls,
+ const char *number, int number_type, int time,
+ ofono_generic_cb_t cb, void *data);
+ void (*deactivation)(struct ofono_modem *modem, int type, int cls,
+ ofono_generic_cb_t cb, void *data);
+ void (*erasure)(struct ofono_modem *modem, int type, int cls,
+ ofono_generic_cb_t cb, void *data);
+ void (*query)(struct ofono_modem *modem, int type, int cls,
+ ofono_call_forwarding_query_cb_t cb, void *data);
+};
+
+int ofono_call_forwarding_register(struct ofono_modem *modem,
+ struct ofono_call_forwarding_ops *ops);
+void ofono_call_forwarding_unregister(struct ofono_modem *modem);
+
+struct ofono_ussd_ops {
+ void (*request)(struct ofono_modem *modem, const char *str,
+ ofono_generic_cb_t cb, void *data);
+ void (*cancel)(struct ofono_modem *modem,
+ ofono_generic_cb_t cb, void *data);
+};
+
+void ofono_ussd_notify(struct ofono_modem *modem, int status, const char *str);
+int ofono_ussd_register(struct ofono_modem *modem, struct ofono_ussd_ops *ops);
+void ofono_ussd_unregister(struct ofono_modem *modem);
+
+struct ofono_call_settings_ops {
+ void (*clip_query)(struct ofono_modem *modem,
+ ofono_call_setting_status_cb_t cb, void *data);
+ void (*colp_query)(struct ofono_modem *modem,
+ ofono_call_setting_status_cb_t cb, void *data);
+ void (*clir_query)(struct ofono_modem *modem, ofono_clir_setting_cb_t cb,
+ void *data);
+ void (*colr_query)(struct ofono_modem *modem,
+ ofono_call_setting_status_cb_t cb, void *data);
+ void (*clir_set)(struct ofono_modem *modem, int mode, ofono_generic_cb_t cb,
+ void *data);
+};
+
+int ofono_call_settings_register(struct ofono_modem *modem,
+ struct ofono_call_settings_ops *ops);
+void ofono_call_settings_unregister(struct ofono_modem *modem);
+
+struct ofono_call_waiting_ops {
+ void (*query)(struct ofono_modem *modem, int cls,
+ ofono_call_waiting_status_cb_t cb, void *data);
+ void (*set)(struct ofono_modem *modem, int mode, int cls,
+ ofono_generic_cb_t cb, void *data);
+};
+
+int ofono_call_waiting_register(struct ofono_modem *modem,
+ struct ofono_call_waiting_ops *ops);
+void ofono_call_waiting_unregister(struct ofono_modem *modem);
+
+struct ofono_call_meter_ops {
+ void (*call_meter_query)(struct ofono_modem *modem,
+ ofono_call_meter_query_cb_t cb, void *data);
+ void (*acm_query)(struct ofono_modem *modem,
+ ofono_call_meter_query_cb_t cb, void *data);
+ void (*acm_reset)(struct ofono_modem *modem, const char *sim_pin2,
+ ofono_generic_cb_t cb, void *data);
+ void (*acm_max_query)(struct ofono_modem *modem,
+ ofono_call_meter_query_cb_t cb, void *data);
+ void (*acm_max_set)(struct ofono_modem *modem, int new_value,
+ const char *sim_pin2, ofono_generic_cb_t cb, void *data);
+ void (*puct_query)(struct ofono_modem *modem,
+ ofono_call_meter_puct_query_cb_t cb, void *data);
+ void (*puct_set)(struct ofono_modem *modem, const char *currency,
+ double ppu, const char *sim_pin2,
+ ofono_generic_cb_t cb, void *data);
+};
+
+int ofono_call_meter_register(struct ofono_modem *modem,
+ struct ofono_call_meter_ops *ops);
+void ofono_call_meter_unregister(struct ofono_modem *modem);
+void ofono_call_meter_maximum_notify(struct ofono_modem *modem);
+void ofono_call_meter_changed_notify(struct ofono_modem *modem, int new_value);
diff --git a/src/main.c b/src/main.c
index 37deecb2..03d294de 100644
--- a/src/main.c
+++ b/src/main.c
@@ -31,6 +31,8 @@
#include "ofono.h"
+#include "dbus-gsm.h"
+
static GMainLoop *event_loop;
static void sig_debug(int sig)
@@ -100,6 +102,12 @@ int main(int argc, char **argv)
__ofono_log_init(option_detach, option_debug);
+ if (dbus_gsm_init() != 0)
+ goto cleanup;
+
+ if (__ofono_manager_init() < 0)
+ goto cleanup;
+
__ofono_plugin_init(NULL, NULL);
memset(&sa, 0, sizeof(sa));
@@ -118,6 +126,11 @@ int main(int argc, char **argv)
__ofono_plugin_cleanup();
+ __ofono_manager_cleanup();
+
+ dbus_gsm_exit();
+
+cleanup:
g_main_loop_unref(event_loop);
__ofono_log_cleanup();
diff --git a/src/manager.c b/src/manager.c
new file mode 100644
index 00000000..eb5dc496
--- /dev/null
+++ b/src/manager.c
@@ -0,0 +1,210 @@
+/*
+ *
+ * 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
+
+#include <string.h>
+#include <dbus/dbus.h>
+#include <glib.h>
+#include <gdbus.h>
+
+#include "ofono.h"
+
+#include "dbus-gsm.h"
+
+#include "modem.h"
+#include "driver.h"
+
+#define MANAGER_INTERFACE "org.ofono.Manager"
+#define MANAGER_PATH "/"
+
+static GSList *g_modem_list = NULL;
+static int g_next_modem_id = 1;
+
+#if 0
+struct ofono_modem *manager_find_modem_by_id(int id)
+{
+ GSList *l;
+ struct ofono_modem *modem;
+
+ for (l = g_modem_list; l; l = l->next) {
+ modem = l->data;
+
+ if (modem->id == id)
+ return modem;
+ }
+
+ return NULL;
+}
+#endif
+
+/* Clients only need to free *modems */
+static int modem_list(char ***modems)
+{
+ GSList *l;
+ int i;
+ struct ofono_modem *modem;
+
+ *modems = g_new0(char *, g_slist_length(g_modem_list) + 1);
+
+ if (!*modems)
+ return -1;
+
+ for (l = g_modem_list, i = 0; l; l = l->next, i++) {
+ modem = l->data;
+
+ (*modems)[i] = modem->path;
+ }
+
+ return 0;
+}
+
+struct ofono_modem *ofono_modem_register(struct ofono_modem_attribute_ops *ops)
+{
+ struct ofono_modem *modem;
+ DBusConnection *conn = dbus_gsm_connection();
+ char **modems;
+
+ modem = modem_create(g_next_modem_id, ops);
+
+ if (modem == NULL)
+ return 0;
+
+ ++g_next_modem_id;
+
+ g_modem_list = g_slist_prepend(g_modem_list, modem);
+
+ if (modem_list(&modems) == 0) {
+ dbus_gsm_signal_array_property_changed(conn, MANAGER_PATH,
+ MANAGER_INTERFACE, "Modems",
+ DBUS_TYPE_OBJECT_PATH, &modems);
+
+ g_free(modems);
+ }
+
+ return modem;
+}
+
+int ofono_modem_unregister(struct ofono_modem *m)
+{
+ struct ofono_modem *modem = m;
+ DBusConnection *conn = dbus_gsm_connection();
+ char **modems;
+
+ if (modem == NULL)
+ return -1;
+
+ modem_remove(modem);
+
+ g_modem_list = g_slist_remove(g_modem_list, modem);
+
+ if (modem_list(&modems) == 0) {
+ dbus_gsm_signal_array_property_changed(conn, MANAGER_PATH,
+ MANAGER_INTERFACE, "Modems",
+ DBUS_TYPE_OBJECT_PATH, &modems);
+
+ g_free(modems);
+ }
+
+ return 0;
+}
+
+static DBusMessage *manager_get_properties(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ DBusMessage *reply;
+ char **modems;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ if (modem_list(&modems) == -1)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ PROPERTIES_ARRAY_SIGNATURE,
+ &dict);
+
+ dbus_gsm_dict_append_array(&dict, "Modems", DBUS_TYPE_OBJECT_PATH,
+ &modems);
+
+ g_free(modems);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static GDBusMethodTable manager_methods[] = {
+ { "GetProperties", "", "a{sv}", manager_get_properties },
+ { }
+};
+
+static GDBusSignalTable manager_signals[] = {
+ { "PropertyChanged", "sv" },
+ { }
+};
+
+int __ofono_manager_init()
+{
+ DBusConnection *conn = dbus_gsm_connection();
+ gboolean ret;
+
+ ret = g_dbus_register_interface(conn, "/", MANAGER_INTERFACE,
+ manager_methods, manager_signals,
+ NULL, NULL, NULL);
+
+ if (ret == FALSE)
+ return -1;
+
+ return 0;
+}
+
+void __ofono_manager_cleanup()
+{
+ GSList *l;
+ struct ofono_modem *modem;
+ DBusConnection *conn = dbus_gsm_connection();
+
+ /* Clean up in case plugins didn't unregister the modems */
+ for (l = g_modem_list; l; l = l->next) {
+ modem = l->data;
+
+ if (!modem)
+ continue;
+
+ ofono_debug("plugin owning %s forgot to unregister, cleaning up",
+ modem->path);
+ modem_remove(modem);
+ }
+
+ g_slist_free(g_modem_list);
+ g_modem_list = 0;
+
+ g_dbus_unregister_interface(conn, "/", MANAGER_INTERFACE);
+}
diff --git a/src/modem.c b/src/modem.c
new file mode 100644
index 00000000..0b8b36ef
--- /dev/null
+++ b/src/modem.c
@@ -0,0 +1,432 @@
+/*
+ *
+ * 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
+
+#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"
+
+#define MODEM_INTERFACE "org.ofono.Modem"
+
+#define MODEM_FLAG_INITIALIZING_ATTRS 1
+
+#define ATTRIBUTE_QUERY_DELAY 0
+
+struct ofono_modem_data {
+ char *manufacturer;
+ char *model;
+ char *revision;
+ char *serial;
+ GSList *interface_list;
+ int flags;
+ unsigned int idlist;
+ struct ofono_modem_attribute_ops *ops;
+ DBusMessage *pending;
+ guint interface_update;
+};
+
+unsigned int modem_alloc_callid(struct ofono_modem *modem)
+{
+ struct ofono_modem_data *d = modem->modem_info;
+ unsigned int i;
+
+ for (i = 1; i < sizeof(d->idlist) * 8; i++) {
+ if (d->idlist & (0x1 << i))
+ continue;
+
+ d->idlist |= (0x1 << i);
+ return i;
+ }
+
+ return 0;
+}
+
+void modem_release_callid(struct ofono_modem *modem, int id)
+{
+ struct ofono_modem_data *d = modem->modem_info;
+
+ d->idlist &= ~(0x1 << id);
+}
+
+void ofono_modem_set_userdata(struct ofono_modem *modem, void *userdata)
+{
+ if (modem)
+ modem->userdata = userdata;
+}
+
+void *ofono_modem_userdata(struct ofono_modem *modem)
+{
+ if (modem)
+ return modem->userdata;
+
+ return NULL;
+}
+
+static void modem_free(gpointer data)
+{
+ struct ofono_modem *modem = data;
+ GSList *l;
+
+ if (modem == NULL)
+ return;
+
+ for (l = modem->modem_info->interface_list; l; l = l->next)
+ g_free(l->data);
+
+ g_slist_free(modem->modem_info->interface_list);
+
+ g_free(modem->modem_info->manufacturer);
+ g_free(modem->modem_info->serial);
+ g_free(modem->modem_info->revision);
+ g_free(modem->modem_info->model);
+
+ if (modem->modem_info->pending)
+ dbus_message_unref(modem->modem_info->pending);
+
+ if (modem->modem_info->interface_update)
+ g_source_remove(modem->modem_info->interface_update);
+
+ g_free(modem->modem_info);
+ g_free(modem->path);
+ g_free(modem);
+}
+
+static DBusMessage *generate_properties_reply(struct ofono_modem *modem,
+ DBusConnection *conn, DBusMessage *msg)
+{
+ struct ofono_modem_data *info = modem->modem_info;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+
+ char **interfaces;
+ int i;
+ GSList *l;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ PROPERTIES_ARRAY_SIGNATURE,
+ &dict);
+
+ if (info->manufacturer)
+ dbus_gsm_dict_append(&dict, "Manufacturer", DBUS_TYPE_STRING,
+ &info->manufacturer);
+
+ if (info->model)
+ dbus_gsm_dict_append(&dict, "Model", DBUS_TYPE_STRING,
+ &info->model);
+
+ if (info->revision)
+ dbus_gsm_dict_append(&dict, "Revision", DBUS_TYPE_STRING,
+ &info->revision);
+
+ if (info->serial)
+ dbus_gsm_dict_append(&dict, "Serial", DBUS_TYPE_STRING,
+ &info->serial);
+
+ interfaces = g_new0(char *, g_slist_length(info->interface_list) + 1);
+ for (i = 0, l = info->interface_list; l; l = l->next, i++)
+ interfaces[i] = l->data;
+
+ dbus_gsm_dict_append_array(&dict, "Interfaces", DBUS_TYPE_STRING,
+ &interfaces);
+
+ g_free(interfaces);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static DBusMessage *modem_get_properties(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct ofono_modem *modem = data;
+
+ if (modem->modem_info->flags & MODEM_FLAG_INITIALIZING_ATTRS) {
+ modem->modem_info->pending = dbus_message_ref(msg);
+ return NULL;
+ }
+
+ return generate_properties_reply(modem, conn, msg);
+}
+
+static GDBusMethodTable modem_methods[] = {
+ { "GetProperties", "", "a{sv}", modem_get_properties,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { }
+};
+
+static GDBusSignalTable modem_signals[] = {
+ { "PropertyChanged", "sv" },
+ { }
+};
+
+static gboolean trigger_interface_update(void *data)
+{
+ struct ofono_modem *modem = data;
+ struct ofono_modem_data *info = modem->modem_info;
+ DBusConnection *conn = dbus_gsm_connection();
+
+ char **interfaces;
+ GSList *l;
+ int i;
+
+ interfaces = g_new0(char *, g_slist_length(info->interface_list) + 1);
+ for (i = 0, l = info->interface_list; l; l = l->next, i++)
+ interfaces[i] = l->data;
+
+ dbus_gsm_signal_array_property_changed(conn, modem->path,
+ MODEM_INTERFACE,
+ "Interfaces", DBUS_TYPE_STRING,
+ &interfaces);
+
+ g_free(interfaces);
+
+ info->interface_update = 0;
+
+ return FALSE;
+}
+
+void modem_add_interface(struct ofono_modem *modem, const char *interface)
+{
+ struct ofono_modem_data *info = modem->modem_info;
+
+ info->interface_list =
+ g_slist_prepend(info->interface_list, g_strdup(interface));
+
+ if (info->interface_update == 0)
+ info->interface_update =
+ g_timeout_add(0, trigger_interface_update, modem);
+}
+
+void modem_remove_interface(struct ofono_modem *modem, const char *interface)
+{
+ struct ofono_modem_data *info = modem->modem_info;
+
+ GSList *found = g_slist_find_custom(info->interface_list,
+ interface,
+ (GCompareFunc) strcmp);
+
+ if (!found) {
+ ofono_error("Interface %s not found on the interface_list",
+ interface);
+ return;
+ }
+
+ g_free(found->data);
+
+ info->interface_list =
+ g_slist_remove(info->interface_list, found->data);
+
+ if (info->interface_update == 0)
+ info->interface_update =
+ g_timeout_add(0, trigger_interface_update, modem);
+}
+
+static void finish_attr_query(struct ofono_modem *modem)
+{
+ //struct ofono_modem_data *info = modem->modem_info;
+ DBusConnection *conn = dbus_gsm_connection();
+ DBusMessage *reply;
+
+ modem->modem_info->flags &= ~MODEM_FLAG_INITIALIZING_ATTRS;
+
+ if (!modem->modem_info->pending)
+ return;
+
+ reply = generate_properties_reply(modem, conn,
+ modem->modem_info->pending);
+
+ if (reply)
+ g_dbus_send_message(conn, reply);
+
+ dbus_message_unref(modem->modem_info->pending);
+ modem->modem_info->pending = NULL;
+}
+
+static void query_serial_cb(const struct ofono_error *error,
+ const char *serial, void *user)
+{
+ struct ofono_modem *modem = user;
+
+ if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+ modem->modem_info->serial = g_strdup(serial);
+
+ finish_attr_query(modem);
+}
+
+static gboolean query_serial(gpointer user)
+{
+ struct ofono_modem *modem = user;
+
+ if (!modem->modem_info->ops->query_serial) {
+ finish_attr_query(modem);
+ return FALSE;
+ }
+
+ modem->modem_info->ops->query_serial(modem, query_serial_cb, modem);
+
+ return FALSE;
+}
+
+static void query_revision_cb(const struct ofono_error *error,
+ const char *revision, void *user)
+{
+ struct ofono_modem *modem = user;
+
+ if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+ modem->modem_info->revision = g_strdup(revision);
+
+ g_timeout_add(0, query_serial, modem);
+}
+
+static gboolean query_revision(gpointer user)
+{
+ struct ofono_modem *modem = user;
+
+ if (!modem->modem_info->ops->query_revision) {
+ g_timeout_add(0, query_serial, modem);
+ return FALSE;
+ }
+
+ modem->modem_info->ops->query_revision(modem, query_revision_cb, modem);
+
+ return FALSE;
+}
+
+static void query_model_cb(const struct ofono_error *error,
+ const char *model, void *user)
+{
+ struct ofono_modem *modem = user;
+
+ if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+ modem->modem_info->model = g_strdup(model);
+
+ g_timeout_add(0, query_revision, modem);
+}
+
+static gboolean query_model(gpointer user)
+{
+ struct ofono_modem *modem = user;
+
+ if (!modem->modem_info->ops->query_model) {
+ /* If model is not supported, don't bother querying revision */
+ g_timeout_add(0, query_serial, modem);
+ return FALSE;
+ }
+
+ modem->modem_info->ops->query_model(modem, query_model_cb, modem);
+
+ return FALSE;
+}
+
+static void query_manufacturer_cb(const struct ofono_error *error,
+ const char *manufacturer, void *user)
+{
+ struct ofono_modem *modem = user;
+
+ if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+ modem->modem_info->manufacturer = g_strdup(manufacturer);
+
+ g_timeout_add(0, query_model, modem);
+}
+
+static gboolean query_manufacturer(gpointer user)
+{
+ struct ofono_modem *modem = user;
+
+ if (!modem->modem_info->ops->query_manufacturer) {
+ g_timeout_add(0, query_model, modem);
+ return FALSE;
+ }
+
+ modem->modem_info->ops->query_manufacturer(modem, query_manufacturer_cb,
+ modem);
+
+ return FALSE;
+}
+
+struct ofono_modem *modem_create(int id, struct ofono_modem_attribute_ops *ops)
+{
+ char path[MAX_DBUS_PATH_LEN];
+ DBusConnection *conn = dbus_gsm_connection();
+ struct ofono_modem *modem;
+
+ modem = g_try_new0(struct ofono_modem, 1);
+ if (modem == NULL)
+ return modem;
+
+ modem->modem_info = g_try_new0(struct ofono_modem_data, 1);
+ if (modem->modem_info == NULL) {
+ g_free(modem);
+ return NULL;
+ }
+
+ modem->id = id;
+ modem->modem_info->ops = ops;
+
+ snprintf(path, MAX_DBUS_PATH_LEN, "/modem%d", modem->id);
+ modem->path = g_strdup(path);
+
+ if (!g_dbus_register_interface(conn, path, MODEM_INTERFACE,
+ modem_methods, modem_signals, NULL,
+ modem, modem_free)) {
+ ofono_error("Modem interface init failed on path %s", path);
+ modem_free(modem);
+ return NULL;
+ }
+
+ modem->modem_info->flags |= MODEM_FLAG_INITIALIZING_ATTRS;
+ g_timeout_add(ATTRIBUTE_QUERY_DELAY, query_manufacturer, modem);
+
+ return modem;
+}
+
+void modem_remove(struct ofono_modem *modem)
+{
+ DBusConnection *conn = dbus_gsm_connection();
+ /* Need to make a copy to keep gdbus happy */
+ char *path = g_strdup(modem->path);
+
+ ofono_debug("Removing modem: %s", modem->path);
+
+ g_dbus_unregister_interface(conn, path, MODEM_INTERFACE);
+
+ g_free(path);
+}
diff --git a/src/modem.h b/src/modem.h
new file mode 100644
index 00000000..ea23d6c5
--- /dev/null
+++ b/src/modem.h
@@ -0,0 +1,49 @@
+/*
+ *
+ * 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
+ *
+ */
+
+struct ofono_modem_attribute_ops;
+
+struct ofono_modem {
+ int id;
+ char *path;
+
+ void *userdata;
+
+ GSList *ss_control_list;
+
+ struct ofono_modem_data *modem_info;
+ struct network_registration_data *network_registration;
+ struct voicecalls_data *voicecalls;
+ struct call_forwarding_data *call_forwarding;
+ struct ussd_data *ussd;
+ struct call_settings_data *call_settings;
+ struct call_waiting_data *call_waiting;
+ struct call_meter_data *call_meter;
+};
+
+struct ofono_modem *modem_create(int id, struct ofono_modem_attribute_ops *ops);
+void modem_remove(struct ofono_modem *modem);
+
+void modem_add_interface(struct ofono_modem *modem, const char *interface);
+void modem_remove_interface(struct ofono_modem *modem, const char *interface);
+
+unsigned int modem_alloc_callid(struct ofono_modem *modem);
+void modem_release_callid(struct ofono_modem *modem, int id);
diff --git a/src/network.c b/src/network.c
new file mode 100644
index 00000000..bebc0c15
--- /dev/null
+++ b/src/network.c
@@ -0,0 +1,1062 @@
+/*
+ *
+ * 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
+
+#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"
+
+#define NETWORK_REGISTRATION_INTERFACE "org.ofono.NetworkRegistration"
+#define NETWORK_OPERATOR_INTERFACE "org.ofono.NetworkOperator"
+
+#define NETWORK_REGISTRATION_FLAG_REQUESTING_OPLIST 0x1
+#define NETWORK_REGISTRATION_FLAG_PENDING 0x2
+
+#define AUTO_REGISTER 1
+
+/* How often we update the operator list, in seconds */
+#define OPERATOR_LIST_UPDATE_TIME 300
+
+struct network_registration_data {
+ int status;
+ int location;
+ int cellid;
+ int technology;
+ struct ofono_network_operator *current_operator;
+ GSList *operator_list;
+ struct ofono_network_registration_ops *ops;
+ int flags;
+ DBusMessage *pending;
+ int signal_strength;
+};
+
+static void operator_list_callback(const struct ofono_error *error, int total,
+ const struct ofono_network_operator *list,
+ void *data);
+
+static void current_operator_callback(const struct ofono_error *error,
+ const struct ofono_network_operator *current,
+ void *data);
+
+static void signal_strength_callback(const struct ofono_error *error,
+ int strength, void *data);
+
+static void registration_status_callback(const struct ofono_error *error,
+ int status, int lac, int ci, int tech,
+ void *data);
+
+struct ofono_network_operator_data {
+ struct ofono_network_operator *operator;
+ struct ofono_modem *modem;
+};
+
+static inline const char *network_operator_status_to_string(int status)
+{
+ switch (status) {
+ case OPERATOR_STATUS_AVAILABLE:
+ return "available";
+ case OPERATOR_STATUS_CURRENT:
+ return "current";
+ case OPERATOR_STATUS_FORBIDDEN:
+ return "forbidden";
+ }
+
+ return "unknown";
+}
+
+static inline const char *registration_status_to_string(int status)
+{
+ switch (status) {
+ case NETWORK_REGISTRATION_STATUS_NOT_REGISTERED:
+ return "unregistered";
+ case NETWORK_REGISTRATION_STATUS_REGISTERED:
+ return "registered";
+ case NETWORK_REGISTRATION_STATUS_SEARCHING:
+ return "searching";
+ case NETWORK_REGISTRATION_STATUS_DENIED:
+ return "denied";
+ case NETWORK_REGISTRATION_STATUS_UNKNOWN:
+ return "unknown";
+ case NETWORK_REGISTRATION_STATUS_ROAMING:
+ return "roaming";
+ }
+
+ return "";
+}
+
+static inline const char *registration_tech_to_string(int tech)
+{
+ switch (tech) {
+ case ACCESS_TECHNOLOGY_GSM:
+ return "GSM";
+ case ACCESS_TECHNOLOGY_GSM_COMPACT:
+ return "GSMCompact";
+ case ACCESS_TECHNOLOGY_UTRAN:
+ return "UTRAN";
+ case ACCESS_TECHNOLOGY_GSM_EGPRS:
+ return "GSM+EGPS";
+ case ACCESS_TECHNOLOGY_UTRAN_HSDPA:
+ return "UTRAN+HSDPA";
+ case ACCESS_TECHNOLOGY_UTRAN_HSUPA:
+ return "UTRAN+HSUPA";
+ case ACCESS_TECHNOLOGY_UTRAN_HSDPA_HSUPA:
+ return "UTRAN+HSDPA+HSUPA";
+ default:
+ return "";
+ }
+}
+
+static void register_callback(const struct ofono_error *error, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct network_registration_data *netreg = modem->network_registration;
+ DBusConnection *conn = dbus_gsm_connection();
+ DBusMessage *reply;
+
+ if (!netreg->pending)
+ goto out;
+
+ if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+ reply = dbus_message_new_method_return(netreg->pending);
+ else
+ reply = dbus_gsm_failed(netreg->pending);
+
+ g_dbus_send_message(conn, reply);
+
+ dbus_message_unref(netreg->pending);
+ netreg->pending = NULL;
+
+out:
+ netreg->flags &= ~NETWORK_REGISTRATION_FLAG_PENDING;
+
+ if (netreg->ops->registration_status)
+ netreg->ops->registration_status(modem,
+ registration_status_callback, modem);
+}
+
+/* Must use dbus_gsm_free_string_array on network_operators */
+static void network_operator_populate_registered(struct ofono_modem *modem,
+ char ***network_operators)
+{
+ DBusConnection *conn = dbus_gsm_connection();
+ char **children;
+ int i;
+ int modem_len;
+ int num_children;
+ GSList *l;
+ int *mccmnc;
+ char path[MAX_DBUS_PATH_LEN];
+
+ modem_len = snprintf(path, MAX_DBUS_PATH_LEN, "%s/operator",
+ modem->path);
+
+ if (!dbus_connection_list_registered(conn, path, &children)) {
+ ofono_debug("Unable to obtain registered NetworkOperator(s)");
+ *network_operators = g_try_new0(char *, 1);
+ return;
+ }
+
+ for (i = 0; children[i]; i++)
+ ;
+
+ num_children = i;
+
+ *network_operators = g_try_new0(char *, num_children + 1);
+
+ mccmnc = g_try_new0(int, num_children * 2);
+ for (i = 0; i < num_children; i++)
+ sscanf(children[i], "%3d%3d", &mccmnc[i*2], &mccmnc[i*2+1]);
+
+ /* Quoting 27.007: "The list of operators shall be in order: home
+ * network, networks referenced in SIM or active application in the
+ * UICC (GSM or USIM) in the following order: HPLMN selector, User
+ * controlled PLMN selector, Operator controlled PLMN selector and
+ * PLMN selector (in the SIM or GSM application), and other networks."
+ * Thus we must make sure we return the list in the same order,
+ * if possible. Luckily the operator_list is stored in order already
+ */
+ i = 0;
+ for (l = modem->network_registration->operator_list; l; l = l->next) {
+ struct ofono_network_operator *op = l->data;
+ int j;
+
+ for (j = 0; children[j]; j++) {
+ if (op->mcc == mccmnc[j*2] && op->mnc == mccmnc[j*2+1]) {
+ /* Enough to store '/' + 3 char wide MCC + 3 char wide MNC + null */
+ (*network_operators)[i] = g_try_new(char, modem_len + 8);
+ snprintf((*network_operators)[i], modem_len + 8, "%s/%s",
+ path, children[j]);
+ ++i;
+ }
+ }
+ }
+
+ g_free(mccmnc);
+
+ dbus_free_string_array(children);
+}
+
+static void network_operator_destroy(gpointer userdata)
+{
+ struct ofono_network_operator_data *op = userdata;
+
+ g_free(op);
+}
+
+static gint network_operator_compare(gconstpointer a, gconstpointer b)
+{
+ const struct ofono_network_operator *opa = a;
+ const struct ofono_network_operator *opb = b;
+
+ if (opa->mcc < opb->mcc)
+ return -1;
+
+ if (opa->mcc > opb->mcc)
+ return 1;
+
+ if (opa->mnc < opb->mnc)
+ return -1;
+
+ if (opa->mnc > opb->mnc)
+ return 1;
+
+ return 0;
+}
+
+static inline const char *network_operator_build_path(struct ofono_modem *modem,
+ struct ofono_network_operator *oper)
+{
+ static char path[MAX_DBUS_PATH_LEN];
+
+ snprintf(path, MAX_DBUS_PATH_LEN, "%s/operator/%03d%03d",
+ modem->path, oper->mcc, oper->mnc);
+
+ return path;
+}
+
+static void network_operator_emit_available_operators(struct ofono_modem *modem)
+{
+ //struct network_registration_data *netreg = modem->network_registration;
+ DBusConnection *conn = dbus_gsm_connection();
+ char **network_operators;
+
+ network_operator_populate_registered(modem, &network_operators);
+
+ dbus_gsm_signal_array_property_changed(conn, modem->path,
+ NETWORK_REGISTRATION_INTERFACE,
+ "AvailableOperators",
+ DBUS_TYPE_OBJECT_PATH,
+ &network_operators);
+
+ dbus_gsm_free_string_array(network_operators);
+}
+
+static void set_network_operator_status(struct ofono_modem *modem,
+ struct ofono_network_operator *op,
+ int status)
+{
+ DBusConnection *conn = dbus_gsm_connection();
+ //struct network_registration_data *netreg = modem->network_registration;
+ const char *status_str;
+ const char *path;
+
+ if (op->status == status)
+ return;
+
+ op->status = status;
+
+ status_str = network_operator_status_to_string(status);
+ path = network_operator_build_path(modem, op);
+
+ dbus_gsm_signal_property_changed(conn, path, NETWORK_OPERATOR_INTERFACE,
+ "Status", DBUS_TYPE_STRING,
+ &status_str);
+}
+
+static void set_network_operator_technology(struct ofono_modem *modem,
+ struct ofono_network_operator *op,
+ int tech)
+{
+ //struct network_registration_data *netreg = modem->network_registration;
+ DBusConnection *conn = dbus_gsm_connection();
+ const char *tech_str;
+ const char *path;
+
+ if (op->tech == tech)
+ return;
+
+ op->tech = tech;
+ tech_str = registration_tech_to_string(tech);
+ path = network_operator_build_path(modem, op);
+
+ dbus_gsm_signal_property_changed(conn, path, NETWORK_OPERATOR_INTERFACE,
+ "Technology", DBUS_TYPE_STRING,
+ &tech_str);
+}
+
+static void set_network_operator_name(struct ofono_modem *modem,
+ struct ofono_network_operator *op,
+ const char *name)
+{
+ struct network_registration_data *netreg = modem->network_registration;
+ DBusConnection *conn = dbus_gsm_connection();
+ const char *path;
+
+ if (!strncmp(op->name, name, OFONO_MAX_OPERATOR_NAME_LENGTH))
+ return;
+
+ strncpy(op->name, name, OFONO_MAX_OPERATOR_NAME_LENGTH);
+ op->name[OFONO_MAX_OPERATOR_NAME_LENGTH] = '\0';
+
+ path = network_operator_build_path(modem, op);
+
+ dbus_gsm_signal_property_changed(conn, path,
+ NETWORK_OPERATOR_INTERFACE,
+ "Name", DBUS_TYPE_STRING,
+ &name);
+
+ if (op == netreg->current_operator)
+ dbus_gsm_signal_property_changed(conn, modem->path,
+ NETWORK_REGISTRATION_INTERFACE,
+ "Operator", DBUS_TYPE_STRING,
+ &name);
+}
+
+static DBusMessage *network_operator_get_properties(DBusConnection *conn,
+ DBusMessage *msg,
+ void *data)
+{
+ struct ofono_network_operator_data *op = data;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+
+ const char *name = op->operator->name;
+ const char *status =
+ network_operator_status_to_string(op->operator->status);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ PROPERTIES_ARRAY_SIGNATURE,
+ &dict);
+
+ dbus_gsm_dict_append(&dict, "Name", DBUS_TYPE_STRING, &name);
+
+ dbus_gsm_dict_append(&dict, "Status", DBUS_TYPE_STRING, &status);
+
+ if (op->operator->mcc != -1) {
+ dbus_uint16_t mcc = op->operator->mcc;
+ dbus_gsm_dict_append(&dict, "MobileCountryCode",
+ DBUS_TYPE_UINT16, &mcc);
+ }
+
+ if (op->operator->mnc != -1) {
+ dbus_uint16_t mnc = op->operator->mnc;
+ dbus_gsm_dict_append(&dict, "MobileNetworkCode",
+ DBUS_TYPE_UINT16, &mnc);
+ }
+
+ if (op->operator->tech != -1) {
+ const char *technology =
+ registration_tech_to_string(op->operator->tech);
+
+ dbus_gsm_dict_append(&dict, "Technology", DBUS_TYPE_STRING,
+ &technology);
+ }
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static DBusMessage *network_operator_register(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct ofono_network_operator_data *op = data;
+ struct network_registration_data *netreg = op->modem->network_registration;
+
+ if (netreg->flags & NETWORK_REGISTRATION_FLAG_PENDING)
+ return dbus_gsm_busy(msg);
+
+ if (netreg->ops->register_manual == NULL)
+ return dbus_gsm_not_implemented(msg);
+
+ netreg->flags |= NETWORK_REGISTRATION_FLAG_PENDING;
+ netreg->pending = dbus_message_ref(msg);
+
+ netreg->ops->register_manual(op->modem, op->operator,
+ register_callback, op->modem);
+
+ return NULL;
+}
+
+static GDBusMethodTable network_operator_methods[] = {
+ { "GetProperties", "", "a{sv}", network_operator_get_properties },
+ { "Register", "", "", network_operator_register,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { }
+};
+
+static GDBusSignalTable network_operator_signals[] = {
+ { "PropertyChanged", "sv" },
+ { }
+};
+
+static gboolean network_operator_dbus_register(struct ofono_modem *modem,
+ struct ofono_network_operator *op)
+{
+ DBusConnection *conn = dbus_gsm_connection();
+ const char *path;
+
+ struct ofono_network_operator_data *opd =
+ g_try_new(struct ofono_network_operator_data, 1);
+
+ if (!opd)
+ return FALSE;
+
+ opd->operator = op;
+ opd->modem = modem;
+
+ path = network_operator_build_path(modem, op);
+
+ if (!g_dbus_register_interface(conn, path, NETWORK_OPERATOR_INTERFACE,
+ network_operator_methods,
+ network_operator_signals,
+ NULL, opd,
+ network_operator_destroy)) {
+ ofono_error("Could not register NetworkOperator %s", path);
+ network_operator_destroy(opd);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean network_operator_dbus_unregister(struct ofono_modem *modem,
+ struct ofono_network_operator *op)
+{
+ DBusConnection *conn = dbus_gsm_connection();
+ const char *path = network_operator_build_path(modem, op);
+
+ return g_dbus_unregister_interface(conn, path,
+ NETWORK_OPERATOR_INTERFACE);
+}
+
+static struct network_registration_data *network_registration_create()
+{
+ struct network_registration_data *data;
+
+ data = g_try_new0(struct network_registration_data, 1);
+ if (data == NULL)
+ return data;
+
+ data->status = NETWORK_REGISTRATION_STATUS_UNKNOWN;
+ data->location = -1;
+ data->cellid = -1;
+ data->technology = -1;
+ data->signal_strength = -1;
+
+ return data;
+}
+
+static void network_registration_destroy(gpointer userdata)
+{
+ struct ofono_modem *modem = userdata;
+ struct network_registration_data *data = modem->network_registration;
+ GSList *l;
+
+ for (l = data->operator_list; l; l = l->next) {
+ network_operator_dbus_unregister(modem, l->data);
+ g_free(l->data);
+ }
+
+ g_slist_free(data->operator_list);
+
+ g_free(data);
+
+ modem->network_registration = 0;
+}
+
+static DBusMessage *network_get_properties(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct network_registration_data *netreg = modem->network_registration;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+
+ const char *status = registration_status_to_string(netreg->status);
+ const char *operator =
+ netreg->current_operator ? netreg->current_operator->name : "";
+
+ char **network_operators;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ PROPERTIES_ARRAY_SIGNATURE,
+ &dict);
+
+ dbus_gsm_dict_append(&dict, "Status", DBUS_TYPE_STRING, &status);
+
+ if (netreg->location != -1) {
+ dbus_uint16_t location = netreg->location;
+ dbus_gsm_dict_append(&dict, "LocationAreaCode",
+ DBUS_TYPE_UINT16, &location);
+ }
+
+ if (netreg->cellid != -1) {
+ dbus_uint32_t cellid = netreg->cellid;
+ dbus_gsm_dict_append(&dict, "CellId",
+ DBUS_TYPE_UINT32, &cellid);
+ }
+
+ if (netreg->technology != -1) {
+ const char *technology =
+ registration_tech_to_string(netreg->technology);
+
+ dbus_gsm_dict_append(&dict, "Technology", DBUS_TYPE_STRING,
+ &technology);
+ }
+
+ dbus_gsm_dict_append(&dict, "Operator", DBUS_TYPE_STRING, &operator);
+
+ network_operator_populate_registered(modem, &network_operators);
+
+ dbus_gsm_dict_append_array(&dict, "AvailableOperators",
+ DBUS_TYPE_OBJECT_PATH,
+ &network_operators);
+
+ dbus_gsm_free_string_array(network_operators);
+
+ if (netreg->signal_strength != -1) {
+ dbus_uint16_t strength = netreg->signal_strength;
+ dbus_gsm_dict_append(&dict, "Strength", DBUS_TYPE_UINT16,
+ &strength);
+ }
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static DBusMessage *network_register(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct network_registration_data *netreg = modem->network_registration;
+
+ if (netreg->flags & NETWORK_REGISTRATION_FLAG_PENDING)
+ return dbus_gsm_busy(msg);
+
+ if (netreg->ops->register_auto == NULL)
+ return dbus_gsm_not_implemented(msg);
+
+ netreg->flags |= NETWORK_REGISTRATION_FLAG_PENDING;
+ netreg->pending = dbus_message_ref(msg);
+
+ netreg->ops->register_auto(modem, register_callback, modem);
+
+ return NULL;
+}
+
+static DBusMessage *network_deregister(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct network_registration_data *netreg = modem->network_registration;
+
+ if (netreg->flags & NETWORK_REGISTRATION_FLAG_PENDING)
+ return dbus_gsm_busy(msg);
+
+ if (netreg->ops->deregister == NULL)
+ return dbus_gsm_not_implemented(msg);
+
+ netreg->flags |= NETWORK_REGISTRATION_FLAG_PENDING;
+ netreg->pending = dbus_message_ref(msg);
+
+ netreg->ops->deregister(modem, register_callback, modem);
+
+ return NULL;
+}
+
+static GDBusMethodTable network_registration_methods[] = {
+ { "GetProperties", "", "a{sv}", network_get_properties },
+ { "Register", "", "", network_register,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "Deregister", "", "", network_deregister,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { }
+};
+
+static GDBusSignalTable network_registration_signals[] = {
+ { "PropertyChanged", "sv" },
+ { }
+};
+
+static void update_network_operator_list(struct ofono_modem *modem)
+{
+ struct network_registration_data *netreg = modem->network_registration;
+
+ if (netreg->flags & NETWORK_REGISTRATION_FLAG_REQUESTING_OPLIST)
+ return;
+
+ if (!netreg->ops->list_operators)
+ return;
+
+ netreg->flags |= NETWORK_REGISTRATION_FLAG_REQUESTING_OPLIST;
+ netreg->ops->list_operators(modem, operator_list_callback, modem);
+}
+
+static gboolean update_network_operator_list_cb(void *user_data)
+{
+ struct ofono_modem *modem = user_data;
+
+ update_network_operator_list(modem);
+
+ return TRUE;
+}
+
+static gboolean update_network_operator_list_init(void *user_data)
+{
+ struct ofono_modem *modem = user_data;
+
+ update_network_operator_list(modem);
+
+ return FALSE;
+}
+
+static void set_registration_status(struct ofono_modem *modem, int status)
+{
+ const char *str_status = registration_status_to_string(status);
+ struct network_registration_data *netreg = modem->network_registration;
+ DBusConnection *conn = dbus_gsm_connection();
+
+ netreg->status = status;
+
+ dbus_gsm_signal_property_changed(conn, modem->path,
+ NETWORK_REGISTRATION_INTERFACE,
+ "Status", DBUS_TYPE_STRING,
+ &str_status);
+}
+
+static void set_registration_location(struct ofono_modem *modem, int lac)
+{
+ struct network_registration_data *netreg = modem->network_registration;
+ DBusConnection *conn = dbus_gsm_connection();
+ dbus_uint16_t dbus_lac = lac;
+
+ if (lac > 0xffff)
+ return;
+
+ netreg->location = lac;
+
+ if (netreg->location == -1)
+ return;
+
+ dbus_gsm_signal_property_changed(conn, modem->path,
+ NETWORK_REGISTRATION_INTERFACE,
+ "LocationAreaCode",
+ DBUS_TYPE_UINT16, &dbus_lac);
+}
+
+static void set_registration_cellid(struct ofono_modem *modem, int ci)
+{
+ struct network_registration_data *netreg = modem->network_registration;
+ DBusConnection *conn = dbus_gsm_connection();
+ dbus_uint16_t dbus_ci = ci;
+
+ netreg->cellid = ci;
+
+ if (netreg->cellid == -1)
+ return;
+
+ dbus_gsm_signal_property_changed(conn, modem->path,
+ NETWORK_REGISTRATION_INTERFACE,
+ "CellId", DBUS_TYPE_UINT32,
+ &dbus_ci);
+}
+
+static void set_registration_technology(struct ofono_modem *modem, int tech)
+{
+ struct network_registration_data *netreg = modem->network_registration;
+ const char *tech_str = registration_tech_to_string(tech);
+ DBusConnection *conn = dbus_gsm_connection();
+
+ netreg->technology = tech;
+
+ if (netreg->technology == -1)
+ return;
+
+ dbus_gsm_signal_property_changed(conn, modem->path,
+ NETWORK_REGISTRATION_INTERFACE,
+ "Technology", DBUS_TYPE_STRING,
+ &tech_str);
+}
+
+static void initialize_network_registration(struct ofono_modem *modem)
+{
+ DBusConnection *conn = dbus_gsm_connection();
+
+ if (!g_dbus_register_interface(conn, modem->path,
+ NETWORK_REGISTRATION_INTERFACE,
+ network_registration_methods,
+ network_registration_signals,
+ NULL, modem,
+ network_registration_destroy)) {
+ ofono_error("Could not register NetworkRegistration interface");
+ network_registration_destroy(modem);
+
+ return;
+ }
+
+ ofono_debug("NetworkRegistration interface for modem: %s created",
+ modem->path);
+
+ modem_add_interface(modem, NETWORK_REGISTRATION_INTERFACE);
+
+ if (modem->network_registration->ops->list_operators) {
+ g_timeout_add_seconds(OPERATOR_LIST_UPDATE_TIME,
+ update_network_operator_list_cb, modem);
+
+ g_timeout_add_seconds(5, update_network_operator_list_init,
+ modem);
+ }
+}
+
+void ofono_network_registration_notify(struct ofono_modem *modem, int status,
+ int lac, int ci, int tech)
+{
+ struct network_registration_data *netreg = modem->network_registration;
+
+ if (!netreg)
+ return;
+
+ if (netreg->status != status)
+ set_registration_status(modem, status);
+
+ if (netreg->location != lac)
+ set_registration_location(modem, lac);
+
+ if (netreg->cellid != ci)
+ set_registration_cellid(modem, ci);
+
+ if (netreg->technology != tech)
+ set_registration_technology(modem, tech);
+
+ if (netreg->status == 1 || netreg->status == 5) {
+ if (netreg->ops->current_operator)
+ netreg->ops->current_operator(modem,
+ current_operator_callback, modem);
+ } else {
+ struct ofono_error error;
+
+ error.type = OFONO_ERROR_TYPE_NO_ERROR;
+ error.error = 0;
+
+ current_operator_callback(&error, NULL, modem);
+
+ netreg->signal_strength = -1;
+ }
+}
+
+static void operator_list_callback(const struct ofono_error *error, int total,
+ const struct ofono_network_operator *list,
+ void *data)
+{
+ struct ofono_modem *modem = data;
+ struct network_registration_data *netreg = modem->network_registration;
+ GSList *n = NULL;
+ GSList *o;
+ int i;
+ gboolean need_to_emit = FALSE;
+
+ netreg->flags &= ~NETWORK_REGISTRATION_FLAG_REQUESTING_OPLIST;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ ofono_debug("Error occurred during operator list");
+ return;
+ }
+
+ for (i = 0; i < total; i++) {
+ o = g_slist_find_custom(netreg->operator_list, &list[i],
+ network_operator_compare);
+
+ if (o) { /* Update and move to a new list */
+ set_network_operator_status(modem, o->data,
+ list[i].status);
+
+ set_network_operator_technology(modem, o->data,
+ list[i].tech);
+
+ set_network_operator_name(modem, o->data,
+ list[i].name);
+
+ n = g_slist_prepend(n, o->data);
+ netreg->operator_list =
+ g_slist_remove(netreg->operator_list, o->data);
+ } else {
+ /* New operator */
+ struct ofono_network_operator *op =
+ g_try_new0(struct ofono_network_operator, 1);
+ if (!op)
+ continue;
+
+ memcpy(op, &list[i], sizeof(struct ofono_network_operator));
+
+ n = g_slist_prepend(n, op);
+
+ network_operator_dbus_register(modem, op);
+
+ need_to_emit = TRUE;
+ }
+ }
+
+ if (n)
+ n = g_slist_reverse(n);
+
+ if (netreg->operator_list)
+ need_to_emit = TRUE;
+
+ for (o = netreg->operator_list; o; o = o->next) {
+ network_operator_dbus_unregister(modem, o->data);
+ g_free(o->data);
+ }
+
+ g_slist_free(netreg->operator_list);
+
+ netreg->operator_list = n;
+
+ if (need_to_emit)
+ network_operator_emit_available_operators(modem);
+}
+
+static void current_operator_callback(const struct ofono_error *error,
+ const struct ofono_network_operator *current,
+ void *data)
+{
+ DBusConnection *conn = dbus_gsm_connection();
+ struct ofono_modem *modem = data;
+ struct network_registration_data *netreg = modem->network_registration;
+ GSList *op = NULL;
+ const char *operator;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ ofono_debug("Error during current operator");
+ return;
+ }
+
+ if (!netreg->current_operator && !current)
+ return;
+
+ /* We got a new network operator, reset the previous one's status */
+ /* It will be updated properly later */
+ if (netreg->current_operator &&
+ (!current ||
+ network_operator_compare(current, netreg->current_operator)))
+ set_network_operator_status(modem, netreg->current_operator,
+ OPERATOR_STATUS_AVAILABLE);
+
+ if (current)
+ op = g_slist_find_custom(netreg->operator_list, current,
+ network_operator_compare);
+
+ if (op) {
+ netreg->current_operator = op->data;
+ set_network_operator_status(modem, op->data,
+ OPERATOR_STATUS_CURRENT);
+ set_network_operator_technology(modem, op->data,
+ current->tech);
+ set_network_operator_name(modem, op->data, current->name);
+
+ return;
+ }
+
+ if (current) {
+ netreg->current_operator =
+ g_try_new0(struct ofono_network_operator, 1);
+
+ if (!netreg->current_operator) {
+ ofono_error("Unable to allocate current operator");
+ return;
+ }
+
+ memcpy(netreg->current_operator, current,
+ sizeof(struct ofono_network_operator));
+
+ netreg->operator_list = g_slist_append(netreg->operator_list,
+ netreg->current_operator);
+
+ network_operator_dbus_register(modem, netreg->current_operator);
+ network_operator_emit_available_operators(modem);
+ } else {
+ /* We don't free this here because operator is registered */
+ /* Taken care of elsewhere */
+ netreg->current_operator = NULL;
+ }
+
+ operator =
+ netreg->current_operator ? netreg->current_operator->name : "";
+
+ dbus_gsm_signal_property_changed(conn, modem->path,
+ NETWORK_REGISTRATION_INTERFACE,
+ "Operator", DBUS_TYPE_STRING,
+ &operator);
+}
+
+static void registration_status_callback(const struct ofono_error *error,
+ int status, int lac, int ci, int tech,
+ void *data)
+{
+ struct ofono_modem *modem = data;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ ofono_debug("Error during registration status query");
+ return;
+ }
+
+ ofono_network_registration_notify(modem, status, lac, ci, tech);
+}
+
+static void init_registration_status(const struct ofono_error *error,
+ int status, int lac, int ci, int tech,
+ void *data)
+{
+ struct ofono_modem *modem = data;
+ struct network_registration_data *netreg = modem->network_registration;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ ofono_debug("Error during registration status query");
+ return;
+ }
+
+ ofono_network_registration_notify(modem, status, lac, ci, tech);
+
+ /* Bootstrap our signal strength value without waiting for the
+ * stack to report it
+ */
+ if (netreg->status == 1 || netreg->status == 5) {
+ if (netreg->ops->signal_strength)
+ netreg->ops->signal_strength(modem,
+ signal_strength_callback, modem);
+ }
+
+ if (AUTO_REGISTER && (status == 0 || status == 3))
+ netreg->ops->register_auto(modem, register_callback, modem);
+}
+
+void ofono_signal_strength_notify(struct ofono_modem *modem, int strength)
+{
+ struct network_registration_data *netreg = modem->network_registration;
+ DBusConnection *conn = dbus_gsm_connection();
+
+ if (netreg->signal_strength == strength)
+ return;
+
+ /* Theoretically we can get signal strength even when not registered
+ * to any network. However, what do we do with it in that case?
+ */
+ if (netreg->status != NETWORK_REGISTRATION_STATUS_REGISTERED &&
+ netreg->status != NETWORK_REGISTRATION_STATUS_ROAMING)
+ return;
+
+ netreg->signal_strength = strength;
+
+ if (strength != -1) {
+ dbus_uint16_t strength = netreg->signal_strength;
+
+ dbus_gsm_signal_property_changed(conn, modem->path,
+ NETWORK_REGISTRATION_INTERFACE,
+ "Strength", DBUS_TYPE_UINT16,
+ &strength);
+ }
+}
+
+static void signal_strength_callback(const struct ofono_error *error,
+ int strength, void *data)
+{
+ struct ofono_modem *modem = data;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ ofono_debug("Error during signal strength query");
+ return;
+ }
+
+ ofono_signal_strength_notify(modem, strength);
+}
+
+int ofono_network_registration_register(struct ofono_modem *modem,
+ struct ofono_network_registration_ops *ops)
+{
+ if (modem == NULL)
+ return -1;
+
+ if (ops == NULL)
+ return -1;
+
+ modem->network_registration = network_registration_create();
+ if (modem->network_registration == NULL)
+ return -1;
+
+ modem->network_registration->ops = ops;
+
+ initialize_network_registration(modem);
+
+ if (ops->registration_status)
+ ops->registration_status(modem, init_registration_status,
+ modem);
+
+ return 0;
+}
+
+void ofono_network_registration_unregister(struct ofono_modem *modem)
+{
+ DBusConnection *conn = dbus_gsm_connection();
+
+ g_dbus_unregister_interface(conn, modem->path,
+ NETWORK_REGISTRATION_INTERFACE);
+ modem_remove_interface(modem, NETWORK_REGISTRATION_INTERFACE);
+}
+
diff --git a/src/ofono.h b/src/ofono.h
index bf59de3f..0a7d32a8 100644
--- a/src/ofono.h
+++ b/src/ofono.h
@@ -23,6 +23,9 @@
#define OFONO_API_SUBJECT_TO_CHANGE
+int __ofono_manager_init();
+void __ofono_manager_cleanup();
+
#include <ofono/log.h>
int __ofono_log_init(gboolean detach, gboolean debug);
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);
+}
diff --git a/src/ussd.h b/src/ussd.h
new file mode 100644
index 00000000..7d8c6d9d
--- /dev/null
+++ b/src/ussd.h
@@ -0,0 +1,39 @@
+/*
+ *
+ * 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
+ *
+ */
+
+struct ussd_data {
+ struct ofono_ussd_ops *ops;
+ int state;
+ DBusMessage *pending;
+ int flags;
+};
+
+typedef gboolean (*ss_control_cb_t)(struct ofono_modem *modem, int type,
+ const char *sc,
+ const char *sia, const char *sib,
+ const char *sic, const char *dn,
+ DBusMessage *msg);
+
+gboolean ss_control_register(struct ofono_modem *modem, const char *str,
+ ss_control_cb_t cb);
+
+void ss_control_unregister(struct ofono_modem *modem, const char *str,
+ ss_control_cb_t cb);
diff --git a/src/util.c b/src/util.c
new file mode 100644
index 00000000..90c36cd9
--- /dev/null
+++ b/src/util.c
@@ -0,0 +1,693 @@
+/*
+ *
+ * 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
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <glib.h>
+
+#include "util.h"
+
+/*
+ Name: GSM 03.38 to Unicode
+ Unicode version: 3.0
+ Table version: 1.1
+ Table format: Format A
+ Date: 2000 May 30
+ Authors: Ken Whistler
+ Kent Karlsson
+ Markus Kuhn
+
+ Copyright (c) 2000 Unicode, Inc. All Rights reserved.
+
+ This file is provided as-is by Unicode, Inc. (The Unicode Consortium).
+ No claims are made as to fitness for any particular purpose. No
+ warranties of any kind are expressed or implied. The recipient
+ agrees to determine applicability of information provided. If this
+ file has been provided on optical media by Unicode, Inc., the sole
+ remedy for any claim will be exchange of defective media within 90
+ days of receipt.
+
+ Unicode, Inc. hereby grants the right to freely use the information
+ supplied in this file in the creation of products supporting the
+ Unicode Standard, and to make copies of this file in any form for
+ internal or external distribution as long as this notice remains
+ attached.
+*/
+
+/* GSM to Unicode extension table, for GSM sequences starting with 0x1B */
+static unsigned short gsm_extension[] =
+{
+ 0x0A, 0x000C, /* See NOTE 3 in 23.038 */
+ 0x14, 0x005E,
+ 0x1B, 0x0020, /* See NOTE 1 in 23.038 */
+ 0x28, 0x007B,
+ 0x29, 0x007D,
+ 0x2F, 0x005C,
+ 0x3C, 0x005B,
+ 0x3D, 0x007E,
+ 0x3E, 0x005D,
+ 0x40, 0x007C,
+ 0x65, 0x20AC
+};
+
+/* Used for conversion of GSM to Unicode */
+static unsigned short gsm_table[] =
+{
+ 0x0040, 0x00A3, 0x0024, 0x00A5, 0x00E8, 0x00E9, 0x00F9, 0x00EC, /* 0x07 */
+ 0x00F2, 0x00E7, 0x000A, 0x00D8, 0x00F8, 0x000D, 0x00C5, 0x00E5, /* 0x0F */
+ 0x0394, 0x005F, 0x03A6, 0x0393, 0x039B, 0x03A9, 0x03A0, 0x03A8, /* 0x17 */
+ 0x03A3, 0x0398, 0x039E, 0x00A0, 0x00C6, 0x00E6, 0x00DF, 0x00C9, /* 0x1F */
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x00A4, 0x0025, 0x0026, 0x0027, /* 0x27 */
+ 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, /* 0x2F */
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, /* 0x37 */
+ 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, /* 0x3F */
+ 0x00A1, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, /* 0x47 */
+ 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, /* 0x4F */
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, /* 0x57 */
+ 0x0058, 0x0059, 0x005A, 0x00C4, 0x00D6, 0x00D1, 0x00DC, 0x00A7, /* 0x5F */
+ 0x00BF, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, /* 0x67 */
+ 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, /* 0x6F */
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, /* 0x77 */
+ 0x0078, 0x0079, 0x007A, 0x00E4, 0x00F6, 0x00F1, 0x00FC, 0x00E0 /* 0x7F */
+};
+
+#define GUND 0xFFFF
+
+/* 3GPP 27.005 Annex A */
+static unsigned short unicode_256_table[] =
+{
+ GUND, GUND, GUND, GUND, GUND, GUND, GUND, GUND, /* 0x07 */
+ GUND, GUND, 0x0A, GUND, 0x1B0A, 0x0D, GUND, GUND, /* 0x0F */
+ GUND, GUND, GUND, GUND, GUND, GUND, GUND, GUND, /* 0x17 */
+ GUND, GUND, GUND, GUND, GUND, GUND, GUND, GUND, /* 0x1F */
+ 0x20, 0x21, 0x22, 0x23, 0x02, 0x25, 0x26, 0x27, /* 0x27 */
+ 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, /* 0x2F */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x37 */
+ 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, /* 0x3F */
+ 0x00, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x47 */
+ 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, /* 0x4F */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x57 */
+ 0x58, 0x59, 0x5A, 0x1B3C, 0x1B2F, 0x1B3E, 0x1B14, 0x11, /* 0x5F */
+ GUND, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x67 */
+ 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, /* 0x6F */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x77 */
+ 0x78, 0x79, 0x7A, 0x1B28, 0x1B40, 0x1B29, 0x1B3D, GUND, /* 0x7F */
+ GUND, GUND, GUND, GUND, GUND, GUND, GUND, GUND, /* 0x87 */
+ GUND, GUND, GUND, GUND, GUND, GUND, GUND, GUND, /* 0x8F */
+ GUND, GUND, GUND, GUND, GUND, GUND, GUND, GUND, /* 0x97 */
+ GUND, GUND, GUND, GUND, GUND, GUND, GUND, GUND, /* 0x9F */
+ GUND, 0x40, GUND, 0x01, 0x24, 0x03, GUND, 0x5f, /* 0xA7 */
+ GUND, GUND, GUND, GUND, GUND, GUND, GUND, GUND, /* 0xAF */
+ GUND, GUND, GUND, GUND, GUND, GUND, GUND, GUND, /* 0xB7 */
+ GUND, GUND, GUND, GUND, GUND, GUND, GUND, 0x60, /* 0xBF */
+ 0x41, 0x41, 0x41, 0x41, 0x5B, 0x0E, 0x1C, 0x09, /* 0xC7 */
+ 0x45, 0x1F, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49, /* 0xCF */
+ GUND, 0x5D, 0x4F, 0x4F, 0x4F, 0x4F, 0x5C, GUND, /* 0xD7 */
+ 0x0B, 0x55, 0x55, 0x55, 0x5E, 0x59, GUND, 0x1E, /* 0xDF */
+ 0x7F, 0x61, 0x61, 0x61, 0x7B, 0x0F, 0x1D, 0x09, /* 0xE7 */
+ 0x04, 0x05, 0x65, 0x65, 0x07, 0x69, 0x69, 0x69, /* 0xEF */
+ GUND, 0x7D, 0x08, 0x6F, 0x6F, 0x6F, 0x7C, GUND, /* 0xF7 */
+ 0x0C, 0x06, 0x75, 0x75, 0x7E, 0x79, GUND, 0x79 /* 0xFF */
+};
+
+/* Starts at 0x0390 */
+static unsigned short greek_unicode_offset = 0x0390;
+
+static unsigned short greek_unicode_table[] =
+{
+ GUND, GUND, GUND, 0x13, 0x10, GUND, GUND, GUND, /* 0x07 */
+ 0x19, GUND, GUND, 0x14, GUND, GUND, 0x1A, GUND, /* 0x0F */
+ 0x16, GUND, GUND, 0x18, GUND, GUND, 0x12, GUND, /* 0x17 */
+ 0x17, 0x15, GUND, GUND, GUND, GUND, GUND, GUND, /* 0x1F */
+};
+
+#define UTF8_LENGTH(c) \
+ ((c) < 0x80 ? 1 : \
+ ((c) < 0x800 ? 2 : 3))
+
+static unsigned short gsm_extension_table_lookup(unsigned char k)
+{
+ static unsigned int ext_table_len =
+ (sizeof(gsm_extension) / sizeof(unsigned short)) >> 1;
+ unsigned int i;
+ unsigned short *t;
+
+ for (i = 0, t = gsm_extension; i < ext_table_len; i++) {
+ if (t[0] == k)
+ return t[1];
+ t += 2;
+ }
+
+ return 0;
+}
+
+/*!
+ * Converts text coded using GSM codec into UTF8 encoded text. If len
+ * is less than 0, and terminator character is given, the length is
+ * computed automatically.
+ *
+ * Returns newly-allocated UTF8 encoded string or NULL if the conversion
+ * could not be performed. Returns the number of bytes read from the
+ * GSM encoded string in items_read (if not NULL), not including the
+ * terminator character. Returns the number of bytes written into the UTF8
+ * encoded string in items_written (if not NULL) not including the terminal
+ * '\0' character. The caller is reponsible for freeing the returned value.
+ */
+char *convert_gsm_to_utf8(const unsigned char *text, long len,
+ long *items_read, long *items_written,
+ unsigned char terminator)
+{
+ char *res = NULL;
+ char *out;
+ long i = 0;
+ long res_length;
+
+ if (len == 0 || (len < 0 && !terminator))
+ goto err_out;
+
+ if (len < 0) {
+ i = 0;
+
+ while (text[i] != terminator)
+ i++;
+
+ len = i;
+ }
+
+ for (i = 0, res_length = 0; i < len; i++) {
+ unsigned short c;
+
+ if (text[i] > 0x7f)
+ goto err_out;
+
+ if (text[i] == 0x1b) {
+ ++i;
+ if (i >= len)
+ goto err_out;
+
+ c = gsm_extension_table_lookup(text[i]);
+
+ if (c == 0)
+ goto err_out;
+ } else {
+ c = gsm_table[text[i]];
+ }
+
+ res_length += UTF8_LENGTH(c);
+ }
+
+ res = g_malloc(res_length + 1);
+
+ if (!res)
+ goto err_out;
+
+ out = res;
+
+ i = 0;
+ while (out < res + res_length) {
+ unsigned short c;
+
+ if (text[i] == 0x1b)
+ c = gsm_extension_table_lookup(text[++i]);
+ else
+ c = gsm_table[text[i]];
+
+ out += g_unichar_to_utf8(c, out);
+
+ ++i;
+ }
+
+ *out = '\0';
+
+ if (items_written)
+ *items_written = out - res;
+
+err_out:
+ if (items_read)
+ *items_read = i;
+
+ return res;
+}
+
+static unsigned short unicode_to_gsm(unsigned short c)
+{
+ static int greek_unicode_size = sizeof(greek_unicode_table) /
+ sizeof(unsigned short);
+ unsigned short converted = GUND;
+
+ if (c == 0x20AC)
+ converted = 0x1B65;
+ else if (c < 256)
+ converted = unicode_256_table[c];
+ else if ((c >= greek_unicode_offset) &&
+ (c < (greek_unicode_offset + greek_unicode_size))) {
+ converted = greek_unicode_table[c-greek_unicode_offset];
+ }
+
+ return converted;
+}
+
+/*!
+ * Converts UTF-8 encoded text to GSM alphabet. The result is unpacked,
+ * with the 7th bit always 0. If terminator is not 0, a terminator character
+ * is appended to the result. This should be in the range 0x80-0xf0
+ *
+ * Returns the encoded data or NULL if the data could not be encoded. The
+ * data must be freed by the caller. If items_read is not NULL, it contains
+ * the actual number of bytes read. If items_written is not NULL, contains
+ * the number of bytes written.
+ */
+unsigned char *convert_utf8_to_gsm(const char *text, long len,
+ long *items_read, long *items_written,
+ unsigned char terminator)
+{
+ long nchars = 0;
+ const char *in;
+ unsigned char *out;
+ unsigned char *res = NULL;
+ long res_len;
+ long i;
+
+ in = text;
+ res_len = 0;
+
+ while ((len < 0 || text + len - in > 0) && *in) {
+ long max = len < 0 ? 6 : text + len - in;
+ gunichar c = g_utf8_get_char_validated(in, max);
+ unsigned short converted = GUND;
+
+ if (c & 0x80000000)
+ goto err_out;
+
+ if (c > 0xffff)
+ goto err_out;
+
+ converted = unicode_to_gsm(c);
+
+ if (converted == GUND)
+ goto err_out;
+
+ if (converted & 0x1b00)
+ res_len += 2;
+ else
+ res_len += 1;
+
+ in = g_utf8_next_char(in);
+ nchars += 1;
+ }
+
+ res = g_malloc(res_len + (terminator ? 1 : 0));
+
+ if (!res)
+ goto err_out;
+
+ in = text;
+ out = res;
+ for (i = 0; i < nchars; i++) {
+ unsigned short converted;
+
+ gunichar c = g_utf8_get_char(in);
+
+ converted = unicode_to_gsm(c);
+ if (converted & 0x1b00) {
+ *out = 0x1b;
+ ++out;
+ }
+
+ *out = converted;
+ ++out;
+
+ in = g_utf8_next_char(in);
+ }
+
+ if (terminator)
+ *out = terminator;
+
+ if (items_written)
+ *items_written = out - res;
+
+err_out:
+ if (items_read)
+ *items_read = in - text;
+
+ return res;
+}
+
+/*!
+ * Decodes the hex encoded data and converts to a byte array. If terminator
+ * is not 0, the terminator character is appended to the end of the result.
+ * This might be useful for converting GSM encoded data if the CSCS is set
+ * to HEX.
+ *
+ * Please note that this since GSM does allow embedded null characeters, use
+ * of the terminator or the items_writen is encouraged to find the real size
+ * of the result.
+ */
+unsigned char *decode_hex_own_buf(const char *in, long len, long *items_written,
+ unsigned char terminator,
+ unsigned char *buf)
+{
+ long i, j;
+ char c;
+ unsigned char b;
+
+ if (len < 0)
+ len = strlen(in);
+
+ len &= ~0x1;
+
+ for (i = 0, j = 0; i < len; i++, j++) {
+ c = toupper(in[i]);
+
+ if (c >= '0' && c <= '9')
+ b = c - '0';
+ else if (c >= 'A' && c <= 'F')
+ b = 10 + c - 'A';
+ else
+ return NULL;
+
+ i += 1;
+
+ c = toupper(in[i]);
+
+ if (c >= '0' && c <= '9')
+ b = b*16 + c - '0';
+ else if (c >= 'A' && c <= 'F')
+ b = b*16 + 10 + c - 'A';
+ else
+ return NULL;
+
+ buf[j] = b;
+ }
+
+ if (terminator)
+ buf[j] = terminator;
+
+ if (items_written)
+ *items_written = j;
+
+ return buf;
+}
+
+unsigned char *decode_hex(const char *in, long len, long *items_written,
+ unsigned char terminator)
+{
+ long i;
+ char c;
+ unsigned char *buf;
+
+ if (len < 0)
+ len = strlen(in);
+
+ len &= ~0x1;
+
+ for (i = 0; i < len; i++) {
+ c = toupper(in[i]);
+
+ if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F'))
+ continue;
+
+ return NULL;
+ }
+
+ buf = g_new(unsigned char, (len >> 1) + (terminator ? 1 : 0));
+
+ return decode_hex_own_buf(in, len, items_written, terminator, buf);
+}
+
+/*!
+ * Encodes the data using hexadecimal characters. len can be negative,
+ * in that case the terminator is used to find the last character. This is
+ * useful for handling GSM-encoded strings which allow ASCII NULL character
+ * in the stream.
+ */
+char *encode_hex_own_buf(const unsigned char *in, long len,
+ unsigned char terminator, char *buf)
+{
+ long i, j;
+ char c;
+
+ if (len < 0) {
+ i = 0;
+
+ while (in[i] != terminator)
+ i++;
+
+ len = i;
+ }
+
+ for (i = 0, j = 0; i < len; i++, j++) {
+ c = (in[i] >> 4) & 0xf;
+
+ if (c <= 9)
+ buf[j] = '0' + c;
+ else
+ buf[j] = 'A' + c - 10;
+
+ j += 1;
+
+ c = (in[i]) & 0xf;
+
+ if (c <= 9)
+ buf[j] = '0' + c;
+ else
+ buf[j] = 'A' + c - 10;
+ }
+
+ buf[j] = '\0';
+
+ return buf;
+}
+
+char *encode_hex(const unsigned char *in, long len, unsigned char terminator)
+{
+ char *buf;
+ int i;
+
+ if (len < 0) {
+ i = 0;
+
+ while (in[i] != terminator)
+ i++;
+
+ len = i;
+ }
+
+ buf = g_new(char, len * 2 + 1);
+
+ return encode_hex_own_buf(in, len, terminator, buf);
+}
+
+unsigned char *unpack_7bit_own_buf(const unsigned char *in, long len,
+ int byte_offset, gboolean cb,
+ long max_to_unpack, long *items_written,
+ unsigned char terminator,
+ unsigned char *buf)
+{
+ unsigned char rest = 0;
+ unsigned char *out = buf;
+ int bits = 7 - (byte_offset % 7);
+ long i;
+
+ if (len <= 0)
+ return NULL;
+
+ /* In the case of CB, unpack as much as possible */
+ if (cb == TRUE)
+ max_to_unpack = len * 8 / 7;
+
+ for (i = 0; (i < len) && ((out-buf) < max_to_unpack); i++) {
+ /* Grab what we have in the current octet */
+ *out = (in[i] & ((1 << bits) - 1)) << (7 - bits);
+
+ /* Append what we have from the previous octet, if any */
+ *out |= rest;
+
+ /* Figure out the remainder */
+ rest = (in[i] >> bits) & ((1 << (8-bits)) - 1);
+
+ /* We have the entire character, here we don't increate
+ * out if this is we started at an offset. Instead
+ * we effectively populate variable rest */
+ if (i != 0 || bits == 7)
+ out++;
+
+ if ((out-buf) == max_to_unpack)
+ break;
+
+ /* We expected only 1 bit from this octet, means there's 7
+ * left, take care of them here */
+ if (bits == 1) {
+ *out = rest;
+ out++;
+ bits = 7;
+ rest = 0;
+ } else
+ bits = bits - 1;
+ }
+
+ /* According to 23.038 6.1.2.3.1, last paragraph:
+ * "If the total number of characters to be sent equals (8n-1)
+ * where n=1,2,3 etc. then there are 7 spare bits at the end
+ * of the message. To avoid the situation where the receiving
+ * entity confuses 7 binary zero pad bits as the @ character,
+ * the carriage return or <CR> character shall be used for
+ * padding in this situation, just as for Cell Broadcast."
+ *
+ * "The receiving entity shall remove the final <CR> character where
+ * the message ends on an octet boundary with <CR> as the last
+ * character.
+ */
+ if (cb && (((out - buf) % 8) == 0) && (*(out-1) == '\r'))
+ out = out - 1;
+
+ if (terminator)
+ *out = terminator;
+
+ if (items_written)
+ *items_written = out - buf;
+
+ return buf;
+}
+
+unsigned char *unpack_7bit(const unsigned char *in, long len, int byte_offset,
+ gboolean cb, long max_to_unpack,
+ long *items_written, unsigned char terminator)
+{
+ unsigned char *buf = g_new(unsigned char,
+ len * 8 / 7 + (terminator ? 1 : 0));
+
+ return unpack_7bit_own_buf(in, len, byte_offset, cb, max_to_unpack,
+ items_written, terminator, buf);
+}
+
+unsigned char *pack_7bit_own_buf(const unsigned char *in, long len,
+ int byte_offset, gboolean cb,
+ long *items_written,
+ unsigned char terminator,
+ unsigned char *buf)
+{
+ int bits = 7 - (byte_offset % 7);
+ unsigned char *out = buf;
+ long i;
+ long total_bits;
+
+ if (len == 0 || !items_written)
+ return NULL;
+
+ if (len < 0) {
+ i = 0;
+
+ while (in[i] != terminator)
+ i++;
+
+ len = i;
+ }
+
+ total_bits = len * 7;
+
+ if (bits != 7) {
+ total_bits += bits;
+ bits = bits - 1;
+ *out = 0;
+ }
+
+ for (i = 0; i < len; i++) {
+ if (bits != 7) {
+ *out |= (in[i] & ((1 << (7 - bits)) - 1)) <<
+ (bits + 1);
+ out++;
+ }
+
+ /* This is a no op when bits == 0, lets keep valgrind happy */
+ if (bits != 0)
+ *out = in[i] >> (7 - bits);
+
+ if (bits == 0)
+ bits = 7;
+ else
+ bits = bits - 1;
+ }
+
+ /* If <CR> is intended to be the last character and the message
+ * (including the wanted <CR>) ends on an octet boundary, then
+ * another <CR> must be added together with a padding bit 0. The
+ * receiving entity will perform the carriage return function twice,
+ * but this will not result in misoperation as the definition of
+ * <CR> in clause 6.1.1 is identical to the definition of <CR><CR>.
+ */
+ if (cb && ((total_bits % 8) == 1))
+ *out |= '\r' << 1;
+
+ if (bits != 7)
+ out++;
+
+ if (cb && ((total_bits % 8) == 0) && (in[len-1] == '\r')) {
+ *out = '\r';
+ out++;
+ }
+
+ *items_written = out - buf;
+
+ return buf;
+}
+
+unsigned char *pack_7bit(const unsigned char *in, long len, int byte_offset,
+ gboolean cb, long *items_written,
+ unsigned char terminator)
+{
+ int bits = 7 - (byte_offset % 7);
+ long i;
+ long total_bits;
+ unsigned char *buf;
+
+ if (len == 0 || !items_written)
+ return NULL;
+
+ if (len < 0) {
+ i = 0;
+
+ while (in[i] != terminator)
+ i++;
+
+ len = i;
+ }
+
+ total_bits = len * 7;
+
+ if (bits != 7)
+ total_bits += bits;
+
+ /* Round up number of bytes, must append <cr> if true */
+ if (cb && ((total_bits % 8) == 0) && (in[len-1] == '\r'))
+ buf = g_new(unsigned char, (total_bits + 14) / 8);
+ else
+ buf = g_new(unsigned char, (total_bits + 7) / 8);
+
+ return pack_7bit_own_buf(in, len, byte_offset, cb, items_written,
+ terminator, buf);
+}
diff --git a/src/util.h b/src/util.h
new file mode 100644
index 00000000..9d90d315
--- /dev/null
+++ b/src/util.h
@@ -0,0 +1,58 @@
+/*
+ *
+ * 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
+ *
+ */
+
+char *convert_gsm_to_utf8(const unsigned char *text, long len, long *items_read,
+ long *items_written, unsigned char terminator);
+unsigned char *convert_utf8_to_gsm(const char *text, long len, long *items_read,
+ long *items_written, unsigned char terminator);
+
+unsigned char *decode_hex_own_buf(const char *in, long len, long *items_written,
+ unsigned char terminator,
+ unsigned char *buf);
+
+unsigned char *decode_hex(const char *in, long len, long *items_written,
+ unsigned char terminator);
+
+char *encode_hex_own_buf(const unsigned char *in, long len,
+ unsigned char terminator, char *buf);
+
+char *encode_hex(const unsigned char *in, long len,
+ unsigned char terminator);
+
+unsigned char *unpack_7bit_own_buf(const unsigned char *in, long len,
+ int byte_offset, gboolean cb,
+ long max_to_unpack, long *items_written,
+ unsigned char terminator,
+ unsigned char *buf);
+
+unsigned char *unpack_7bit(const unsigned char *in, long len, int byte_offset,
+ gboolean cb, long max_to_unpack,
+ long *items_written, unsigned char terminator);
+
+unsigned char *pack_7bit_own_buf(const unsigned char *in, long len,
+ int byte_offset, gboolean cb,
+ long *items_written,
+ unsigned char terminator,
+ unsigned char *buf);
+
+unsigned char *pack_7bit(const unsigned char *in, long len, int byte_offset,
+ gboolean cb_or_ussd,
+ long *items_written, unsigned char terminator);
diff --git a/src/voicecall.c b/src/voicecall.c
new file mode 100644
index 00000000..7a5a4b9d
--- /dev/null
+++ b/src/voicecall.c
@@ -0,0 +1,1684 @@
+/*
+ *
+ * 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
+
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+
+#include <dbus/dbus.h>
+#include <glib.h>
+#include <gdbus.h>
+
+#include "ofono.h"
+
+#include "driver.h"
+#include "common.h"
+#include "dbus-gsm.h"
+#include "modem.h"
+
+#define VOICECALL_MANAGER_INTERFACE "org.ofono.VoiceCallManager"
+#define VOICECALL_INTERFACE "org.ofono.VoiceCall"
+
+#define VOICECALLS_FLAG_PENDING 0x1
+#define VOICECALLS_FLAG_MULTI_RELEASE 0x2
+#define VOICECALLS_FLAG_UPDATING_CALL_LIST 0x4
+#define VOICECALLS_FLAG_UPDATING_MPTY_CALL_LIST 0x8
+
+#define MAX_VOICE_CALLS 16
+
+struct voicecalls_data {
+ GSList *call_list;
+ GSList *release_list;
+ GSList *multiparty_list;
+ struct ofono_voicecall_ops *ops;
+ int flags;
+ DBusMessage *pending;
+};
+
+struct voicecall {
+ struct ofono_call *call;
+ struct ofono_modem *modem;
+ time_t start_time;
+};
+
+static void generic_callback(const struct ofono_error *error, void *data);
+static void dial_callback(const struct ofono_error *error, void *data);
+static void multirelease_callback(const struct ofono_error *err, void *data);
+static void multiparty_create_callback(const struct ofono_error *error,
+ void *data);
+static void private_chat_callback(const struct ofono_error *error, void *data);
+
+static gint call_compare_by_id(gconstpointer a, gconstpointer b)
+{
+ const struct ofono_call *call = ((struct voicecall *)a)->call;
+ unsigned int id = GPOINTER_TO_UINT(b);
+
+ if (id < call->id)
+ return -1;
+
+ if (id > call->id)
+ return 1;
+
+ return 0;
+}
+
+static gint call_compare(gconstpointer a, gconstpointer b)
+{
+ const struct voicecall *ca = a;
+ const struct voicecall *cb = b;
+
+ if (ca->call->id < cb->call->id)
+ return -1;
+
+ if (ca->call->id > cb->call->id)
+ return 1;
+
+ return 0;
+}
+
+static const char *call_status_to_string(int status)
+{
+ switch (status) {
+ case CALL_STATUS_ACTIVE:
+ return "active";
+ case CALL_STATUS_HELD:
+ return "held";
+ case CALL_STATUS_DIALING:
+ return "dialing";
+ case CALL_STATUS_ALERTING:
+ return "alerting";
+ case CALL_STATUS_INCOMING:
+ return "incoming";
+ case CALL_STATUS_WAITING:
+ return "waiting";
+ default:
+ return "disconnected";
+ }
+}
+
+static const char *phone_and_clip_to_string(const char *number, int type,
+ int clip_validity)
+{
+ if (clip_validity == CLIP_VALIDITY_WITHHELD && !strlen(number))
+ return "withheld";
+
+ if (clip_validity == CLIP_VALIDITY_NOT_AVAILABLE)
+ return "";
+
+ return phone_number_to_string(number, type);
+}
+
+static const char *time_to_str(const time_t *t)
+{
+ static char buf[128];
+
+ strftime(buf, 127, "%a, %d %b %Y %H:%M:%S %z", localtime(t));
+ buf[127] = '\0';
+
+ return buf;
+}
+
+static DBusMessage *voicecall_get_properties(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct voicecall *v = data;
+ struct ofono_call *call = v->call;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ const char *status;
+ const char *callerid;
+ //char timebuf[512];
+ const char *timestr = "";
+
+ reply = dbus_message_new_method_return(msg);
+
+ if (!reply)
+ return NULL;
+
+ status = call_status_to_string(call->status);
+ callerid = phone_number_to_string(call->phone_number,
+ call->number_type);
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ PROPERTIES_ARRAY_SIGNATURE,
+ &dict);
+
+ dbus_gsm_dict_append(&dict, "State", DBUS_TYPE_STRING, &status);
+
+ dbus_gsm_dict_append(&dict, "LineIdentification",
+ DBUS_TYPE_STRING, &callerid);
+
+ if (call->status == CALL_STATUS_ACTIVE ||
+ (call->status == CALL_STATUS_DISCONNECTED && v->start_time != 0) ||
+ call->status == CALL_STATUS_HELD) {
+ timestr = time_to_str(&v->start_time);
+
+ dbus_gsm_dict_append(&dict, "StartTime", DBUS_TYPE_STRING,
+ &timestr);
+ }
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static DBusMessage *voicecall_busy(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct voicecall *v = data;
+ struct ofono_modem *modem = v->modem;
+ struct voicecalls_data *voicecalls = modem->voicecalls;
+ struct ofono_call *call = v->call;
+
+ if (call->status != CALL_STATUS_INCOMING &&
+ call->status != CALL_STATUS_WAITING)
+ return dbus_gsm_failed(msg);
+
+ if (!voicecalls->ops->release_specific)
+ return dbus_gsm_not_implemented(msg);
+
+ if (voicecalls->flags & VOICECALLS_FLAG_PENDING)
+ return dbus_gsm_busy(msg);
+
+ voicecalls->flags |= VOICECALLS_FLAG_PENDING;
+ voicecalls->pending = dbus_message_ref(msg);
+
+ voicecalls->ops->set_udub(modem, generic_callback, voicecalls);
+
+ return NULL;
+}
+
+static DBusMessage *voicecall_deflect(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct voicecall *v = data;
+ struct ofono_modem *modem = v->modem;
+ struct voicecalls_data *voicecalls = modem->voicecalls;
+ struct ofono_call *call = v->call;
+
+ const char *number;
+ int number_type;
+
+ if (call->status != CALL_STATUS_INCOMING &&
+ call->status != CALL_STATUS_WAITING)
+ return dbus_gsm_failed(msg);
+
+ if (!voicecalls->ops->deflect)
+ return dbus_gsm_not_implemented(msg);
+
+ if (voicecalls->flags & VOICECALLS_FLAG_PENDING)
+ return dbus_gsm_busy(msg);
+
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
+ DBUS_TYPE_INVALID) == FALSE)
+ return dbus_gsm_invalid_args(msg);
+
+ if (!valid_phone_number_format(number))
+ return dbus_gsm_invalid_format(msg);
+
+ voicecalls->flags |= VOICECALLS_FLAG_PENDING;
+ voicecalls->pending = dbus_message_ref(msg);
+
+ string_to_phone_number(number, &number_type, &number);
+
+ voicecalls->ops->deflect(modem, number, number_type,
+ generic_callback, voicecalls);
+
+ return NULL;
+}
+
+static DBusMessage *voicecall_hangup(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct voicecall *v = data;
+ struct ofono_modem *modem = v->modem;
+ struct voicecalls_data *voicecalls = modem->voicecalls;
+ struct ofono_call *call = v->call;
+
+ if (call->status == CALL_STATUS_DISCONNECTED)
+ return dbus_gsm_failed(msg);
+
+ if (!voicecalls->ops->release_specific)
+ return dbus_gsm_not_implemented(msg);
+
+ if (voicecalls->flags & VOICECALLS_FLAG_PENDING)
+ return dbus_gsm_busy(msg);
+
+ voicecalls->flags |= VOICECALLS_FLAG_PENDING;
+ voicecalls->pending = dbus_message_ref(msg);
+
+ voicecalls->ops->release_specific(modem, call->id,
+ generic_callback, voicecalls);
+
+ return NULL;
+}
+
+static DBusMessage *voicecall_answer(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct voicecall *v = data;
+ struct ofono_modem *modem = v->modem;
+ struct voicecalls_data *voicecalls = modem->voicecalls;
+ struct ofono_call *call = v->call;
+
+ if (call->status != CALL_STATUS_INCOMING)
+ return dbus_gsm_failed(msg);
+
+ if (!voicecalls->ops->answer)
+ return dbus_gsm_not_implemented(msg);
+
+ if (voicecalls->flags & VOICECALLS_FLAG_PENDING)
+ return dbus_gsm_busy(msg);
+
+ voicecalls->flags |= VOICECALLS_FLAG_PENDING;
+ voicecalls->pending = dbus_message_ref(msg);
+
+ voicecalls->ops->answer(modem, generic_callback, voicecalls);
+
+ return NULL;
+}
+
+static GDBusMethodTable voicecall_methods[] = {
+ { "GetProperties", "", "a{sv}", voicecall_get_properties },
+ { "Busy", "", "", voicecall_busy,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "Deflect", "s", "", voicecall_deflect,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "Hangup", "", "", voicecall_hangup,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "Answer", "", "", voicecall_answer,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { }
+};
+
+static GDBusSignalTable voicecall_signals[] = {
+ { "PropertyChanged", "sv" },
+ { "DisconnectReason", "s" },
+ { }
+};
+
+static struct voicecall *voicecall_create(struct ofono_modem *modem,
+ struct ofono_call *call)
+{
+ struct voicecall *v;
+
+ v = g_try_new0(struct voicecall, 1);
+
+ if (!v)
+ return NULL;
+
+ v->call = call;
+ v->modem = modem;
+
+ return v;
+}
+
+static void voicecall_destroy(gpointer userdata)
+{
+ struct voicecall *voicecall = (struct voicecall *)userdata;
+
+ g_free(voicecall->call);
+
+ g_free(voicecall);
+}
+
+static const char *voicecall_build_path(struct ofono_modem *modem,
+ const struct ofono_call *call)
+{
+ static char path[MAX_DBUS_PATH_LEN];
+
+ snprintf(path, MAX_DBUS_PATH_LEN, "%s/voicecall%02d",
+ modem->path, call->id);
+
+ return path;
+}
+
+static void voicecall_set_call_status(struct ofono_modem *modem,
+ struct voicecall *call,
+ int status)
+{
+ DBusConnection *conn = dbus_gsm_connection();
+ const char *path;
+ const char *status_str;
+ int old_status;
+
+ if (call->call->status == status)
+ return;
+
+ old_status = call->call->status;
+
+ call->call->status = status;
+
+ status_str = call_status_to_string(status);
+ path = voicecall_build_path(modem, call->call);
+
+ dbus_gsm_signal_property_changed(conn, path, VOICECALL_INTERFACE,
+ "State", DBUS_TYPE_STRING,
+ &status_str);
+
+ if (status == CALL_STATUS_ACTIVE &&
+ (old_status == CALL_STATUS_INCOMING ||
+ old_status == CALL_STATUS_DIALING ||
+ old_status == CALL_STATUS_ALERTING ||
+ old_status == CALL_STATUS_WAITING)) {
+ const char *timestr;
+
+ call->start_time = time(NULL);
+ timestr = time_to_str(&call->start_time);
+
+ dbus_gsm_signal_property_changed(conn, path,
+ VOICECALL_INTERFACE,
+ "StartTime",
+ DBUS_TYPE_STRING,
+ &timestr);
+ }
+}
+
+static void voicecall_set_call_lineid(struct ofono_modem *modem,
+ struct voicecall *v,
+ const char *number, int number_type,
+ int clip_validity)
+{
+ struct ofono_call *call = v->call;
+ DBusConnection *conn = dbus_gsm_connection();
+ const char *path;
+ const char *lineid_str;
+
+ if (!strcmp(call->phone_number, number) &&
+ call->number_type == number_type &&
+ call->clip_validity == clip_validity)
+ return;
+
+ /* Two cases: We get an incoming call with CLIP factored in, or
+ * CLIP comes in later as a separate event
+ * For COLP only the phone number should be checked, it can come
+ * in with the initial call event or later as a separate event */
+
+ /* For plugins that don't keep state, ignore */
+ if (call->clip_validity == CLIP_VALIDITY_VALID &&
+ clip_validity == CLIP_VALIDITY_NOT_AVAILABLE)
+ return;
+
+ strcpy(call->phone_number, number);
+ call->clip_validity = clip_validity;
+ call->number_type = number_type;
+
+ path = voicecall_build_path(modem, call);
+
+ if (call->direction == CALL_DIRECTION_MOBILE_TERMINATED)
+ lineid_str = phone_and_clip_to_string(number, number_type,
+ clip_validity);
+ else
+ lineid_str = phone_number_to_string(number, number_type);
+
+ dbus_gsm_signal_property_changed(conn, path, VOICECALL_INTERFACE,
+ "LineIdentification",
+ DBUS_TYPE_STRING, &lineid_str);
+}
+
+static gboolean voicecall_dbus_register(struct voicecall *voicecall)
+{
+ DBusConnection *conn = dbus_gsm_connection();
+ const char *path;
+
+ if (!voicecall)
+ return FALSE;
+
+ path = voicecall_build_path(voicecall->modem, voicecall->call);
+
+ if (!g_dbus_register_interface(conn, path, VOICECALL_INTERFACE,
+ voicecall_methods,
+ voicecall_signals,
+ NULL, voicecall,
+ voicecall_destroy)) {
+ ofono_error("Could not register VoiceCall %s", path);
+ voicecall_destroy(voicecall);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean voicecall_dbus_unregister(struct ofono_modem *modem,
+ struct voicecall *call)
+{
+ DBusConnection *conn = dbus_gsm_connection();
+ const char *path = voicecall_build_path(modem, call->call);
+
+ return g_dbus_unregister_interface(conn, path,
+ VOICECALL_INTERFACE);
+}
+
+static struct voicecalls_data *voicecalls_create()
+{
+ struct voicecalls_data *calls;
+
+ calls = g_try_new0(struct voicecalls_data, 1);
+
+ return calls;
+}
+
+static void voicecalls_destroy(gpointer userdata)
+{
+ struct ofono_modem *modem = userdata;
+ struct voicecalls_data *calls = modem->voicecalls;
+ GSList *l;
+
+ for (l = calls->call_list; l; l = l->next)
+ voicecall_dbus_unregister(modem, l->data);
+
+ g_slist_free(calls->call_list);
+
+ g_free(calls);
+
+ modem->voicecalls = 0;
+}
+
+static int voicecalls_path_list(struct ofono_modem *modem, GSList *call_list,
+ char ***objlist)
+{
+ GSList *l;
+ int i;
+ struct voicecall *v;
+
+ *objlist = g_new0(char *, g_slist_length(call_list) + 1);
+
+ if (*objlist == NULL)
+ return -1;
+
+ for (i = 0, l = call_list; l; l = l->next, i++) {
+ v = l->data;
+ (*objlist)[i] = g_strdup(voicecall_build_path(modem, v->call));
+ }
+
+ return 0;
+}
+
+static gboolean voicecalls_have_active(struct voicecalls_data *calls)
+{
+ GSList *l;
+ struct voicecall *v;
+
+ for (l = calls->call_list; l; l = l->next) {
+ v = l->data;
+
+ if (v->call->status == CALL_STATUS_ACTIVE ||
+ v->call->status == CALL_STATUS_INCOMING ||
+ v->call->status == CALL_STATUS_DIALING ||
+ v->call->status == CALL_STATUS_ALERTING)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean voicecalls_have_connected(struct voicecalls_data *calls)
+{
+ GSList *l;
+ struct voicecall *v;
+
+ for (l = calls->call_list; l; l = l->next) {
+ v = l->data;
+
+ if (v->call->status == CALL_STATUS_ACTIVE)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean voicecalls_have_held(struct voicecalls_data *calls)
+{
+ GSList *l;
+ struct voicecall *v;
+
+ for (l = calls->call_list; l; l = l->next) {
+ v = l->data;
+
+ if (v->call->status == CALL_STATUS_HELD)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static int voicecalls_num_with_status(struct voicecalls_data *calls,
+ int status)
+{
+ GSList *l;
+ struct voicecall *v;
+ int num = 0;
+
+ for (l = calls->call_list; l; l = l->next) {
+ v = l->data;
+
+ if (v->call->status == status)
+ num += 1;
+ }
+
+ return num;
+}
+
+static int voicecalls_num_active(struct voicecalls_data *calls)
+{
+ return voicecalls_num_with_status(calls, CALL_STATUS_ACTIVE);
+}
+
+static int voicecalls_num_held(struct voicecalls_data *calls)
+{
+ return voicecalls_num_with_status(calls, CALL_STATUS_HELD);
+}
+
+static int voicecalls_num_connecting(struct voicecalls_data *calls)
+{
+ int r = 0;
+
+ r += voicecalls_num_with_status(calls, CALL_STATUS_DIALING);
+ r += voicecalls_num_with_status(calls, CALL_STATUS_ALERTING);
+
+ return r;
+}
+
+static GSList *voicecalls_held_list(struct voicecalls_data *calls)
+{
+ GSList *l;
+ GSList *r = NULL;
+ struct voicecall *v;
+
+ for (l = calls->call_list; l; l = l->next) {
+ v = l->data;
+
+ if (v->call->status == CALL_STATUS_HELD)
+ r = g_slist_prepend(r, v);
+ }
+
+ if (r)
+ r = g_slist_reverse(r);
+
+ return r;
+}
+
+/* Intended to be used for multiparty, which cannot be incoming,
+ * alerting or dialing */
+static GSList *voicecalls_active_list(struct voicecalls_data *calls)
+{
+ GSList *l;
+ GSList *r = NULL;
+ struct voicecall *v;
+
+ for (l = calls->call_list; l; l = l->next) {
+ v = l->data;
+
+ if (v->call->status == CALL_STATUS_ACTIVE)
+ r = g_slist_prepend(r, v);
+ }
+
+ if (r)
+ r = g_slist_reverse(r);
+
+ return r;
+}
+
+static gboolean voicecalls_have_waiting(struct voicecalls_data *calls)
+{
+ GSList *l;
+ struct voicecall *v;
+
+ for (l = calls->call_list; l; l = l->next) {
+ v = l->data;
+
+ if (v->call->status == CALL_STATUS_WAITING)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void voicecalls_release_queue(struct ofono_modem *modem, GSList *calls)
+{
+ struct voicecalls_data *voicecalls = modem->voicecalls;
+ GSList *l;
+
+ g_slist_free(voicecalls->release_list);
+ voicecalls->release_list = NULL;
+
+ for (l = calls; l; l = l->next) {
+ voicecalls->release_list =
+ g_slist_prepend(voicecalls->release_list, l->data);
+ }
+}
+
+static void voicecalls_release_next(struct ofono_modem *modem)
+{
+ struct voicecalls_data *voicecalls = modem->voicecalls;
+ struct voicecall *call;
+
+ if (!voicecalls->release_list)
+ return;
+
+ call = voicecalls->release_list->data;
+
+ voicecalls->release_list = g_slist_remove(voicecalls->release_list,
+ call);
+
+ voicecalls->ops->release_specific(modem, call->call->id,
+ multirelease_callback, modem);
+}
+
+static DBusMessage *manager_get_properties(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct voicecalls_data *calls = modem->voicecalls;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+
+ char **callobj_list;
+
+ reply = dbus_message_new_method_return(msg);
+
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ PROPERTIES_ARRAY_SIGNATURE, &dict);
+
+ voicecalls_path_list(modem, calls->call_list, &callobj_list);
+
+ dbus_gsm_dict_append_array(&dict, "Calls", DBUS_TYPE_OBJECT_PATH,
+ &callobj_list);
+
+ dbus_gsm_free_string_array(callobj_list);
+
+ voicecalls_path_list(modem, calls->multiparty_list, &callobj_list);
+
+ dbus_gsm_dict_append_array(&dict, "MultipartyCalls",
+ DBUS_TYPE_OBJECT_PATH, &callobj_list);
+
+ dbus_gsm_free_string_array(callobj_list);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static DBusMessage *manager_dial(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct voicecalls_data *calls = modem->voicecalls;
+ const char *number;
+ int number_type;
+ const char *clirstr;
+ enum ofono_clir_option clir;
+
+ if (calls->flags & VOICECALLS_FLAG_PENDING)
+ return dbus_gsm_busy(msg);
+
+ if (g_slist_length(calls->call_list) >= MAX_VOICE_CALLS)
+ return dbus_gsm_failed(msg);
+
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
+ DBUS_TYPE_STRING, &clirstr,
+ DBUS_TYPE_INVALID) == FALSE)
+ return dbus_gsm_invalid_args(msg);
+
+ if (!valid_phone_number_format(number))
+ return dbus_gsm_invalid_format(msg);
+
+ if (strlen(clirstr) == 0 || !strcmp(clirstr, "default"))
+ clir = OFONO_CLIR_OPTION_DEFAULT;
+ else if (!strcmp(clirstr, "disabled"))
+ clir = OFONO_CLIR_OPTION_SUPPRESSION;
+ else if (!strcmp(clirstr, "enabled"))
+ clir = OFONO_CLIR_OPTION_INVOCATION;
+ else
+ return dbus_gsm_invalid_format(msg);
+
+ if (!calls->ops->dial)
+ return dbus_gsm_not_implemented(msg);
+
+ if (voicecalls_have_active(calls) &&
+ voicecalls_have_held(calls))
+ return dbus_gsm_failed(msg);
+
+ calls->flags |= VOICECALLS_FLAG_PENDING;
+ calls->pending = dbus_message_ref(msg);
+
+ string_to_phone_number(number, &number_type, &number);
+
+ calls->ops->dial(modem, number, number_type, clir,
+ OFONO_CUG_OPTION_DEFAULT,
+ dial_callback, modem);
+
+ return NULL;
+}
+
+static DBusMessage *manager_transfer(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct voicecalls_data *calls = modem->voicecalls;
+ int numactive;
+ int numheld;
+
+ if (calls->flags & VOICECALLS_FLAG_PENDING)
+ return dbus_gsm_busy(msg);
+
+ numactive = voicecalls_num_active(calls);
+
+ /* According to 22.091 section 5.8, the network has the option of
+ * implementing the call transfer operation for a call that is
+ * still dialing/alerting.
+ */
+ numactive += voicecalls_num_connecting(calls);
+
+ numheld = voicecalls_num_held(calls);
+
+ if ((numactive != 1) && (numheld != 1))
+ return dbus_gsm_failed(msg);
+
+ if (!calls->ops->transfer)
+ return dbus_gsm_not_implemented(msg);
+
+ calls->flags |= VOICECALLS_FLAG_PENDING;
+ calls->pending = dbus_message_ref(msg);
+
+ calls->ops->transfer(modem, generic_callback, calls);
+
+ return NULL;
+}
+
+static DBusMessage *manager_swap_calls(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct voicecalls_data *calls = modem->voicecalls;
+
+ if (calls->flags & VOICECALLS_FLAG_PENDING)
+ return dbus_gsm_busy(msg);
+
+ if (voicecalls_have_waiting(calls))
+ return dbus_gsm_failed(msg);
+
+ if (!calls->ops->hold_all_active)
+ return dbus_gsm_not_implemented(msg);
+
+ calls->flags |= VOICECALLS_FLAG_PENDING;
+ calls->pending = dbus_message_ref(msg);
+
+ calls->ops->hold_all_active(modem, generic_callback, calls);
+
+ return NULL;
+}
+
+static DBusMessage *manager_release_and_answer(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct voicecalls_data *calls = modem->voicecalls;
+
+ if (calls->flags & VOICECALLS_FLAG_PENDING)
+ return dbus_gsm_busy(msg);
+
+ if (!voicecalls_have_active(calls) || !voicecalls_have_waiting(calls))
+ return dbus_gsm_failed(msg);
+
+ if (!calls->ops->release_all_active)
+ return dbus_gsm_not_implemented(msg);
+
+ calls->flags |= VOICECALLS_FLAG_PENDING;
+ calls->pending = dbus_message_ref(msg);
+
+ calls->ops->release_all_active(modem, generic_callback, calls);
+
+ return NULL;
+}
+
+static DBusMessage *manager_hold_and_answer(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct voicecalls_data *calls = modem->voicecalls;
+
+ if (calls->flags & VOICECALLS_FLAG_PENDING)
+ return dbus_gsm_busy(msg);
+
+ if (voicecalls_have_active(calls) && voicecalls_have_held(calls) &&
+ voicecalls_have_waiting(calls))
+ return dbus_gsm_failed(msg);
+
+ if (!calls->ops->hold_all_active)
+ return dbus_gsm_not_implemented(msg);
+
+ calls->flags |= VOICECALLS_FLAG_PENDING;
+ calls->pending = dbus_message_ref(msg);
+
+ calls->ops->hold_all_active(modem, generic_callback, calls);
+
+ return NULL;
+}
+
+static DBusMessage *manager_hangup_all(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct voicecalls_data *calls = modem->voicecalls;
+
+ if (calls->flags & VOICECALLS_FLAG_PENDING)
+ return dbus_gsm_busy(msg);
+
+ if (!calls->ops->release_specific)
+ return dbus_gsm_not_implemented(msg);
+
+ if (g_slist_length(calls->call_list) == 0) {
+ DBusMessage *reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+
+ calls->flags |= VOICECALLS_FLAG_PENDING;
+ calls->flags |= VOICECALLS_FLAG_MULTI_RELEASE;
+
+ calls->pending = dbus_message_ref(msg);
+
+ voicecalls_release_queue(modem, calls->call_list);
+ voicecalls_release_next(modem);
+
+ return NULL;
+}
+
+static DBusMessage *multiparty_private_chat(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct voicecalls_data *calls = modem->voicecalls;
+ const char *callpath;
+ const char *c;
+ unsigned int id;
+ GSList *l;
+
+ if (calls->flags & VOICECALLS_FLAG_PENDING)
+ return dbus_gsm_busy(msg);
+
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &callpath,
+ DBUS_TYPE_INVALID) == FALSE)
+ return dbus_gsm_invalid_args(msg);
+
+ if (strlen(callpath) == 0 || strlen(callpath) > MAX_DBUS_PATH_LEN)
+ return dbus_gsm_invalid_format(msg);
+
+ c = strrchr(callpath, '/');
+
+ if (!c || strncmp(modem->path, callpath, c-callpath))
+ return dbus_gsm_not_found(msg);
+
+ if (!sscanf(c, "/voicecall%2u", &id))
+ return dbus_gsm_not_found(msg);
+
+ for (l = calls->multiparty_list; l; l = l->next) {
+ struct voicecall *v = l->data;
+ if (v->call->id == id)
+ break;
+ }
+
+ if (!l)
+ return dbus_gsm_not_found(msg);
+
+ /* If we found id on the list of multiparty calls, then by definition
+ * the multiparty call exists. Only thing to check is whether we have
+ * held calls
+ */
+ if (voicecalls_have_held(calls))
+ return dbus_gsm_failed(msg);
+
+ if (!calls->ops->private_chat)
+ return dbus_gsm_not_implemented(msg);
+
+ calls->flags |= VOICECALLS_FLAG_PENDING;
+ calls->pending = dbus_message_ref(msg);
+
+ calls->ops->private_chat(modem, id, private_chat_callback, modem);
+
+ return NULL;
+}
+
+static DBusMessage *multiparty_create(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct voicecalls_data *calls = modem->voicecalls;
+
+ if (calls->flags & VOICECALLS_FLAG_PENDING)
+ return dbus_gsm_busy(msg);
+
+ if (!voicecalls_have_held(calls) || !voicecalls_have_active(calls))
+ return dbus_gsm_failed(msg);
+
+ if (!calls->ops->create_multiparty)
+ return dbus_gsm_not_implemented(msg);
+
+ calls->flags |= VOICECALLS_FLAG_PENDING;
+ calls->pending = dbus_message_ref(msg);
+
+ calls->ops->create_multiparty(modem, multiparty_create_callback, modem);
+
+ return NULL;
+}
+
+static DBusMessage *multiparty_hangup(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct voicecalls_data *calls = modem->voicecalls;
+
+ if (calls->flags & VOICECALLS_FLAG_PENDING)
+ return dbus_gsm_busy(msg);
+
+ if (!calls->ops->release_specific)
+ return dbus_gsm_not_implemented(msg);
+
+ if (g_slist_length(calls->multiparty_list) == 0) {
+ DBusMessage *reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+
+ calls->flags |= VOICECALLS_FLAG_PENDING;
+ calls->pending = dbus_message_ref(msg);
+
+ /* We have waiting calls, can't use +CHLD to release */
+ if (voicecalls_have_waiting(calls)) {
+ calls->flags |= VOICECALLS_FLAG_MULTI_RELEASE;
+ voicecalls_release_queue(modem, calls->multiparty_list);
+ voicecalls_release_next(modem);
+ } else {
+ struct ofono_call *v = calls->multiparty_list->data;
+
+ if (v->status == CALL_STATUS_HELD)
+ calls->ops->release_all_held(modem, generic_callback,
+ calls);
+ else
+ calls->ops->release_all_active(modem, generic_callback,
+ calls);
+ }
+
+ return NULL;
+}
+
+static DBusMessage *manager_tone(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct voicecalls_data *calls = modem->voicecalls;
+ const char *in_tones;
+ char *tones;
+ int i, len;
+
+ if (calls->flags & VOICECALLS_FLAG_PENDING)
+ return dbus_gsm_busy(msg);
+
+ if (!calls->ops->send_tones)
+ return dbus_gsm_not_implemented(msg);
+
+ /* Send DTMFs only if we have at least one connected call */
+ if (!voicecalls_have_connected(calls))
+ return dbus_gsm_failed(msg);
+
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &in_tones,
+ DBUS_TYPE_INVALID) == FALSE)
+ return dbus_gsm_invalid_args(msg);
+
+ len = strlen(in_tones);
+
+ if (len == 0)
+ return dbus_gsm_invalid_format(msg);
+
+ tones = g_ascii_strup(in_tones, len);
+
+ /* Tones can be 0-9, *, #, A-D according to 27.007 C.2.11 */
+ for (i = 0; i < len; i++) {
+ if (g_ascii_isdigit(tones[i]) ||
+ tones[i] == '*' || tones[i] == '#' ||
+ (tones[i] >= 'A' && tones[i] <= 'D'))
+ continue;
+
+ g_free(tones);
+ return dbus_gsm_invalid_format(msg);
+ }
+
+ calls->flags |= VOICECALLS_FLAG_PENDING;
+ calls->pending = dbus_message_ref(msg);
+
+ calls->ops->send_tones(modem, tones, generic_callback, calls);
+
+ g_free(tones);
+
+ return NULL;
+}
+
+static GDBusMethodTable manager_methods[] = {
+ { "GetProperties", "", "a{sv}", manager_get_properties },
+ { "Dial", "ss", "o", manager_dial,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "Transfer", "", "", manager_transfer,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "SwapCalls", "", "", manager_swap_calls,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "ReleaseAndAnswer", "", "", manager_release_and_answer,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "HoldAndAnswer", "", "", manager_hold_and_answer,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "HangupAll", "", "", manager_hangup_all,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "PrivateChat", "o", "ao", multiparty_private_chat,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "CreateMultiparty", "", "ao", multiparty_create,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "HangupMultiparty", "", "", multiparty_hangup,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "SendTones", "s", "", manager_tone,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { }
+};
+
+static GDBusSignalTable manager_signals[] = {
+ { "PropertyChanged", "sv" },
+ { }
+};
+
+static gboolean real_emit_call_list_changed(void *data)
+{
+ struct ofono_modem *modem = data;
+ struct voicecalls_data *voicecalls = modem->voicecalls;
+ DBusConnection *conn = dbus_gsm_connection();
+ char **objpath_list;
+
+ voicecalls_path_list(modem, voicecalls->call_list, &objpath_list);
+
+ dbus_gsm_signal_array_property_changed(conn, modem->path,
+ VOICECALL_MANAGER_INTERFACE,
+ "Calls",
+ DBUS_TYPE_OBJECT_PATH,
+ &objpath_list);
+
+ dbus_gsm_free_string_array(objpath_list);
+
+ ofono_debug("Resetting updating flag");
+ voicecalls->flags &= ~VOICECALLS_FLAG_UPDATING_CALL_LIST;
+
+ return FALSE;
+}
+
+static void emit_call_list_changed(struct ofono_modem *modem)
+{
+ //struct voicecalls_data *calls = modem->voicecalls;
+
+#ifdef DELAY_EMIT
+ if (!(calls->flags & VOICECALLS_FLAG_UPDATING_CALL_LIST)) {
+ calls->flags |= VOICECALLS_FLAG_UPDATING_CALL_LIST;
+ g_timeout_add(0, real_emit_call_list_changed, modem);
+ }
+#else
+ real_emit_call_list_changed(modem);
+#endif
+}
+
+static gboolean real_emit_multiparty_call_list_changed(void *data)
+{
+ struct ofono_modem *modem = data;
+ struct voicecalls_data *voicecalls = modem->voicecalls;
+ DBusConnection *conn = dbus_gsm_connection();
+ char **objpath_list;
+
+ voicecalls_path_list(modem, voicecalls->multiparty_list, &objpath_list);
+
+ dbus_gsm_signal_array_property_changed(conn, modem->path,
+ VOICECALL_MANAGER_INTERFACE, "MultipartyCalls",
+ DBUS_TYPE_OBJECT_PATH,
+ &objpath_list);
+
+ dbus_gsm_free_string_array(objpath_list);
+
+ voicecalls->flags &= ~VOICECALLS_FLAG_UPDATING_MPTY_CALL_LIST;
+
+ return FALSE;
+}
+
+static void emit_multiparty_call_list_changed(struct ofono_modem *modem)
+{
+ //struct voicecalls_data *calls = modem->voicecalls;
+
+#ifdef DELAY_EMIT
+ if (!(calls->flags & VOICECALLS_FLAG_UPDATING_MPTY_CALL_LIST)) {
+ calls->flags |= VOICECALLS_FLAG_UPDATING_MPTY_CALL_LIST;
+ g_timeout_add(0, real_emit_multiparty_call_list_changed, modem);
+ }
+#else
+ real_emit_multiparty_call_list_changed(modem);
+#endif
+}
+
+void ofono_voicecall_disconnected(struct ofono_modem *modem, int id,
+ enum ofono_disconnect_reason reason,
+ const struct ofono_error *error)
+{
+ GSList *l;
+ struct voicecalls_data *calls = modem->voicecalls;
+ struct voicecall *call;
+
+ ofono_debug("Got disconnection event for id: %d, reason: %d", id, reason);
+
+ l = g_slist_find_custom(calls->call_list, GINT_TO_POINTER(id),
+ call_compare_by_id);
+
+ if (!l) {
+ ofono_error("Plugin notified us of call disconnect for"
+ " unknown call");
+ return;
+ }
+
+ call = l->data;
+
+ l = g_slist_find_custom(calls->multiparty_list, GINT_TO_POINTER(id),
+ call_compare_by_id);
+
+ if (l) {
+ calls->multiparty_list =
+ g_slist_remove(calls->multiparty_list, call);
+
+ if (calls->multiparty_list->next == NULL) { /* Size == 1 */
+ g_slist_free(calls->multiparty_list);
+ calls->multiparty_list = 0;
+ }
+
+ emit_multiparty_call_list_changed(modem);
+ }
+
+ calls->release_list = g_slist_remove(calls->release_list, call);
+
+ modem_release_callid(modem, id);
+
+ /* TODO: Emit disconnect reason */
+ voicecall_set_call_status(modem, call, CALL_STATUS_DISCONNECTED);
+
+ voicecall_dbus_unregister(modem, call);
+
+ calls->call_list = g_slist_remove(calls->call_list, call);
+
+ emit_call_list_changed(modem);
+}
+
+void ofono_voicecall_notify(struct ofono_modem *modem, const struct ofono_call *call)
+{
+ GSList *l;
+ struct voicecalls_data *calls = modem->voicecalls;
+ struct voicecall *v;
+ struct ofono_call *newcall = NULL;
+ //const char *member;
+
+ ofono_debug("Got a voicecall event, status: %d, id: %u, number: %s",
+ call->status, call->id, call->phone_number);
+
+ l = g_slist_find_custom(calls->call_list, GINT_TO_POINTER(call->id),
+ call_compare_by_id);
+
+ if (l) {
+ ofono_debug("Found call with id: %d\n", call->id);
+ voicecall_set_call_status(modem, l->data, call->status);
+ voicecall_set_call_lineid(modem, l->data, call->phone_number,
+ call->number_type, call->clip_validity);
+
+ return;
+ }
+
+ ofono_debug("Did not find a call with id: %d\n", call->id);
+
+ newcall = g_try_new0(struct ofono_call, 1);
+
+ if (!call) {
+ ofono_error("Unable to allocate call");
+ goto err;
+ }
+
+ memcpy(newcall, call, sizeof(struct ofono_call));
+
+ if (modem_alloc_callid(modem) != call->id) {
+ ofono_error("Warning: Call id and internally tracked id"
+ " do not correspond");
+ goto err;
+ }
+
+ v = voicecall_create(modem, newcall);
+
+ if (!v) {
+ ofono_error("Unable to allocate voicecall_data");
+ goto err;
+ }
+
+ if (!voicecall_dbus_register(v)) {
+ ofono_error("Unable to register voice call");
+ goto err;
+ }
+
+ calls->call_list = g_slist_insert_sorted(calls->call_list, v,
+ call_compare);
+
+ emit_call_list_changed(modem);
+
+ return;
+
+err:
+ if (newcall)
+ g_free(newcall);
+
+ if (v)
+ g_free(v);
+}
+
+void ofono_voicecall_cssi(struct ofono_modem *modem, int code, int index)
+{
+
+}
+
+void ofono_voicecall_cssu(struct ofono_modem *modem, int code, int index,
+ const char *number, int number_type)
+{
+
+}
+
+static void generic_callback(const struct ofono_error *error, void *data)
+{
+ struct voicecalls_data *calls = data;
+ DBusConnection *conn = dbus_gsm_connection();
+ DBusMessage *reply;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
+ ofono_debug("command failed with error: %s",
+ telephony_error_to_str(error));
+
+ calls->flags &= ~VOICECALLS_FLAG_PENDING;
+
+ if (!calls->pending)
+ return;
+
+ if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+ reply = dbus_message_new_method_return(calls->pending);
+ else
+ reply = dbus_gsm_failed(calls->pending);
+
+ g_dbus_send_message(conn, reply);
+
+ dbus_message_unref(calls->pending);
+ calls->pending = NULL;
+}
+
+static void multirelease_callback(const struct ofono_error *error, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct voicecalls_data *calls = modem->voicecalls;
+ DBusConnection *conn = dbus_gsm_connection();
+ DBusMessage *reply;
+
+ if (g_slist_length(calls->release_list)) {
+ voicecalls_release_next(modem);
+ return;
+ }
+
+ calls->flags &= ~VOICECALLS_FLAG_MULTI_RELEASE;
+ calls->flags &= ~VOICECALLS_FLAG_PENDING;
+
+ if (!calls->pending)
+ return;
+
+ reply = dbus_message_new_method_return(calls->pending);
+
+ g_dbus_send_message(conn, reply);
+
+ dbus_message_unref(calls->pending);
+ calls->pending = NULL;
+}
+
+static struct ofono_call *synthesize_outgoing_call(struct ofono_modem *modem,
+ DBusMessage *msg)
+{
+ const char *number;
+ int number_type;
+ struct ofono_call *call;
+
+ call = g_try_new0(struct ofono_call, 1);
+
+ if (!call)
+ return call;
+
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
+ DBUS_TYPE_INVALID) == FALSE)
+ number = "";
+ else
+ string_to_phone_number(number, &number_type, &number);
+
+ call->id = modem_alloc_callid(modem);
+
+ if (call->id == 0) {
+ ofono_error("Failed to alloc callid, too many calls");
+ g_free(call);
+ return NULL;
+ }
+
+ call->direction = CALL_DIRECTION_MOBILE_ORIGINATED;
+ call->status = CALL_STATUS_DIALING;
+ strcpy(call->phone_number, number);
+ call->number_type = number_type;
+ call->clip_validity = CLIP_VALIDITY_VALID;
+
+ return call;
+}
+
+static void dial_callback(const struct ofono_error *error, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct voicecalls_data *calls = modem->voicecalls;
+ DBusConnection *conn = dbus_gsm_connection();
+ DBusMessage *reply;
+ GSList *l;
+ struct ofono_call *call;
+ const char *path;
+ gboolean need_to_emit = FALSE;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
+ ofono_debug("Dial callback returned error: %s",
+ telephony_error_to_str(error));
+
+ calls->flags &= ~VOICECALLS_FLAG_PENDING;
+
+ if (!calls->pending)
+ return;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ reply = dbus_gsm_failed(calls->pending);
+ g_dbus_send_message(conn, reply);
+
+ goto out;
+ }
+
+ reply = dbus_message_new_method_return(calls->pending);
+ if (!reply)
+ goto out;
+
+ /* Two things can happen, the call notification arrived before dial
+ * callback or dial callback was first. Handle here */
+ for (l = calls->call_list; l; l = l->next) {
+ struct voicecall *v = l->data;
+
+ if (v->call->status == CALL_STATUS_DIALING ||
+ v->call->status == CALL_STATUS_ALERTING)
+ break;
+ }
+
+ if (!l) {
+ struct voicecall *v;
+ call = synthesize_outgoing_call(modem, calls->pending);
+
+ if (!call) {
+ reply = dbus_gsm_failed(calls->pending);
+ g_dbus_send_message(conn, reply);
+
+ goto out;
+ }
+
+ v = voicecall_create(modem, call);
+
+ if (!v) {
+ reply = dbus_gsm_failed(calls->pending);
+ g_dbus_send_message(conn, reply);
+
+ goto out;
+ }
+
+ ofono_debug("Registering new call: %d", call->id);
+ voicecall_dbus_register(voicecall_create(modem, call));
+
+ calls->call_list = g_slist_insert_sorted(calls->call_list, v,
+ call_compare);
+
+ need_to_emit = TRUE;
+ } else {
+ struct voicecall *v = l->data;
+
+ call = v->call;
+ }
+
+ path = voicecall_build_path(modem, call);
+
+ dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID);
+
+ g_dbus_send_message(conn, reply);
+
+ if (need_to_emit)
+ emit_call_list_changed(modem);
+
+out:
+ dbus_message_unref(calls->pending);
+ calls->pending = NULL;
+}
+
+
+static void multiparty_callback_common(struct ofono_modem *modem,
+ DBusMessage *reply)
+{
+ struct voicecalls_data *voicecalls = modem->voicecalls;
+ DBusMessageIter iter;
+ DBusMessageIter array_iter;
+ char **objpath_list;
+ int i;
+
+ voicecalls_path_list(modem, voicecalls->multiparty_list, &objpath_list);
+
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_OBJECT_PATH_AS_STRING, &array_iter);
+
+ for (i = 0; objpath_list[i]; i++)
+ dbus_message_iter_append_basic(&array_iter,
+ DBUS_TYPE_OBJECT_PATH, &objpath_list[i]);
+
+ dbus_message_iter_close_container(&iter, &array_iter);
+}
+
+static void multiparty_create_callback(const struct ofono_error *error, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct voicecalls_data *calls = modem->voicecalls;
+ DBusConnection *conn = dbus_gsm_connection();
+ DBusMessage *reply;
+ gboolean need_to_emit = FALSE;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
+ ofono_debug("command failed with error: %s",
+ telephony_error_to_str(error));
+
+ calls->flags &= ~VOICECALLS_FLAG_PENDING;
+
+ if (!calls->pending)
+ return;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ reply = dbus_gsm_failed(calls->pending);
+ goto out;
+ }
+
+ /* We just created a multiparty call, gather all held
+ * active calls and add them to the multiparty list
+ */
+ if (calls->multiparty_list) {
+ g_slist_free(calls->multiparty_list);
+ calls->multiparty_list = 0;
+ }
+
+ calls->multiparty_list = g_slist_concat(calls->multiparty_list,
+ voicecalls_held_list(calls));
+
+ calls->multiparty_list = g_slist_concat(calls->multiparty_list,
+ voicecalls_active_list(calls));
+
+ calls->multiparty_list = g_slist_sort(calls->multiparty_list,
+ call_compare);
+
+ if (g_slist_length(calls->multiparty_list) < 2) {
+ ofono_error("Created multiparty call, but size is less than 2"
+ " panic!");
+
+ reply = dbus_gsm_failed(calls->pending);
+ } else {
+ reply = dbus_message_new_method_return(calls->pending);
+
+ multiparty_callback_common(modem, reply);
+ need_to_emit = TRUE;
+ }
+
+out:
+ g_dbus_send_message(conn, reply);
+
+ if (need_to_emit)
+ emit_multiparty_call_list_changed(modem);
+
+ dbus_message_unref(calls->pending);
+ calls->pending = NULL;
+}
+
+static void private_chat_callback(const struct ofono_error *error, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct voicecalls_data *calls = modem->voicecalls;
+ DBusConnection *conn = dbus_gsm_connection();
+ DBusMessage *reply;
+ gboolean need_to_emit = FALSE;
+ const char *callpath;
+ const char *c;
+ int id;
+ GSList *l;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
+ ofono_debug("command failed with error: %s",
+ telephony_error_to_str(error));
+
+ calls->flags &= ~VOICECALLS_FLAG_PENDING;
+
+ if (!calls->pending)
+ return;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ reply = dbus_gsm_failed(calls->pending);
+ goto out;
+ }
+
+ dbus_message_get_args(calls->pending, NULL,
+ DBUS_TYPE_OBJECT_PATH, &callpath,
+ DBUS_TYPE_INVALID);
+
+ c = strrchr(callpath, '/');
+ sscanf(c, "/voicecall%2u", &id);
+
+ l = g_slist_find_custom(calls->multiparty_list, GINT_TO_POINTER(id),
+ call_compare_by_id);
+
+ if (l) {
+ calls->multiparty_list =
+ g_slist_remove(calls->multiparty_list, l->data);
+
+ if (g_slist_length(calls->multiparty_list) < 2) {
+ g_slist_free(calls->multiparty_list);
+ calls->multiparty_list = 0;
+ }
+ }
+
+ reply = dbus_message_new_method_return(calls->pending);
+
+ multiparty_callback_common(modem, reply);
+ need_to_emit = TRUE;
+
+out:
+ g_dbus_send_message(conn, reply);
+
+ if (need_to_emit)
+ emit_multiparty_call_list_changed(modem);
+
+ dbus_message_unref(calls->pending);
+ calls->pending = NULL;
+}
+
+int ofono_voicecall_register(struct ofono_modem *modem, struct ofono_voicecall_ops *ops)
+{
+ DBusConnection *conn = dbus_gsm_connection();
+
+ if (modem == NULL)
+ return -1;
+
+ if (ops == NULL)
+ return -1;
+
+ modem->voicecalls = voicecalls_create();
+
+ if (modem->voicecalls == NULL)
+ return -1;
+
+ modem->voicecalls->ops = ops;
+
+ if (!g_dbus_register_interface(conn, modem->path,
+ VOICECALL_MANAGER_INTERFACE,
+ manager_methods, manager_signals, NULL,
+ modem, voicecalls_destroy)) {
+ ofono_error("Could not create %s interface",
+ VOICECALL_MANAGER_INTERFACE);
+
+ voicecalls_destroy(modem->voicecalls);
+
+ return -1;
+ }
+
+ modem_add_interface(modem, VOICECALL_MANAGER_INTERFACE);
+
+ return 0;
+}
+
+void ofono_voicecall_unregister(struct ofono_modem *modem)
+{
+ DBusConnection *conn = dbus_gsm_connection();
+
+ if (!modem->voicecalls)
+ return;
+
+ modem_remove_interface(modem, VOICECALL_MANAGER_INTERFACE);
+ g_dbus_unregister_interface(conn, modem->path,
+ VOICECALL_MANAGER_INTERFACE);
+
+ modem->voicecalls = NULL;
+}