diff options
Diffstat (limited to 'src/sim.c')
-rw-r--r-- | src/sim.c | 353 |
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; +} |