/*
 *
 *  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

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>

#include <glib.h>
#include <ell/ell.h>

#include <ofono/log.h>
#include <ofono/modem.h>
#include <ofono/sim.h>

#include <gril/gril.h>
#include <gril/grilutil.h>

#include "ofono.h"

#include "simutil.h"
#include "util.h"
#include "rilutil.h"

#include <drivers/infineonmodem/infineon_constants.h>

#define EF_STATUS_INVALIDATED 0
#define EF_STATUS_VALID 1

/* Number of passwords in EPINC response */
#define MTK_EPINC_NUM_PASSWD 4

/* Commands defined for TS 27.007 +CRSM */
#define CMD_READ_BINARY   176 /* 0xB0   */
#define CMD_READ_RECORD   178 /* 0xB2   */
#define CMD_GET_RESPONSE  192 /* 0xC0   */
#define CMD_UPDATE_BINARY 214 /* 0xD6   */
#define CMD_UPDATE_RECORD 220 /* 0xDC   */

/*
 * 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_set_facility_lock(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;
	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 gboolean parse_sim_io(GRil *ril, struct ril_msg *message,
				int *sw1, int *sw2, char **hex_response)
{
	struct parcel rilp;

	/*
	 * Minimum length of SIM_IO_Response is 12:
	 * sw1 (int32)
	 * sw2 (int32)
	 * simResponse (string)
	 */
	if (message->buf_len < 12) {
		ofono_error("Invalid SIM IO reply: size too small (< 12): %u",
				message->buf_len);
		return FALSE;
	}

	g_ril_init_parcel(message, &rilp);
	*sw1 = parcel_r_int32(&rilp);
	*sw2 = parcel_r_int32(&rilp);

	*hex_response = parcel_r_string(&rilp);

	g_ril_append_print_buf(ril, "(sw1=0x%.2X,sw2=0x%.2X,%s)",
				*sw1, *sw2, *hex_response);
	g_ril_print_response(ril, message);

	if (rilp.malformed) {
		g_free(*hex_response);
		return FALSE;
	}

	return TRUE;
}

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;
	int sw1, sw2;
	char *hex_response;
	unsigned char *response = NULL;
	size_t len;
	gboolean ok = FALSE;
	int flen = 0, rlen = 0, str = 0;
	guchar access[3] = { 0x00, 0x00, 0x00 };
	guchar file_status;

	/* 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));
		goto error;
	}

	/*
	 * The reply can have event data even when message->error is not zero
	 * in mako.
	 *
	 */

	if (parse_sim_io(sd->ril, message, &sw1, &sw2, &hex_response) == FALSE)
		goto error;

	if (hex_response != NULL) {
		response = l_util_from_hexstring(hex_response, &len);
		g_free(hex_response);
		hex_response = NULL;

		if (response == NULL)
			goto error;
	}

	/*
	 * 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));
		goto error;
	}

	if (message->error != RIL_E_SUCCESS) {
		ofono_error("%s: Reply failure: %s, %02x, %02x", __func__,
				ril_error_to_string(message->error), sw1, sw2);
		goto error;
	}

	if ((sw1 != 0x90 && sw1 != 0x91 && sw1 != 0x92 && sw1 != 0x9f) ||
			(sw1 == 0x90 && sw2 != 0x00)) {
		struct ofono_error error;

		ofono_error("Error reply, invalid values: sw1: %02x sw2: %02x",
				sw1, sw2);

		g_free(response);
		response = NULL;

		memset(&error, 0, sizeof(error));
		error.type = OFONO_ERROR_TYPE_SIM;
		error.error = (sw1 << 8) | sw2;

		cb(&error, -1, -1, -1, NULL, EF_STATUS_INVALIDATED, cbd->data);
		return;
	}

	if (response[0] == 0x62) {
		ok = sim_parse_3g_get_response(response, len,
						&flen, &rlen, &str,
						access, NULL);
		file_status = EF_STATUS_VALID;
	} else
		ok = sim_parse_2g_get_response(response, len,
						&flen, &rlen, &str,
						access, &file_status);

	l_free(response);

	if (!ok)
		goto error;

	CALLBACK_WITH_SUCCESS(cb, flen, str, rlen,
					access, file_status, cbd->data);
	return;

error:
	l_free(response);
	CALLBACK_WITH_FAILURE(cb, -1, -1, -1, NULL,
				EF_STATUS_INVALIDATED, cbd->data);
}

#define ROOTMF ((char[]) {'\x3F', '\x00'})
#define ROOTMF_SZ sizeof(ROOTMF)

static char *get_path(int vendor, guint app_type, const int fileid,
			const unsigned char *path, unsigned int path_len)
{
	unsigned char db_path[6] = { 0x00 };
	unsigned char *comm_path = db_path;
	int len = 0;

	if (path_len > 0 && path_len < 7) {
		memcpy(db_path, path, path_len);
		len = path_len;
		goto done;
	}

	switch (app_type) {
	case RIL_APPTYPE_USIM:
		len = sim_ef_db_get_path_3g(fileid, db_path);
		break;
	case RIL_APPTYPE_SIM:
		len = sim_ef_db_get_path_2g(fileid, db_path);
		break;
	default:
		ofono_error("Unsupported app_type: 0%x", app_type);
		return NULL;
	}

done:
	/*
	 * db_path contains the ID of the MF, but MediaTek modems return an
	 * error if we do not remove it. Other devices work the other way
	 * around: they need the MF in the path. In fact MTK behaviour seem to
	 * be the right one: to have the MF in the file is forbidden following
	 * ETSI TS 102 221, section 8.4.2 (we are accessing the card in mode
	 * "select by path from MF", see 3gpp 27.007, +CRSM).
	 */
	if (vendor == OFONO_RIL_VENDOR_MTK && len >= (int) ROOTMF_SZ &&
			memcmp(db_path, ROOTMF, ROOTMF_SZ) == 0) {
		comm_path = db_path + ROOTMF_SZ;
		len -= ROOTMF_SZ;
	}

	if (len == 0)
		return NULL;

	return l_util_hexstring(comm_path, len);
}

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;
	char *hex_path;

	DBG("file %04x", fileid);

	hex_path = get_path(g_ril_vendor(sd->ril),
					sd->app_type, fileid, path, path_len);
	if (hex_path == NULL) {
		ofono_error("Couldn't build SIM read info request - NULL path");
		goto error;
	}

	parcel_init(&rilp);

	parcel_w_int32(&rilp, CMD_GET_RESPONSE);
	parcel_w_int32(&rilp, fileid);
	parcel_w_string(&rilp, hex_path);
	parcel_w_int32(&rilp, 0);           /* P1 */
	parcel_w_int32(&rilp, 0);           /* P2 */

	/*
	 * TODO: review parameters values used by Android.
	 * The values of P1-P3 in this code were based on
	 * values used by the atmodem driver impl.
	 *
	 * NOTE:
	 * GET_RESPONSE_EF_SIZE_BYTES == 15; !255
	 */
	parcel_w_int32(&rilp, 15);         /* P3 - max length */
	parcel_w_string(&rilp, NULL);       /* data; only req'd for writes */
	parcel_w_string(&rilp, NULL);       /* pin2; only req'd for writes */
	parcel_w_string(&rilp, sd->aid_str); /* AID (Application ID) */

	/*
	 * sessionId, specific to latest MTK modems (harmless for older ones).
	 * It looks like this field selects one or another SIM application, but
	 * we use only one at a time so using zero here seems safe.
	 */
	if (g_ril_vendor(sd->ril) == OFONO_RIL_VENDOR_MTK)
		parcel_w_int32(&rilp, 0);

	g_ril_append_print_buf(sd->ril, "(cmd=0x%.2X,efid=0x%.4X,path=%s,"
					"0,0,15,(null),pin2=(null),aid=%s)",
					CMD_GET_RESPONSE, fileid, hex_path,
					sd->aid_str);
	l_free(hex_path);

	if (g_ril_send(sd->ril, RIL_REQUEST_SIM_IO, &rilp,
				ril_file_info_cb, cbd, g_free) > 0)
		return;

error:
	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;
	int sw1, sw2;
	char *hex_response;
	unsigned char *response = NULL;
	size_t len;

	if (message->error != RIL_E_SUCCESS) {
		ofono_error("RILD reply failure: %s",
				ril_error_to_string(message->error));
		goto error;
	}

	if (parse_sim_io(sd->ril, message, &sw1, &sw2, &hex_response) == FALSE)
		goto error;

	if (hex_response == NULL)
		goto error;

	response = l_util_from_hexstring(hex_response, &len);
	g_free(hex_response);
	hex_response = NULL;

	if (response == NULL || len == 0) {
		ofono_error("Null SIM IO response from RILD");
		goto error;
	}

	CALLBACK_WITH_SUCCESS(cb, response, len, cbd->data);
	l_free(response);
	return;

error:
	l_free(response);
	CALLBACK_WITH_FAILURE(cb, 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;
	int sw1, sw2;
	char *hex_response;

	if (message->error != RIL_E_SUCCESS) {
		ofono_error("%s: RILD reply failure: %s",
				__func__, ril_error_to_string(message->error));
		goto error;
	}

	if (parse_sim_io(sd->ril, message, &sw1, &sw2, &hex_response) == FALSE)
		goto error;

	g_free(hex_response);

	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);
	char *hex_path;
	struct parcel rilp;

	DBG("file %04x", fileid);

	hex_path = get_path(g_ril_vendor(sd->ril),
					sd->app_type, fileid, path, path_len);
	if (hex_path == NULL) {
		ofono_error("Couldn't build SIM read info request - NULL path");
		goto error;
	}

	parcel_init(&rilp);
	parcel_w_int32(&rilp, CMD_READ_BINARY);
	parcel_w_int32(&rilp, fileid);
	parcel_w_string(&rilp, hex_path);
	parcel_w_int32(&rilp, start >> 8);   /* P1 */
	parcel_w_int32(&rilp, start & 0xff); /* P2 */
	parcel_w_int32(&rilp, length);         /* P3 */
	parcel_w_string(&rilp, NULL);          /* data; only req'd for writes */
	parcel_w_string(&rilp, NULL);          /* pin2; only req'd for writes */
	parcel_w_string(&rilp, sd->aid_str);

	/* sessionId, specific to latest MTK modems (harmless for older ones) */
	if (g_ril_vendor(sd->ril) == OFONO_RIL_VENDOR_MTK)
		parcel_w_int32(&rilp, 0);

	g_ril_append_print_buf(sd->ril, "(cmd=0x%.2X,efid=0x%.4X,path=%s,"
					"%d,%d,%d,(null),pin2=(null),aid=%s)",
					CMD_READ_BINARY, fileid, hex_path,
					start >> 8, start & 0xff,
					length, sd->aid_str);
	l_free(hex_path);

	if (g_ril_send(sd->ril, RIL_REQUEST_SIM_IO, &rilp,
				ril_file_io_cb, cbd, g_free) > 0)
		return;

error:
	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);
	char *hex_path;
	struct parcel rilp;

	DBG("file %04x", fileid);

	hex_path = get_path(g_ril_vendor(sd->ril),
					sd->app_type, fileid, path, path_len);
	if (hex_path == NULL) {
		ofono_error("Couldn't build SIM read info request - NULL path");
		goto error;
	}

	parcel_init(&rilp);
	parcel_w_int32(&rilp, CMD_READ_RECORD);
	parcel_w_int32(&rilp, fileid);
	parcel_w_string(&rilp, hex_path);
	parcel_w_int32(&rilp, record);      /* P1 */
	parcel_w_int32(&rilp, 4);           /* P2 */
	parcel_w_int32(&rilp, length);      /* P3 */
	parcel_w_string(&rilp, NULL);       /* data; only req'd for writes */
	parcel_w_string(&rilp, NULL);       /* pin2; only req'd for writes */
	parcel_w_string(&rilp, sd->aid_str); /* AID (Application ID) */

	/* sessionId, specific to latest MTK modems (harmless for older ones) */
	if (g_ril_vendor(sd->ril) == OFONO_RIL_VENDOR_MTK)
		parcel_w_int32(&rilp, 0);

	g_ril_append_print_buf(sd->ril, "(cmd=0x%.2X,efid=0x%.4X,path=%s,"
					"%d,%d,%d,(null),pin2=(null),aid=%s)",
					CMD_READ_RECORD, fileid, hex_path,
					record, 4, length, sd->aid_str);
	l_free(hex_path);

	if (g_ril_send(sd->ril, RIL_REQUEST_SIM_IO, &rilp,
				ril_file_io_cb, cbd, g_free) > 0)
		return;

error:
	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);
	char *hex_path;
	struct parcel rilp;
	char *hex_data;
	int p1, p2;

	DBG("file 0x%04x", fileid);

	hex_path = get_path(g_ril_vendor(sd->ril),
					sd->app_type, fileid, path, path_len);
	if (hex_path == NULL) {
		ofono_error("Couldn't build SIM read info request - NULL path");
		goto error;
	}

	p1 = start >> 8;
	p2 = start & 0xff;
	hex_data = l_util_hexstring(value, length);

	parcel_init(&rilp);
	parcel_w_int32(&rilp, CMD_UPDATE_BINARY);
	parcel_w_int32(&rilp, fileid);
	parcel_w_string(&rilp, hex_path);
	parcel_w_int32(&rilp, p1);		/* P1 */
	parcel_w_int32(&rilp, p2);		/* P2 */
	parcel_w_int32(&rilp, length);		/* P3 (Lc) */
	parcel_w_string(&rilp, hex_data);	/* data */
	parcel_w_string(&rilp, NULL);		/* pin2; only for FDN/BDN */
	parcel_w_string(&rilp, sd->aid_str);	/* AID (Application ID) */

	/* sessionId, specific to latest MTK modems (harmless for older ones) */
	if (g_ril_vendor(sd->ril) == OFONO_RIL_VENDOR_MTK)
		parcel_w_int32(&rilp, 0);

	g_ril_append_print_buf(sd->ril, "(cmd=0x%02X,efid=0x%04X,path=%s,"
					"%d,%d,%d,%s,pin2=(null),aid=%s),",
					CMD_UPDATE_BINARY, fileid, hex_path,
					p1, p2, length, hex_data, sd->aid_str);
	l_free(hex_path);
	l_free(hex_data);

	if (g_ril_send(sd->ril, RIL_REQUEST_SIM_IO, &rilp,
				ril_file_write_cb, cbd, g_free) > 0)
		return;

error:
	g_free(cbd);
	CALLBACK_WITH_FAILURE(cb, data);
}

static void update_record(struct ofono_sim *sim, int fileid,
				int access_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);
	char *hex_path;
	struct parcel rilp;
	char *hex_data;

	DBG("file 0x%04x", fileid);

	hex_path = get_path(g_ril_vendor(sd->ril),
					sd->app_type, fileid, path, path_len);
	if (hex_path == NULL) {
		ofono_error("Couldn't build SIM read info request - NULL path");
		goto error;
	}

	hex_data = l_util_hexstring(value, length);

	parcel_init(&rilp);
	parcel_w_int32(&rilp, CMD_UPDATE_RECORD);
	parcel_w_int32(&rilp, fileid);
	parcel_w_string(&rilp, hex_path);
	parcel_w_int32(&rilp, record);	/* P1 */
	parcel_w_int32(&rilp, access_mode);	/* P2 (access mode) */
	parcel_w_int32(&rilp, length);	/* P3 (Lc) */
	parcel_w_string(&rilp, hex_data);	/* data */
	parcel_w_string(&rilp, NULL);		/* pin2; only for FDN/BDN */
	parcel_w_string(&rilp, sd->aid_str);	/* AID (Application ID) */

	/* sessionId, specific to latest MTK modems (harmless for older ones) */
	if (g_ril_vendor(sd->ril) == OFONO_RIL_VENDOR_MTK)
		parcel_w_int32(&rilp, 0);

	g_ril_append_print_buf(sd->ril, "(cmd=0x%02X,efid=0x%04X,path=%s,"
					"%d,%d,%d,%s,pin2=(null),aid=%s)",
					CMD_UPDATE_RECORD, fileid, hex_path,
					record, access_mode, length, hex_data,
					sd->aid_str);
	l_free(hex_path);
	l_free(hex_data);

	if (g_ril_send(sd->ril, RIL_REQUEST_SIM_IO, &rilp,
				ril_file_write_cb, cbd, g_free) > 0)
		return;
error:
	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)
{
	DBG("");

	update_record(sim, fileid, 4, 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)
{
	DBG("");

	/* Only mode valid for cyclic files is PREVIOUS */
	update_record(sim, fileid, 3, 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 parcel rilp;
	gchar *imsi;

	DBG("");

	if (message->error != RIL_E_SUCCESS) {
		ofono_error("Reply failure: %s",
				ril_error_to_string(message->error));
		goto error;
	}

	g_ril_init_parcel(message, &rilp);
	imsi = parcel_r_string(&rilp);

	g_ril_append_print_buf(sd->ril, "{%s}", imsi ? imsi : "NULL");
	g_ril_print_response(sd->ril, message);

	if (imsi == NULL)
		goto error;

	CALLBACK_WITH_SUCCESS(cb, imsi, cbd->data);
	g_free(imsi);
	return;

error:
	CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
}

static void ril_read_imsi(struct ofono_sim *sim, ofono_sim_imsi_cb_t cb,
				void *data)
{
	static const int GET_IMSI_NUM_PARAMS = 1;
	struct sim_data *sd = ofono_sim_get_data(sim);
	struct cb_data *cbd = cb_data_new(cb, data, sd);
	struct parcel rilp;

	DBG("");

	parcel_init(&rilp);
	parcel_w_int32(&rilp, GET_IMSI_NUM_PARAMS);
	parcel_w_string(&rilp, sd->aid_str);

	g_ril_append_print_buf(sd->ril, "(%d,%s)",
					GET_IMSI_NUM_PARAMS, sd->aid_str);

	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 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 parcel rilp;
	int card_state;
	int universal_pin_state;
	int gsm_umts_app_index;
	int cdma_app_index;
	int ims_app_index;
	int num_apps;
	int i;
	int app_state;
	int perso_substate;

	DBG("");

	g_ril_init_parcel(message, &rilp);

	card_state = parcel_r_int32(&rilp);

	/*
	 * NOTE:
	 *
	 * The global pin_status is used for multi-application
	 * UICC cards.  For example, there are SIM cards that
	 * can be used in both GSM and CDMA phones.  Instead
	 * of managed PINs for both applications, a global PIN
	 * is set instead.  It's not clear at this point if
	 * such SIM cards are supported by ofono or RILD.
	 */
	universal_pin_state = parcel_r_int32(&rilp);
	gsm_umts_app_index = parcel_r_int32(&rilp);
	cdma_app_index = parcel_r_int32(&rilp);
	ims_app_index = parcel_r_int32(&rilp);
	num_apps = parcel_r_int32(&rilp);

	if (rilp.malformed)
		return;

	if (gsm_umts_app_index >= num_apps)
		return;

	DBG("[%d,%04d]< %s: card_state=%d,universal_pin_state=%d,"
			"gsm_umts_index=%d,cdma_index=%d,ims_index=%d,"
			"num_apps=%d",
			g_ril_get_slot(sd->ril),
			message->serial_no,
			"RIL_REQUEST_GET_SIM_STATUS",
			card_state, universal_pin_state,
			gsm_umts_app_index, cdma_app_index, ims_app_index,
			num_apps);

	switch (card_state) {
	case RIL_CARDSTATE_PRESENT:
		break;
	case RIL_CARDSTATE_ABSENT:
		ofono_sim_inserted_notify(sim, FALSE);
		return;
	default:
		ofono_error("%s: bad SIM state (%u)", __func__, card_state);
		return;
	}

	ofono_sim_inserted_notify(sim, TRUE);

	for (i = 0; i != gsm_umts_app_index; i++) {
		parcel_r_int32(&rilp);		/* AppType */
		parcel_r_int32(&rilp);		/* AppState */
		parcel_r_int32(&rilp);		/* PersoSubstate */
		parcel_skip_string(&rilp);	/* AID */
		parcel_skip_string(&rilp);	/* App Label */
		parcel_r_int32(&rilp);		/* PIN1 Replaced */
		parcel_r_int32(&rilp);		/* PIN1 PinState */
		parcel_r_int32(&rilp);		/* PIN2 PinState */

		if (rilp.malformed)
			return;
	}

	/*
	 * 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.
	 */
	sd->app_type = parcel_r_int32(&rilp);	/* AppType */
	app_state = parcel_r_int32(&rilp);	/* AppState */
	perso_substate = parcel_r_int32(&rilp);	/* PersoSubstate */

	switch (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 (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;
	}

	g_free(sd->aid_str);
	sd->aid_str = parcel_r_string(&rilp);	/* AID */

	DBG("[%d,%04d]< app_type: %d, passwd_state: %d, aid_str (AID): %s",
		g_ril_get_slot(sd->ril), message->serial_no,
		sd->app_type, sd->passwd_state, sd->aid_str);

	/*
	 * 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);
}

static void send_get_sim_status(struct ofono_sim *sim)
{
	struct sim_data *sd = ofono_sim_get_data(sim);

	DBG("");

	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 parcel rilp;
	int32_t *data;
	int len;
	char *hex_dump;
	int expected;

	DBG("");

	if (message->error != RIL_E_SUCCESS) {
		ofono_error("Reply failure: %s",
				ril_error_to_string(message->error));
		goto error;
	}

	g_ril_init_parcel(message, &rilp);

	data = parcel_r_raw(&rilp, &len);
	if (data == NULL) {
		ofono_error("%s: malformed parcel", __func__);
		goto error;
	}

	hex_dump = l_util_hexstring((const unsigned char *) data, len);
	g_ril_append_print_buf(sd->ril, "{%d,%s}", len, hex_dump);
	g_ril_print_response(sd->ril, message);
	l_free(hex_dump);

	expected = sizeof(int32_t) * 5;
	if (len < expected) {
		ofono_error("%s: reply too small", __func__);
		g_free(data);
		goto error;
	}

	/* First integer is INF_RIL_REQUEST_OEM_GET_REMAIN_SIM_PIN_ATTEMPTS */
	sd->retries[OFONO_SIM_PASSWORD_SIM_PIN] = data[1];
	sd->retries[OFONO_SIM_PASSWORD_SIM_PIN2] = data[2];
	sd->retries[OFONO_SIM_PASSWORD_SIM_PUK] = data[3];
	sd->retries[OFONO_SIM_PASSWORD_SIM_PUK2] = data[4];

	g_free(data);
	CALLBACK_WITH_SUCCESS(cb, sd->retries, cbd->data);
	return;

error:
	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 rilp;
	char **strv;
	char *debug_str;
	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;
	}

	g_ril_init_parcel(message, &rilp);

	strv = parcel_r_strv(&rilp);
	if (strv == NULL) {
		ofono_error("%s: parse error", __func__);
		goto error;
	}

	debug_str = g_strjoinv(",", strv);
	g_ril_append_print_buf(sd->ril, "{%d,%s}",
					g_strv_length(strv), debug_str);
	g_free(debug_str);
	g_ril_print_response(sd->ril, message);

	num_pin = sscanf(strv[0], "+EPINC:%d,%d,%d,%d",
					&pin[0], &pin[1], &pin[2], &pin[3]);
	g_strfreev(strv);

	if (num_pin != MTK_EPINC_NUM_PASSWD)
		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];

	CALLBACK_WITH_SUCCESS(cb, sd->retries, cbd->data);
	return;

error:
	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;

		parcel_init(&rilp);
		parcel_w_raw(&rilp, &oem_req, sizeof(oem_req));

		/* 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;

		parcel_init(&rilp);
		parcel_w_int32(&rilp, 2);
		parcel_w_string(&rilp, "AT+EPINC");
		parcel_w_string(&rilp, "+EPINC:");

		if (g_ril_send(sd->ril, RIL_REQUEST_OEM_HOOK_STRINGS, &rilp,
				mtk_pin_retries_cb, cbd, g_free) > 0)
			return;

		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_enter_sim_pin_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);
	struct parcel rilp;

	/*
	 * 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);

	g_ril_init_parcel(message, &rilp);

	parcel_r_int32(&rilp);

	if (message->error == RIL_E_SUCCESS)
		sd->retries[sd->passwd_type] = -1;
	else
		sd->retries[sd->passwd_type] = parcel_r_int32(&rilp);

	g_ril_append_print_buf(sd->ril, "{%d}",
				sd->retries[sd->passwd_type]);
	g_ril_print_response(sd->ril, message);

	if (message->error == RIL_E_SUCCESS) {
		CALLBACK_WITH_SUCCESS(cb, cbd->data);
		return;
	}

	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;

	parcel_init(&rilp);

	parcel_w_int32(&rilp, 2);
	parcel_w_string(&rilp, passwd);
	parcel_w_string(&rilp, sd->aid_str);

	g_ril_append_print_buf(sd->ril, "(%s,aid=%s)", passwd, sd->aid_str);

	if (g_ril_send(sd->ril, RIL_REQUEST_ENTER_SIM_PIN, &rilp,
			ril_enter_sim_pin_cb, cbd, g_free) > 0)
		return;

	g_free(cbd);
	CALLBACK_WITH_FAILURE(cb, data);
}

static const char *const clck_cpwd_fac[] = {
	[OFONO_SIM_PASSWORD_SIM_PIN] = "SC",
	[OFONO_SIM_PASSWORD_SIM_PIN2] = "P2",
	[OFONO_SIM_PASSWORD_PHSIM_PIN] = "PS",
	[OFONO_SIM_PASSWORD_PHFSIM_PIN] = "PF",
	[OFONO_SIM_PASSWORD_PHNET_PIN] = "PN",
	[OFONO_SIM_PASSWORD_PHNETSUB_PIN] = "PU",
	[OFONO_SIM_PASSWORD_PHSP_PIN] = "PP",
	[OFONO_SIM_PASSWORD_PHCORP_PIN] = "PC",
};

#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))

static void ril_set_facility_lock_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);
	struct parcel rilp;

	/*
	 * 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);

	g_ril_init_parcel(message, &rilp);

	parcel_r_int32(&rilp);

	if (message->error == RIL_E_SUCCESS)
		sd->retries[sd->passwd_type] = -1;
	else
		sd->retries[sd->passwd_type] = parcel_r_int32(&rilp);

	g_ril_append_print_buf(sd->ril, "{%d}",
				sd->retries[sd->passwd_type]);
	g_ril_print_response(sd->ril, message);

	if (message->error == RIL_E_SUCCESS) {
		CALLBACK_WITH_SUCCESS(cb, cbd->data);
		return;
	}

	CALLBACK_WITH_FAILURE(cb, cbd->data);
}

static void ril_set_facility_lock(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;

	sd->unlock_pending = FALSE;
	sd->passwd_type = passwd_type;

	if (passwd_type >= ARRAY_SIZE(clck_cpwd_fac) ||
			clck_cpwd_fac[passwd_type] == NULL)
		goto error;

	cbd = cb_data_new(cb, data, sim);

	parcel_init(&rilp);
	parcel_w_int32(&rilp, 5);
	parcel_w_string(&rilp, clck_cpwd_fac[passwd_type]);
	parcel_w_string(&rilp, enable ? "1" : "0");
	parcel_w_string(&rilp, passwd);
	/* TODO: make this a constant... */
	parcel_w_string(&rilp, "0");		/* class */
	parcel_w_string(&rilp, sd->aid_str);

	g_ril_append_print_buf(sd->ril, "(%s,%d,%s,0,aid=%s)",
				clck_cpwd_fac[passwd_type], enable, passwd,
				sd->aid_str);

	if (g_ril_send(sd->ril, RIL_REQUEST_SET_FACILITY_LOCK, &rilp,
				ril_set_facility_lock_cb, cbd, g_free) > 0)
		return;

	g_free(cbd);
error:
	CALLBACK_WITH_FAILURE(cb, data);
}

static void ril_enter_sim_puk_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);
	struct parcel rilp;

	g_ril_init_parcel(message, &rilp);

	parcel_r_int32(&rilp);

	if (message->error != RIL_E_SUCCESS) {
		sd->retries[OFONO_SIM_PASSWORD_SIM_PUK] = parcel_r_int32(&rilp);
	} else {
		sd->retries[OFONO_SIM_PASSWORD_SIM_PIN] = -1;
		sd->retries[OFONO_SIM_PASSWORD_SIM_PUK] = -1;
	}

	g_ril_append_print_buf(sd->ril, "{%d}",
				sd->retries[OFONO_SIM_PASSWORD_SIM_PUK]);
	g_ril_print_response(sd->ril, message);

	if (message->error == RIL_E_SUCCESS) {
		CALLBACK_WITH_SUCCESS(cb, cbd->data);
		return;
	}

	CALLBACK_WITH_FAILURE(cb, cbd->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;

	parcel_init(&rilp);

	parcel_w_int32(&rilp, 3);
	parcel_w_string(&rilp, puk);
	parcel_w_string(&rilp, passwd);
	parcel_w_string(&rilp, sd->aid_str);

	g_ril_append_print_buf(sd->ril, "(puk=%s,pin=%s,aid=%s)",
				puk, passwd, sd->aid_str);

	if (g_ril_send(sd->ril, RIL_REQUEST_ENTER_SIM_PUK, &rilp,
			ril_enter_sim_puk_cb, cbd, g_free) > 0)
		return;

	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;

	switch (passwd_type) {
	case OFONO_SIM_PASSWORD_SIM_PIN:
		request = RIL_REQUEST_CHANGE_SIM_PIN;
		break;
	case OFONO_SIM_PASSWORD_SIM_PIN2:
		request = RIL_REQUEST_CHANGE_SIM_PIN2;
		break;
	default:
		goto error;
	};

	parcel_init(&rilp);

	parcel_w_int32(&rilp, 3);
	parcel_w_string(&rilp, old_passwd);
	parcel_w_string(&rilp, new_passwd);
	parcel_w_string(&rilp, sd->aid_str);

	g_ril_append_print_buf(sd->ril, "(old=%s,new=%s,aid=%s)",
				old_passwd, new_passwd, sd->aid_str);

	if (g_ril_send(sd->ril, request, &rilp, ril_enter_sim_pin_cb,
			cbd, g_free) > 0)
		return;

error:
	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);

	DBG("");

	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;

	DBG("");

	ofono_sim_register(sim);

	/*
	 * 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)
{
	GRil *ril = data;
	struct sim_data *sd;
	int i;

	DBG("");

	sd = g_new0(struct sim_data, 1);
	sd->ril = g_ril_clone(ril);
	sd->vendor = vendor;
	sd->aid_str = NULL;
	sd->app_type = RIL_APPTYPE_UNKNOWN;
	sd->passwd_state = OFONO_SIM_PASSWORD_NONE;
	sd->passwd_type = OFONO_SIM_PASSWORD_NONE;

	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_query_facility_lock_cb(struct ril_msg *message,
							gpointer user_data)
{
	struct cb_data *cbd = user_data;
	ofono_query_facility_lock_cb_t cb = cbd->cb;
	struct sim_data *sd = cbd->user;
	struct parcel rilp;
	ofono_bool_t status;
	int numparams;

	if (message->error != RIL_E_SUCCESS)
		goto error;

	g_ril_init_parcel(message, &rilp);

	numparams = parcel_r_int32(&rilp);
	if (numparams < 1)
		goto error;

	status = (ofono_bool_t) parcel_r_int32(&rilp);

	g_ril_append_print_buf(sd->ril, "{%d}", status);
	g_ril_print_response(sd->ril, message);

	CALLBACK_WITH_SUCCESS(cb, status, cbd->data);
	return;

error:
	CALLBACK_WITH_FAILURE(cb, 0, cbd->data);
}

static void ril_query_facility_lock(struct ofono_sim *sim,
					enum ofono_sim_password_type lock,
					ofono_query_facility_lock_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;

	parcel_init(&rilp);
	parcel_w_int32(&rilp, 4);	/* # of strings */
	parcel_w_string(&rilp, clck_cpwd_fac[lock]);
	parcel_w_string(&rilp, "");	/* Password is empty when not needed */
	parcel_w_string(&rilp, "0"); /* Class is "0" */
	parcel_w_string(&rilp, NULL); /* AID value is NULL */

	if (g_ril_send(sd->ril, RIL_REQUEST_QUERY_FACILITY_LOCK, &rilp,
			ril_query_facility_lock_cb, cbd, g_free) > 0)
		return;

	g_free(cbd);
	CALLBACK_WITH_FAILURE(cb, 0, data);
}

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);
}

static const struct ofono_sim_driver driver = {
	.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_set_facility_lock,
	.query_facility_lock    = ril_query_facility_lock,
};

OFONO_ATOM_DRIVER_BUILTIN(sim, rilmodem, &driver)
