Refactor OPL code

This commit is contained in:
Denis Kenzior 2009-07-13 18:30:17 -05:00
parent 0fd5080630
commit 0fa6897213
5 changed files with 221 additions and 202 deletions

View File

@ -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,

133
src/sim.c
View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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,