2009-05-22 11:57:18 +00:00
|
|
|
/*
|
|
|
|
*
|
|
|
|
* 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"
|
2009-06-18 08:46:08 +00:00
|
|
|
#include "smsutil.h"
|
2009-05-22 11:57:18 +00:00
|
|
|
#include "sim.h"
|
|
|
|
|
|
|
|
#define SIM_MANAGER_INTERFACE "org.ofono.SimManager"
|
|
|
|
|
|
|
|
struct sim_manager_data {
|
|
|
|
struct ofono_sim_ops *ops;
|
|
|
|
int flags;
|
|
|
|
DBusMessage *pending;
|
|
|
|
char *imsi;
|
2009-05-26 23:04:31 +00:00
|
|
|
GSList *own_numbers;
|
2009-07-09 21:52:54 +00:00
|
|
|
GSList *ready_notify;
|
2009-07-09 22:06:59 +00:00
|
|
|
gboolean ready;
|
2009-07-09 21:52:54 +00:00
|
|
|
|
2009-05-22 11:57:18 +00:00
|
|
|
int dcbyte;
|
|
|
|
|
2009-06-21 01:40:48 +00:00
|
|
|
GSList *spdi;
|
|
|
|
|
2009-06-18 03:44:56 +00:00
|
|
|
int own_numbers_num;
|
|
|
|
int own_numbers_size;
|
|
|
|
int own_numbers_current;
|
2009-07-05 03:14:15 +00:00
|
|
|
|
|
|
|
GSList *opl;
|
|
|
|
int opl_num;
|
|
|
|
int opl_size;
|
|
|
|
int opl_current;
|
|
|
|
|
|
|
|
struct pnn_operator *pnn;
|
|
|
|
int pnn_num;
|
|
|
|
int pnn_size;
|
|
|
|
int pnn_current;
|
2009-05-22 11:57:18 +00:00
|
|
|
};
|
|
|
|
|
2009-06-18 08:59:13 +00:00
|
|
|
static char **get_own_numbers(GSList *own_numbers)
|
2009-05-26 23:04:31 +00:00
|
|
|
{
|
2009-06-18 08:59:13 +00:00
|
|
|
int nelem = 0;
|
2009-05-26 23:04:31 +00:00
|
|
|
GSList *l;
|
2009-06-18 08:59:13 +00:00
|
|
|
struct ofono_phone_number *num;
|
2009-05-26 23:04:31 +00:00
|
|
|
char **ret;
|
|
|
|
|
2009-06-18 08:59:13 +00:00
|
|
|
if (own_numbers)
|
|
|
|
nelem = g_slist_length(own_numbers);
|
2009-05-26 23:04:31 +00:00
|
|
|
|
|
|
|
ret = g_new0(char *, nelem + 1);
|
|
|
|
|
|
|
|
nelem = 0;
|
|
|
|
for (l = own_numbers; l; l = l->next) {
|
|
|
|
num = l->data;
|
|
|
|
|
2009-06-18 08:59:13 +00:00
|
|
|
ret[nelem++] = g_strdup(phone_number_to_string(num));
|
2009-05-26 23:04:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-07-05 03:14:15 +00:00
|
|
|
struct pnn_operator {
|
|
|
|
char *longname;
|
|
|
|
char *shortname;
|
|
|
|
};
|
|
|
|
|
2009-05-22 11:57:18 +00:00
|
|
|
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;
|
2009-07-05 03:14:15 +00:00
|
|
|
int i;
|
2009-05-22 11:57:18 +00:00
|
|
|
|
|
|
|
if (data->imsi) {
|
|
|
|
g_free(data->imsi);
|
|
|
|
data->imsi = NULL;
|
|
|
|
}
|
2009-05-26 18:48:42 +00:00
|
|
|
|
2009-05-26 23:04:31 +00:00
|
|
|
if (data->own_numbers) {
|
|
|
|
g_slist_foreach(data->own_numbers, (GFunc)g_free, NULL);
|
|
|
|
g_slist_free(data->own_numbers);
|
|
|
|
data->own_numbers = NULL;
|
2009-05-22 11:57:18 +00:00
|
|
|
}
|
2009-05-26 18:48:42 +00:00
|
|
|
|
2009-06-21 01:40:48 +00:00
|
|
|
if (data->spdi) {
|
|
|
|
g_slist_foreach(data->spdi, (GFunc)g_free, NULL);
|
|
|
|
g_slist_free(data->spdi);
|
|
|
|
data->spdi = NULL;
|
|
|
|
}
|
2009-07-05 03:14:15 +00:00
|
|
|
|
|
|
|
if (data->opl) {
|
|
|
|
g_slist_foreach(data->opl, (GFunc)g_free, NULL);
|
|
|
|
g_slist_free(data->opl);
|
|
|
|
data->opl = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data->pnn) {
|
|
|
|
for (i = 0; i < data->pnn_num; i ++) {
|
|
|
|
if (data->pnn[i].longname)
|
|
|
|
g_free(data->pnn[i].longname);
|
|
|
|
if (data->pnn[i].shortname)
|
|
|
|
g_free(data->pnn[i].shortname);
|
|
|
|
}
|
|
|
|
g_free(data->pnn);
|
|
|
|
data->pnn = NULL;
|
|
|
|
data->pnn_num = 0;
|
|
|
|
}
|
2009-05-22 11:57:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2009-06-18 08:59:13 +00:00
|
|
|
char **own_numbers;
|
2009-05-22 11:57:18 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2009-05-26 23:04:31 +00:00
|
|
|
if (sim->imsi)
|
|
|
|
dbus_gsm_dict_append(&dict, "SubscriberIdentity",
|
|
|
|
DBUS_TYPE_STRING, &sim->imsi);
|
|
|
|
|
2009-06-18 08:59:13 +00:00
|
|
|
own_numbers = get_own_numbers(sim->own_numbers);
|
2009-05-26 23:04:31 +00:00
|
|
|
|
2009-06-18 08:59:13 +00:00
|
|
|
dbus_gsm_dict_append_array(&dict, "SubscriberNumbers",
|
|
|
|
DBUS_TYPE_STRING, &own_numbers);
|
|
|
|
dbus_gsm_free_string_array(own_numbers);
|
2009-05-22 11:57:18 +00:00
|
|
|
|
|
|
|
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[] = { { } };
|
|
|
|
|
2009-07-05 03:14:15 +00:00
|
|
|
static char *network_name_parse(const unsigned char *buffer, int length)
|
|
|
|
{
|
|
|
|
unsigned char *endp;
|
2009-07-09 16:42:04 +00:00
|
|
|
unsigned char dcs;
|
|
|
|
int i;
|
2009-07-05 03:14:15 +00:00
|
|
|
|
|
|
|
if (length < 1)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
dcs = *buffer ++;
|
|
|
|
length --;
|
|
|
|
|
2009-07-09 16:42:04 +00:00
|
|
|
/* TODO: "The MS should add the letters for the Country's
|
|
|
|
* Initials and a separator (e.g. a space)" */
|
|
|
|
if (is_bit_set(dcs, 4))
|
|
|
|
ofono_error("Network Name DCS implies country initials");
|
2009-07-05 03:14:15 +00:00
|
|
|
|
|
|
|
switch (dcs & (7 << 4)) {
|
|
|
|
case 0x00:
|
|
|
|
endp = memchr(buffer, 0xff, length);
|
|
|
|
if (endp)
|
|
|
|
length = endp - buffer;
|
|
|
|
return convert_gsm_to_utf8(buffer, length,
|
|
|
|
NULL, NULL, 0xff);
|
|
|
|
case 0x10:
|
2009-07-09 16:42:04 +00:00
|
|
|
if ((length % 2) == 1) {
|
|
|
|
if (buffer[length - 1] != 0xff)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
length = length - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < length; i += 2)
|
|
|
|
if (buffer[i] == 0xff && buffer[i + 1] == 0xff)
|
|
|
|
break;
|
|
|
|
|
|
|
|
return g_convert(buffer, length, "UTF-8//TRANSLIT", "UCS-2BE",
|
|
|
|
NULL, NULL, NULL);
|
2009-07-05 03:14:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2009-05-22 11:57:18 +00:00
|
|
|
enum sim_fileids {
|
2009-06-18 03:44:56 +00:00
|
|
|
SIM_EFMSISDN_FILEID = 0x6f40,
|
2009-05-22 11:57:18 +00:00
|
|
|
SIM_EFSPN_FILEID = 0x6f46,
|
2009-07-05 03:14:15 +00:00
|
|
|
SIM_EFPNN_FILEID = 0x6fc5,
|
|
|
|
SIM_EFOPL_FILEID = 0x6fc6,
|
2009-06-21 01:40:48 +00:00
|
|
|
SIM_EFSPDI_FILEID = 0x6fcd,
|
2009-05-22 11:57:18 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
#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;
|
|
|
|
|
2009-07-06 20:10:21 +00:00
|
|
|
/* TS 31.102 says:
|
2009-05-22 11:57:18 +00:00
|
|
|
*
|
|
|
|
* 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].
|
|
|
|
*
|
2009-07-06 20:10:21 +00:00
|
|
|
* 31.101 has no such annex though. 51.101 refers to Annex B of
|
|
|
|
* itself which is not there either. 11.11 contains the same
|
|
|
|
* paragraph as 51.101 and has an Annex B which we implement.
|
2009-05-22 11:57:18 +00:00
|
|
|
*/
|
2009-07-06 20:10:21 +00:00
|
|
|
sim->spn = sim_string_to_utf8(sdata, length);
|
2009-05-22 11:57:18 +00:00
|
|
|
|
|
|
|
for (l = sim->update_spn_notify; l; l = l->next)
|
|
|
|
sim_spn_notify(modem, l->data);
|
|
|
|
}
|
|
|
|
|
2009-06-18 07:23:37 +00:00
|
|
|
static void sim_spn_info_cb(const struct ofono_error *error, int length,
|
|
|
|
enum ofono_sim_file_structure structure,
|
|
|
|
int dummy, void *data)
|
2009-05-22 11:57:18 +00:00
|
|
|
{
|
|
|
|
struct ofono_modem *modem = data;
|
|
|
|
struct sim_manager_data *sim = modem->sim_manager;
|
|
|
|
|
2009-06-18 03:44:44 +00:00
|
|
|
if (error->type != OFONO_ERROR_TYPE_NO_ERROR || length <= 1 ||
|
2009-06-18 07:23:37 +00:00
|
|
|
structure != OFONO_SIM_FILE_STRUCTURE_TRANSPARENT)
|
2009-05-22 11:57:18 +00:00
|
|
|
return;
|
|
|
|
|
2009-06-18 03:44:44 +00:00
|
|
|
sim->ops->read_file_transparent(modem, SIM_EFSPN_FILEID, 0, length,
|
2009-06-18 07:23:37 +00:00
|
|
|
sim_spn_read_cb, modem);
|
2009-05-22 11:57:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean sim_retrieve_spn(void *user_data)
|
|
|
|
{
|
|
|
|
struct ofono_modem *modem = user_data;
|
|
|
|
struct sim_manager_data *sim = modem->sim_manager;
|
|
|
|
|
2009-06-18 03:44:44 +00:00
|
|
|
sim->ops->read_file_info(modem, SIM_EFSPN_FILEID,
|
2009-06-18 07:23:37 +00:00
|
|
|
sim_spn_info_cb, modem);
|
2009-05-22 11:57:18 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-06-18 03:44:56 +00:00
|
|
|
static void sim_msisdn_read_cb(const struct ofono_error *error,
|
|
|
|
const unsigned char *sdata, int length, void *data)
|
2009-05-22 11:57:18 +00:00
|
|
|
{
|
|
|
|
struct ofono_modem *modem = data;
|
|
|
|
struct sim_manager_data *sim = modem->sim_manager;
|
2009-06-18 08:59:13 +00:00
|
|
|
struct ofono_phone_number *ph;
|
2009-06-18 03:44:56 +00:00
|
|
|
int number_len;
|
|
|
|
int ton_npi;
|
2009-05-22 11:57:18 +00:00
|
|
|
|
|
|
|
if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
|
2009-06-18 03:44:56 +00:00
|
|
|
goto skip;
|
2009-05-22 11:57:18 +00:00
|
|
|
|
2009-06-18 03:44:56 +00:00
|
|
|
if (length < sim->own_numbers_size)
|
|
|
|
goto skip;
|
2009-05-26 23:04:31 +00:00
|
|
|
|
2009-06-18 03:44:56 +00:00
|
|
|
/* Skip Alpha-Identifier field */
|
|
|
|
sdata += sim->own_numbers_size - 14;
|
|
|
|
|
|
|
|
number_len = *sdata++;
|
|
|
|
ton_npi = *sdata++;
|
|
|
|
|
|
|
|
if (number_len > 11 || ton_npi == 0xff)
|
|
|
|
goto skip;
|
|
|
|
|
2009-06-18 08:59:13 +00:00
|
|
|
ph = g_new(struct ofono_phone_number, 1);
|
2009-06-18 03:44:56 +00:00
|
|
|
|
2009-06-18 08:59:13 +00:00
|
|
|
ph->type = bit_field(ton_npi, 4, 3);
|
2009-06-18 08:46:08 +00:00
|
|
|
|
|
|
|
/* BCD coded, however the TON/NPI is given by the first byte */
|
|
|
|
number_len = (number_len - 1) * 2;
|
|
|
|
|
2009-06-18 08:59:13 +00:00
|
|
|
extract_bcd_number(sdata, number_len, ph->number);
|
2009-06-18 03:44:56 +00:00
|
|
|
|
|
|
|
sim->own_numbers = g_slist_prepend(sim->own_numbers, ph);
|
|
|
|
|
|
|
|
skip:
|
|
|
|
sim->own_numbers_current ++;
|
|
|
|
if (sim->own_numbers_current < sim->own_numbers_num)
|
|
|
|
sim->ops->read_file_linear(modem, SIM_EFMSISDN_FILEID,
|
2009-06-18 08:46:08 +00:00
|
|
|
sim->own_numbers_current,
|
|
|
|
sim->own_numbers_size,
|
|
|
|
sim_msisdn_read_cb, modem);
|
2009-06-18 03:44:56 +00:00
|
|
|
else
|
|
|
|
/* All records retrieved */
|
|
|
|
if (sim->own_numbers)
|
|
|
|
sim->own_numbers = g_slist_reverse(sim->own_numbers);
|
|
|
|
}
|
|
|
|
|
2009-06-18 08:46:08 +00:00
|
|
|
static void sim_msisdn_info_cb(const struct ofono_error *error, int length,
|
|
|
|
enum ofono_sim_file_structure structure,
|
|
|
|
int record_length, void *data)
|
2009-06-18 03:44:56 +00:00
|
|
|
{
|
|
|
|
struct ofono_modem *modem = data;
|
|
|
|
struct sim_manager_data *sim = modem->sim_manager;
|
|
|
|
|
|
|
|
if (error->type != OFONO_ERROR_TYPE_NO_ERROR || length < 14 ||
|
|
|
|
record_length < 14 ||
|
2009-06-18 08:46:08 +00:00
|
|
|
structure != OFONO_SIM_FILE_STRUCTURE_FIXED)
|
2009-06-18 03:44:56 +00:00
|
|
|
return;
|
2009-05-26 23:04:31 +00:00
|
|
|
|
2009-06-18 03:44:56 +00:00
|
|
|
sim->own_numbers_current = 0;
|
|
|
|
sim->own_numbers_size = record_length;
|
|
|
|
sim->own_numbers_num = length / record_length;
|
|
|
|
sim->ops->read_file_linear(modem, SIM_EFMSISDN_FILEID, 0,
|
|
|
|
record_length, sim_msisdn_read_cb, modem);
|
2009-05-22 11:57:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean sim_retrieve_own_number(void *user_data)
|
|
|
|
{
|
|
|
|
struct ofono_modem *modem = user_data;
|
|
|
|
struct sim_manager_data *sim = modem->sim_manager;
|
|
|
|
|
2009-06-18 03:44:56 +00:00
|
|
|
sim->ops->read_file_info(modem, SIM_EFMSISDN_FILEID,
|
|
|
|
sim_msisdn_info_cb, modem);
|
2009-05-22 11:57:18 +00:00
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2009-07-05 03:14:15 +00:00
|
|
|
struct sim_operator {
|
2009-06-21 01:40:48 +00:00
|
|
|
char mcc[OFONO_MAX_MCC_LENGTH + 1];
|
|
|
|
char mnc[OFONO_MAX_MNC_LENGTH + 1];
|
|
|
|
};
|
|
|
|
|
2009-07-05 03:14:15 +00:00
|
|
|
static void parse_mcc_mnc(struct sim_operator *oper, const guint8 *bcd)
|
2009-06-21 01:40:48 +00:00
|
|
|
{
|
2009-07-05 03:14:15 +00:00
|
|
|
char *mcc = oper->mcc;
|
|
|
|
char *mnc = oper->mnc;
|
2009-06-21 01:40:48 +00:00
|
|
|
guint8 digit;
|
|
|
|
|
|
|
|
digit = (bcd[0] >> 0) & 0xf;
|
|
|
|
if (digit != 0xf)
|
|
|
|
*mcc ++ = '0' + digit;
|
|
|
|
digit = (bcd[0] >> 4) & 0xf;
|
|
|
|
if (digit != 0xf)
|
|
|
|
*mcc ++ = '0' + digit;
|
|
|
|
digit = (bcd[1] >> 0) & 0xf;
|
|
|
|
if (digit != 0xf)
|
|
|
|
*mcc ++ = '0' + digit;
|
|
|
|
digit = (bcd[2] >> 0) & 0xf;
|
|
|
|
if (digit != 0xf)
|
|
|
|
*mnc ++ = '0' + digit;
|
|
|
|
digit = (bcd[2] >> 4) & 0xf;
|
|
|
|
if (digit != 0xf)
|
|
|
|
*mnc ++ = '0' + digit;
|
|
|
|
digit = (bcd[1] >> 4) & 0xf;
|
|
|
|
if (digit != 0xf)
|
|
|
|
*mnc ++ = '0' + digit;
|
2009-07-05 03:14:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct sim_operator *sim_operator_alloc(const guint8 *bcd)
|
|
|
|
{
|
|
|
|
struct sim_operator *spdi = g_new0(struct sim_operator, 1);
|
|
|
|
|
|
|
|
parse_mcc_mnc(spdi, bcd);
|
2009-06-21 01:40:48 +00:00
|
|
|
|
|
|
|
return spdi;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gint spdi_operator_compare(gconstpointer a, gconstpointer b)
|
|
|
|
{
|
2009-07-05 03:14:15 +00:00
|
|
|
const struct sim_operator *opa = a;
|
|
|
|
const struct sim_operator *opb = b;
|
2009-06-22 18:20:01 +00:00
|
|
|
gint r;
|
2009-06-21 01:40:48 +00:00
|
|
|
|
2009-07-14 03:29:11 +00:00
|
|
|
if ((r = strcmp(opa->mcc, opb->mcc)))
|
2009-06-22 18:20:01 +00:00
|
|
|
return r;
|
|
|
|
|
|
|
|
return strcmp(opa->mnc, opb->mnc);
|
2009-06-21 01:40:48 +00:00
|
|
|
}
|
|
|
|
|
2009-06-22 18:20:01 +00:00
|
|
|
gboolean ofono_operator_in_spdi(struct ofono_modem *modem,
|
2009-06-21 01:40:48 +00:00
|
|
|
const struct ofono_network_operator *op)
|
|
|
|
{
|
|
|
|
struct sim_manager_data *sim = modem->sim_manager;
|
2009-07-05 03:14:15 +00:00
|
|
|
struct sim_operator spdi_op;
|
2009-06-21 01:40:48 +00:00
|
|
|
|
|
|
|
if (!sim)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
g_strlcpy(spdi_op.mcc, op->mcc, sizeof(spdi_op.mcc));
|
|
|
|
g_strlcpy(spdi_op.mnc, op->mnc, sizeof(spdi_op.mnc));
|
|
|
|
|
2009-06-22 18:20:01 +00:00
|
|
|
return g_slist_find_custom(sim->spdi,
|
|
|
|
&spdi_op, spdi_operator_compare) != NULL;
|
2009-06-21 01:40:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void sim_spdi_read_cb(const struct ofono_error *error,
|
|
|
|
const unsigned char *spdidata,
|
|
|
|
int length, void *data)
|
|
|
|
{
|
|
|
|
struct ofono_modem *modem = data;
|
|
|
|
struct sim_manager_data *sim = modem->sim_manager;
|
|
|
|
const guint8 *plmn_list;
|
|
|
|
GSList *l;
|
|
|
|
|
|
|
|
if (error->type != OFONO_ERROR_TYPE_NO_ERROR || length <= 5)
|
|
|
|
return;
|
|
|
|
|
|
|
|
plmn_list = ber_tlv_find_by_tag(spdidata, 0x80, length, &length);
|
|
|
|
if (!plmn_list) {
|
|
|
|
ofono_debug("Couldn't parse the EF-SPDI contents as a TLV");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (length /= 3; length --; plmn_list += 3) {
|
|
|
|
if ((plmn_list[0] & plmn_list[1] & plmn_list[2]) == 0xff)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
sim->spdi = g_slist_insert_sorted(sim->spdi,
|
2009-07-05 03:14:15 +00:00
|
|
|
sim_operator_alloc(plmn_list),
|
2009-06-21 01:40:48 +00:00
|
|
|
spdi_operator_compare);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sim->spdi)
|
|
|
|
for (l = sim->update_spn_notify; l; l = l->next)
|
|
|
|
sim_spn_notify(modem, l->data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void sim_spdi_info_cb(const struct ofono_error *error, int length,
|
|
|
|
enum ofono_sim_file_structure structure,
|
|
|
|
int dummy, void *data)
|
|
|
|
{
|
|
|
|
struct ofono_modem *modem = data;
|
|
|
|
struct sim_manager_data *sim = modem->sim_manager;
|
|
|
|
|
|
|
|
if (error->type != OFONO_ERROR_TYPE_NO_ERROR || length <= 5 ||
|
|
|
|
structure != OFONO_SIM_FILE_STRUCTURE_TRANSPARENT)
|
|
|
|
return;
|
|
|
|
|
|
|
|
sim->ops->read_file_transparent(modem, SIM_EFSPDI_FILEID, 0, length,
|
|
|
|
sim_spdi_read_cb, modem);
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean sim_retrieve_spdi(void *user_data)
|
|
|
|
{
|
|
|
|
struct ofono_modem *modem = user_data;
|
|
|
|
struct sim_manager_data *sim = modem->sim_manager;
|
|
|
|
|
|
|
|
sim->ops->read_file_info(modem, SIM_EFSPDI_FILEID,
|
|
|
|
sim_spdi_info_cb, modem);
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2009-07-05 03:14:15 +00:00
|
|
|
struct opl_operator {
|
|
|
|
struct sim_operator mcc_mnc;
|
|
|
|
guint16 lac_tac_low;
|
|
|
|
guint16 lac_tac_high;
|
|
|
|
guint8 id;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct opl_operator *opl_operator_alloc(const guint8 *record)
|
|
|
|
{
|
|
|
|
struct opl_operator *oper = g_new0(struct opl_operator, 1);
|
|
|
|
|
|
|
|
parse_mcc_mnc(&oper->mcc_mnc, record);
|
|
|
|
record += 3;
|
|
|
|
|
|
|
|
oper->lac_tac_low = (record[0] << 8) | record[1];
|
|
|
|
record += 2;
|
|
|
|
oper->lac_tac_high = (record[0] << 8) | record[1];
|
|
|
|
record += 2;
|
|
|
|
|
|
|
|
oper->id = record[0];
|
|
|
|
if (!oper->id) {
|
|
|
|
/* TODO: name to be taken from other sources, see TS 22.101 */
|
|
|
|
}
|
|
|
|
|
|
|
|
return oper;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gint opl_operator_compare(gconstpointer a, gconstpointer b)
|
|
|
|
{
|
|
|
|
const struct opl_operator *opa = a;
|
|
|
|
const struct sim_operator *opb = b;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; opb->mcc[i] | opa->mcc_mnc.mcc[i]; i ++)
|
|
|
|
if (opb->mcc[i] != opa->mcc_mnc.mcc[i] &&
|
|
|
|
!(opa->mcc_mnc.mcc[i] == '0' + 0xd &&
|
|
|
|
opb->mcc[i]))
|
|
|
|
return opa->mcc_mnc.mcc[i] - opb->mcc[i];
|
|
|
|
for (i = 0; opb->mnc[i] | opa->mcc_mnc.mnc[i]; i ++)
|
|
|
|
if (opb->mnc[i] != opa->mcc_mnc.mnc[i] &&
|
|
|
|
!(opa->mcc_mnc.mnc[i] == '0' + 0xd &&
|
|
|
|
opb->mnc[i]))
|
|
|
|
return opa->mcc_mnc.mnc[i] - opb->mnc[i];
|
|
|
|
|
|
|
|
if (opa->lac_tac_low > 0x0000 || opa->lac_tac_high < 0xfffe)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void sim_opl_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;
|
|
|
|
struct opl_operator *oper;
|
|
|
|
|
|
|
|
if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
|
|
|
|
goto skip;
|
|
|
|
|
|
|
|
if (length < sim->opl_size)
|
|
|
|
goto skip;
|
|
|
|
|
|
|
|
oper = opl_operator_alloc(sdata);
|
|
|
|
if (oper->id > sim->pnn_num) {
|
|
|
|
g_free(oper);
|
|
|
|
goto skip;
|
|
|
|
}
|
|
|
|
|
|
|
|
sim->opl = g_slist_prepend(sim->opl, oper);
|
|
|
|
|
|
|
|
skip:
|
|
|
|
sim->opl_current ++;
|
|
|
|
if (sim->opl_current < sim->opl_num)
|
|
|
|
sim->ops->read_file_linear(modem, SIM_EFOPL_FILEID,
|
|
|
|
sim->opl_current,
|
|
|
|
sim->opl_size,
|
|
|
|
sim_opl_read_cb, modem);
|
|
|
|
else
|
|
|
|
/* All records retrieved */
|
|
|
|
if (sim->opl)
|
|
|
|
sim->opl = g_slist_reverse(sim->opl);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void sim_opl_info_cb(const struct ofono_error *error, int length,
|
|
|
|
enum ofono_sim_file_structure structure,
|
|
|
|
int record_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 < 8 ||
|
|
|
|
record_length < 8 ||
|
|
|
|
structure != OFONO_SIM_FILE_STRUCTURE_FIXED)
|
|
|
|
return;
|
|
|
|
|
|
|
|
sim->opl_current = 0;
|
|
|
|
sim->opl_size = record_length;
|
|
|
|
sim->opl_num = length / record_length;
|
|
|
|
sim->ops->read_file_linear(modem, SIM_EFOPL_FILEID, 0,
|
|
|
|
record_length, sim_opl_read_cb, modem);
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean sim_retrieve_opl(void *user_data)
|
|
|
|
{
|
|
|
|
struct ofono_modem *modem = user_data;
|
|
|
|
struct sim_manager_data *sim = modem->sim_manager;
|
|
|
|
|
|
|
|
sim->ops->read_file_info(modem, SIM_EFOPL_FILEID,
|
|
|
|
sim_opl_info_cb, modem);
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *ofono_operator_name_sim_override(struct ofono_modem *modem,
|
|
|
|
const char *mcc, const char *mnc)
|
|
|
|
{
|
|
|
|
struct sim_manager_data *sim = modem->sim_manager;
|
|
|
|
struct sim_operator op;
|
|
|
|
GSList *l;
|
|
|
|
const struct opl_operator *opl_op;
|
|
|
|
|
|
|
|
g_strlcpy(op.mcc, mcc, sizeof(op.mcc));
|
|
|
|
g_strlcpy(op.mnc, mnc, sizeof(op.mnc));
|
|
|
|
|
|
|
|
l = g_slist_find_custom(sim->opl, &op, opl_operator_compare);
|
|
|
|
if (!l)
|
|
|
|
return NULL;
|
|
|
|
opl_op = l->data;
|
|
|
|
|
|
|
|
return sim->pnn[opl_op->id - 1].longname;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean pnn_operator_parse(struct pnn_operator *oper,
|
|
|
|
const guint8 *tlv, int length)
|
|
|
|
{
|
|
|
|
const char *name;
|
|
|
|
int namelength;
|
|
|
|
|
|
|
|
name = ber_tlv_find_by_tag(tlv, 0x43, length, &namelength);
|
|
|
|
if (!name || !namelength)
|
|
|
|
return FALSE;
|
|
|
|
oper->longname = network_name_parse(name, namelength);
|
|
|
|
|
|
|
|
name = ber_tlv_find_by_tag(tlv, 0x45, length, &namelength);
|
|
|
|
if (name && namelength)
|
|
|
|
oper->shortname = network_name_parse(name, namelength);
|
|
|
|
|
|
|
|
if (ber_tlv_find_by_tag(tlv, 0x80, length, &namelength))
|
|
|
|
ofono_debug("%i octets of addition PLMN information "
|
|
|
|
"present in EF-PNN");
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void sim_pnn_read_cb(const struct ofono_error *error,
|
|
|
|
const unsigned char *pnndata, int length, void *data)
|
|
|
|
{
|
|
|
|
struct ofono_modem *modem = data;
|
|
|
|
struct sim_manager_data *sim = modem->sim_manager;
|
|
|
|
struct opl_operator *oper;
|
|
|
|
|
|
|
|
if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
|
|
|
|
goto skip;
|
|
|
|
|
|
|
|
if (length < sim->pnn_size)
|
|
|
|
goto skip;
|
|
|
|
|
|
|
|
pnn_operator_parse(&sim->pnn[sim->pnn_current], pnndata, length);
|
|
|
|
|
|
|
|
skip:
|
|
|
|
sim->pnn_current ++;
|
|
|
|
if (sim->pnn_current < sim->pnn_num)
|
|
|
|
sim->ops->read_file_linear(modem, SIM_EFPNN_FILEID,
|
|
|
|
sim->pnn_current,
|
|
|
|
sim->pnn_size,
|
|
|
|
sim_pnn_read_cb, modem);
|
|
|
|
else
|
|
|
|
/* All records retrieved */
|
|
|
|
/* We now need EF-OPL if it's there for PNN to be
|
|
|
|
* useful. */
|
|
|
|
sim_retrieve_opl(modem);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void sim_pnn_info_cb(const struct ofono_error *error, int length,
|
|
|
|
enum ofono_sim_file_structure structure,
|
|
|
|
int record_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 < 3 ||
|
|
|
|
record_length < 3 ||
|
|
|
|
structure != OFONO_SIM_FILE_STRUCTURE_FIXED)
|
|
|
|
/* If PNN is not present then OPL is not useful, don't
|
|
|
|
* retrieve it. If OPL is not there then PNN[1] will
|
|
|
|
* still be used for the HPLMN and/or EHPLMN, if PNN
|
|
|
|
* is present. */
|
|
|
|
return;
|
|
|
|
|
|
|
|
sim->pnn_current = 0;
|
|
|
|
sim->pnn_size = record_length;
|
|
|
|
sim->pnn_num = length / record_length;
|
|
|
|
sim->pnn = g_new0(struct pnn_operator, sim->pnn_num);
|
|
|
|
sim->ops->read_file_linear(modem, SIM_EFPNN_FILEID, 0,
|
|
|
|
record_length, sim_pnn_read_cb, modem);
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean sim_retrieve_pnn(void *user_data)
|
|
|
|
{
|
|
|
|
struct ofono_modem *modem = user_data;
|
|
|
|
struct sim_manager_data *sim = modem->sim_manager;
|
|
|
|
|
|
|
|
sim->ops->read_file_info(modem, SIM_EFPNN_FILEID,
|
|
|
|
sim_pnn_info_cb, modem);
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2009-05-22 11:57:18 +00:00
|
|
|
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);
|
|
|
|
|
2009-06-18 03:44:44 +00:00
|
|
|
if (modem->sim_manager->ops->read_file_transparent)
|
2009-05-22 11:57:18 +00:00
|
|
|
g_timeout_add(0, sim_retrieve_spn, modem);
|
|
|
|
|
|
|
|
if (modem->sim_manager->ops->read_imsi)
|
|
|
|
g_timeout_add(0, sim_retrieve_imsi, modem);
|
|
|
|
|
2009-06-18 03:44:56 +00:00
|
|
|
if (modem->sim_manager->ops->read_file_linear)
|
2009-05-22 11:57:18 +00:00
|
|
|
g_timeout_add(0, sim_retrieve_own_number, modem);
|
2009-06-21 01:40:48 +00:00
|
|
|
|
|
|
|
if (modem->sim_manager->ops->read_file_transparent)
|
|
|
|
g_timeout_add(0, sim_retrieve_spdi, modem);
|
2009-07-05 03:14:15 +00:00
|
|
|
|
|
|
|
if (modem->sim_manager->ops->read_file_linear)
|
|
|
|
g_timeout_add(0, sim_retrieve_pnn, modem);
|
2009-05-22 11:57:18 +00:00
|
|
|
}
|
|
|
|
|
2009-07-09 22:16:00 +00:00
|
|
|
const char *ofono_sim_get_imsi(struct ofono_modem *modem)
|
|
|
|
{
|
|
|
|
if (modem->sim_manager == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return modem->sim_manager->imsi;
|
|
|
|
}
|
|
|
|
|
2009-07-09 21:54:10 +00:00
|
|
|
int ofono_sim_ready_notify_register(struct ofono_modem *modem,
|
|
|
|
ofono_sim_ready_notify_cb_t cb)
|
|
|
|
{
|
|
|
|
if (modem->sim_manager == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
modem->sim_manager->ready_notify =
|
|
|
|
g_slist_append(modem->sim_manager->ready_notify, cb);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ofono_sim_ready_notify_unregister(struct ofono_modem *modem,
|
|
|
|
ofono_sim_ready_notify_cb_t cb)
|
|
|
|
{
|
|
|
|
if (modem->sim_manager == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
modem->sim_manager->ready_notify =
|
|
|
|
g_slist_remove(modem->sim_manager->ready_notify, cb);
|
|
|
|
}
|
|
|
|
|
2009-07-09 22:06:59 +00:00
|
|
|
int ofono_sim_get_ready(struct ofono_modem *modem)
|
|
|
|
{
|
|
|
|
if (modem->sim_manager == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (modem->sim_manager->ready == TRUE)
|
|
|
|
return 1;
|
|
|
|
|
2009-07-09 21:54:10 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-07-09 22:06:59 +00:00
|
|
|
void ofono_sim_set_ready(struct ofono_modem *modem)
|
|
|
|
{
|
|
|
|
GSList *l;
|
|
|
|
|
|
|
|
if (modem->sim_manager == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (modem->sim_manager->ready == TRUE)
|
|
|
|
return;
|
|
|
|
|
|
|
|
modem->sim_manager->ready = TRUE;
|
|
|
|
|
|
|
|
for (l = modem->sim_manager->ready_notify; l; l = l->next) {
|
|
|
|
ofono_sim_ready_notify_cb_t cb = l->data;
|
|
|
|
|
|
|
|
cb(modem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-22 11:57:18 +00:00
|
|
|
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;
|
|
|
|
}
|