summaryrefslogtreecommitdiffstats
path: root/src/sim.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sim.c')
-rw-r--r--src/sim.c353
1 files changed, 353 insertions, 0 deletions
diff --git a/src/sim.c b/src/sim.c
new file mode 100644
index 00000000..0fa13143
--- /dev/null
+++ b/src/sim.c
@@ -0,0 +1,353 @@
+/*
+ *
+ * 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"
+#include "util.h"
+#include "sim.h"
+
+#define SIM_MANAGER_INTERFACE "org.ofono.SimManager"
+
+#define SIM_FLAG_PENDING 0x1
+
+struct sim_manager_data {
+ struct ofono_sim_ops *ops;
+ int flags;
+ DBusMessage *pending;
+ char *imsi;
+ char **numbers;
+ char *spn;
+ int dcbyte;
+
+ GSList *update_spn_notify;
+};
+
+static struct sim_manager_data *sim_manager_create()
+{
+ return g_try_new0(struct sim_manager_data, 1);
+}
+
+static void sim_manager_destroy(gpointer userdata)
+{
+ struct ofono_modem *modem = userdata;
+ struct sim_manager_data *data = modem->sim_manager;
+
+ if (data->imsi) {
+ g_free(data->imsi);
+ data->imsi = NULL;
+ }
+ if (data->numbers) {
+ dbus_gsm_free_string_array(data->numbers);
+ data->numbers = NULL;
+ }
+ if (data->spn) {
+ g_free(data->spn);
+ data->spn = NULL;
+ }
+}
+
+static DBusMessage *sim_get_properties(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct sim_manager_data *sim = modem->sim_manager;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+
+ 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, "IMSI",
+ DBUS_TYPE_STRING, &sim->imsi);
+ dbus_gsm_dict_append(&dict, "ServiceProvider",
+ DBUS_TYPE_STRING, &sim->spn);
+ dbus_gsm_dict_append_array(&dict, "OwnNumbers",
+ DBUS_TYPE_STRING, &sim->numbers);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static GDBusMethodTable sim_manager_methods[] = {
+ { "GetProperties", "", "a{sv}", sim_get_properties },
+ { }
+};
+
+static GDBusSignalTable sim_manager_signals[] = { { } };
+
+enum sim_fileids {
+ SIM_EFSPN_FILEID = 0x6f46,
+};
+
+#define SIM_EFSPN_DC_HOME_PLMN_BIT 0x1
+#define SIM_EFSPN_DC_ROAMING_SPN_BIT 0x2
+
+static void sim_spn_notify(struct ofono_modem *modem, update_spn_cb cb)
+{
+ struct sim_manager_data *sim = modem->sim_manager;
+
+ cb(modem, sim->spn,
+ sim->dcbyte & SIM_EFSPN_DC_HOME_PLMN_BIT,
+ !(sim->dcbyte & SIM_EFSPN_DC_ROAMING_SPN_BIT));
+}
+
+static void sim_spn_read_cb(const struct ofono_error *error,
+ const unsigned char *sdata, int length, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct sim_manager_data *sim = modem->sim_manager;
+ unsigned char *endp;
+ GSList *l;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR || length <= 1)
+ return;
+
+ sim->dcbyte = sdata[0];
+ sdata++;
+ length--;
+
+ /* Successfully read the SPN from the SIM DB */
+ endp = memchr(sdata, 0xff, length);
+ if (endp)
+ length = endp - sdata;
+
+ /*
+ * The specification has this to say about the encoding of SPN:
+ * Coding:
+ *
+ * the string shall use:
+ *
+ * - either the SMS default 7-bit coded alphabet as defined in
+ * TS 23.038 [5] with bit 8 set to 0. The string shall be left
+ * justified. Unused bytes shall be set to 'FF'.
+ *
+ * - or one of the UCS2 code options defined in the annex of TS
+ * 31.101 [11].
+ *
+ * Assume the first option.
+ */
+ sim->spn = convert_gsm_to_utf8(sdata, length, NULL, NULL, 0xff);
+
+ for (l = sim->update_spn_notify; l; l = l->next)
+ sim_spn_notify(modem, l->data);
+}
+
+static void sim_spn_len_cb(const struct ofono_error *error,
+ int length, void *data)
+{
+ struct ofono_modem *modem = data;
+ struct sim_manager_data *sim = modem->sim_manager;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR || length <= 1)
+ return;
+
+ sim->ops->read_file(modem, SIM_EFSPN_FILEID, 0, length,
+ sim_spn_read_cb, modem);
+}
+
+static gboolean sim_retrieve_spn(void *user_data)
+{
+ struct ofono_modem *modem = user_data;
+ struct sim_manager_data *sim = modem->sim_manager;
+
+ sim->ops->read_file_len(modem, SIM_EFSPN_FILEID,
+ sim_spn_len_cb, modem);
+
+ return FALSE;
+}
+
+static void sim_imsi_cb(const struct ofono_error *error, const char *imsi,
+ void *data)
+{
+ struct ofono_modem *modem = data;
+ struct sim_manager_data *sim = modem->sim_manager;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
+ return;
+
+ sim->imsi = g_strdup(imsi);
+}
+
+static gboolean sim_retrieve_imsi(void *user_data)
+{
+ struct ofono_modem *modem = user_data;
+ struct sim_manager_data *sim = modem->sim_manager;
+
+ sim->ops->read_imsi(modem, sim_imsi_cb, modem);
+
+ return FALSE;
+}
+
+static void sim_own_number_cb(const struct ofono_error *error, GSList *numbers,
+ void *data)
+{
+ struct ofono_modem *modem = data;
+ struct sim_manager_data *sim = modem->sim_manager;
+ GSList *l;
+ struct ofono_own_number *msisdn;
+ char **number_str;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
+ return;
+
+ sim->numbers = g_try_new0(char *, g_slist_length(numbers) + 1);
+ number_str = sim->numbers;
+
+ for (l = numbers; l; l = l->next) {
+ msisdn = l->data;
+ *number_str++ = g_strdup(phone_number_to_string(
+ msisdn->phone_number,
+ msisdn->number_type));
+ }
+}
+
+static gboolean sim_retrieve_own_number(void *user_data)
+{
+ struct ofono_modem *modem = user_data;
+ struct sim_manager_data *sim = modem->sim_manager;
+
+ sim->ops->read_own_numbers(modem, sim_own_number_cb, modem);
+
+ return FALSE;
+}
+
+static void initialize_sim_manager(struct ofono_modem *modem)
+{
+ DBusConnection *conn = dbus_gsm_connection();
+
+ if (!g_dbus_register_interface(conn, modem->path,
+ SIM_MANAGER_INTERFACE,
+ sim_manager_methods,
+ sim_manager_signals,
+ NULL, modem,
+ sim_manager_destroy)) {
+ ofono_error("Could not register SIMManager interface");
+ sim_manager_destroy(modem);
+
+ return;
+ }
+
+ ofono_debug("SIMManager interface for modem: %s created",
+ modem->path);
+
+ modem_add_interface(modem, SIM_MANAGER_INTERFACE);
+
+ if (modem->sim_manager->ops->read_file)
+ g_timeout_add(0, sim_retrieve_spn, modem);
+
+ if (modem->sim_manager->ops->read_imsi)
+ g_timeout_add(0, sim_retrieve_imsi, modem);
+
+ if (modem->sim_manager->ops->read_own_numbers)
+ g_timeout_add(0, sim_retrieve_own_number, modem);
+}
+
+int ofono_sim_manager_register(struct ofono_modem *modem,
+ struct ofono_sim_ops *ops)
+{
+ if (modem == NULL)
+ return -1;
+ if (modem->sim_manager == NULL)
+ return -1;
+
+ if (ops == NULL)
+ return -1;
+
+ modem->sim_manager->ops = ops;
+
+ initialize_sim_manager(modem);
+
+ return 0;
+}
+
+void ofono_sim_manager_unregister(struct ofono_modem *modem)
+{
+ DBusConnection *conn = dbus_gsm_connection();
+
+ g_dbus_unregister_interface(conn, modem->path,
+ SIM_MANAGER_INTERFACE);
+ modem_remove_interface(modem, SIM_MANAGER_INTERFACE);
+}
+
+void ofono_sim_manager_init(struct ofono_modem *modem)
+{
+ modem->sim_manager = sim_manager_create();
+}
+
+void ofono_sim_manager_exit(struct ofono_modem *modem)
+{
+ if (modem->sim_manager == NULL)
+ return;
+
+ g_free(modem->sim_manager);
+
+ modem->sim_manager = 0;
+}
+
+int ofono_spn_update_notify_register(struct ofono_modem *modem,
+ update_spn_cb cb)
+{
+ if (modem->sim_manager == NULL)
+ return -1;
+
+ modem->sim_manager->update_spn_notify =
+ g_slist_append(modem->sim_manager->update_spn_notify, cb);
+
+ if (modem->sim_manager->spn)
+ sim_spn_notify(modem, cb);
+
+ return 0;
+}
+
+int ofono_spn_update_notify_unregister(struct ofono_modem *modem,
+ update_spn_cb cb)
+{
+ if (modem->sim_manager == NULL)
+ return -1;
+
+ modem->sim_manager->update_spn_notify =
+ g_slist_remove(modem->sim_manager->update_spn_notify, cb);
+ return 0;
+}