ofono/drivers/isimodem/uicc.c

1710 lines
39 KiB
C

/*
*
* oFono - Open Source Telephony
*
* Copyright (C) 2011 ST-Ericsson AB.
* 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 <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <glib.h>
#include <gisi/message.h>
#include <gisi/client.h>
#include <gisi/iter.h>
#include <ofono/log.h>
#include <ofono/modem.h>
#include <ofono/sim.h>
#include "simutil.h"
#include "isimodem.h"
#include "isiutil.h"
#include "sim.h"
#include "uicc.h"
#include "uicc-util.h"
#include "debug.h"
/* File info parameters */
#define FCP_TEMPLATE 0x62
#define FCP_FILE_SIZE 0x80
#define FCP_FILE_DESC 0x82
#define FCP_FILE_ID 0x83
#define FCP_FILE_LIFECYCLE 0x8A
#define FCP_FILE_SECURITY_ARR 0x8B
#define FCP_FILE_SECURITY_COMPACT 0x8C
#define FCP_FILE_SECURITY_EXPANDED 0xAB
#define FCP_PIN_STATUS 0xC6
#define SIM_EFARR_FILEID 0x6f06
#define MAX_SIM_APPS 10
#define MAX_IMSI_LENGTH 15
enum uicc_flag {
UICC_FLAG_APP_STARTED = 1 << 0,
UICC_FLAG_PIN_STATE_RECEIVED = 1 << 1,
UICC_FLAG_PASSWD_REQUIRED = 1 << 2,
};
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;
}
struct uicc_file_info_cb_data {
void *cb;
void *data;
void *user;
struct ofono_sim *sim;
};
static gboolean decode_uicc_usim_type(GIsiSubBlockIter *iter, uint16_t *length,
uint16_t *file_id,
uint16_t *record_length,
uint8_t *records, uint8_t *structure)
{
uint8_t fcp = 0;
uint8_t desc = 0;
uint8_t coding = 0;
uint8_t fcp_len = 0;
uint8_t read = 0;
uint8_t item_len = 0;
if (!g_isi_sb_iter_get_byte(iter, &fcp, 8))
return FALSE;
if (fcp != FCP_TEMPLATE)
return FALSE;
if (!g_isi_sb_iter_get_byte(iter, &fcp_len, 9))
return FALSE;
for (read = 0; read < fcp_len; read += item_len + 2) {
uint8_t id;
if (!g_isi_sb_iter_get_byte(iter, &id, read + 10))
return FALSE;
if (!g_isi_sb_iter_get_byte(iter, &item_len, read + 11))
return FALSE;
switch (id) {
case FCP_FILE_SIZE:
if (item_len != 2)
return FALSE;
if (!g_isi_sb_iter_get_word(iter, length, read + 10 + 2))
return FALSE;
break;
case FCP_FILE_ID:
if (item_len != 2)
return FALSE;
if (!g_isi_sb_iter_get_word(iter, file_id, read + 10 + 2))
return FALSE;
break;
case FCP_FILE_DESC:
if (item_len < 2)
return FALSE;
if (!g_isi_sb_iter_get_byte(iter, &desc, read + 10 + 2))
return FALSE;
if (!g_isi_sb_iter_get_byte(iter, &coding, read + 10 + 3))
return FALSE;
if (item_len < 4)
break;
if (!g_isi_sb_iter_get_word(iter, record_length,
read + 10 + 4))
return FALSE;
if (!g_isi_sb_iter_get_byte(iter, records, read + 10 + 6))
return FALSE;
break;
/*
* Not implemented, using static access rules
* as these are used only for cacheing See
* ETSI TS 102 221, ch 11.1.1.4.7 and Annexes
* E, F and G.
*/
case FCP_FILE_SECURITY_ARR:
case FCP_FILE_SECURITY_COMPACT:
case FCP_FILE_SECURITY_EXPANDED:
case FCP_FILE_LIFECYCLE:
default:
DBG("FCP id %02X not supported", id);
break;
}
}
if ((desc & 7) == 1)
*structure = OFONO_SIM_FILE_STRUCTURE_TRANSPARENT;
else if ((desc & 7) == 2)
*structure = OFONO_SIM_FILE_STRUCTURE_FIXED;
else if ((desc & 7) == 6)
*structure = OFONO_SIM_FILE_STRUCTURE_CYCLIC;
return TRUE;
}
static void uicc_file_info_resp_cb(const GIsiMessage *msg, void *opaque)
{
struct uicc_file_info_cb_data *cbd = opaque;
struct uicc_sim_data *sd = ofono_sim_get_data(cbd->sim);
struct file_info const *info = cbd->user;
ofono_sim_file_info_cb_t cb = cbd->cb;
GIsiSubBlockIter iter;
uint16_t length = 0;
uint16_t record_length = 0;
uint8_t structure = 0xFF;
uint8_t records = 0;
uint16_t file_id = 0;
uint8_t access[3] = {0, 0, 0};
uint8_t item_len = 0;
uint8_t message_id = 0;
uint8_t service_type = 0;
uint8_t status = 0;
uint8_t details = 0;
uint8_t num_subblocks = 0;
uint8_t file_status = 1;
message_id = g_isi_msg_id(msg);
DBG("uicc_file_info_resp_cb: msg_id=%d, msg len=%zu", message_id,
g_isi_msg_data_len(msg));
if (message_id != UICC_APPL_CMD_RESP)
goto error;
if (!g_isi_msg_data_get_byte(msg, 0, &service_type) ||
!g_isi_msg_data_get_byte(msg, 1, &status) ||
!g_isi_msg_data_get_byte(msg, 2, &details) ||
!g_isi_msg_data_get_byte(msg, 5, &num_subblocks))
goto error;
DBG("%s, service %s, status %s, details %s, nm_sb %d",
uicc_message_id_name(message_id),
uicc_service_type_name(service_type),
uicc_status_name(status), uicc_details_name(details),
num_subblocks);
if (info) {
access[0] = info->access[0];
access[1] = info->access[1];
access[2] = info->access[2];
file_status = info->file_status;
}
for (g_isi_sb_iter_init_full(&iter, msg, 6, TRUE, num_subblocks);
g_isi_sb_iter_is_valid(&iter);
g_isi_sb_iter_next(&iter)) {
uint8_t sb_id = g_isi_sb_iter_get_id(&iter);
DBG("Subblock %s", uicc_subblock_name(sb_id));
if (sb_id != UICC_SB_FCI)
continue;
DBG("Decoding UICC_SB_FCI");
switch (sd->app_type) {
case UICC_APPL_TYPE_UICC_USIM:
DBG("UICC_APPL_TYPE_UICC_USIM");
if (!decode_uicc_usim_type(&iter, &length, &file_id,
&record_length,
&records,
&structure))
goto error;
break;
case UICC_APPL_TYPE_ICC_SIM:
DBG("UICC_APPL_TYPE_ICC_SIM");
if (!g_isi_sb_iter_get_word(&iter, &length, 10))
goto error;
if (!g_isi_sb_iter_get_word(&iter, &file_id, 12))
goto error;
if (!g_isi_sb_iter_get_byte(&iter, &access[0], 16))
goto error;
if (!g_isi_sb_iter_get_byte(&iter, &access[0], 17))
goto error;
if (!g_isi_sb_iter_get_byte(&iter, &access[0], 18))
goto error;
if (!g_isi_sb_iter_get_byte(&iter, &item_len, 20))
goto error;
if (!g_isi_sb_iter_get_byte(&iter, &structure, 21))
goto error;
if (item_len == 2) {
uint8_t byte;
if (!g_isi_sb_iter_get_byte(&iter, &byte, 22))
goto error;
record_length = byte;
}
break;
default:
DBG("Application type %d not supported", sd->app_type);
break;
}
DBG("fileid=%04X, filelen=%d, records=%d, reclen=%d, structure=%d",
file_id, length, records, record_length, structure);
CALLBACK_WITH_SUCCESS(cb, length, structure, record_length,
access, file_status, cbd->data);
return;
}
error:
DBG("Error reading file info");
CALLBACK_WITH_FAILURE(cb, -1, -1, -1, NULL, 0, cbd->data);
}
static gboolean send_uicc_read_file_info(GIsiClient *client, uint8_t app_id,
int fileid, uint8_t df_len,
int mf_path, int df1_path,
int df2_path,
GIsiNotifyFunc notify, void *data,
GDestroyNotify destroy)
{
const uint8_t msg[] = {
UICC_APPL_CMD_REQ,
UICC_APPL_FILE_INFO, /* Service type */
app_id,
UICC_SESSION_ID_NOT_USED,
0, 0, /* Filler */
1, /* Number of subblocks */
ISI_16BIT(UICC_SB_APPL_PATH),
ISI_16BIT(16), /* Subblock length */
ISI_16BIT(fileid),
uicc_get_sfi(fileid), /* Elementary file short file id */
0, /* Filler */
df_len,
0, /* Filler */
ISI_16BIT(mf_path),
ISI_16BIT(df1_path),
ISI_16BIT(df2_path),
};
return g_isi_client_send(client, msg, sizeof(msg), notify, data, destroy);
}
static void uicc_read_file_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 uicc_sim_data *sd = ofono_sim_get_data(sim);
struct uicc_file_info_cb_data *cbd;
/* Prepare for static file info used for access rights */
int i;
int N = sizeof(static_file_info) / sizeof(static_file_info[0]);
int mf_path = 0;
int df1_path = 0;
int df2_path = 0;
uint8_t df_len = 0;
cbd = g_try_new0(struct uicc_file_info_cb_data, 1);
if (!cbd)
goto error;
cbd->cb = cb;
cbd->data = data;
cbd->sim = sim;
cbd->user = NULL;
DBG("File info for ID=%04X app id %d", fileid, sd->app_id);
for (i = 0; i < N; i++) {
if (fileid == static_file_info[i].fileid) {
cbd->user = (void *) &static_file_info[i];
break;
}
}
DBG("File info for ID=%04X: %p", fileid, cbd->user);
if (!uicc_get_fileid_path(sd, &mf_path, &df1_path, &df2_path,
&df_len, fileid))
goto error;
if (send_uicc_read_file_info(sd->client, sd->app_id, fileid, df_len,
mf_path, df1_path, df2_path,
uicc_file_info_resp_cb,
cbd, g_free))
return;
error:
CALLBACK_WITH_FAILURE(cb, -1, -1, -1, NULL, 0, data);
g_free(cbd);
}
static void uicc_read_file_transp_resp_cb(const GIsiMessage *msg, void *opaque)
{
struct isi_cb_data *cbd = opaque;
ofono_sim_read_cb_t cb = cbd->cb;
GIsiSubBlockIter iter;
uint32_t filelen = 0;
uint8_t *filedata = NULL;
uint8_t num_sb = 0;
DBG("");
if (!check_resp(msg, UICC_APPL_CMD_RESP, UICC_APPL_READ_TRANSPARENT))
goto error;
if (!g_isi_msg_data_get_byte(msg, 5, &num_sb))
goto error;
for (g_isi_sb_iter_init_full(&iter, msg, 6, TRUE, num_sb);
g_isi_sb_iter_is_valid(&iter);
g_isi_sb_iter_next(&iter)) {
int sb_id = g_isi_sb_iter_get_id(&iter);
DBG("Subblock %s", uicc_subblock_name(sb_id));
if (sb_id != UICC_SB_FILE_DATA)
continue;
if (!g_isi_sb_iter_get_dword(&iter, &filelen, 4))
goto error;
if (!g_isi_sb_iter_get_struct(&iter, (void **) &filedata,
filelen, 8))
goto error;
DBG("Transparent EF read: 1st byte %02x, len %d",
filedata[0], filelen);
CALLBACK_WITH_SUCCESS(cb, filedata, filelen, cbd->data);
return;
}
error:
DBG("Error reading transparent EF");
CALLBACK_WITH_FAILURE(cb, NULL, 0, cbd->data);
}
static gboolean send_uicc_read_file_transparent(GIsiClient *client,
uint8_t app_id, uint8_t client_id,
int fileid, uint8_t df_len,
int mf_path, int df1_path,
int df2_path,
GIsiNotifyFunc notify,
void *data,
GDestroyNotify destroy)
{
const uint8_t msg[] = {
UICC_APPL_CMD_REQ,
UICC_APPL_READ_TRANSPARENT,
app_id,
UICC_SESSION_ID_NOT_USED,
0, 0, /* Filler */
3, /* Number of subblocks */
ISI_16BIT(UICC_SB_CLIENT),
ISI_16BIT(8), /* Subblock length*/
0, 0, 0, /* Filler */
client_id,
ISI_16BIT(UICC_SB_TRANSPARENT),
ISI_16BIT(8), /* Subblock length */
ISI_16BIT(0), /* File offset */
ISI_16BIT(0), /* Data amount (0=all) */
ISI_16BIT(UICC_SB_APPL_PATH),
ISI_16BIT(16), /* Subblock length */
ISI_16BIT(fileid),
uicc_get_sfi(fileid), /* Elementary file short file id */
0, /* Filler */
df_len,
0,
ISI_16BIT(mf_path),
ISI_16BIT(df1_path),
ISI_16BIT(df2_path),
};
return g_isi_client_send(client, msg, sizeof(msg), notify, data, destroy);
}
static void uicc_read_file_transparent(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 uicc_sim_data *sd = ofono_sim_get_data(sim);
struct isi_cb_data *cbd = isi_cb_data_new(sim, cb, data);
int mf_path = 0;
int df1_path = 0;
int df2_path = 0;
uint8_t df_len = 0;
if (!cbd || !sd)
goto error;
DBG("File ID=%04X, client %d, AID %d", fileid, sd->client_id,
sd->app_id);
if (!uicc_get_fileid_path(sd, &mf_path, &df1_path,
&df2_path, &df_len, fileid))
goto error;
if (send_uicc_read_file_transparent(sd->client, sd->app_id, sd->client_id,
fileid, df_len, mf_path,
df1_path, df2_path,
uicc_read_file_transp_resp_cb,
cbd, g_free))
return;
error:
DBG("Read file transparent failed");
CALLBACK_WITH_FAILURE(cb, NULL, 0, data);
g_free(cbd);
}
static void read_file_linear_resp(const GIsiMessage *msg, void *opaque)
{
struct isi_cb_data *cbd = opaque;
ofono_sim_read_cb_t cb = cbd->cb;
GIsiSubBlockIter iter;
uint8_t num_sb = 0;
uint8_t *filedata = NULL;
uint32_t filelen = 0;
DBG("");
if (!check_resp(msg, UICC_APPL_CMD_RESP, UICC_APPL_READ_LINEAR_FIXED))
goto error;
if (!g_isi_msg_data_get_byte(msg, 5, &num_sb))
goto error;
for (g_isi_sb_iter_init_full(&iter, msg, 6, TRUE, num_sb);
g_isi_sb_iter_is_valid(&iter);
g_isi_sb_iter_next(&iter)) {
uint8_t sb_id = g_isi_sb_iter_get_id(&iter);
DBG("Subblock %s", uicc_subblock_name(sb_id));
if (sb_id != UICC_SB_FILE_DATA)
continue;
if (!g_isi_sb_iter_get_dword(&iter, &filelen, 4))
goto error;
if (!g_isi_sb_iter_get_struct(&iter, (void **) &filedata,
filelen, 8))
goto error;
DBG("Linear fixed EF read: 1st byte %02x, len %d", filedata[0],
filelen);
CALLBACK_WITH_SUCCESS(cb, filedata, filelen, cbd->data);
return;
}
error:
CALLBACK_WITH_FAILURE(cb, NULL, 0, cbd->data);
}
static gboolean send_uicc_read_file_linear(GIsiClient *client, uint8_t app_id,
uint8_t client_id,
int fileid, int record,
int rec_length,
unsigned char df_len,
int mf_path, int df1_path,
int df2_path,
GIsiNotifyFunc notify,
void *data,
GDestroyNotify destroy)
{
const uint8_t msg[] = {
UICC_APPL_CMD_REQ,
UICC_APPL_READ_LINEAR_FIXED,
app_id,
UICC_SESSION_ID_NOT_USED,
0, 0, /* Filler */
3, /* Number of subblocks */
ISI_16BIT(UICC_SB_CLIENT),
ISI_16BIT(8), /*Subblock length */
0, 0, 0, /* Filler */
client_id,
ISI_16BIT(UICC_SB_LINEAR_FIXED),
ISI_16BIT(8), /*Subblock length */
record,
0, /* Record offset */
rec_length & 0xff, /*Data amount (0=all)*/
0,
ISI_16BIT(UICC_SB_APPL_PATH),
ISI_16BIT(16), /* Subblock length */
ISI_16BIT(fileid),
uicc_get_sfi(fileid), /* Elementary file short file id */
0, /* Filler */
df_len,
0,
ISI_16BIT(mf_path),
ISI_16BIT(df1_path),
ISI_16BIT(df2_path),
};
return g_isi_client_send(client, msg, sizeof(msg), notify, data, destroy);
}
static void uicc_read_file_linear(struct ofono_sim *sim, int fileid, int record,
int rec_length,
const unsigned char *path,
unsigned int path_len,
ofono_sim_read_cb_t cb, void *data)
{
struct uicc_sim_data *sd = ofono_sim_get_data(sim);
struct isi_cb_data *cbd = isi_cb_data_new(sim, cb, data);
int mf_path = 0;
int df1_path = 0;
int df2_path = 0;
uint8_t df_len = 0;
if (!sd || !cbd)
goto error;
DBG("File ID=%04X, record %d, client %d AID %d", fileid, record,
sd->client_id, sd->app_id);
if (!uicc_get_fileid_path(sd, &mf_path, &df1_path, &df2_path,
&df_len, fileid))
goto error;
if (send_uicc_read_file_linear(sd->client, sd->app_id, sd->client_id,
fileid, record, rec_length, df_len,
mf_path, df1_path, df2_path,
read_file_linear_resp, cbd, g_free))
return;
error:
CALLBACK_WITH_FAILURE(cb, NULL, 0, data);
g_free(cbd);
}
static void uicc_read_file_cyclic(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)
{
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,
const unsigned char *path,
unsigned int path_len,
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,
const unsigned char *path,
unsigned int path_len,
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,
const unsigned char *path,
unsigned int path_len,
ofono_sim_write_cb_t cb, void *data)
{
DBG("Not implemented");
CALLBACK_WITH_FAILURE(cb, data);
}
static gboolean decode_imsi(uint8_t *data, int len, char *imsi)
{
int i = 1; /* Skip first byte, the length field */
int j = 0;
if (data == NULL || len == 0)
return FALSE;
if (data[0] != 8 || data[0] > len)
return FALSE;
/* Ignore low-order semi-octet of the first byte */
imsi[j] = ((data[i] & 0xF0) >> 4) + '0';
for (i++, j++; i - 1 < data[0] && j < MAX_IMSI_LENGTH; i++) {
char nibble;
imsi[j++] = (data[i] & 0x0F) + '0';
nibble = (data[i] & 0xF0) >> 4;
if (nibble != 0x0F)
imsi[j++] = nibble + '0';
}
imsi[j] = '\0';
return TRUE;
}
static void uicc_read_imsi_resp(const GIsiMessage *msg, void *opaque)
{
struct isi_cb_data *cbd = opaque;
ofono_sim_imsi_cb_t cb = cbd->cb;
GIsiSubBlockIter iter;
uint32_t filelen = 0;
uint8_t *filedata = NULL;
uint8_t num_sb = 0;
char imsi[MAX_IMSI_LENGTH + 1] = { 0 };
DBG("");
if (!check_resp(msg, UICC_APPL_CMD_RESP, UICC_APPL_READ_TRANSPARENT))
goto error;
if (!g_isi_msg_data_get_byte(msg, 5, &num_sb))
goto error;
for (g_isi_sb_iter_init_full(&iter, msg, 6, TRUE, num_sb);
g_isi_sb_iter_is_valid(&iter);
g_isi_sb_iter_next(&iter)) {
int sb_id = g_isi_sb_iter_get_id(&iter);
DBG("Subblock %s", uicc_subblock_name(sb_id));
if (sb_id != UICC_SB_FILE_DATA)
continue;
if (!g_isi_sb_iter_get_dword(&iter, &filelen, 4))
goto error;
if (!g_isi_sb_iter_get_struct(&iter, (void **) &filedata,
filelen, 8))
goto error;
DBG("Transparent EF read: 1st byte %02x, len %d",
filedata[0], filelen);
if (!decode_imsi(filedata, filelen, imsi))
goto error;
DBG("IMSI %s", imsi);
CALLBACK_WITH_SUCCESS(cb, imsi, cbd->data);
return;
}
error:
CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
}
static void uicc_read_imsi(struct ofono_sim *sim, ofono_sim_imsi_cb_t cb,
void *data)
{
struct uicc_sim_data *sd = ofono_sim_get_data(sim);
struct isi_cb_data *cbd = isi_cb_data_new(sim, cb, data);
int mf_path = 0;
int df1_path = 0;
int df2_path = 0;
uint8_t df_len = 0;
if (!cbd)
goto error;
DBG("Client %d, AID %d", sd->client_id, sd->app_id);
if (!uicc_get_fileid_path(sd, &mf_path, &df1_path, &df2_path, &df_len,
SIM_EFIMSI_FILEID))
goto error;
if (send_uicc_read_file_transparent(sd->client, sd->app_id, sd->client_id,
SIM_EFIMSI_FILEID, df_len,
mf_path, df1_path, df2_path,
uicc_read_imsi_resp,
cbd, g_free))
return;
error:
CALLBACK_WITH_FAILURE(cb, NULL, data);
g_free(cbd);
}
static void uicc_query_passwd_state_resp(const GIsiMessage *msg, void *opaque)
{
struct isi_cb_data *cbd = opaque;
ofono_sim_passwd_cb_t cb = cbd->cb;
uint8_t type;
uint8_t cause;
DBG("");
if (g_isi_msg_error(msg) < 0) {
DBG("Error: %s", g_isi_msg_strerror(msg));
goto error;
}
if (g_isi_msg_id(msg) != UICC_PIN_RESP) {
DBG("Unexpected msg: %s", sim_message_id_name(g_isi_msg_id(msg)));
goto error;
}
if (!g_isi_msg_data_get_byte(msg, 0, &type) ||
type != UICC_PIN_PROMPT_VERIFY) {
DBG("Unexpected service: 0x%02X (0x%02X)", type,
UICC_PIN_PROMPT_VERIFY);
goto error;
}
if (!g_isi_msg_data_get_byte(msg, 1, &cause))
goto error;
DBG("Status: %d %s", cause, uicc_status_name(cause));
if (cause == UICC_STATUS_PIN_DISABLED) {
CALLBACK_WITH_SUCCESS(cb, OFONO_SIM_PASSWORD_NONE, cbd->data);
return;
}
DBG("Request failed or not implemented: %s", uicc_status_name(cause));
error:
CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
}
static void uicc_query_passwd_state(struct ofono_sim *sim,
ofono_sim_passwd_cb_t cb, void *data)
{
struct uicc_sim_data *sd = ofono_sim_get_data(sim);
struct isi_cb_data *cbd = isi_cb_data_new(sim, cb, data);
const uint8_t req[] = {
UICC_PIN_REQ,
UICC_PIN_PROMPT_VERIFY,
sd->app_id,
0, 0, 0, /* Filler */
1, /* Number of subblocks */
ISI_16BIT(UICC_SB_PIN_REF),
ISI_16BIT(8), /*Sub block length*/
sd->pin1_id, /* Pin ID */
0, 0, 0, /* Filler */
};
DBG("");
if (g_isi_client_send(sd->client, req, sizeof(req),
uicc_query_passwd_state_resp, cbd, g_free))
return;
CALLBACK_WITH_FAILURE(cb, -1, data);
g_free(cbd);
}
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_resp(const GIsiMessage *msg, void *opaque)
{
struct isi_cb_data *cbd = opaque;
ofono_sim_pin_retries_cb_t cb = cbd->cb;
int retries[OFONO_SIM_PASSWORD_INVALID];
GIsiSubBlockIter iter;
uint8_t num_sb = 0;
uint8_t pins = 0;
uint8_t pina = 0;
uint8_t puka = 0;
DBG("");
if (!check_resp(msg, UICC_PIN_RESP, UICC_PIN_INFO))
goto error;
if (!g_isi_msg_data_get_byte(msg, 5, &num_sb))
goto error;
DBG("Subblock count %d", num_sb);
for (g_isi_sb_iter_init_full(&iter, msg, 6, TRUE, num_sb);
g_isi_sb_iter_is_valid(&iter);
g_isi_sb_iter_next(&iter)) {
uint8_t sb_id = g_isi_sb_iter_get_id(&iter);
DBG("Sub-block %s", uicc_subblock_name(sb_id));
if (sb_id != UICC_SB_PIN_INFO)
continue;
if (!g_isi_sb_iter_get_byte(&iter, &pins, 4))
goto error;
if (!g_isi_sb_iter_get_byte(&iter, &pina, 5))
goto error;
if (!g_isi_sb_iter_get_byte(&iter, &puka, 6))
goto error;
DBG("PIN status %X PIN Attrib %d PUK attrib %d", pins,
pina, puka);
retries[OFONO_SIM_PASSWORD_SIM_PIN] = pina;
retries[OFONO_SIM_PASSWORD_SIM_PUK] = puka;
CALLBACK_WITH_SUCCESS(cb, retries, cbd->data);
return;
}
error:
CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
}
static void uicc_query_pin_retries(struct ofono_sim *sim,
ofono_sim_pin_retries_cb_t cb,
void *data)
{
struct uicc_sim_data *sd = ofono_sim_get_data(sim);
struct isi_cb_data *cbd = isi_cb_data_new(sim, cb, data);
const uint8_t req[] = {
UICC_PIN_REQ,
UICC_PIN_INFO,
sd->app_id,
0, 0, 0, /* Filler */
1, /* Number of subblocks */
ISI_16BIT(UICC_SB_PIN_REF),
ISI_16BIT(8), /* Subblock length */
sd->pin1_id, /* Pin ID */
0, 0, 0, /* Filler */
};
DBG("");
if (g_isi_client_send(sd->client, req, sizeof(req),
uicc_query_pin_retries_resp, cbd, g_free))
return;
CALLBACK_WITH_FAILURE(cb, NULL, data);
g_free(cbd);
}
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 gboolean decode_fcp_pin_status(const GIsiSubBlockIter *iter, uint8_t read,
uint8_t *pin1, uint8_t *pin2)
{
uint8_t do_len;
uint8_t len;
uint8_t tag;
uint8_t id;
uint8_t tag_pos;
DBG("Decoding PIN status");
if (!g_isi_sb_iter_get_byte(iter, &do_len, read))
return FALSE;
tag_pos = read + 1 + do_len;
if (!g_isi_sb_iter_get_byte(iter, &tag, tag_pos))
return FALSE;
while (tag == 0x83) {
if (!g_isi_sb_iter_get_byte(iter, &len, tag_pos + 1))
return FALSE;
if (!g_isi_sb_iter_get_byte(iter, &id, tag_pos + 2))
return FALSE;
tag_pos += 2 + len;
if (!g_isi_sb_iter_get_byte(iter, &tag, tag_pos))
return FALSE;
DBG("PIN_len %d, PIN id %02x, PIN tag %02x", len, id, tag);
if (id >= 0x01 && id <= 0x08)
*pin1 = id;
else if (id >= 0x81 && id <= 0x88)
*pin2 = id;
}
return TRUE;
}
static gboolean decode_fci_sb(const GIsiSubBlockIter *iter, int app_type,
uint8_t *pin1, uint8_t *pin2)
{
uint8_t fcp = 0;
uint8_t fcp_len = 0;
uint8_t read = 0;
uint8_t item_len = 0;
DBG("Decoding UICC_SB_FCI");
if (app_type != UICC_APPL_TYPE_UICC_USIM)
return FALSE;
if (!g_isi_sb_iter_get_byte(iter, &fcp, 8))
return FALSE;
if (fcp != FCP_TEMPLATE)
return FALSE;
if (!g_isi_sb_iter_get_byte(iter, &fcp_len, 9))
return FALSE;
for (read = 0; read < fcp_len; read += item_len + 2) {
uint8_t id;
if (!g_isi_sb_iter_get_byte(iter, &id, read + 10))
return FALSE;
if (!g_isi_sb_iter_get_byte(iter, &item_len, read + 11))
return FALSE;
if (id != FCP_PIN_STATUS)
continue;
if (!decode_fcp_pin_status(iter, read + 13, pin1, pin2))
return FALSE;
}
return TRUE;
}
static gboolean decode_chv_sb(const GIsiSubBlockIter *iter, int app_type,
uint8_t *pin1, uint8_t *pin2)
{
uint8_t chv_id = 0;
uint8_t pin_id = 0;
DBG("Decoding UICC_SB_CHV");
if (app_type != UICC_APPL_TYPE_ICC_SIM)
return FALSE;
if (!g_isi_sb_iter_get_byte(iter, &chv_id, 4))
return FALSE;
if (!g_isi_sb_iter_get_byte(iter, &pin_id, 5))
return FALSE;
switch (chv_id) {
case 1:
*pin1 = pin_id;
break;
case 2:
*pin2 = pin_id;
break;
default:
return FALSE;
}
DBG("CHV=%d, pin_id=%2x, PIN1 %02x, PIN2 %02x", chv_id, pin_id, *pin1,
*pin2);
return TRUE;
}
static void uicc_application_activate_resp(const GIsiMessage *msg, void *opaque)
{
struct ofono_sim *sim = opaque;
struct uicc_sim_data *sd = ofono_sim_get_data(sim);
GIsiSubBlockIter iter;
uint8_t cause, num_sb;
DBG("");
if (g_isi_msg_error(msg) < 0) {
DBG("Error: %s", g_isi_msg_strerror(msg));
return;
}
if (g_isi_msg_id(msg) != UICC_APPLICATION_RESP) {
DBG("Unexpected msg: %s",
sim_message_id_name(g_isi_msg_id(msg)));
return;
}
if (!g_isi_msg_data_get_byte(msg, 1, &cause))
return;
if (cause != UICC_STATUS_OK && cause != UICC_STATUS_APPL_ACTIVE) {
DBG("TODO: handle application activation");
return;
}
if (!sd->uicc_app_started) {
sd->app_id = sd->trying_app_id;
sd->app_type = sd->trying_app_type;
sd->uicc_app_started = TRUE;
DBG("UICC application activated");
ofono_sim_inserted_notify(sim, TRUE);
ofono_sim_register(sim);
g_hash_table_remove_all(sd->app_table);
}
if (!g_isi_msg_data_get_byte(msg, 5, &num_sb))
return;
for (g_isi_sb_iter_init_full(&iter, msg, 6, TRUE, num_sb);
g_isi_sb_iter_is_valid(&iter);
g_isi_sb_iter_next(&iter)) {
uint8_t sb_id = g_isi_sb_iter_get_id(&iter);
DBG("Subblock %s", uicc_subblock_name(sb_id));
switch (sb_id) {
case UICC_SB_CLIENT:
if (!g_isi_sb_iter_get_byte(&iter, &sd->client_id, 7))
return;
DBG("Client id %d", sd->client_id);
break;
case UICC_SB_FCI:
if (!decode_fci_sb(&iter, sd->app_type, &sd->pin1_id,
&sd->pin2_id))
return;
DBG("PIN1 %02x, PIN2 %02x", sd->pin1_id, sd->pin2_id);
break;
case UICC_SB_CHV:
if (!decode_chv_sb(&iter, sd->app_type, &sd->pin1_id,
&sd->pin2_id))
return;
DBG("PIN1 %02x, PIN2 %02x", sd->pin1_id, sd->pin2_id);
break;
default:
DBG("Skipping sub-block: %s (%zu bytes)",
uicc_subblock_name(g_isi_sb_iter_get_id(&iter)),
g_isi_sb_iter_get_len(&iter));
break;
}
}
}
static gboolean send_application_activate_req(GIsiClient *client,
uint8_t app_type,
uint8_t app_id,
GIsiNotifyFunc notify,
void *data,
GDestroyNotify destroy)
{
const uint8_t msg[] = {
UICC_APPLICATION_REQ,
UICC_APPL_HOST_ACTIVATE,
2, /* Number of subblocks */
ISI_16BIT(UICC_SB_APPLICATION),
ISI_16BIT(8), /* Subblock length */
0, 0, /* Filler */
app_type,
app_id,
ISI_16BIT(UICC_SB_APPL_INFO),
ISI_16BIT(8), /* Subblock length */
0, 0, 0, /* Filler */
/*
* Next field indicates whether the application
* initialization procedure will follow the activation
* or not
*/
UICC_APPL_START_UP_INIT_PROC,
};
DBG("App type %d, AID %d", app_type, app_id);
return g_isi_client_send(client, msg, sizeof(msg), notify, data, destroy);
}
static void uicc_application_list_resp(const GIsiMessage *msg, void *data)
{
struct ofono_sim *sim = data;
struct uicc_sim_data *sd = ofono_sim_get_data(sim);
GIsiSubBlockIter iter;
uint8_t num_sb;
struct uicc_sim_application *sim_app;
/* Throw away old app table */
g_hash_table_remove_all(sd->app_table);
if (!check_resp(msg, UICC_APPLICATION_RESP, UICC_APPL_LIST))
goto error;
if (!g_isi_msg_data_get_byte(msg, 5, &num_sb))
goto error;
/* Iterate through the application list */
for (g_isi_sb_iter_init_full(&iter, msg, 6, TRUE, num_sb);
g_isi_sb_iter_is_valid(&iter);
g_isi_sb_iter_next(&iter)) {
uint8_t app_type;
uint8_t app_id;
uint8_t app_status;
uint8_t app_len;
if (g_isi_sb_iter_get_id(&iter) != UICC_SB_APPL_DATA_OBJECT)
continue;
if (!g_isi_sb_iter_get_byte(&iter, &app_type, 6))
goto error;
if (!g_isi_sb_iter_get_byte(&iter, &app_id, 7))
goto error;
if (!g_isi_sb_iter_get_byte(&iter, &app_status, 8))
goto error;
if (!g_isi_sb_iter_get_byte(&iter, &app_len, 9))
goto error;
if (app_type != UICC_APPL_TYPE_ICC_SIM &&
app_type != UICC_APPL_TYPE_UICC_USIM)
continue;
sim_app = g_try_new0(struct uicc_sim_application, 1);
if (!sim_app) {
DBG("out of memory!");
goto error;
}
sim_app->type = app_type;
sim_app->id = app_id;
sim_app->status = app_status;
sim_app->length = app_len;
sim_app->sim = sd;
g_hash_table_replace(sd->app_table, &sim_app->id, sim_app);
}
if (!sd->uicc_app_started) {
GHashTableIter app_iter;
struct uicc_sim_application *app;
gpointer key;
gpointer value;
g_hash_table_iter_init(&app_iter, sd->app_table);
if (!g_hash_table_iter_next(&app_iter, &key, &value))
return;
app = value;
sd->trying_app_type = app->type;
sd->trying_app_id = app->id;
g_hash_table_remove(sd->app_table, &app->id);
if (!send_application_activate_req(sd->client, app->type, app->id,
uicc_application_activate_resp,
data, NULL)) {
DBG("Failed to activate: 0x%02X (type=0x%02X)",
app->id, app->type);
return;
}
}
return;
error:
DBG("Decoding application list failed");
g_isi_client_destroy(sd->client);
sd->client = NULL;
ofono_sim_remove(sim);
}
static void uicc_card_status_resp(const GIsiMessage *msg, void *data)
{
struct ofono_sim *sim = data;
struct uicc_sim_data *sd = ofono_sim_get_data(sim);
GIsiSubBlockIter iter;
uint8_t card_status = 0;
uint8_t num_sb = 0;
DBG("");
if (!sd->server_running)
return;
if (!check_resp(msg, UICC_CARD_RESP, UICC_CARD_STATUS_GET))
goto error;
if (!g_isi_msg_data_get_byte(msg, 1, &card_status))
goto error;
if (!g_isi_msg_data_get_byte(msg, 5, &num_sb))
goto error;
DBG("Subblock count %d", num_sb);
for (g_isi_sb_iter_init_full(&iter, msg, 6, TRUE, num_sb);
g_isi_sb_iter_is_valid(&iter);
g_isi_sb_iter_next(&iter)) {
if (g_isi_sb_iter_get_id(&iter) != UICC_SB_CARD_STATUS)
continue;
if (!g_isi_sb_iter_get_byte(&iter, &card_status, 7))
goto error;
DBG("card_status = 0x%X", card_status);
/* Check if card is ready */
if (card_status == 0x21) {
const uint8_t req[] = {
UICC_APPLICATION_REQ,
UICC_APPL_LIST,
0, /* Number of subblocks */
};
DBG("card is ready");
ofono_sim_inserted_notify(sim, TRUE);
if (g_isi_client_send(sd->client, req, sizeof(req),
uicc_application_list_resp,
data, NULL))
return;
DBG("Failed to query application list");
goto error;
} else {
DBG("card not ready");
ofono_sim_inserted_notify(sim, FALSE);
return;
}
}
error:
g_isi_client_destroy(sd->client);
sd->client = NULL;
ofono_sim_remove(sim);
}
static void uicc_card_status_req(struct ofono_sim *sim,
struct uicc_sim_data *sd)
{
const uint8_t req[] = {
UICC_CARD_REQ,
UICC_CARD_STATUS_GET,
0,
};
DBG("");
if (g_isi_client_send(sd->client, req, sizeof(req),
uicc_card_status_resp, sim, NULL))
return;
g_isi_client_destroy(sd->client);
sd->client = NULL;
ofono_sim_remove(sim);
}
static void uicc_card_ind_cb(const GIsiMessage *msg, void *data)
{
struct ofono_sim *sim = data;
struct uicc_sim_data *sd = ofono_sim_get_data(sim);
DBG("");
if (g_isi_msg_id(msg) != UICC_CARD_IND)
return;
/* We're not interested in card indications if server isn't running */
if (!sd->server_running)
return;
/* Request card status */
uicc_card_status_req(sim, sd);
}
static void uicc_status_resp(const GIsiMessage *msg, void *data)
{
struct ofono_sim *sim = data;
struct uicc_sim_data *sd = ofono_sim_get_data(sim);
uint8_t status = 0, server_status = 0;
gboolean server_running = FALSE;
if (!check_resp(msg, UICC_RESP, UICC_STATUS_GET))
goto error;
if (g_isi_msg_error(msg) < 0)
goto error;
if (!g_isi_msg_data_get_byte(msg, 1, &status) ||
!g_isi_msg_data_get_byte(msg, 3, &server_status))
goto error;
DBG("status=0x%X, server_status=0x%X", status, server_status);
if (status == UICC_STATUS_OK &&
server_status == UICC_STATUS_START_UP_COMPLETED) {
DBG("server is up!");
server_running = TRUE;
}
if (!server_running) {
sd->server_running = FALSE;
/* TODO: Remove SIM etc... */
return;
}
if (sd->server_running && server_running) {
DBG("Server status didn't change...");
return;
}
/* Server is running */
sd->server_running = TRUE;
/* Request card status */
uicc_card_status_req(sim, sd);
return;
error:
g_isi_client_destroy(sd->client);
sd->client = NULL;
ofono_sim_remove(sim);
}
static void uicc_ind_cb(const GIsiMessage *msg, void *data)
{
struct ofono_sim *sim = data;
struct uicc_sim_data *sd = ofono_sim_get_data(sim);
const uint8_t req[] = { UICC_REQ, UICC_STATUS_GET, 0 };
int msg_id = g_isi_msg_id(msg);
DBG("%s", uicc_message_id_name(msg_id));
if (msg_id != UICC_IND)
return;
/* Request status */
if (g_isi_client_send(sd->client, req, sizeof(req), uicc_status_resp,
data, NULL))
return;
DBG("status request failed!");
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 uicc_sim_data *sd = ofono_sim_get_data(sim);
const uint8_t req[] = {
UICC_REQ,
UICC_STATUS_GET,
0, /* Number of Sub Blocks (only from version 4.0) */
};
ISI_RESOURCE_DBG(msg);
if (g_isi_msg_error(msg) < 0)
goto error;
sd->version.major = g_isi_msg_version_major(msg);
sd->version.minor = g_isi_msg_version_minor(msg);
/* UICC server is reachable: request indications */
g_isi_client_ind_subscribe(sd->client, UICC_IND, uicc_ind_cb, sim);
g_isi_client_ind_subscribe(sd->client, UICC_CARD_IND, uicc_card_ind_cb,
sim);
/* Update status */
if (g_isi_client_send(sd->client, req,
sizeof(req) - ((sd->version.major < 4) ? 1 : 0),
uicc_status_resp, data, NULL))
return;
error:
g_isi_client_destroy(sd->client);
sd->client = NULL;
ofono_sim_remove(sim);
}
static void sim_app_destroy(gpointer p)
{
struct uicc_sim_application *app = p;
if (!app)
return;
g_free(app);
}
static int uicc_sim_probe(struct ofono_sim *sim, unsigned int vendor,
void *user)
{
GIsiModem *modem = user;
struct uicc_sim_data *sd;
sd = g_try_new0(struct uicc_sim_data, 1);
if (sd == NULL)
return -ENOMEM;
/* Create hash table for the UICC applications */
sd->app_table = g_hash_table_new_full(g_int_hash, g_int_equal, NULL,
sim_app_destroy);
if (sd->app_table == NULL) {
g_free(sd);
return -ENOMEM;
}
sd->client = g_isi_client_create(modem, PN_UICC);
if (sd->client == NULL) {
g_hash_table_destroy(sd->app_table);
g_free(sd);
return -ENOMEM;
}
g_hash_table_insert(g_modems, g_isi_client_modem(sd->client), sim);
sd->server_running = FALSE;
sd->uicc_app_started = FALSE;
sd->pin_state_received = FALSE;
sd->passwd_required = TRUE;
ofono_sim_set_data(sim, sd);
g_isi_client_verify(sd->client, uicc_reachable_cb, sim, NULL);
return 0;
}
static void uicc_sim_remove(struct ofono_sim *sim)
{
struct uicc_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_hash_table_destroy(data->app_table);
g_isi_client_destroy(data->client);
g_free(data);
}
static const 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,
};
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 uicc_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;
}