diff options
author | Andrzej Zaborowski <andrew.zaborowski@intel.com> | 2009-07-05 05:14:15 +0200 |
---|---|---|
committer | Denis Kenzior <denkenz@gmail.com> | 2009-07-14 15:45:03 -0500 |
commit | 98a4308b307cefd3b6daa60195d4e8d91ec7c709 (patch) | |
tree | e5057f07616b97e357684e88dc76870f598df929 /src | |
parent | feb36e2d5b0edb882e152c1fd958166e22eb0cbd (diff) | |
download | ofono-98a4308b307cefd3b6daa60195d4e8d91ec7c709.tar.bz2 |
Read EF-PNN, EF-OPL and override network names accordingly.
Diffstat (limited to 'src')
-rw-r--r-- | src/driver.h | 2 | ||||
-rw-r--r-- | src/network.c | 54 | ||||
-rw-r--r-- | src/sim.c | 313 | ||||
-rw-r--r-- | src/sim.h | 4 |
4 files changed, 344 insertions, 29 deletions
diff --git a/src/driver.h b/src/driver.h index f324c1c3..595c980b 100644 --- a/src/driver.h +++ b/src/driver.h @@ -84,6 +84,8 @@ struct ofono_network_operator { char mnc[OFONO_MAX_MNC_LENGTH + 1]; int status; int tech; + + const char *override_name; }; /* 27.007 Section 7.11 Call Forwarding */ diff --git a/src/network.c b/src/network.c index 402d2af1..92e52e5a 100644 --- a/src/network.c +++ b/src/network.c @@ -341,13 +341,15 @@ static char *get_operator_display_name(struct ofono_modem *modem) return name; } + plmn = netreg->current_operator->name; + if (netreg->current_operator->override_name) + plmn = netreg->current_operator->override_name; + if (!netreg->spname || strlen(netreg->spname) == 0) { - g_strlcpy(name, netreg->current_operator->name, len); + g_strlcpy(name, plmn, len); return name; } - plmn = netreg->current_operator->name; - home_or_spdi = (netreg->status == NETWORK_REGISTRATION_STATUS_REGISTERED) || ofono_operator_in_spdi(modem, netreg->current_operator); @@ -385,20 +387,22 @@ static void set_network_operator_name(struct ofono_modem *modem, strncpy(op->name, name, OFONO_MAX_OPERATOR_NAME_LENGTH); op->name[OFONO_MAX_OPERATOR_NAME_LENGTH] = '\0'; - path = network_operator_build_path(modem, op); + if (!op->override_name) { + path = network_operator_build_path(modem, op); - dbus_gsm_signal_property_changed(conn, path, - NETWORK_OPERATOR_INTERFACE, - "Name", DBUS_TYPE_STRING, - &name); + dbus_gsm_signal_property_changed(conn, path, + NETWORK_OPERATOR_INTERFACE, + "Name", DBUS_TYPE_STRING, + &name); - if (op == netreg->current_operator) { - operator = get_operator_display_name(modem); + if (op == netreg->current_operator) { + operator = get_operator_display_name(modem); - dbus_gsm_signal_property_changed(conn, modem->path, - NETWORK_REGISTRATION_INTERFACE, - "Operator", DBUS_TYPE_STRING, - &operator); + dbus_gsm_signal_property_changed(conn, modem->path, + NETWORK_REGISTRATION_INTERFACE, + "Operator", DBUS_TYPE_STRING, + &operator); + } } } @@ -415,6 +419,9 @@ static DBusMessage *network_operator_get_properties(DBusConnection *conn, const char *status = network_operator_status_to_string(op->operator->status); + if (op->operator->override_name) + name = op->operator->override_name; + reply = dbus_message_new_method_return(msg); if (!reply) return NULL; @@ -854,6 +861,13 @@ void ofono_network_registration_notify(struct ofono_modem *modem, int status, } } +static void network_operator_name_override(struct ofono_modem *modem, + struct ofono_network_operator *op) +{ + op->override_name = + ofono_operator_name_sim_override(modem, op->mcc, op->mnc); +} + static void operator_list_callback(const struct ofono_error *error, int total, const struct ofono_network_operator *list, void *data) @@ -892,11 +906,12 @@ static void operator_list_callback(const struct ofono_error *error, int total, } else { /* New operator */ struct ofono_network_operator *op = - g_try_new0(struct ofono_network_operator, 1); + g_memdup(&list[i], + sizeof(struct ofono_network_operator)); if (!op) continue; - memcpy(op, &list[i], sizeof(struct ofono_network_operator)); + network_operator_name_override(modem, op); if (network_operator_dbus_register(modem, op)) { n = g_slist_prepend(n, op); @@ -970,15 +985,14 @@ static void current_operator_callback(const struct ofono_error *error, if (current) { netreg->current_operator = - g_try_new0(struct ofono_network_operator, 1); - + g_memdup(current, + sizeof(struct ofono_network_operator)); if (!netreg->current_operator) { ofono_error("Unable to allocate current operator"); return; } - memcpy(netreg->current_operator, current, - sizeof(struct ofono_network_operator)); + network_operator_name_override(modem, netreg->current_operator); netreg->current_operator->status = OPERATOR_STATUS_CURRENT; @@ -58,6 +58,16 @@ struct sim_manager_data { int own_numbers_num; int own_numbers_size; int own_numbers_current; + + GSList *opl; + int opl_num; + int opl_size; + int opl_current; + + struct pnn_operator *pnn; + int pnn_num; + int pnn_size; + int pnn_current; }; static char **get_own_numbers(GSList *own_numbers) @@ -120,6 +130,11 @@ static const guint8 *ber_tlv_find_by_tag(const guint8 *pdu, guint8 in_tag, return NULL; } +struct pnn_operator { + char *longname; + char *shortname; +}; + static struct sim_manager_data *sim_manager_create() { return g_try_new0(struct sim_manager_data, 1); @@ -129,6 +144,7 @@ static void sim_manager_destroy(gpointer userdata) { struct ofono_modem *modem = userdata; struct sim_manager_data *data = modem->sim_manager; + int i; if (data->imsi) { g_free(data->imsi); @@ -151,6 +167,24 @@ static void sim_manager_destroy(gpointer userdata) g_slist_free(data->spdi); data->spdi = NULL; } + + 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; + } } static DBusMessage *sim_get_properties(DBusConnection *conn, @@ -199,9 +233,42 @@ static GDBusMethodTable sim_manager_methods[] = { static GDBusSignalTable sim_manager_signals[] = { { } }; +static char *network_name_parse(const unsigned char *buffer, int length) +{ + unsigned char *endp; + unsigned int dcs; + + if (length < 1) + return NULL; + + dcs = *buffer ++; + length --; + + if (dcs & (1 << 3)) { + /* TODO: "The MS should add the letters for the Country's + * Initials and a separator (e.g. a space)" */ + } + + 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: + return convert_ucs2_to_utf8(buffer, length, + NULL, NULL, 0xffff); + } + + return NULL; +} + enum sim_fileids { SIM_EFMSISDN_FILEID = 0x6f40, SIM_EFSPN_FILEID = 0x6f46, + SIM_EFPNN_FILEID = 0x6fc5, + SIM_EFOPL_FILEID = 0x6fc6, SIM_EFSPDI_FILEID = 0x6fcd, }; @@ -384,16 +451,15 @@ static gboolean sim_retrieve_own_number(void *user_data) return FALSE; } -struct spdi_operator { +struct sim_operator { char mcc[OFONO_MAX_MCC_LENGTH + 1]; char mnc[OFONO_MAX_MNC_LENGTH + 1]; }; -static struct spdi_operator *spdi_operator_alloc(const guint8 *bcd) +static void parse_mcc_mnc(struct sim_operator *oper, const guint8 *bcd) { - struct spdi_operator *spdi = g_new0(struct spdi_operator, 1); - char *mcc = spdi->mcc; - char *mnc = spdi->mnc; + char *mcc = oper->mcc; + char *mnc = oper->mnc; guint8 digit; digit = (bcd[0] >> 0) & 0xf; @@ -414,14 +480,21 @@ static struct spdi_operator *spdi_operator_alloc(const guint8 *bcd) digit = (bcd[1] >> 4) & 0xf; if (digit != 0xf) *mnc ++ = '0' + digit; +} + +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); return spdi; } static gint spdi_operator_compare(gconstpointer a, gconstpointer b) { - const struct spdi_operator *opa = a; - const struct spdi_operator *opb = b; + const struct sim_operator *opa = a; + const struct sim_operator *opb = b; gint r; if ((r = strcmp(opa->mcc, opb->mcc))) @@ -434,7 +507,7 @@ gboolean ofono_operator_in_spdi(struct ofono_modem *modem, const struct ofono_network_operator *op) { struct sim_manager_data *sim = modem->sim_manager; - struct spdi_operator spdi_op; + struct sim_operator spdi_op; if (!sim) return FALSE; @@ -469,7 +542,7 @@ static void sim_spdi_read_cb(const struct ofono_error *error, continue; sim->spdi = g_slist_insert_sorted(sim->spdi, - spdi_operator_alloc(plmn_list), + sim_operator_alloc(plmn_list), spdi_operator_compare); } @@ -504,6 +577,225 @@ static gboolean sim_retrieve_spdi(void *user_data) return FALSE; } +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; +} + static void initialize_sim_manager(struct ofono_modem *modem) { DBusConnection *conn = dbus_gsm_connection(); @@ -536,6 +828,9 @@ static void initialize_sim_manager(struct ofono_modem *modem) if (modem->sim_manager->ops->read_file_transparent) g_timeout_add(0, sim_retrieve_spdi, modem); + + if (modem->sim_manager->ops->read_file_linear) + g_timeout_add(0, sim_retrieve_pnn, modem); } int ofono_sim_manager_register(struct ofono_modem *modem, @@ -31,3 +31,7 @@ int ofono_spn_update_notify_unregister(struct ofono_modem *modem, gboolean ofono_operator_in_spdi(struct ofono_modem *modem, const struct ofono_network_operator *op); + +const char *ofono_operator_name_sim_override(struct ofono_modem *modem, + const char *mcc, + const char *mnc); |