mirror of git://git.sysmocom.de/ofono
Refactor OPL code
This commit is contained in:
parent
0fd5080630
commit
0fa6897213
|
@ -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
133
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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
209
src/simutil.c
209
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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue