summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/network.c48
-rw-r--r--src/sim.c133
-rw-r--r--src/sim.h3
-rw-r--r--src/simutil.c209
-rw-r--r--src/simutil.h30
5 files changed, 221 insertions, 202 deletions
diff --git a/src/network.c b/src/network.c
index 570866ec..186b5eec 100644
--- a/src/network.c
+++ b/src/network.c
@@ -65,8 +65,8 @@ struct network_registration_data {
DBusMessage *pending;
int signal_strength;
char *spname;
- GSList *pnn_list;
struct sim_spdi *spdi;
+ struct sim_eons *eons;
};
static void operator_list_callback(const struct ofono_error *error, int total,
@@ -571,8 +571,11 @@ static void network_registration_destroy(gpointer userdata)
g_slist_free(data->operator_list);
- g_slist_foreach(data->pnn_list, (GFunc)sim_pnn_operator_free, NULL);
- g_slist_free(data->pnn_list);
+ if (data->eons)
+ sim_eons_free(data->eons);
+
+ if (data->spdi)
+ sim_spdi_free(data->spdi);
if (data->spname)
g_free(data->spname);
@@ -1106,6 +1109,34 @@ static void signal_strength_callback(const struct ofono_error *error,
ofono_signal_strength_notify(modem, strength);
}
+static void sim_opl_read_cb(struct ofono_modem *modem, int ok,
+ enum ofono_sim_file_structure structure,
+ int length, int record,
+ const unsigned char *data,
+ int record_length, void *userdata)
+{
+ struct network_registration_data *netreg = modem->network_registration;
+ int total = length / record_length;
+
+ if (!ok)
+ return;
+
+ if (structure != OFONO_SIM_FILE_STRUCTURE_FIXED)
+ return;
+
+ if (length < 8 || record_length < 8 || length < record_length)
+ return;
+
+ /* If we don't have PNN, ignore this completely */
+ if (!netreg->eons)
+ return;
+
+ sim_eons_add_pnn_record(netreg->eons, record, data, record_length);
+
+ if (record == total)
+ sim_eons_optimize(netreg->eons);
+}
+
static void sim_pnn_read_cb(struct ofono_modem *modem, int ok,
enum ofono_sim_file_structure structure,
int length, int record,
@@ -1113,7 +1144,6 @@ static void sim_pnn_read_cb(struct ofono_modem *modem, int ok,
int record_length, void *userdata)
{
struct network_registration_data *netreg = modem->network_registration;
- struct sim_pnn_operator *oper;
int total = length / record_length;
if (!ok)
@@ -1125,19 +1155,17 @@ static void sim_pnn_read_cb(struct ofono_modem *modem, int ok,
if (length < 3 || record_length < 3 || length < record_length)
return;
- oper = sim_pnn_operator_parse(data, record_length);
+ if (!netreg->eons)
+ netreg->eons = sim_eons_new(total);
- if (oper)
- netreg->pnn_list = g_slist_prepend(netreg->pnn_list, oper);
+ sim_eons_add_pnn_record(netreg->eons, record, data, record_length);
/* 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. */
- if (record == total && g_slist_length(netreg->pnn_list) > 0) {
- netreg->pnn_list = g_slist_reverse(netreg->pnn_list);
+ if (record == total && !sim_eons_pnn_is_empty(netreg->eons))
ofono_sim_read(modem, SIM_EFOPL_FILEID, sim_opl_read_cb, NULL);
- }
}
static void sim_spdi_read_cb(struct ofono_modem *modem, int ok,
diff --git a/src/sim.c b/src/sim.c
index e22cac83..cd8d7386 100644
--- a/src/sim.c
+++ b/src/sim.c
@@ -245,139 +245,6 @@ check:
}
}
-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 void sim_ready(struct ofono_modem *modem)
{
ofono_sim_read(modem, SIM_EFMSISDN_FILEID, sim_msisdn_read_cb, NULL);
diff --git a/src/sim.h b/src/sim.h
index b0c02bf4..8e7870d2 100644
--- a/src/sim.h
+++ b/src/sim.h
@@ -37,9 +37,6 @@ int ofono_sim_ready_notify_register(struct ofono_modem *modem,
void ofono_sim_ready_notify_unregister(struct ofono_modem *modem,
ofono_sim_ready_notify_cb_t cb);
-const char *ofono_operator_name_sim_override(struct ofono_modem *modem,
- const char *mcc,
- const char *mnc);
int ofono_sim_get_ready(struct ofono_modem *modem);
void ofono_sim_set_ready(struct ofono_modem *modem);
diff --git a/src/simutil.c b/src/simutil.c
index 465b8f34..6830faca 100644
--- a/src/simutil.c
+++ b/src/simutil.c
@@ -31,14 +31,30 @@
#include "simutil.h"
#include "util.h"
+struct pnn_operator {
+ char *longname;
+ gboolean long_ci;
+ char *shortname;
+ gboolean short_ci;
+ char *info;
+};
+
struct spdi_operator {
char mcc[OFONO_MAX_MCC_LENGTH + 1];
char mnc[OFONO_MAX_MNC_LENGTH + 1];
};
+struct opl_operator {
+ char mcc[OFONO_MAX_MCC_LENGTH + 1];
+ char mnc[OFONO_MAX_MNC_LENGTH + 1];
+ guint16 lac_tac_low;
+ guint16 lac_tac_high;
+ guint8 id;
+};
+
/* Parse ASN.1 Basic Encoding Rules TLVs per ISO/IEC 7816 */
-const guint8 *ber_tlv_find_by_tag(const guint8 *pdu, guint8 in_tag,
- int in_len, int *out_len)
+static const guint8 *ber_tlv_find_by_tag(const guint8 *pdu, guint8 in_tag,
+ int in_len, int *out_len)
{
guint8 tag;
int len;
@@ -74,8 +90,8 @@ const guint8 *ber_tlv_find_by_tag(const guint8 *pdu, guint8 in_tag,
return NULL;
}
-char *sim_network_name_parse(const unsigned char *buffer, int length,
- gboolean *add_ci)
+static char *sim_network_name_parse(const unsigned char *buffer, int length,
+ gboolean *add_ci)
{
char *ret = NULL;
unsigned char *endp;
@@ -125,44 +141,6 @@ char *sim_network_name_parse(const unsigned char *buffer, int length,
return ret;
}
-struct sim_pnn_operator *sim_pnn_operator_parse(const guint8 *tlv, int length)
-{
- const char *name;
- int namelength;
- gboolean add_ci;
- struct sim_pnn_operator *oper;
-
- name = ber_tlv_find_by_tag(tlv, 0x43, length, &namelength);
-
- if (!name || !namelength)
- return NULL;
-
- oper = g_new0(struct sim_pnn_operator, 1);
-
- oper->longname = sim_network_name_parse(name, namelength,
- &oper->long_ci);
-
- name = ber_tlv_find_by_tag(tlv, 0x45, length, &namelength);
-
- if (name && namelength)
- oper->shortname = sim_network_name_parse(name, namelength,
- &oper->short_ci);
-
- name = ber_tlv_find_by_tag(tlv, 0x80, length, &namelength);
-
- if (name && namelength)
- oper->info = sim_string_to_utf8(name, namelength);
-
- return oper;
-}
-
-void sim_pnn_operator_free(struct sim_pnn_operator *oper)
-{
- g_free(oper->info);
- g_free(oper->shortname);
- g_free(oper->longname);
-}
-
static void parse_mcc_mnc(const guint8 *bcd, char *mcc, char *mnc)
{
static const char digit_lut[] = "0123456789*#abd\0";
@@ -248,3 +226,150 @@ void sim_spdi_free(struct sim_spdi *spdi)
g_slist_free(spdi->operators);
g_free(spdi);
}
+
+static void pnn_operator_free(struct pnn_operator *oper)
+{
+ g_free(oper->info);
+ g_free(oper->shortname);
+ g_free(oper->longname);
+}
+
+struct sim_eons *sim_eons_new(int pnn_records)
+{
+ struct sim_eons *eons = g_new0(struct sim_eons, 1);
+
+ eons->pnn_list = g_new0(struct pnn_operator, pnn_records);
+ eons->pnn_max = pnn_records;
+
+ return eons;
+}
+
+gboolean sim_eons_pnn_is_empty(struct sim_eons *eons)
+{
+ return !eons->pnn_valid;
+}
+
+void sim_eons_add_pnn_record(struct sim_eons *eons, int record,
+ const guint8 *tlv, int length)
+{
+ const char *name;
+ int namelength;
+ gboolean add_ci;
+ struct pnn_operator *oper = &eons->pnn_list[record-1];
+
+ name = ber_tlv_find_by_tag(tlv, 0x43, length, &namelength);
+
+ if (!name || !namelength)
+ return;
+
+ oper->longname = sim_network_name_parse(name, namelength,
+ &oper->long_ci);
+
+ name = ber_tlv_find_by_tag(tlv, 0x45, length, &namelength);
+
+ if (name && namelength)
+ oper->shortname = sim_network_name_parse(name, namelength,
+ &oper->short_ci);
+
+ name = ber_tlv_find_by_tag(tlv, 0x80, length, &namelength);
+
+ if (name && namelength)
+ oper->info = sim_string_to_utf8(name, namelength);
+
+ eons->pnn_valid = TRUE;
+}
+
+static struct opl_operator *opl_operator_alloc(const guint8 *record)
+{
+ struct opl_operator *oper = g_new0(struct opl_operator, 1);
+
+ parse_mcc_mnc(record, oper->mcc, oper->mnc);
+ 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];
+
+ return oper;
+}
+
+void sim_eons_add_opl_record(struct sim_eons *eons,
+ const guint8 *tlv, int length)
+{
+ struct opl_operator *oper;
+
+ oper = opl_operator_alloc(tlv);
+
+ if (oper->id > eons->pnn_max) {
+ g_free(oper);
+ return;
+ }
+
+ eons->opl_list = g_slist_prepend(eons->opl_list, oper);
+}
+
+void sim_eons_optimize(struct sim_eons *eons)
+{
+ eons->opl_list = g_slist_reverse(eons->opl_list);
+}
+
+void sim_eons_free(struct sim_eons *eons)
+{
+ int i;
+
+ for (i = 0; i < eons->pnn_max; i++)
+ pnn_operator_free(eons->pnn_list + i);
+
+ g_free(eons->pnn_list);
+
+ g_slist_foreach(eons->opl_list, (GFunc)g_free, NULL);
+ g_slist_free(eons->opl_list);
+
+ g_free(eons);
+}
+
+static gint opl_operator_compare(gconstpointer a, gconstpointer b)
+{
+}
+
+const char *sim_eons_lookup(struct sim_eons *eons,
+ const char *mcc, const char *mnc, guint16 lac)
+{
+ GSList *l;
+ const struct opl_operator *opl;
+ int i;
+
+ for (l = eons->opl_list; l; l = l->next) {
+ opl = l->data;
+
+ for (i = 0; i < OFONO_MAX_MCC_LENGTH; i++)
+ if (mcc[i] != opl->mcc[i] &&
+ !(opl->mcc[i] == 'd' && mcc[i]))
+ continue;
+
+ for (i = 0; i < OFONO_MAX_MNC_LENGTH; i++)
+ if (mnc[i] != opl->mnc[i] &&
+ !(opl->mnc[i] == 'd' && mnc[i]))
+ continue;
+
+ if (opl->lac_tac_low == 0 && opl->lac_tac_high == 0xfffe)
+ break;
+
+ if ((lac >= opl->lac_tac_low) && (lac <= opl->lac_tac_high))
+ break;
+ }
+
+ if (!l)
+ return NULL;
+
+ opl = l->data;
+
+ /* 0 is not a valid record id */
+ if (opl->id == 0)
+ return NULL;
+
+ return eons->pnn_list[opl->id - 1].longname;
+}
diff --git a/src/simutil.h b/src/simutil.h
index eff2d99e..49677d72 100644
--- a/src/simutil.h
+++ b/src/simutil.h
@@ -30,25 +30,27 @@ enum sim_fileid {
#define SIM_EFSPN_DC_HOME_PLMN_BIT 0x1
#define SIM_EFSPN_DC_ROAMING_SPN_BIT 0x2
-struct sim_pnn_operator {
- char *longname;
- gboolean long_ci;
- char *shortname;
- gboolean short_ci;
- char *info;
-};
-
struct sim_spdi {
GSList *operators;
};
-void sim_pnn_operator_free(struct sim_pnn_operator *oper);
+struct sim_eons {
+ struct pnn_operator *pnn_list;
+ GSList *opl_list;
+ gboolean pnn_valid;
+ int pnn_max;
+};
-const guint8 *ber_tlv_find_by_tag(const guint8 *pdu, guint8 in_tag,
- int in_len, int *out_len);
-char *sim_network_name_parse(const unsigned char *buffer, int length,
- gboolean *add_ci);
-struct sim_pnn_operator *sim_pnn_operator_parse(const guint8 *tlv, int length);
+struct sim_eons *sim_eons_new(int pnn_records);
+void sim_eons_add_pnn_record(struct sim_eons *eons, int record,
+ const guint8 *tlv, int length);
+gboolean sim_eons_pnn_is_empty(struct sim_eons *eons);
+void sim_eons_add_opl_record(struct sim_eons *eons,
+ const guint8 *tlv, int length);
+void sim_eons_optimize(struct sim_eons *eons);
+const char *sim_eons_lookup(struct sim_eons *eons, const char *mcc,
+ const char *mnc, guint16 lac);
+void sim_eons_free(struct sim_eons *eons);
struct sim_spdi *sim_spdi_new(const guint8 *tlv, int length);
gboolean sim_spdi_lookup(struct sim_spdi *spdi,