diff options
Diffstat (limited to 'drivers/rilmodem/sim.c')
-rw-r--r-- | drivers/rilmodem/sim.c | 1200 |
1 files changed, 1200 insertions, 0 deletions
diff --git a/drivers/rilmodem/sim.c b/drivers/rilmodem/sim.c new file mode 100644 index 00000000..178cb97b --- /dev/null +++ b/drivers/rilmodem/sim.c @@ -0,0 +1,1200 @@ +/* + * + * oFono - Open Source Telephony - RIL Modem Support + * + * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. + * Copyright (C) 2013 Canonical, Ltd. All rights reserved. + * Copyright (C) 2015 Ratchanan Srirattanamet. + * + * 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 <config.h> +#endif + +#define _GNU_SOURCE +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> + +#include <glib.h> + +#include <ofono/log.h> +#include <ofono/modem.h> +#include <ofono/sim.h> + +#include "ofono.h" + +#include "simutil.h" +#include "util.h" + +#include "gril.h" +#include "grilutil.h" +#include "parcel.h" +#include "ril_constants.h" +#include "rilmodem.h" + +#include "grilreply.h" +#include "grilrequest.h" +#include "grilunsol.h" + +#include "drivers/infineonmodem/infineon_constants.h" + +/* Number of passwords in EPINC response */ +#define MTK_EPINC_NUM_PASSWD 4 + +/* + * Based on ../drivers/atmodem/sim.c. + * + * TODO: + * 1. Defines constants for hex literals + * 2. Document P1-P3 usage (+CSRM) + */ + +/* + * TODO: CDMA/IMS + * + * This code currently only grabs the AID/application ID from + * the gsm_umts application on the SIM card. This code will + * need to be modified for CDMA support, and possibly IMS-based + * applications. In this case, app_id should be changed to an + * array or HashTable of app_status structures. + * + * The same applies to the app_type. + */ + +static void ril_pin_change_state(struct ofono_sim *sim, + enum ofono_sim_password_type passwd_type, + int enable, const char *passwd, + ofono_sim_lock_unlock_cb_t cb, void *data); + +struct sim_data { + GRil *ril; + enum ofono_ril_vendor vendor; + gchar *aid_str; + guint app_type; + gchar *app_str; + guint app_index; + enum ofono_sim_password_type passwd_type; + int retries[OFONO_SIM_PASSWORD_INVALID]; + enum ofono_sim_password_type passwd_state; + struct ofono_modem *modem; + ofono_sim_state_event_cb_t ril_state_watch; + ofono_bool_t unlock_pending; +}; + +struct change_state_cbd { + struct ofono_sim *sim; + enum ofono_sim_password_type passwd_type; + int enable; + const char *passwd; + ofono_sim_lock_unlock_cb_t cb; + void *data; +}; + +static void send_get_sim_status(struct ofono_sim *sim); + +static void ril_file_info_cb(struct ril_msg *message, gpointer user_data) +{ + struct cb_data *cbd = user_data; + ofono_sim_file_info_cb_t cb = cbd->cb; + struct sim_data *sd = cbd->user; + struct ofono_error error; + gboolean ok = FALSE; + int sw1, sw2; + int flen = 0, rlen = 0, str = 0; + guchar access[3] = { 0x00, 0x00, 0x00 }; + guchar file_status = EF_STATUS_VALID; + struct reply_sim_io *reply = NULL; + + /* Error, and no data */ + if (message->error != RIL_E_SUCCESS && message->buf_len == 0) { + ofono_error("%s: Reply failure: %s", __func__, + ril_error_to_string(message->error)); + decode_ril_error(&error, "FAIL"); + goto error; + } + + /* + * The reply can have event data even when message->error is not zero + * in mako. + */ + reply = g_ril_reply_parse_sim_io(sd->ril, message); + if (reply == NULL) { + decode_ril_error(&error, "FAIL"); + goto error; + } + + sw1 = reply->sw1; + sw2 = reply->sw2; + + /* + * SIM app file not found || USIM app file not found + * See 3gpp TS 51.011, 9.4.4, and ETSI TS 102 221, 10.2.1.5.3 + * This can happen with result SUCCESS (maguro) or GENERIC_FAILURE + * (mako) + */ + if ((sw1 == 0x94 && sw2 == 0x04) || (sw1 == 0x6A && sw2 == 0x82)) { + DBG("File not found. Error %s", + ril_error_to_string(message->error)); + decode_ril_error(&error, "FAIL"); + goto error; + } + + if (message->error == RIL_E_SUCCESS) { + decode_ril_error(&error, "OK"); + } else { + ofono_error("%s: Reply failure: %s, %02x, %02x", __func__, + ril_error_to_string(message->error), sw1, sw2); + decode_ril_error(&error, "FAIL"); + goto error; + } + + if ((sw1 != 0x90 && sw1 != 0x91 && sw1 != 0x92 && sw1 != 0x9f) || + (sw1 == 0x90 && sw2 != 0x00)) { + ofono_error("Error reply, invalid values: sw1: %02x sw2: %02x", + sw1, sw2); + + /* TODO: fix decode_ril_error to take type & error */ + + error.type = OFONO_ERROR_TYPE_SIM; + error.error = (sw1 << 8) | sw2; + + goto error; + } + + if (reply->hex_len) { + if (reply->hex_response[0] == 0x62) { + ok = sim_parse_3g_get_response(reply->hex_response, + reply->hex_len, + &flen, &rlen, &str, + access, NULL); + } else { + ok = sim_parse_2g_get_response(reply->hex_response, + reply->hex_len, + &flen, &rlen, &str, + access, &file_status); + } + } + + if (!ok) { + ofono_error("%s: parse response failed", __func__); + decode_ril_error(&error, "FAIL"); + goto error; + } + + cb(&error, flen, str, rlen, access, file_status, cbd->data); + + g_ril_reply_free_sim_io(reply); + + return; + +error: + g_ril_reply_free_sim_io(reply); + + cb(&error, -1, -1, -1, NULL, EF_STATUS_INVALIDATED, cbd->data); +} + +static void ril_sim_read_info(struct ofono_sim *sim, int fileid, + const unsigned char *path, + unsigned int path_len, + ofono_sim_file_info_cb_t cb, void *data) +{ + struct sim_data *sd = ofono_sim_get_data(sim); + struct cb_data *cbd = cb_data_new(cb, data, sd); + struct parcel rilp; + struct req_sim_read_info req; + guint ret = 0; + + DBG("file %04x", fileid); + + req.app_type = sd->app_type; + req.aid_str = sd->aid_str; + req.fileid = fileid; + req.path = path; + req.path_len = path_len; + + if (!g_ril_request_sim_read_info(sd->ril, + &req, + &rilp)) { + ofono_error("Couldn't build SIM read info request"); + goto error; + } + + g_ril_append_print_buf(sd->ril, + "%s0,0,15,(null),pin2=(null),aid=%s)", + print_buf, + sd->aid_str); + + ret = g_ril_send(sd->ril, RIL_REQUEST_SIM_IO, &rilp, + ril_file_info_cb, cbd, g_free); + +error: + if (ret == 0) { + g_free(cbd); + CALLBACK_WITH_FAILURE(cb, -1, -1, -1, NULL, + EF_STATUS_INVALIDATED, data); + } +} + +static void ril_file_io_cb(struct ril_msg *message, gpointer user_data) +{ + struct cb_data *cbd = user_data; + ofono_sim_read_cb_t cb = cbd->cb; + struct sim_data *sd = cbd->user; + struct ofono_error error; + struct reply_sim_io *reply; + + if (message->error == RIL_E_SUCCESS) { + decode_ril_error(&error, "OK"); + } else { + ofono_error("RILD reply failure: %s", + ril_error_to_string(message->error)); + goto error; + } + + reply = g_ril_reply_parse_sim_io(sd->ril, message); + if (reply == NULL) { + ofono_error("Can't parse SIM IO response from RILD"); + goto error; + } + + if (reply->hex_len == 0) { + ofono_error("Null SIM IO response from RILD"); + g_ril_reply_free_sim_io(reply); + goto error; + } + + cb(&error, reply->hex_response, reply->hex_len, cbd->data); + + g_ril_reply_free_sim_io(reply); + + return; + +error: + decode_ril_error(&error, "FAIL"); + cb(&error, NULL, 0, cbd->data); +} + +static void ril_file_write_cb(struct ril_msg *message, gpointer user_data) +{ + struct cb_data *cbd = user_data; + ofono_sim_write_cb_t cb = cbd->cb; + struct sim_data *sd = cbd->user; + struct reply_sim_io *reply; + int sw1, sw2; + + if (message->error != RIL_E_SUCCESS) { + ofono_error("%s: RILD reply failure: %s", + __func__, ril_error_to_string(message->error)); + goto error; + } + + reply = g_ril_reply_parse_sim_io(sd->ril, message); + if (reply == NULL) { + ofono_error("%s: Can't parse SIM IO response", __func__); + goto error; + } + + sw1 = reply->sw1; + sw2 = reply->sw2; + + g_ril_reply_free_sim_io(reply); + + if ((sw1 != 0x90 && sw1 != 0x91 && sw1 != 0x92 && sw1 != 0x9f) || + (sw1 == 0x90 && sw2 != 0x00)) { + struct ofono_error error; + + ofono_error("%s: error sw1 %02x sw2 %02x", __func__, sw1, sw2); + + error.type = OFONO_ERROR_TYPE_SIM; + error.error = (sw1 << 8) | sw2; + + cb(&error, cbd->data); + + return; + } + + CALLBACK_WITH_SUCCESS(cb, cbd->data); + + return; + +error: + CALLBACK_WITH_FAILURE(cb, cbd->data); +} + +static void ril_sim_read_binary(struct ofono_sim *sim, int fileid, + int start, int length, + const unsigned char *path, + unsigned int path_len, + ofono_sim_read_cb_t cb, void *data) +{ + struct sim_data *sd = ofono_sim_get_data(sim); + struct cb_data *cbd = cb_data_new(cb, data, sd); + struct parcel rilp; + struct req_sim_read_binary req; + gint ret = 0; + + DBG("file %04x", fileid); + + req.app_type = sd->app_type; + req.aid_str = sd->aid_str; + req.fileid = fileid; + req.path = path; + req.path_len = path_len; + req.start = start; + req.length = length; + + if (!g_ril_request_sim_read_binary(sd->ril, + &req, + &rilp)) { + ofono_error("Couldn't build SIM read binary request"); + goto error; + } + + g_ril_append_print_buf(sd->ril, + "%s%d,%d,%d,(null),pin2=(null),aid=%s)", + print_buf, + (start >> 8), + (start & 0xff), + length, + sd->aid_str); + + ret = g_ril_send(sd->ril, RIL_REQUEST_SIM_IO, &rilp, + ril_file_io_cb, cbd, g_free); +error: + if (ret == 0) { + g_free(cbd); + CALLBACK_WITH_FAILURE(cb, NULL, 0, data); + } +} + +static void ril_sim_read_record(struct ofono_sim *sim, int fileid, + int record, int length, + const unsigned char *path, + unsigned int path_len, + ofono_sim_read_cb_t cb, void *data) +{ + struct sim_data *sd = ofono_sim_get_data(sim); + struct cb_data *cbd = cb_data_new(cb, data, sd); + struct parcel rilp; + struct req_sim_read_record req; + guint ret = 0; + + DBG("file %04x", fileid); + + req.app_type = sd->app_type; + req.aid_str = sd->aid_str; + req.fileid = fileid; + req.path = path; + req.path_len = path_len; + req.record = record; + req.length = length; + + if (!g_ril_request_sim_read_record(sd->ril, + &req, + &rilp)) { + ofono_error("Couldn't build SIM read record request"); + goto error; + } + + g_ril_append_print_buf(sd->ril, + "%s%d,%d,%d,(null),pin2=(null),aid=%s)", + print_buf, + record, + 4, + length, + sd->aid_str); + + ret = g_ril_send(sd->ril, RIL_REQUEST_SIM_IO, &rilp, + ril_file_io_cb, cbd, g_free); + +error: + if (ret == 0) { + g_free(cbd); + CALLBACK_WITH_FAILURE(cb, NULL, 0, data); + } +} + +static void ril_sim_update_binary(struct ofono_sim *sim, int fileid, + int start, int length, + const unsigned char *value, + const unsigned char *path, + unsigned int path_len, + ofono_sim_write_cb_t cb, void *data) +{ + struct sim_data *sd = ofono_sim_get_data(sim); + struct cb_data *cbd = cb_data_new(cb, data, sd); + struct parcel rilp; + struct req_sim_write_binary req; + guint ret = 0; + + DBG("file 0x%04x", fileid); + + req.app_type = sd->app_type; + req.aid_str = sd->aid_str; + req.fileid = fileid; + req.path = path; + req.path_len = path_len; + req.start = start; + req.length = length; + req.data = value; + + if (!g_ril_request_sim_write_binary(sd->ril, &req, &rilp)) { + ofono_error("%s: Couldn't build SIM write request", __func__); + goto error; + } + + ret = g_ril_send(sd->ril, RIL_REQUEST_SIM_IO, &rilp, + ril_file_write_cb, cbd, g_free); + +error: + if (ret == 0) { + g_free(cbd); + CALLBACK_WITH_FAILURE(cb, data); + } +} + +static void update_record(struct ofono_sim *sim, int fileid, + enum req_record_access_mode mode, + int record, int length, + const unsigned char *value, + const unsigned char *path, + unsigned int path_len, + ofono_sim_write_cb_t cb, void *data) +{ + struct sim_data *sd = ofono_sim_get_data(sim); + struct cb_data *cbd = cb_data_new(cb, data, sd); + struct parcel rilp; + struct req_sim_write_record req; + guint ret = 0; + + DBG("file 0x%04x", fileid); + + req.app_type = sd->app_type; + req.aid_str = sd->aid_str; + req.fileid = fileid; + req.path = path; + req.path_len = path_len; + req.mode = mode; + req.record = record; + req.length = length; + req.data = value; + + if (!g_ril_request_sim_write_record(sd->ril, &req, &rilp)) { + ofono_error("%s: Couldn't build SIM write request", __func__); + goto error; + } + + ret = g_ril_send(sd->ril, RIL_REQUEST_SIM_IO, &rilp, + ril_file_write_cb, cbd, g_free); + +error: + if (ret == 0) { + g_free(cbd); + CALLBACK_WITH_FAILURE(cb, data); + } +} + +static void ril_sim_update_record(struct ofono_sim *sim, int fileid, + int record, int length, + const unsigned char *value, + const unsigned char *path, + unsigned int path_len, + ofono_sim_write_cb_t cb, void *data) +{ + update_record(sim, fileid, GRIL_REC_ACCESS_MODE_ABSOLUTE, record, + length, value, path, path_len, cb, data); +} + +static void ril_sim_update_cyclic(struct ofono_sim *sim, int fileid, + int length, const unsigned char *value, + const unsigned char *path, + unsigned int path_len, + ofono_sim_write_cb_t cb, void *data) +{ + /* Only mode valid for cyclic files is PREVIOUS */ + update_record(sim, fileid, GRIL_REC_ACCESS_MODE_PREVIOUS, 0, + length, value, path, path_len, cb, data); +} + +static void ril_imsi_cb(struct ril_msg *message, gpointer user_data) +{ + struct cb_data *cbd = user_data; + ofono_sim_imsi_cb_t cb = cbd->cb; + struct sim_data *sd = cbd->user; + struct ofono_error error; + gchar *imsi; + + if (message->error == RIL_E_SUCCESS) { + DBG("GET IMSI reply - OK"); + decode_ril_error(&error, "OK"); + } else { + ofono_error("Reply failure: %s", + ril_error_to_string(message->error)); + goto error; + } + + imsi = g_ril_reply_parse_imsi(sd->ril, message); + if (imsi == NULL) { + ofono_error("Error empty IMSI"); + goto error; + } + + cb(&error, imsi, cbd->data); + g_free(imsi); + + return; + +error: + decode_ril_error(&error, "FAIL"); + cb(&error, NULL, cbd->data); +} + +static void ril_read_imsi(struct ofono_sim *sim, ofono_sim_imsi_cb_t cb, + void *data) +{ + struct sim_data *sd = ofono_sim_get_data(sim); + struct cb_data *cbd = cb_data_new(cb, data, sd); + struct parcel rilp; + + g_ril_request_read_imsi(sd->ril, sd->aid_str, &rilp); + + if (g_ril_send(sd->ril, RIL_REQUEST_GET_IMSI, &rilp, + ril_imsi_cb, cbd, g_free) == 0) { + g_free(cbd); + CALLBACK_WITH_FAILURE(cb, NULL, data); + } +} + +static void configure_active_app(struct sim_data *sd, + struct reply_sim_app *app, + guint index) +{ + g_free(sd->aid_str); + g_free(sd->app_str); + sd->app_type = app->app_type; + sd->aid_str = g_strdup(app->aid_str); + sd->app_str = g_strdup(app->app_str); + sd->app_index = index; + + DBG("setting aid_str (AID) to: %s", sd->aid_str); + switch (app->app_state) { + case RIL_APPSTATE_PIN: + sd->passwd_state = OFONO_SIM_PASSWORD_SIM_PIN; + break; + case RIL_APPSTATE_PUK: + sd->passwd_state = OFONO_SIM_PASSWORD_SIM_PUK; + break; + case RIL_APPSTATE_SUBSCRIPTION_PERSO: + switch (app->perso_substate) { + case RIL_PERSOSUBSTATE_SIM_NETWORK: + sd->passwd_state = OFONO_SIM_PASSWORD_PHNET_PIN; + break; + case RIL_PERSOSUBSTATE_SIM_NETWORK_SUBSET: + sd->passwd_state = OFONO_SIM_PASSWORD_PHNETSUB_PIN; + break; + case RIL_PERSOSUBSTATE_SIM_CORPORATE: + sd->passwd_state = OFONO_SIM_PASSWORD_PHCORP_PIN; + break; + case RIL_PERSOSUBSTATE_SIM_SERVICE_PROVIDER: + sd->passwd_state = OFONO_SIM_PASSWORD_PHSP_PIN; + break; + case RIL_PERSOSUBSTATE_SIM_SIM: + sd->passwd_state = OFONO_SIM_PASSWORD_PHSIM_PIN; + break; + case RIL_PERSOSUBSTATE_SIM_NETWORK_PUK: + sd->passwd_state = OFONO_SIM_PASSWORD_PHNET_PUK; + break; + case RIL_PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK: + sd->passwd_state = OFONO_SIM_PASSWORD_PHNETSUB_PUK; + break; + case RIL_PERSOSUBSTATE_SIM_CORPORATE_PUK: + sd->passwd_state = OFONO_SIM_PASSWORD_PHCORP_PUK; + break; + case RIL_PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK: + sd->passwd_state = OFONO_SIM_PASSWORD_PHSP_PUK; + break; + case RIL_PERSOSUBSTATE_SIM_SIM_PUK: + sd->passwd_state = OFONO_SIM_PASSWORD_PHFSIM_PUK; + break; + default: + sd->passwd_state = OFONO_SIM_PASSWORD_NONE; + break; + }; + break; + case RIL_APPSTATE_READY: + sd->passwd_state = OFONO_SIM_PASSWORD_NONE; + break; + case RIL_APPSTATE_UNKNOWN: + case RIL_APPSTATE_DETECTED: + default: + sd->passwd_state = OFONO_SIM_PASSWORD_INVALID; + break; + } +} + +static void sim_status_cb(struct ril_msg *message, gpointer user_data) +{ + struct ofono_sim *sim = user_data; + struct sim_data *sd = ofono_sim_get_data(sim); + struct reply_sim_status *status; + guint search_index; + + status = g_ril_reply_parse_sim_status(sd->ril, message); + if (status == NULL) { + ofono_error("%s: Cannot parse SIM status reply", __func__); + return; + } + + DBG("SIM status is %u", status->card_state); + + if (status->card_state == RIL_CARDSTATE_PRESENT) + ofono_sim_inserted_notify(sim, TRUE); + else if (status && status->card_state == RIL_CARDSTATE_ABSENT) + ofono_sim_inserted_notify(sim, FALSE); + else + ofono_error("%s: bad SIM state (%u)", + __func__, status->card_state); + + if (status->card_state == RIL_CARDSTATE_PRESENT) { + /* + * TODO(CDMA): need some kind of logic + * to set the correct app_index + */ + search_index = status->gsm_umts_index; + if (search_index < status->num_apps) { + struct reply_sim_app *app = status->apps[search_index]; + + if (app->app_type != RIL_APPTYPE_UNKNOWN) { + /* + * We cache the current password state. Ideally + * this should be done by issuing a + * GET_SIM_STATUS request from + * ril_query_passwd_state, which is called by + * the core after sending a password, but + * unfortunately the response to GET_SIM_STATUS + * is not reliable in mako when sent just after + * sending the password. Some time is needed + * before the modem refreshes its internal + * state, and when it does it sends a + * SIM_STATUS_CHANGED event. In that moment we + * retrieve the status and this function is + * executed. We call __ofono_sim_recheck_pin as + * it is the only way to indicate the core to + * call query_passwd_state again. An option + * that can be explored in the future is wait + * before invoking core callback for send_passwd + * until we know the real password state. + */ + configure_active_app(sd, app, search_index); + DBG("passwd_state: %d", sd->passwd_state); + + /* + * Note: There doesn't seem to be any other way + * to force the core SIM code to recheck the + * PIN. This call causes the core to call this + * atom's query_passwd() function. + */ + __ofono_sim_recheck_pin(sim); + } + } + } + + g_ril_reply_free_sim_status(status); +} + +static void send_get_sim_status(struct ofono_sim *sim) +{ + struct sim_data *sd = ofono_sim_get_data(sim); + + g_ril_send(sd->ril, RIL_REQUEST_GET_SIM_STATUS, NULL, + sim_status_cb, sim, NULL); +} + +static void ril_sim_status_changed(struct ril_msg *message, gpointer user_data) +{ + struct ofono_sim *sim = (struct ofono_sim *) user_data; + struct sim_data *sd = ofono_sim_get_data(sim); + + DBG(""); + + g_ril_print_unsol_no_args(sd->ril, message); + + send_get_sim_status(sim); +} + +static void inf_pin_retries_cb(struct ril_msg *message, gpointer user_data) +{ + struct cb_data *cbd = user_data; + ofono_sim_pin_retries_cb_t cb = cbd->cb; + struct sim_data *sd = cbd->user; + struct reply_oem_hook *reply = NULL; + int32_t *ret_data; + + if (message->error != RIL_E_SUCCESS) { + ofono_error("Reply failure: %s", + ril_error_to_string(message->error)); + goto error; + } + + reply = g_ril_reply_oem_hook_raw(sd->ril, message); + if (reply == NULL) { + ofono_error("%s: parse error", __func__); + goto error; + } + + if (reply->length < 5 * (int) sizeof(int32_t)) { + ofono_error("%s: reply too small", __func__); + goto error; + } + + /* First integer is INF_RIL_REQUEST_OEM_GET_REMAIN_SIM_PIN_ATTEMPTS */ + ret_data = reply->data; + sd->retries[OFONO_SIM_PASSWORD_SIM_PIN] = *(++ret_data); + sd->retries[OFONO_SIM_PASSWORD_SIM_PIN2] = *(++ret_data); + sd->retries[OFONO_SIM_PASSWORD_SIM_PUK] = *(++ret_data); + sd->retries[OFONO_SIM_PASSWORD_SIM_PUK2] = *(++ret_data); + + g_ril_reply_free_oem_hook(reply); + CALLBACK_WITH_SUCCESS(cb, sd->retries, cbd->data); + + return; + +error: + g_ril_reply_free_oem_hook(reply); + CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); +} + +static void mtk_pin_retries_cb(struct ril_msg *message, gpointer user_data) +{ + struct cb_data *cbd = user_data; + ofono_sim_pin_retries_cb_t cb = cbd->cb; + struct sim_data *sd = cbd->user; + struct parcel_str_array *str_arr = NULL; + int pin[MTK_EPINC_NUM_PASSWD]; + int num_pin; + + if (message->error != RIL_E_SUCCESS) { + ofono_error("Reply failure: %s", + ril_error_to_string(message->error)); + goto error; + } + + str_arr = g_ril_reply_oem_hook_strings(sd->ril, message); + if (str_arr == NULL || str_arr->num_str < 1) { + ofono_error("%s: parse error", __func__); + goto error; + } + + num_pin = sscanf(str_arr->str[0], "+EPINC:%d,%d,%d,%d", + &pin[0], &pin[1], &pin[2], &pin[3]); + + if (num_pin != MTK_EPINC_NUM_PASSWD) { + ofono_error("%s: failed parsing %s", __func__, str_arr->str[0]); + goto error; + } + + sd->retries[OFONO_SIM_PASSWORD_SIM_PIN] = pin[0]; + sd->retries[OFONO_SIM_PASSWORD_SIM_PIN2] = pin[1]; + sd->retries[OFONO_SIM_PASSWORD_SIM_PUK] = pin[2]; + sd->retries[OFONO_SIM_PASSWORD_SIM_PUK2] = pin[3]; + + parcel_free_str_array(str_arr); + CALLBACK_WITH_SUCCESS(cb, sd->retries, cbd->data); + return; + +error: + parcel_free_str_array(str_arr); + CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); +} + +static void ril_query_pin_retries(struct ofono_sim *sim, + ofono_sim_pin_retries_cb_t cb, + void *data) +{ + struct sim_data *sd = ofono_sim_get_data(sim); + + DBG(""); + + if (sd->vendor == OFONO_RIL_VENDOR_INFINEON) { + struct cb_data *cbd = cb_data_new(cb, data, sd); + struct parcel rilp; + int32_t oem_req = + INF_RIL_REQUEST_OEM_GET_REMAIN_SIM_PIN_ATTEMPTS; + + g_ril_request_oem_hook_raw(sd->ril, &oem_req, + sizeof(oem_req), &rilp); + + /* Send request to RIL */ + if (g_ril_send(sd->ril, RIL_REQUEST_OEM_HOOK_RAW, &rilp, + inf_pin_retries_cb, cbd, g_free) == 0) { + g_free(cbd); + CALLBACK_WITH_FAILURE(cb, NULL, data); + } + } else if (sd->vendor == OFONO_RIL_VENDOR_MTK) { + struct cb_data *cbd = cb_data_new(cb, data, sd); + struct parcel rilp; + const char *at_epinc[] = { "AT+EPINC", "+EPINC:" }; + + g_ril_request_oem_hook_strings(sd->ril, at_epinc, + G_N_ELEMENTS(at_epinc), &rilp); + + if (g_ril_send(sd->ril, RIL_REQUEST_OEM_HOOK_STRINGS, &rilp, + mtk_pin_retries_cb, cbd, g_free) == 0) { + g_free(cbd); + CALLBACK_WITH_FAILURE(cb, NULL, data); + } + } else { + CALLBACK_WITH_SUCCESS(cb, sd->retries, data); + } +} + +static void ril_query_passwd_state(struct ofono_sim *sim, + ofono_sim_passwd_cb_t cb, void *data) +{ + struct sim_data *sd = ofono_sim_get_data(sim); + DBG("passwd_state %u", sd->passwd_state); + + if (sd->passwd_state == OFONO_SIM_PASSWORD_INVALID) + CALLBACK_WITH_FAILURE(cb, -1, data); + else + CALLBACK_WITH_SUCCESS(cb, sd->passwd_state, data); +} + +static void ril_pin_change_state_cb(struct ril_msg *message, gpointer user_data) +{ + struct cb_data *cbd = user_data; + ofono_sim_lock_unlock_cb_t cb = cbd->cb; + struct ofono_sim *sim = cbd->user; + struct sim_data *sd = ofono_sim_get_data(sim); + int *retries; + /* + * There is no reason to ask SIM status until + * unsolicited sim status change indication + * Looks like state does not change before that. + */ + + DBG("Enter password: type %d, result %d", + sd->passwd_type, message->error); + + retries = g_ril_reply_parse_retries(sd->ril, message, sd->passwd_type); + if (retries != NULL) { + memcpy(sd->retries, retries, sizeof(sd->retries)); + g_free(retries); + } + + /* TODO: re-factor to not use macro for FAILURE; + doesn't return error! */ + if (message->error == RIL_E_SUCCESS) { + CALLBACK_WITH_SUCCESS(cb, cbd->data); + } else { + CALLBACK_WITH_FAILURE(cb, cbd->data); + /* + * Refresh passwd_state (not needed if the unlock is + * successful, as an event will refresh the state in that case) + */ + send_get_sim_status(sim); + } +} + +static void ril_pin_send(struct ofono_sim *sim, const char *passwd, + ofono_sim_lock_unlock_cb_t cb, void *data) +{ + /* + * TODO: This function is supposed to enter the pending password, which + * might be also PIN2. So we must check the pending PIN in the future. + */ + + struct sim_data *sd = ofono_sim_get_data(sim); + struct cb_data *cbd = cb_data_new(cb, data, sim); + struct parcel rilp; + + sd->passwd_type = OFONO_SIM_PASSWORD_SIM_PIN; + + g_ril_request_pin_send(sd->ril, + passwd, + sd->aid_str, + &rilp); + + if (g_ril_send(sd->ril, RIL_REQUEST_ENTER_SIM_PIN, &rilp, + ril_pin_change_state_cb, cbd, g_free) == 0) { + g_free(cbd); + CALLBACK_WITH_FAILURE(cb, data); + } +} + +static void enter_pin_done(const struct ofono_error *error, void *data) +{ + struct change_state_cbd *csd = data; + struct sim_data *sd = ofono_sim_get_data(csd->sim); + + if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { + ofono_error("%s: wrong password", __func__); + sd->unlock_pending = FALSE; + CALLBACK_WITH_FAILURE(csd->cb, csd->data); + } else { + ril_pin_change_state(csd->sim, csd->passwd_type, csd->enable, + csd->passwd, csd->cb, csd->data); + } + + g_free(csd); +} + +static void ril_pin_change_state(struct ofono_sim *sim, + enum ofono_sim_password_type passwd_type, + int enable, const char *passwd, + ofono_sim_lock_unlock_cb_t cb, void *data) +{ + struct sim_data *sd = ofono_sim_get_data(sim); + struct cb_data *cbd; + struct parcel rilp; + struct req_pin_change_state req; + int ret = 0; + + /* + * If we want to unlock a password that has not been entered yet, + * we enter it before trying to unlock. We need sd->unlock_pending as + * the password still has not yet been refreshed when this function is + * called from enter_pin_done(). + */ + if (ofono_sim_get_password_type(sim) == passwd_type + && enable == FALSE && sd->unlock_pending == FALSE) { + struct change_state_cbd *csd = g_malloc0(sizeof(*csd)); + csd->sim = sim; + csd->passwd_type = passwd_type; + csd->enable = enable; + csd->passwd = passwd; + csd->cb = cb; + csd->data = data; + sd->unlock_pending = TRUE; + + ril_pin_send(sim, passwd, enter_pin_done, csd); + + return; + } + + sd->unlock_pending = FALSE; + + cbd = cb_data_new(cb, data, sim); + + sd->passwd_type = passwd_type; + + req.aid_str = sd->aid_str; + req.passwd_type = passwd_type; + req.enable = enable; + req.passwd = passwd; + + if (!g_ril_request_pin_change_state(sd->ril, + &req, + &rilp)) { + ofono_error("Couldn't build pin change state request"); + goto error; + } + + ret = g_ril_send(sd->ril, RIL_REQUEST_SET_FACILITY_LOCK, &rilp, + ril_pin_change_state_cb, cbd, g_free); + +error: + if (ret == 0) { + g_free(cbd); + CALLBACK_WITH_FAILURE(cb, data); + } +} + +static void ril_pin_send_puk(struct ofono_sim *sim, + const char *puk, const char *passwd, + ofono_sim_lock_unlock_cb_t cb, void *data) +{ + struct sim_data *sd = ofono_sim_get_data(sim); + struct cb_data *cbd = cb_data_new(cb, data, sim); + struct parcel rilp; + + sd->passwd_type = OFONO_SIM_PASSWORD_SIM_PUK; + + g_ril_request_pin_send_puk(sd->ril, + puk, + passwd, + sd->aid_str, + &rilp); + + if (g_ril_send(sd->ril, RIL_REQUEST_ENTER_SIM_PUK, &rilp, + ril_pin_change_state_cb, cbd, g_free) == 0) { + g_free(cbd); + CALLBACK_WITH_FAILURE(cb, data); + } +} + +static void ril_change_passwd(struct ofono_sim *sim, + enum ofono_sim_password_type passwd_type, + const char *old_passwd, const char *new_passwd, + ofono_sim_lock_unlock_cb_t cb, void *data) +{ + struct sim_data *sd = ofono_sim_get_data(sim); + struct cb_data *cbd = cb_data_new(cb, data, sim); + struct parcel rilp; + int request = RIL_REQUEST_CHANGE_SIM_PIN; + + sd->passwd_type = passwd_type; + + g_ril_request_change_passwd(sd->ril, + old_passwd, + new_passwd, + sd->aid_str, + &rilp); + + if (passwd_type == OFONO_SIM_PASSWORD_SIM_PIN2) + request = RIL_REQUEST_CHANGE_SIM_PIN2; + + if (g_ril_send(sd->ril, request, &rilp, ril_pin_change_state_cb, + cbd, g_free) == 0) { + g_free(cbd); + CALLBACK_WITH_FAILURE(cb, data); + } +} + +static gboolean listen_and_get_sim_status(gpointer user) +{ + struct ofono_sim *sim = user; + struct sim_data *sd = ofono_sim_get_data(sim); + + send_get_sim_status(sim); + + g_ril_register(sd->ril, RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED, + (GRilNotifyFunc) ril_sim_status_changed, sim); + + /* TODO: should we also register for RIL_UNSOL_SIM_REFRESH? */ + return FALSE; +} + +static gboolean ril_sim_register(gpointer user) +{ + struct ofono_sim *sim = user; + struct sim_data *sd = ofono_sim_get_data(sim); + + DBG(""); + + ofono_sim_register(sim); + + if (sd->ril_state_watch != NULL && + !ofono_sim_add_state_watch(sim, sd->ril_state_watch, + sd->modem, NULL)) + ofono_error("Error registering ril sim watch"); + + /* + * We use g_idle_add here to make sure that the presence of the SIM + * interface is signalled before signalling anything else from the said + * interface, as ofono_sim_register also uses g_idle_add. + */ + g_idle_add(listen_and_get_sim_status, sim); + + return FALSE; +} + +static int ril_sim_probe(struct ofono_sim *sim, unsigned int vendor, + void *data) +{ + struct ril_sim_data *ril_data = data; + GRil *ril = ril_data->gril; + struct sim_data *sd; + int i; + + sd = g_new0(struct sim_data, 1); + sd->ril = g_ril_clone(ril); + sd->vendor = vendor; + sd->aid_str = NULL; + sd->app_str = NULL; + sd->app_type = RIL_APPTYPE_UNKNOWN; + sd->passwd_state = OFONO_SIM_PASSWORD_NONE; + sd->passwd_type = OFONO_SIM_PASSWORD_NONE; + sd->modem = ril_data->modem; + sd->ril_state_watch = ril_data->ril_state_watch; + + for (i = 0; i < OFONO_SIM_PASSWORD_INVALID; i++) + sd->retries[i] = -1; + + ofono_sim_set_data(sim, sd); + + /* + * TODO: analyze if capability check is needed + * and/or timer should be adjusted. + * + * ofono_sim_register() needs to be called after the + * driver has been set in ofono_sim_create(), which + * calls this function. Most other drivers make some + * kind of capabilities query to the modem, and then + * call register in the callback; we use an idle event + * instead. + */ + g_idle_add(ril_sim_register, sim); + + return 0; +} + +static void ril_sim_remove(struct ofono_sim *sim) +{ + struct sim_data *sd = ofono_sim_get_data(sim); + + ofono_sim_set_data(sim, NULL); + + g_ril_unref(sd->ril); + g_free(sd->aid_str); + g_free(sd->app_str); + g_free(sd); +} + +static struct ofono_sim_driver driver = { + .name = RILMODEM, + .probe = ril_sim_probe, + .remove = ril_sim_remove, + .read_file_info = ril_sim_read_info, + .read_file_transparent = ril_sim_read_binary, + .read_file_linear = ril_sim_read_record, + .read_file_cyclic = ril_sim_read_record, + .write_file_transparent = ril_sim_update_binary, + .write_file_linear = ril_sim_update_record, + .write_file_cyclic = ril_sim_update_cyclic, + .read_imsi = ril_read_imsi, + .query_passwd_state = ril_query_passwd_state, + .send_passwd = ril_pin_send, + .query_pin_retries = ril_query_pin_retries, + .reset_passwd = ril_pin_send_puk, + .change_passwd = ril_change_passwd, + .lock = ril_pin_change_state, +/* + * TODO: Implmenting PIN/PUK support requires defining + * the following driver methods. + * + * In the meanwhile, as long as the SIM card is present, + * and unlocked, the core SIM code will check for the + * presence of query_passwd_state, and if null, then the + * function sim_initialize_after_pin() is called. + * + * .query_locked = ril_pin_query_enabled, + */ +}; + +void ril_sim_init(void) +{ + DBG(""); + ofono_sim_driver_register(&driver); +} + +void ril_sim_exit(void) +{ + ofono_sim_driver_unregister(&driver); +} |