From 8cbb0252b4db86e8a218862e9105e8e478dd2ef9 Mon Sep 17 00:00:00 2001 From: Aki Niemi Date: Thu, 16 Jun 2011 14:47:50 +0300 Subject: [PATCH] isimodem: Add baseline for UICC driver --- drivers/isimodem/isimodem.c | 2 + drivers/isimodem/isimodem.h | 3 + drivers/isimodem/uicc.c | 492 ++++++++++++++++++++++++++++++++++++ drivers/isimodem/uicc.h | 6 + 4 files changed, 503 insertions(+) create mode 100644 drivers/isimodem/uicc.c diff --git a/drivers/isimodem/isimodem.c b/drivers/isimodem/isimodem.c index 1e3d3f34..094a8c6e 100644 --- a/drivers/isimodem/isimodem.c +++ b/drivers/isimodem/isimodem.c @@ -53,6 +53,7 @@ static int isimodem_init(void) isi_gprs_init(); isi_gprs_context_init(); isi_audio_settings_init(); + isi_uicc_init(); return 0; } @@ -75,6 +76,7 @@ static void isimodem_exit(void) isi_gprs_exit(); isi_gprs_context_exit(); isi_audio_settings_exit(); + isi_uicc_exit(); } OFONO_PLUGIN_DEFINE(isimodem, "PhoNet / ISI modem driver", VERSION, diff --git a/drivers/isimodem/isimodem.h b/drivers/isimodem/isimodem.h index f5e1657d..9593f609 100644 --- a/drivers/isimodem/isimodem.h +++ b/drivers/isimodem/isimodem.h @@ -66,3 +66,6 @@ extern void isi_gprs_context_exit(void); extern void isi_audio_settings_init(void); extern void isi_audio_settings_exit(void); + +extern void isi_uicc_init(void); +extern void isi_uicc_exit(void); diff --git a/drivers/isimodem/uicc.c b/drivers/isimodem/uicc.c new file mode 100644 index 00000000..2b43793a --- /dev/null +++ b/drivers/isimodem/uicc.c @@ -0,0 +1,492 @@ +/* + * + * oFono - Open Source Telephony + * + * Copyright (C) ST-Ericsson SA 2011. + * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#define _GNU_SOURCE +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include + +#include "simutil.h" +#include "isimodem.h" +#include "isiutil.h" +#include "sim.h" +#include "uicc.h" +#include "debug.h" + +#define USIM_APP_DEDICATED_FILE 0x7FFF +#define MAX_SIM_APPS 8 + +enum uicc_flag { + UICC_FLAG_APP_STARTED = 1 << 0, + UICC_FLAG_PIN_STATE_RECEIVED = 1 << 1, + UICC_FLAG_PASSWD_REQUIRED = 1 << 2, +}; + +struct sim_data { + GIsiClient *client; + unsigned flags; + int app_id; + int app_type; + uint8_t client_id; +}; + +static GHashTable *g_modems; + +struct file_info { + int fileid; + int length; + int structure; + int record_length; + uint8_t access[3]; + uint8_t file_status; +}; + +static const struct file_info static_file_info[] = { + { SIM_EFSPN_FILEID, 17, 0, 0, { 0x0e, 0xff, 0xee }, 1 }, + { SIM_EF_ICCID_FILEID, 10, 0, 10, { 0x0f, 0xff, 0xee }, 1 }, + { SIM_EFPL_FILEID, 1, 0, 1, { 0x0f, 0xff, 0xff }, 1 }, + { SIM_EFLI_FILEID, 1, 0, 1, { 0x0f, 0xff, 0xff }, 1 }, + { SIM_EFMSISDN_FILEID, 28, 1, 28, { 0x01, 0xff, 0xee }, 1 }, + { SIM_EFAD_FILEID, 20, 0, 20, { 0x0e, 0xff, 0xee }, 1 }, + { SIM_EFPHASE_FILEID, 1, 0, 1, { 0x0e, 0xff, 0xee }, 1 }, + { SIM_EFPNN_FILEID, 4 * 18, 1, 18, { 0x0e, 0xff, 0xee }, 1 }, + { SIM_EFOPL_FILEID, 4 * 24, 1, 24, { 0x0e, 0xff, 0xee }, 1 }, + { SIM_EFMBI_FILEID, 5, 1, 5, { 0x0e, 0xff, 0xee }, 1 }, + { SIM_EFMWIS_FILEID, 6, 1, 6, { 0x01, 0xff, 0xee }, 1 }, + { SIM_EFSPDI_FILEID, 64, 0, 64, { 0x0e, 0xff, 0xee }, 1 }, + { SIM_EFECC_FILEID, 5 * 3, 0, 3, { 0x0e, 0xff, 0xee }, 1 }, + { SIM_EFCBMIR_FILEID, 8 * 4, 0, 4, { 0x01, 0xff, 0xee }, 1 }, + { SIM_EFCBMI_FILEID, 8 * 2, 0, 2, { 0x01, 0xff, 0xee }, 1 }, + { SIM_EFCBMID_FILEID, 8 * 2, 0, 2, { 0x01, 0xff, 0x11 }, 1 }, + { SIM_EFSMSP_FILEID, 56, 1, 56, { 0x01, 0xff, 0xee }, 1 }, + { SIM_EFIMSI_FILEID, 9, 0, 9, { 0x0e, 0xff, 0xee }, 1 }, +}; + +static gboolean check_resp(const GIsiMessage *msg, uint8_t msgid, uint8_t service) +{ + uint8_t type; + uint8_t cause; + + if (g_isi_msg_error(msg) < 0) { + DBG("Error: %s", g_isi_msg_strerror(msg)); + return FALSE; + } + + if (g_isi_msg_id(msg) != msgid) { + DBG("Unexpected msg: %s", + sim_message_id_name(g_isi_msg_id(msg))); + return FALSE; + } + + if (!g_isi_msg_data_get_byte(msg, 1, &cause) || + cause != UICC_STATUS_OK) { + DBG("Request failed: %s", uicc_status_name(cause)); + return FALSE; + } + + if (!g_isi_msg_data_get_byte(msg, 0, &type) || type != service) { + DBG("Unexpected service: 0x%02X (0x%02X)", type, service); + return FALSE; + } + return TRUE; +} + +static void uicc_read_file_info(struct ofono_sim *sim, int fileid, + ofono_sim_file_info_cb_t cb, void *data) +{ + DBG("Not implemented"); + CALLBACK_WITH_FAILURE(cb, -1, -1, -1, NULL, 0, data); +} + +static void uicc_read_file_transparent(struct ofono_sim *sim, int fileid, + int start, int length, + ofono_sim_read_cb_t cb, void *data) +{ + DBG("Not implemented"); + CALLBACK_WITH_FAILURE(cb, NULL, 0, data); +} + +static void uicc_read_file_linear(struct ofono_sim *sim, int fileid, int record, + int rec_length, ofono_sim_read_cb_t cb, + void *data) +{ + DBG("Not implemented"); + CALLBACK_WITH_FAILURE(cb, NULL, 0, data); +} + +static void uicc_read_file_cyclic(struct ofono_sim *sim, int fileid, + int record, int length, + ofono_sim_read_cb_t cb, void *data) +{ + DBG("Not implemented"); + CALLBACK_WITH_FAILURE(cb, NULL, 0, data); +} + +static void uicc_write_file_transparent(struct ofono_sim *sim, int fileid, + int start, int length, + const unsigned char *value, + ofono_sim_write_cb_t cb, void *data) +{ + DBG("Not implemented"); + CALLBACK_WITH_FAILURE(cb, data); +} + +static void uicc_write_file_linear(struct ofono_sim *sim, int fileid, int record, + int length, const unsigned char *value, + ofono_sim_write_cb_t cb, void *data) +{ + DBG("Not implemented"); + CALLBACK_WITH_FAILURE(cb, data); +} + +static void uicc_write_file_cyclic(struct ofono_sim *sim, int fileid, int length, + const unsigned char *value, + ofono_sim_write_cb_t cb, void *data) +{ + DBG("Not implemented"); + CALLBACK_WITH_FAILURE(cb, data); +} + +static void uicc_read_imsi(struct ofono_sim *sim, + ofono_sim_imsi_cb_t cb, void *data) +{ + DBG("Not implemented"); + CALLBACK_WITH_FAILURE(cb, NULL, data); +} + +static void uicc_query_passwd_state(struct ofono_sim *sim, + ofono_sim_passwd_cb_t cb, void *data) +{ + DBG("Not implemented"); + CALLBACK_WITH_FAILURE(cb, -1, data); +} + +static void uicc_send_passwd(struct ofono_sim *sim, const char *passwd, + ofono_sim_lock_unlock_cb_t cb, void *data) +{ + DBG("Not implemented"); + CALLBACK_WITH_FAILURE(cb, data); +} + +static void uicc_query_pin_retries(struct ofono_sim *sim, + ofono_sim_pin_retries_cb_t cb, + void *data) +{ + DBG("Not implemented"); + CALLBACK_WITH_FAILURE(cb, NULL, data); +} + +static void uicc_reset_passwd(struct ofono_sim *sim, const char *puk, + const char *passwd, ofono_sim_lock_unlock_cb_t cb, + void *data) +{ + DBG("Not implemented"); + CALLBACK_WITH_FAILURE(cb, data); +} + +static void uicc_change_passwd(struct ofono_sim *sim, + enum ofono_sim_password_type passwd_type, + const char *old, const char *new, + ofono_sim_lock_unlock_cb_t cb, void *data) +{ + DBG("Not implemented"); + CALLBACK_WITH_FAILURE(cb, data); +} + +static void uicc_lock(struct ofono_sim *sim, enum ofono_sim_password_type type, + int enable, const char *passwd, + ofono_sim_lock_unlock_cb_t cb, void *data) +{ + DBG("Not implemented"); + CALLBACK_WITH_FAILURE(cb, data); +} + +static void uicc_query_locked(struct ofono_sim *sim, + enum ofono_sim_password_type type, + ofono_sim_locked_cb_t cb, void *data) +{ + DBG("Not implemented"); + CALLBACK_WITH_FAILURE(cb, -1, data); +} + +static void pin_ind_cb(const GIsiMessage *msg, void *data) +{ + DBG("%s", uicc_message_id_name(g_isi_msg_id(msg))); +} + +static void card_ind_cb(const GIsiMessage *msg, void *data) +{ + DBG("%s", uicc_message_id_name(g_isi_msg_id(msg))); +} + +static void uicc_ind_cb(const GIsiMessage *msg, void *data) +{ + DBG("%s", uicc_message_id_name(g_isi_msg_id(msg))); +} + +static void app_ind_cb(const GIsiMessage *msg, void *data) +{ + DBG("%s", uicc_message_id_name(g_isi_msg_id(msg))); +} + +static gboolean decode_data_object(uint8_t *buf, size_t len) +{ + DBG("template=0x%02X length=%u bytes AID=0x%02X", + buf[0], buf[1], buf[2]); + + return TRUE; +} + +struct data_object { + uint16_t filler; + uint8_t type; + uint8_t id; + uint8_t status; + uint8_t len; +}; + +static void uicc_app_list_resp(const GIsiMessage *msg, void *data) +{ + struct ofono_sim *sim = data; + struct sim_data *sd = ofono_sim_get_data(sim); + GIsiSubBlockIter iter; + uint8_t sb; + struct data_object *app; + size_t len = sizeof(struct data_object); + uint8_t *obj; + + if (!check_resp(msg, UICC_APPLICATION_RESP, UICC_APPL_LIST)) + goto fail; + + if (!g_isi_msg_data_get_byte(msg, 5, &sb)) + goto fail; + + for (g_isi_sb_iter_init_full(&iter, msg, 6, TRUE, sb); + g_isi_sb_iter_is_valid(&iter); + g_isi_sb_iter_next(&iter)) { + + if (g_isi_sb_iter_get_id(&iter) != UICC_SB_APPL_DATA_OBJECT) + continue; + + if (!g_isi_sb_iter_get_struct(&iter, (void **) &app, len, 4)) + goto fail; + + if (!g_isi_sb_iter_get_struct(&iter, (void **) &obj, app->len, + 4 + len)) + goto fail; + + DBG("type=0x%02X id=0x%02X status=0x%02X length=%u bytes", + app->type, app->id, app->status, app->len); + + decode_data_object(obj, app->len); + } + + ofono_sim_register(sim); + ofono_sim_inserted_notify(sim, TRUE); + return; + +fail: + DBG("Decoding application list failed"); + + g_isi_client_destroy(sd->client); + sd->client = NULL; + + ofono_sim_remove(sim); +} + +static void uicc_status_resp(const GIsiMessage *msg, void *data) +{ + struct ofono_sim *sim = data; + struct sim_data *sd = ofono_sim_get_data(sim); + + const uint8_t req[] = { + UICC_APPLICATION_REQ, + UICC_APPL_LIST, + 0, /* Number of subblocks */ + }; + + if (!check_resp(msg, UICC_RESP, UICC_STATUS_GET)) + goto fail; + + DBG(""); + + if (g_isi_msg_error(msg) < 0) + goto fail; + + if (g_isi_client_send(sd->client, req, sizeof(req), uicc_app_list_resp, + data, NULL)) + return; + +fail: + g_isi_client_destroy(sd->client); + sd->client = NULL; + + ofono_sim_remove(sim); +} + +static void uicc_reachable_cb(const GIsiMessage *msg, void *data) +{ + struct ofono_sim *sim = data; + struct sim_data *sd = ofono_sim_get_data(sim); + + const uint8_t req[] = { + UICC_REQ, UICC_STATUS_GET, + }; + + ISI_RESOURCE_DBG(msg); + + if (g_isi_msg_error(msg) < 0) + goto fail; + + if (g_isi_client_send(sd->client, req, sizeof(req), uicc_status_resp, + data, NULL)) + return; + +fail: + g_isi_client_destroy(sd->client); + sd->client = NULL; + + ofono_sim_remove(sim); +} + +static int uicc_sim_probe(struct ofono_sim *sim, unsigned int vendor, + void *user) +{ + GIsiModem *modem = user; + struct sim_data *sd; + + sd = g_try_new0(struct sim_data, 1); + if (sd == NULL) + return -ENOMEM; + + sd->client = g_isi_client_create(modem, PN_UICC); + if (sd->client == NULL) { + g_free(sd); + return -ENOMEM; + } + + g_hash_table_insert(g_modems, g_isi_client_modem(sd->client), sim); + + ofono_sim_set_data(sim, sd); + + g_isi_client_ind_subscribe(sd->client, UICC_IND, uicc_ind_cb, sim); + g_isi_client_ind_subscribe(sd->client, UICC_CARD_IND, card_ind_cb, + sim); + g_isi_client_ind_subscribe(sd->client, UICC_CARD_READER_IND, + card_ind_cb, sim); + g_isi_client_ind_subscribe(sd->client, UICC_PIN_IND, pin_ind_cb, sim); + g_isi_client_ind_subscribe(sd->client, UICC_APPLICATION_IND, + app_ind_cb, sim); + + g_isi_client_verify(sd->client, uicc_reachable_cb, sim, NULL); + + return 0; +} + +static void uicc_sim_remove(struct ofono_sim *sim) +{ + struct sim_data *data = ofono_sim_get_data(sim); + + ofono_sim_set_data(sim, NULL); + + if (data == NULL) + return; + + g_hash_table_remove(g_modems, g_isi_client_modem(data->client)); + + g_isi_client_destroy(data->client); + g_free(data); +} + +static struct ofono_sim_driver driver = { + .name = "wgmodem2.5", + .probe = uicc_sim_probe, + .remove = uicc_sim_remove, + .read_file_info = uicc_read_file_info, + .read_file_transparent = uicc_read_file_transparent, + .read_file_linear = uicc_read_file_linear, + .read_file_cyclic = uicc_read_file_cyclic, + .write_file_transparent = uicc_write_file_transparent, + .write_file_linear = uicc_write_file_linear, + .write_file_cyclic = uicc_write_file_cyclic, + .read_imsi = uicc_read_imsi, + .query_passwd_state = uicc_query_passwd_state, + .send_passwd = uicc_send_passwd, + .query_pin_retries = uicc_query_pin_retries, + .reset_passwd = uicc_reset_passwd, + .change_passwd = uicc_change_passwd, + .lock = uicc_lock, + .query_locked = uicc_query_locked, +}; + +void isi_uicc_init(void) +{ + g_modems = g_hash_table_new(g_direct_hash, g_direct_equal); + ofono_sim_driver_register(&driver); +} + +void isi_uicc_exit(void) +{ + g_hash_table_destroy(g_modems); + ofono_sim_driver_unregister(&driver); +} + +gboolean isi_uicc_properties(GIsiModem *modem, int *app_id, int *app_type, + int *client_id) +{ + struct ofono_sim *sim; + struct sim_data *sd; + + sim = g_hash_table_lookup(g_modems, modem); + if (sim == NULL) + return FALSE; + + sd = ofono_sim_get_data(sim); + if (sd == NULL) + return FALSE; + + if (app_id != NULL) + *app_id = sd->app_id; + + if (app_type != NULL) + *app_type = sd->app_type; + + if (client_id != NULL) + *client_id = sd->client_id; + + return TRUE; +} diff --git a/drivers/isimodem/uicc.h b/drivers/isimodem/uicc.h index ad698096..4b613ebb 100644 --- a/drivers/isimodem/uicc.h +++ b/drivers/isimodem/uicc.h @@ -26,7 +26,10 @@ extern "C" { #endif +#include + #include +#include #define PN_UICC 0x8C @@ -287,6 +290,9 @@ enum uicc_app_param { UICC_APP_PARAM_URL = 0x5F50, }; +gboolean isi_uicc_properties(GIsiModem *modem, int *app_id, int *app_type, + int *client_id); + #ifdef __cplusplus }; #endif