3
* oFono - Open Source Telephony
5
* Copyright (C) 2008-2009 Intel Corporation. All rights reserved.
7
* This program is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License version 2 as
9
* published by the Free Software Foundation.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
36
struct sim_eons_operator_info *pnn_list;
42
struct spdi_operator {
43
char mcc[OFONO_MAX_MCC_LENGTH + 1];
44
char mnc[OFONO_MAX_MNC_LENGTH + 1];
48
char mcc[OFONO_MAX_MCC_LENGTH + 1];
49
char mnc[OFONO_MAX_MNC_LENGTH + 1];
55
/* Parse ASN.1 Basic Encoding Rules TLVs per ISO/IEC 7816 */
56
static const guint8 *ber_tlv_find_by_tag(const guint8 *pdu, guint8 in_tag,
57
int in_len, int *out_len)
61
const guint8 *end = pdu + in_len;
64
while (pdu < end && (*pdu == 0x00 || *pdu == 0xff))
71
while (pdu < end && (*pdu++ & 0x80))
76
for (len = 0; pdu + 1 < end && (*pdu & 0x80);
77
len = (len | (*pdu++ & 0x7f)) << 7)
84
if (tag == in_tag && pdu + len <= end) {
96
static char *sim_network_name_parse(const unsigned char *buffer, int length,
111
/* "The MS should add the letters for the Country's Initials and a
112
* separator (e.g. a space)" */
113
if (is_bit_set(dcs, 4))
116
switch (dcs & (7 << 4)) {
118
endp = memchr(buffer, 0xff, length);
120
length = endp - buffer;
121
ret = convert_gsm_to_utf8(buffer, length,
125
if ((length % 2) == 1) {
126
if (buffer[length - 1] != 0xff)
132
for (i = 0; i < length; i += 2)
133
if (buffer[i] == 0xff && buffer[i + 1] == 0xff)
136
ret = g_convert((const char *)buffer, length,
137
"UTF-8//TRANSLIT", "UCS-2BE",
148
static void parse_mcc_mnc(const guint8 *bcd, char *mcc, char *mnc)
150
static const char digit_lut[] = "0123456789*#abd\0";
153
digit = (bcd[0] >> 0) & 0xf;
154
*mcc++ = digit_lut[digit];
156
digit = (bcd[0] >> 4) & 0xf;
157
*mcc++ = digit_lut[digit];
159
digit = (bcd[1] >> 0) & 0xf;
160
*mcc++ = digit_lut[digit];
162
digit = (bcd[2] >> 0) & 0xf;
163
*mnc++ = digit_lut[digit];
165
digit = (bcd[2] >> 4) & 0xf;
166
*mnc++ = digit_lut[digit];
168
digit = (bcd[1] >> 4) & 0xf;
169
*mnc++ = digit_lut[digit];
172
static gint spdi_operator_compare(gconstpointer a, gconstpointer b)
174
const struct spdi_operator *opa = a;
175
const struct spdi_operator *opb = b;
176
gint r = strcmp(opa->mcc, opb->mcc);
181
return strcmp(opa->mnc, opb->mnc);
188
struct sim_spdi *sim_spdi_new(const guint8 *tlv, int length)
190
const guint8 *plmn_list;
191
struct sim_spdi *spdi;
192
struct spdi_operator *oper;
198
plmn_list = ber_tlv_find_by_tag(tlv, 0x80, length, &tlv_length);
203
spdi = g_new0(struct sim_spdi, 1);
205
for (tlv_length /= 3; tlv_length--; plmn_list += 3) {
206
if ((plmn_list[0] & plmn_list[1] & plmn_list[2]) == 0xff)
209
oper = g_new0(struct spdi_operator, 1);
211
parse_mcc_mnc(plmn_list, oper->mcc, oper->mnc);
212
spdi->operators = g_slist_insert_sorted(spdi->operators, oper,
213
spdi_operator_compare);
219
gboolean sim_spdi_lookup(struct sim_spdi *spdi,
220
const char *mcc, const char *mnc)
222
struct spdi_operator spdi_op;
224
g_strlcpy(spdi_op.mcc, mcc, sizeof(spdi_op.mcc));
225
g_strlcpy(spdi_op.mnc, mnc, sizeof(spdi_op.mnc));
227
return g_slist_find_custom(spdi->operators, &spdi_op,
228
spdi_operator_compare) != NULL;
231
void sim_spdi_free(struct sim_spdi *spdi)
233
g_slist_foreach(spdi->operators, (GFunc)g_free, NULL);
234
g_slist_free(spdi->operators);
238
static void pnn_operator_free(struct sim_eons_operator_info *oper)
241
g_free(oper->shortname);
242
g_free(oper->longname);
245
struct sim_eons *sim_eons_new(int pnn_records)
247
struct sim_eons *eons = g_new0(struct sim_eons, 1);
249
eons->pnn_list = g_new0(struct sim_eons_operator_info, pnn_records);
250
eons->pnn_max = pnn_records;
255
gboolean sim_eons_pnn_is_empty(struct sim_eons *eons)
257
return !eons->pnn_valid;
260
void sim_eons_add_pnn_record(struct sim_eons *eons, int record,
261
const guint8 *tlv, int length)
263
const unsigned char *name;
265
struct sim_eons_operator_info *oper = &eons->pnn_list[record-1];
267
name = ber_tlv_find_by_tag(tlv, 0x43, length, &namelength);
269
if (!name || !namelength)
272
oper->longname = sim_network_name_parse(name, namelength,
275
name = ber_tlv_find_by_tag(tlv, 0x45, length, &namelength);
277
if (name && namelength)
278
oper->shortname = sim_network_name_parse(name, namelength,
281
name = ber_tlv_find_by_tag(tlv, 0x80, length, &namelength);
283
if (name && namelength)
284
oper->info = sim_string_to_utf8(name, namelength);
286
eons->pnn_valid = TRUE;
289
static struct opl_operator *opl_operator_alloc(const guint8 *record)
291
struct opl_operator *oper = g_new0(struct opl_operator, 1);
293
parse_mcc_mnc(record, oper->mcc, oper->mnc);
296
oper->lac_tac_low = (record[0] << 8) | record[1];
298
oper->lac_tac_high = (record[0] << 8) | record[1];
301
oper->id = record[0];
306
void sim_eons_add_opl_record(struct sim_eons *eons,
307
const guint8 *contents, int length)
309
struct opl_operator *oper;
311
oper = opl_operator_alloc(contents);
313
if (oper->id > eons->pnn_max) {
318
eons->opl_list = g_slist_prepend(eons->opl_list, oper);
321
void sim_eons_optimize(struct sim_eons *eons)
323
eons->opl_list = g_slist_reverse(eons->opl_list);
326
void sim_eons_free(struct sim_eons *eons)
330
for (i = 0; i < eons->pnn_max; i++)
331
pnn_operator_free(eons->pnn_list + i);
333
g_free(eons->pnn_list);
335
g_slist_foreach(eons->opl_list, (GFunc)g_free, NULL);
336
g_slist_free(eons->opl_list);
341
static const struct sim_eons_operator_info *
342
sim_eons_lookup_common(struct sim_eons *eons,
343
const char *mcc, const char *mnc,
344
gboolean have_lac, guint16 lac)
347
const struct opl_operator *opl;
350
for (l = eons->opl_list; l; l = l->next) {
353
for (i = 0; i < OFONO_MAX_MCC_LENGTH; i++)
354
if (mcc[i] != opl->mcc[i] &&
355
!(opl->mcc[i] == 'b' && mcc[i]))
357
if (i < OFONO_MAX_MCC_LENGTH)
360
for (i = 0; i < OFONO_MAX_MNC_LENGTH; i++)
361
if (mnc[i] != opl->mnc[i] &&
362
!(opl->mnc[i] == 'b' && mnc[i]))
364
if (i < OFONO_MAX_MNC_LENGTH)
367
if (opl->lac_tac_low == 0 && opl->lac_tac_high == 0xfffe)
370
if (have_lac == FALSE)
373
if ((lac >= opl->lac_tac_low) && (lac <= opl->lac_tac_high))
382
/* 0 is not a valid record id */
386
return &eons->pnn_list[opl->id - 1];
389
const struct sim_eons_operator_info *sim_eons_lookup(struct sim_eons *eons,
393
return sim_eons_lookup_common(eons, mcc, mnc, FALSE, 0);
396
const struct sim_eons_operator_info *sim_eons_lookup_with_lac(
397
struct sim_eons *eons,
402
return sim_eons_lookup_common(eons, mcc, mnc, TRUE, lac);
405
gboolean sim_adn_parse(const unsigned char *data, int length,
406
struct ofono_phone_number *ph)
414
/* Skip Alpha-Identifier field */
417
number_len = *data++;
420
if (number_len > 11 || ton_npi == 0xff)
425
/* BCD coded, however the TON/NPI is given by the first byte */
426
number_len = (number_len - 1) * 2;
428
extract_bcd_number(data, number_len, ph->number);
433
void sim_adn_build(unsigned char *data, int length,
434
const struct ofono_phone_number *ph)
436
int number_len = strlen(ph->number);
438
/* Alpha-Identifier field */
440
memset(data, 0xff, length - 14);
444
number_len = (number_len + 1) / 2;
445
*data++ = number_len + 1;
447
/* Use given number type and 'Unknown' for Numbering Plan */
450
encode_bcd_number(ph->number, data);
451
memset(data + number_len, 0xff, 10 - number_len);