ofono/drivers/atmodem/sim.c

1692 lines
40 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
*
* oFono - Open Source Telephony
*
* Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
*
* 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 "gatchat.h"
#include "gatresult.h"
#include "simutil.h"
#include "vendor.h"
#include "atmodem.h"
#define EF_STATUS_INVALIDATED 0
#define EF_STATUS_VALID 1
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
struct sim_data {
GAtChat *chat;
unsigned int vendor;
guint ready_id;
guint passwd_type_mask;
struct at_util_sim_state_query *sim_state_query;
};
static const char *crsm_prefix[] = { "+CRSM:", NULL };
static const char *cpin_prefix[] = { "+CPIN:", NULL };
static const char *clck_prefix[] = { "+CLCK:", NULL };
static const char *huawei_cpin_prefix[] = { "^CPIN:", NULL };
static const char *xpincnt_prefix[] = { "+XPINCNT:", NULL };
static const char *zpinpuk_prefix[] = { "+ZPINPUK:", NULL };
static const char *pinnum_prefix[] = { "%PINNUM:", NULL };
static const char *oercn_prefix[] = { "_OERCN:", NULL };
static const char *cpinr_prefixes[] = { "+CPINR:", "+CPINRE:", NULL };
static const char *epin_prefix[] = { "*EPIN:", NULL };
static const char *spic_prefix[] = { "+SPIC:", NULL };
static const char *pct_prefix[] = { "#PCT:", NULL };
static const char *pnnm_prefix[] = { "+PNNM:", NULL };
static const char *qpinc_prefix[] = { "+QPINC:", NULL };
static const char *upincnt_prefix[] = { "+UPINCNT:", NULL };
static const char *none_prefix[] = { NULL };
static void at_crsm_info_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct cb_data *cbd = user_data;
GAtResultIter iter;
ofono_sim_file_info_cb_t cb = cbd->cb;
struct ofono_error error;
const guint8 *response;
gint sw1, sw2, len;
int flen, rlen;
int str;
unsigned char access[3];
unsigned char file_status;
decode_at_error(&error, g_at_result_final_response(result));
if (!ok) {
cb(&error, -1, -1, -1, NULL, EF_STATUS_INVALIDATED, cbd->data);
return;
}
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+CRSM:"))
goto error;
g_at_result_iter_next_number(&iter, &sw1);
g_at_result_iter_next_number(&iter, &sw2);
if (!g_at_result_iter_next_hexstring(&iter, &response, &len) ||
(sw1 != 0x90 && sw1 != 0x91 && sw1 != 0x92) ||
(sw1 == 0x90 && sw2 != 0x00)) {
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;
}
DBG("crsm_info_cb: %02x, %02x, %i", sw1, sw2, len);
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);
if (!ok)
goto error;
cb(&error, flen, str, rlen, access, file_status, cbd->data);
return;
error:
CALLBACK_WITH_FAILURE(cb, -1, -1, -1, NULL,
EF_STATUS_INVALIDATED, cbd->data);
}
static void at_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;
char buf[128];
unsigned int len;
if (sd->vendor == OFONO_VENDOR_OPTION_HSO) {
unsigned char access[3] = { 0x00, 0x00, 0x00 };
if (fileid == SIM_EFAD_FILEID) {
CALLBACK_WITH_SUCCESS(cb, 4, 0, 0, access,
EF_STATUS_VALID, data);
return;
}
}
cbd = cb_data_new(cb, data);
len = snprintf(buf, sizeof(buf), "AT+CRSM=192,%i", fileid);
switch (sd->vendor) {
default:
if (path_len == 0)
break;
/* Fall through */
case OFONO_VENDOR_ZTE:
case OFONO_VENDOR_HUAWEI:
case OFONO_VENDOR_SIERRA:
case OFONO_VENDOR_SPEEDUP:
case OFONO_VENDOR_QUALCOMM_MSM:
case OFONO_VENDOR_SIMCOM:
/* Maximum possible length */
len += sprintf(buf + len, ",0,0,255");
break;
}
if (path_len > 0) {
len += sprintf(buf + len, ",,\"");
for (; path_len; path_len--)
len += sprintf(buf + len, "%02hhX", *path++);
buf[len++] = '\"';
buf[len] = '\0';
}
if (g_at_chat_send(sd->chat, buf, crsm_prefix,
at_crsm_info_cb, cbd, g_free) > 0)
return;
g_free(cbd);
CALLBACK_WITH_FAILURE(cb, -1, -1, -1, NULL,
EF_STATUS_INVALIDATED, data);
}
static void at_crsm_read_cb(gboolean ok, GAtResult *result,
gpointer user_data)
{
struct cb_data *cbd = user_data;
GAtResultIter iter;
ofono_sim_read_cb_t cb = cbd->cb;
struct ofono_error error;
const guint8 *response;
gint sw1, sw2, len;
decode_at_error(&error, g_at_result_final_response(result));
if (!ok) {
cb(&error, NULL, 0, cbd->data);
return;
}
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+CRSM:")) {
CALLBACK_WITH_FAILURE(cb, NULL, 0, cbd->data);
return;
}
g_at_result_iter_next_number(&iter, &sw1);
g_at_result_iter_next_number(&iter, &sw2);
if ((sw1 != 0x90 && sw1 != 0x91 && sw1 != 0x92 && sw1 != 0x9f) ||
(sw1 == 0x90 && sw2 != 0x00)) {
memset(&error, 0, sizeof(error));
error.type = OFONO_ERROR_TYPE_SIM;
error.error = (sw1 << 8) | sw2;
cb(&error, NULL, 0, cbd->data);
return;
}
if (!g_at_result_iter_next_hexstring(&iter, &response, &len)) {
CALLBACK_WITH_FAILURE(cb, NULL, 0, cbd->data);
return;
}
DBG("crsm_read_cb: %02x, %02x, %d", sw1, sw2, len);
cb(&error, response, len, cbd->data);
}
static void at_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);
char buf[64];
unsigned int len;
len = snprintf(buf, sizeof(buf), "AT+CRSM=176,%i,%i,%i,%i", fileid,
start >> 8, start & 0xff, length);
if (path_len > 0) {
buf[len++] = ',';
buf[len++] = ',';
buf[len++] = '\"';
for (; path_len; path_len--)
len += sprintf(buf + len, "%02hhX", *path++);
buf[len++] = '\"';
buf[len] = '\0';
}
if (g_at_chat_send(sd->chat, buf, crsm_prefix,
at_crsm_read_cb, cbd, g_free) > 0)
return;
g_free(cbd);
CALLBACK_WITH_FAILURE(cb, NULL, 0, data);
}
static void at_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);
char buf[128];
snprintf(buf, sizeof(buf), "AT+CRSM=178,%i,%i,4,%i", fileid,
record, length);
if (g_at_chat_send(sd->chat, buf, crsm_prefix,
at_crsm_read_cb, cbd, g_free) > 0)
return;
g_free(cbd);
CALLBACK_WITH_FAILURE(cb, NULL, 0, data);
}
static void at_crsm_update_cb(gboolean ok, GAtResult *result,
gpointer user_data)
{
struct cb_data *cbd = user_data;
GAtResultIter iter;
ofono_sim_write_cb_t cb = cbd->cb;
struct ofono_error error;
gint sw1, sw2;
decode_at_error(&error, g_at_result_final_response(result));
if (!ok) {
cb(&error, cbd->data);
return;
}
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+CRSM:")) {
CALLBACK_WITH_FAILURE(cb, cbd->data);
return;
}
g_at_result_iter_next_number(&iter, &sw1);
g_at_result_iter_next_number(&iter, &sw2);
if ((sw1 != 0x90 && sw1 != 0x91 && sw1 != 0x92 && sw1 != 0x9f) ||
(sw1 == 0x90 && sw2 != 0x00)) {
memset(&error, 0, sizeof(error));
error.type = OFONO_ERROR_TYPE_SIM;
error.error = (sw1 << 8) | sw2;
}
DBG("crsm_update_cb: %02x, %02x", sw1, sw2);
cb(&error, cbd->data);
}
static void at_sim_update_file(struct ofono_sim *sim, int cmd, int fileid,
int p1, int p2, int p3,
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);
char *buf;
int len, ret;
int size = 38 + p3 * 2;
DBG("");
buf = g_try_new(char, size);
if (buf == NULL)
goto error;
len = sprintf(buf, "AT+CRSM=%i,%i,%i,%i,%i,\"", cmd, fileid,p1, p2, p3);
for (; p3; p3--)
len += sprintf(buf + len, "%02hhX", *value++);
buf[len++] = '\"';
buf[len] = '\0';
ret = g_at_chat_send(sd->chat, buf, crsm_prefix,
at_crsm_update_cb, cbd, g_free);
g_free(buf);
if (ret > 0)
return;
error:
g_free(cbd);
CALLBACK_WITH_FAILURE(cb, data);
}
static void at_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)
{
at_sim_update_file(sim, 214, fileid, start >> 8, start & 0xff,
length, value, path, path_len, cb, data);
}
static void at_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)
{
at_sim_update_file(sim, 220, fileid, record, 4, length,
value, path, path_len, cb, data);
}
static void at_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)
{
at_sim_update_file(sim, 220, fileid, 0, 3, length, value,
path, path_len, cb, data);
}
static void at_cimi_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct cb_data *cbd = user_data;
GAtResultIter iter;
ofono_sim_imsi_cb_t cb = cbd->cb;
struct ofono_error error;
const char *imsi;
int i;
decode_at_error(&error, g_at_result_final_response(result));
if (!ok) {
cb(&error, NULL, cbd->data);
return;
}
g_at_result_iter_init(&iter, result);
for (i = 0; i < g_at_result_num_response_lines(result); i++)
g_at_result_iter_next(&iter, NULL);
imsi = g_at_result_iter_raw_line(&iter);
DBG("cimi_cb: %s", imsi);
cb(&error, imsi, cbd->data);
}
static void at_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);
if (g_at_chat_send(sd->chat, "AT+CIMI", NULL,
at_cimi_cb, cbd, g_free) > 0)
return;
g_free(cbd);
CALLBACK_WITH_FAILURE(cb, NULL, data);
}
static struct {
enum ofono_sim_password_type type;
const char *name;
} const at_sim_name[] = {
{ OFONO_SIM_PASSWORD_NONE, "READY" },
{ OFONO_SIM_PASSWORD_SIM_PIN, "SIM PIN" },
{ OFONO_SIM_PASSWORD_SIM_PUK, "SIM PUK" },
{ OFONO_SIM_PASSWORD_PHSIM_PIN, "PH-SIM PIN" },
{ OFONO_SIM_PASSWORD_PHFSIM_PIN, "PH-FSIM PIN" },
{ OFONO_SIM_PASSWORD_PHFSIM_PUK, "PH-FSIM PUK" },
{ OFONO_SIM_PASSWORD_SIM_PIN2, "SIM PIN2" },
{ OFONO_SIM_PASSWORD_SIM_PUK2, "SIM PUK2" },
{ OFONO_SIM_PASSWORD_PHNET_PIN, "PH-NET PIN" },
{ OFONO_SIM_PASSWORD_PHNET_PUK, "PH-NET PUK" },
{ OFONO_SIM_PASSWORD_PHNETSUB_PIN, "PH-NETSUB PIN" },
{ OFONO_SIM_PASSWORD_PHNETSUB_PUK, "PH-NETSUB PUK" },
{ OFONO_SIM_PASSWORD_PHSP_PIN, "PH-SP PIN" },
{ OFONO_SIM_PASSWORD_PHSP_PUK, "PH-SP PUK" },
{ OFONO_SIM_PASSWORD_PHCORP_PIN, "PH-CORP PIN" },
{ OFONO_SIM_PASSWORD_PHCORP_PUK, "PH-CORP PUK" },
};
#define BUILD_PIN_RETRIES_ARRAY(passwd_types, passwd_types_cnt, retry) \
for (i = 0; i < OFONO_SIM_PASSWORD_INVALID; i++) \
retry[i] = -1; \
\
for (i = 0; i < passwd_types_cnt; i++) { \
int val; \
\
if (!g_at_result_iter_next_number(&iter, &val)) \
goto error; \
\
retry[passwd_types[i]] = val; \
\
DBG("retry counter id=%d, val=%d", passwd_types[i], \
retry[passwd_types[i]]); \
} \
static void huawei_cpin_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct cb_data *cbd = user_data;
ofono_sim_pin_retries_cb_t cb = cbd->cb;
const char *final = g_at_result_final_response(result);
GAtResultIter iter;
struct ofono_error error;
int retries[OFONO_SIM_PASSWORD_INVALID];
size_t i;
static enum ofono_sim_password_type password_types[] = {
OFONO_SIM_PASSWORD_SIM_PUK,
OFONO_SIM_PASSWORD_SIM_PIN,
OFONO_SIM_PASSWORD_SIM_PUK2,
OFONO_SIM_PASSWORD_SIM_PIN2,
};
decode_at_error(&error, final);
if (!ok) {
cb(&error, NULL, cbd->data);
return;
}
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "^CPIN:"))
goto error;
/* Skip status since we are not interested in this */
if (!g_at_result_iter_skip_next(&iter))
goto error;
/* Skip "overall counter" since we'll grab each one individually */
if (!g_at_result_iter_skip_next(&iter))
goto error;
BUILD_PIN_RETRIES_ARRAY(password_types, ARRAY_SIZE(password_types),
retries);
cb(&error, retries, cbd->data);
return;
error:
CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
}
static void pinnum_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct cb_data *cbd = user_data;
ofono_sim_pin_retries_cb_t cb = cbd->cb;
const char *final = g_at_result_final_response(result);
GAtResultIter iter;
struct ofono_error error;
int retries[OFONO_SIM_PASSWORD_INVALID];
size_t i;
static enum ofono_sim_password_type password_types[] = {
OFONO_SIM_PASSWORD_SIM_PIN,
OFONO_SIM_PASSWORD_SIM_PUK,
OFONO_SIM_PASSWORD_SIM_PIN2,
OFONO_SIM_PASSWORD_SIM_PUK2,
};
decode_at_error(&error, final);
if (!ok) {
cb(&error, NULL, cbd->data);
return;
}
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "%PINNUM:"))
goto error;
BUILD_PIN_RETRIES_ARRAY(password_types, ARRAY_SIZE(password_types),
retries);
cb(&error, retries, cbd->data);
return;
error:
CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
}
static void zpinpuk_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct cb_data *cbd = user_data;
ofono_sim_pin_retries_cb_t cb = cbd->cb;
const char *final = g_at_result_final_response(result);
GAtResultIter iter;
struct ofono_error error;
int retries[OFONO_SIM_PASSWORD_INVALID];
size_t i;
static enum ofono_sim_password_type password_types[] = {
OFONO_SIM_PASSWORD_SIM_PIN,
OFONO_SIM_PASSWORD_SIM_PUK,
};
decode_at_error(&error, final);
if (!ok) {
cb(&error, NULL, cbd->data);
return;
}
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+ZPINPUK:"))
goto error;
BUILD_PIN_RETRIES_ARRAY(password_types, ARRAY_SIZE(password_types),
retries);
cb(&error, retries, cbd->data);
return;
error:
CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
}
static void xpincnt_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct cb_data *cbd = user_data;
ofono_sim_pin_retries_cb_t cb = cbd->cb;
const char *final = g_at_result_final_response(result);
GAtResultIter iter;
struct ofono_error error;
int retries[OFONO_SIM_PASSWORD_INVALID];
size_t i;
static enum ofono_sim_password_type password_types[] = {
OFONO_SIM_PASSWORD_SIM_PIN,
OFONO_SIM_PASSWORD_SIM_PIN2,
OFONO_SIM_PASSWORD_SIM_PUK,
OFONO_SIM_PASSWORD_SIM_PUK2,
};
decode_at_error(&error, final);
if (!ok) {
cb(&error, NULL, cbd->data);
return;
}
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+XPINCNT:"))
goto error;
BUILD_PIN_RETRIES_ARRAY(password_types, ARRAY_SIZE(password_types),
retries);
cb(&error, retries, cbd->data);
return;
error:
CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
}
static void oercn_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct cb_data *cbd = user_data;
ofono_sim_pin_retries_cb_t cb = cbd->cb;
const char *final = g_at_result_final_response(result);
GAtResultIter iter;
struct ofono_error error;
int retries[OFONO_SIM_PASSWORD_INVALID];
size_t i;
static enum ofono_sim_password_type password_types[] = {
OFONO_SIM_PASSWORD_SIM_PIN,
OFONO_SIM_PASSWORD_SIM_PUK,
};
decode_at_error(&error, final);
if (!ok) {
cb(&error, NULL, cbd->data);
return;
}
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "_OERCN:"))
goto error;
BUILD_PIN_RETRIES_ARRAY(password_types, ARRAY_SIZE(password_types),
retries);
cb(&error, retries, cbd->data);
return;
error:
CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
}
static void cpnnum_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct cb_data *cbd = user_data;
ofono_sim_pin_retries_cb_t cb = cbd->cb;
const char *final = g_at_result_final_response(result);
GAtResultIter iter;
struct ofono_error error;
const char *line;
int num;
char **entries;
int retries[OFONO_SIM_PASSWORD_INVALID];
size_t i;
decode_at_error(&error, final);
if (!ok) {
cb(&error, NULL, cbd->data);
return;
}
g_at_result_iter_init(&iter, result);
for (num = 0; num < g_at_result_num_response_lines(result); num++)
g_at_result_iter_next(&iter, NULL);
line = g_at_result_iter_raw_line(&iter);
DBG("%s", line);
for (i = 0; i < OFONO_SIM_PASSWORD_INVALID; i++)
retries[i] = -1;
entries = g_strsplit(line, "; ", -1);
for (num = 0; entries[num]; num++) {
int retry;
if (strlen(entries[num]) < 5)
continue;
retry = strtol(entries[num] + 5, NULL, 10);
if (retry == 0 && errno == EINVAL)
continue;
if (g_str_has_prefix(entries[num], "PIN1=") == TRUE)
retries[OFONO_SIM_PASSWORD_SIM_PIN] = retry;
else if (g_str_has_prefix(entries[num], "PUK1=") == TRUE)
retries[OFONO_SIM_PASSWORD_SIM_PUK] = retry;
else if (g_str_has_prefix(entries[num], "PIN2=") == TRUE)
retries[OFONO_SIM_PASSWORD_SIM_PIN2] = retry;
else if (g_str_has_prefix(entries[num], "PUK2=") == TRUE)
retries[OFONO_SIM_PASSWORD_SIM_PUK2] = retry;
}
g_strfreev(entries);
cb(&error, retries, cbd->data);
}
static void at_epin_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct cb_data *cbd = user_data;
ofono_sim_pin_retries_cb_t cb = cbd->cb;
const char *final = g_at_result_final_response(result);
GAtResultIter iter;
struct ofono_error error;
int retries[OFONO_SIM_PASSWORD_INVALID];
size_t i;
static enum ofono_sim_password_type password_types[] = {
OFONO_SIM_PASSWORD_SIM_PIN,
OFONO_SIM_PASSWORD_SIM_PUK,
OFONO_SIM_PASSWORD_SIM_PIN2,
OFONO_SIM_PASSWORD_SIM_PUK2,
};
decode_at_error(&error, final);
if (!ok) {
cb(&error, NULL, cbd->data);
return;
}
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "*EPIN:"))
goto error;
BUILD_PIN_RETRIES_ARRAY(password_types, ARRAY_SIZE(password_types),
retries);
cb(&error, retries, cbd->data);
return;
error:
CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
}
static void at_cpinr_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct cb_data *cbd = user_data;
ofono_sim_pin_retries_cb_t cb = cbd->cb;
GAtResultIter iter;
struct ofono_error error;
int retries[OFONO_SIM_PASSWORD_INVALID];
size_t len = sizeof(at_sim_name) / sizeof(*at_sim_name);
size_t i;
decode_at_error(&error, g_at_result_final_response(result));
if (!ok) {
cb(&error, NULL, cbd->data);
return;
}
for (i = 0; i < OFONO_SIM_PASSWORD_INVALID; i++)
retries[i] = -1;
g_at_result_iter_init(&iter, result);
/* Ignore +CPINRE results... */
while (g_at_result_iter_next(&iter, "+CPINR:")) {
const char *name;
int val;
if (!g_at_result_iter_next_unquoted_string(&iter, &name))
continue;
if (!g_at_result_iter_next_number(&iter, &val))
continue;
for (i = 1; i < len; i++) {
if (!strcmp(name, at_sim_name[i].name)) {
retries[at_sim_name[i].type] = val;
break;
}
}
}
cb(&error, retries, cbd->data);
}
static void at_spic_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct cb_data *cbd = user_data;
ofono_sim_pin_retries_cb_t cb = cbd->cb;
const char *final = g_at_result_final_response(result);
GAtResultIter iter;
struct ofono_error error;
int retries[OFONO_SIM_PASSWORD_INVALID];
size_t i;
static enum ofono_sim_password_type password_types[] = {
OFONO_SIM_PASSWORD_SIM_PIN,
OFONO_SIM_PASSWORD_SIM_PUK,
OFONO_SIM_PASSWORD_SIM_PIN2,
OFONO_SIM_PASSWORD_SIM_PUK2,
};
decode_at_error(&error, final);
if (!ok) {
cb(&error, NULL, cbd->data);
return;
}
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+SPIC:"))
goto error;
BUILD_PIN_RETRIES_ARRAY(password_types, ARRAY_SIZE(password_types),
retries);
cb(&error, retries, cbd->data);
return;
error:
CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
}
#define AT_PCT_SET_RETRIES(retries, pin_type, value) \
retries[pin_type] = value; \
DBG("retry counter id=%d, val=%d", pin_type, value);
static void at_pct_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct cb_data *cbd = user_data;
ofono_sim_pin_retries_cb_t cb = cbd->cb;
struct ofono_sim *sim = cbd->user;
const char *final = g_at_result_final_response(result);
GAtResultIter iter;
struct ofono_error error;
int retries[OFONO_SIM_PASSWORD_INVALID];
size_t i;
enum ofono_sim_password_type pin_type;
decode_at_error(&error, final);
if (!ok) {
cb(&error, NULL, cbd->data);
return;
}
g_at_result_iter_init(&iter, result);
for (i = 0; i < OFONO_SIM_PASSWORD_INVALID; i++)
retries[i] = -1;
pin_type = ofono_sim_get_password_type(sim);
if (pin_type == OFONO_SIM_PASSWORD_NONE) {
DBG("Note: No password required, returning maximum retries:");
AT_PCT_SET_RETRIES(retries, OFONO_SIM_PASSWORD_SIM_PIN, 3);
AT_PCT_SET_RETRIES(retries, OFONO_SIM_PASSWORD_SIM_PIN2, 3);
AT_PCT_SET_RETRIES(retries, OFONO_SIM_PASSWORD_SIM_PUK, 10);
AT_PCT_SET_RETRIES(retries, OFONO_SIM_PASSWORD_SIM_PUK2, 10);
goto callback;
}
if (g_at_result_iter_next(&iter, "#PCT:") == FALSE)
goto error;
if (g_at_result_iter_next_number(&iter, &retries[pin_type]) == FALSE)
goto error;
DBG("retry counter id=%d, val=%d", pin_type, retries[pin_type]);
callback:
cb(&error, retries, cbd->data);
return;
error:
CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
}
static void at_pnnm_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct cb_data *cbd = user_data;
ofono_sim_pin_retries_cb_t cb = cbd->cb;
const char *final = g_at_result_final_response(result);
GAtResultIter iter;
struct ofono_error error;
int retries[OFONO_SIM_PASSWORD_INVALID];
size_t i;
static enum ofono_sim_password_type password_types[] = {
OFONO_SIM_PASSWORD_SIM_PIN,
OFONO_SIM_PASSWORD_SIM_PUK,
};
decode_at_error(&error, final);
if (!ok) {
cb(&error, NULL, cbd->data);
return;
}
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+PNNM:"))
goto error;
BUILD_PIN_RETRIES_ARRAY(password_types, ARRAY_SIZE(password_types),
retries);
cb(&error, retries, cbd->data);
return;
error:
CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
}
static void at_qpinc_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct cb_data *cbd = user_data;
ofono_sim_pin_retries_cb_t cb = cbd->cb;
const char *final = g_at_result_final_response(result);
GAtResultIter iter;
struct ofono_error error;
int retries[OFONO_SIM_PASSWORD_INVALID];
size_t i;
decode_at_error(&error, final);
if (!ok) {
cb(&error, NULL, cbd->data);
return;
}
for (i = 0; i < OFONO_SIM_PASSWORD_INVALID; i++)
retries[i] = -1;
g_at_result_iter_init(&iter, result);
while (g_at_result_iter_next(&iter, "+QPINC:")) {
const char *name;
int pin, puk;
if (!g_at_result_iter_next_string(&iter, &name))
continue;
if (!g_at_result_iter_next_number(&iter, &pin))
continue;
if (!g_at_result_iter_next_number(&iter, &puk))
continue;
if (!strcmp(name, "SC")) {
retries[OFONO_SIM_PASSWORD_SIM_PIN] = pin;
retries[OFONO_SIM_PASSWORD_SIM_PUK] = puk;
} else if (!strcmp(name, "P2")) {
retries[OFONO_SIM_PASSWORD_SIM_PIN2] = pin;
retries[OFONO_SIM_PASSWORD_SIM_PUK2] = puk;
}
}
cb(&error, retries, cbd->data);
}
static void upincnt_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct cb_data *cbd = user_data;
ofono_sim_pin_retries_cb_t cb = cbd->cb;
const char *final = g_at_result_final_response(result);
GAtResultIter iter;
struct ofono_error error;
int retries[OFONO_SIM_PASSWORD_INVALID];
size_t i;
static enum ofono_sim_password_type password_types[] = {
OFONO_SIM_PASSWORD_SIM_PIN,
OFONO_SIM_PASSWORD_SIM_PIN2,
OFONO_SIM_PASSWORD_SIM_PUK,
OFONO_SIM_PASSWORD_SIM_PUK2,
};
decode_at_error(&error, final);
if (!ok) {
cb(&error, NULL, cbd->data);
return;
}
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+UPINCNT:"))
goto error;
BUILD_PIN_RETRIES_ARRAY(password_types, ARRAY_SIZE(password_types),
retries);
cb(&error, retries, cbd->data);
return;
error:
CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
}
static void at_pin_retries_query(struct ofono_sim *sim,
ofono_sim_pin_retries_cb_t cb,
void *data)
{
struct sim_data *sd = ofono_sim_get_data(sim);
struct cb_data *cbd = cb_data_new(cb, data);
cbd->user = sim;
DBG("");
switch (sd->vendor) {
case OFONO_VENDOR_IFX:
if (g_at_chat_send(sd->chat, "AT+XPINCNT", xpincnt_prefix,
xpincnt_cb, cbd, g_free) > 0)
return;
break;
case OFONO_VENDOR_SPEEDUP:
if (g_at_chat_send(sd->chat, "AT+CPNNUM", NULL,
cpnnum_cb, cbd, g_free) > 0)
return;
break;
case OFONO_VENDOR_OPTION_HSO:
if (g_at_chat_send(sd->chat, "AT_OERCN?", oercn_prefix,
oercn_cb, cbd, g_free) > 0)
return;
break;
case OFONO_VENDOR_HUAWEI:
if (g_at_chat_send(sd->chat, "AT^CPIN?", huawei_cpin_prefix,
huawei_cpin_cb, cbd, g_free) > 0)
return;
break;
case OFONO_VENDOR_ICERA:
if (g_at_chat_send(sd->chat, "AT%PINNUM?", pinnum_prefix,
pinnum_cb, cbd, g_free) > 0)
return;
break;
case OFONO_VENDOR_ZTE:
if (g_at_chat_send(sd->chat, "AT+ZPINPUK=?", zpinpuk_prefix,
zpinpuk_cb, cbd, g_free) > 0)
return;
break;
case OFONO_VENDOR_MBM:
if (g_at_chat_send(sd->chat, "AT*EPIN?", epin_prefix,
at_epin_cb, cbd, g_free) > 0)
return;
break;
case OFONO_VENDOR_SIMCOM:
if (g_at_chat_send(sd->chat, "AT+SPIC", spic_prefix,
at_spic_cb, cbd, g_free) > 0)
return;
break;
case OFONO_VENDOR_TELIT:
if (g_at_chat_send(sd->chat, "AT#PCT", pct_prefix,
at_pct_cb, cbd, g_free) > 0)
return;
break;
case OFONO_VENDOR_ALCATEL:
if (g_at_chat_send(sd->chat, "AT+PNNM?", pnnm_prefix,
at_pnnm_cb, cbd, g_free) > 0)
return;
break;
case OFONO_VENDOR_QUECTEL:
if (g_at_chat_send(sd->chat, "AT+QPINC?", qpinc_prefix,
at_qpinc_cb, cbd, g_free) > 0)
return;
break;
case OFONO_VENDOR_UBLOX:
case OFONO_VENDOR_UBLOX_TOBY_L2:
if (g_at_chat_send(sd->chat, "AT+UPINCNT", upincnt_prefix,
upincnt_cb, cbd, g_free) > 0)
return;
break;
default:
if (g_at_chat_send(sd->chat, "AT+CPINR", cpinr_prefixes,
at_cpinr_cb, cbd, g_free) > 0)
return;
break;
}
g_free(cbd);
CALLBACK_WITH_FAILURE(cb, NULL, data);
}
static int needs_wavecom_sim_quirk(int vendor)
{
return vendor == OFONO_VENDOR_WAVECOM ||
vendor == OFONO_VENDOR_WAVECOM_Q2XXX;
}
static void at_cpin_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct cb_data *cbd = user_data;
struct sim_data *sd = ofono_sim_get_data(cbd->user);
GAtResultIter iter;
ofono_sim_passwd_cb_t cb = cbd->cb;
struct ofono_error error;
const char *pin_required;
int pin_type = OFONO_SIM_PASSWORD_INVALID;
int i;
int len = sizeof(at_sim_name) / sizeof(*at_sim_name);
const char *final = g_at_result_final_response(result);
if (needs_wavecom_sim_quirk(sd->vendor) && ok && strlen(final) > 7)
decode_at_error(&error, "OK");
else
decode_at_error(&error, final);
if (!ok) {
cb(&error, -1, cbd->data);
return;
}
if (needs_wavecom_sim_quirk(sd->vendor)) {
/* +CPIN: <pin> */
pin_required = final + 7;
} else {
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+CPIN:")) {
CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
return;
}
g_at_result_iter_next_unquoted_string(&iter, &pin_required);
}
for (i = 0; i < len; i++) {
if (strcmp(pin_required, at_sim_name[i].name))
continue;
pin_type = at_sim_name[i].type;
break;
}
if (pin_type == OFONO_SIM_PASSWORD_INVALID) {
CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
return;
}
DBG("crsm_pin_cb: %s", pin_required);
cb(&error, pin_type, cbd->data);
}
static void at_pin_query(struct ofono_sim *sim, ofono_sim_passwd_cb_t cb,
void *data)
{
struct sim_data *sd = ofono_sim_get_data(sim);
struct cb_data *cbd = cb_data_new(cb, data);
cbd->user = sim;
if (g_at_chat_send(sd->chat, "AT+CPIN?", cpin_prefix,
at_cpin_cb, cbd, g_free) > 0)
return;
g_free(cbd);
CALLBACK_WITH_FAILURE(cb, -1, data);
}
static void at_xsim_notify(GAtResult *result, gpointer user_data)
{
struct cb_data *cbd = user_data;
struct sim_data *sd = cbd->user;
ofono_sim_lock_unlock_cb_t cb = cbd->cb;
struct ofono_error error = { .type = OFONO_ERROR_TYPE_NO_ERROR };
GAtResultIter iter;
int state;
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+XSIM:"))
return;
if (!g_at_result_iter_next_number(&iter, &state))
return;
switch (state) {
case 3: /* PIN verified Ready */
break;
default:
return;
}
cb(&error, cbd->data);
g_at_chat_unregister(sd->chat, sd->ready_id);
sd->ready_id = 0;
}
static void at_epev_notify(GAtResult *result, gpointer user_data)
{
struct cb_data *cbd = user_data;
struct sim_data *sd = cbd->user;
ofono_sim_lock_unlock_cb_t cb = cbd->cb;
struct ofono_error error = { .type = OFONO_ERROR_TYPE_NO_ERROR };
cb(&error, cbd->data);
g_at_chat_unregister(sd->chat, sd->ready_id);
sd->ready_id = 0;
}
static void at_qss_notify(GAtResult *result, gpointer user_data)
{
struct cb_data *cbd = user_data;
struct sim_data *sd = cbd->user;
ofono_sim_lock_unlock_cb_t cb = cbd->cb;
struct ofono_error error = { .type = OFONO_ERROR_TYPE_NO_ERROR };
GAtResultIter iter;
int state;
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "#QSS:"))
return;
if (!g_at_result_iter_next_number(&iter, &state))
return;
switch (state) {
case 3: /* SIM inserted and READY. */
break;
default:
return;
}
cb(&error, cbd->data);
g_at_chat_unregister(sd->chat, sd->ready_id);
sd->ready_id = 0;
}
static void sim_state_cb(gboolean present, gpointer user_data)
{
struct cb_data *cbd = user_data;
struct sim_data *sd = cbd->user;
ofono_sim_lock_unlock_cb_t cb = cbd->cb;
void *data = cbd->data;
at_util_sim_state_query_free(sd->sim_state_query);
sd->sim_state_query = NULL;
if (present == 1)
CALLBACK_WITH_SUCCESS(cb, data);
else
CALLBACK_WITH_FAILURE(cb, data);
}
static void at_pin_send_cb(gboolean ok, GAtResult *result,
gpointer user_data)
{
struct cb_data *cbd = user_data;
struct sim_data *sd = cbd->user;
ofono_sim_lock_unlock_cb_t cb = cbd->cb;
struct ofono_error error;
decode_at_error(&error, g_at_result_final_response(result));
if (!ok)
goto done;
switch (sd->vendor) {
case OFONO_VENDOR_IFX:
/*
* On the IFX modem, AT+CPIN? can return READY too
* early and so use +XSIM notification to detect
* the ready state of the SIM.
*/
sd->ready_id = g_at_chat_register(sd->chat, "+XSIM",
at_xsim_notify,
FALSE, cbd, g_free);
return;
case OFONO_VENDOR_MBM:
/*
* On the MBM modem, AT+CPIN? keeps returning SIM PIN
* for a moment after successful AT+CPIN="..", but then
* sends *EPEV when that changes.
*/
sd->ready_id = g_at_chat_register(sd->chat, "*EPEV",
at_epev_notify,
FALSE, cbd, g_free);
return;
case OFONO_VENDOR_TELIT:
/*
* On the Telit modem, AT+CPIN? can return READY too
* early and so use #QSS notification to detect
* the ready state of the SIM.
*/
sd->ready_id = g_at_chat_register(sd->chat, "#QSS",
at_qss_notify,
FALSE, cbd, g_free);
return;
case OFONO_VENDOR_ZTE:
case OFONO_VENDOR_ALCATEL:
case OFONO_VENDOR_HUAWEI:
case OFONO_VENDOR_SIMCOM:
case OFONO_VENDOR_SIERRA:
/*
* On ZTE modems, after pin is entered, SIM state is checked
* by polling CPIN as their modem doesn't provide unsolicited
* notification of SIM readiness.
*
* On SIMCOM modems, SIM is busy after pin is entered (we
* got a "+CME ERROR: 14" for the "AT+CPIN?" request) and
* ofono don't catch the "+CPIN: READY" message sent by the
* modem when SIM is ready. So, use extra CPIN to check the
* state.
*/
sd->sim_state_query = at_util_sim_state_query_new(sd->chat,
2, 20, sim_state_cb, cbd,
g_free);
return;
}
done:
cb(&error, cbd->data);
g_free(cbd);
}
static void at_pin_send(struct ofono_sim *sim, 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);
char buf[64];
int ret;
cbd->user = sd;
snprintf(buf, sizeof(buf), "AT+CPIN=\"%s\"", passwd);
ret = g_at_chat_send(sd->chat, buf, none_prefix,
at_pin_send_cb, cbd, NULL);
memset(buf, 0, sizeof(buf));
if (ret > 0)
return;
g_free(cbd);
CALLBACK_WITH_FAILURE(cb, data);
}
static void at_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);
char buf[64];
int ret;
cbd->user = sd;
snprintf(buf, sizeof(buf), "AT+CPIN=\"%s\",\"%s\"", puk, passwd);
ret = g_at_chat_send(sd->chat, buf, none_prefix,
at_pin_send_cb, cbd, NULL);
memset(buf, 0, sizeof(buf));
if (ret > 0)
return;
g_free(cbd);
CALLBACK_WITH_FAILURE(cb, data);
}
static void at_lock_unlock_cb(gboolean ok, GAtResult *result,
gpointer user_data)
{
struct cb_data *cbd = user_data;
ofono_sim_lock_unlock_cb_t cb = cbd->cb;
struct ofono_error error;
decode_at_error(&error, g_at_result_final_response(result));
cb(&error, cbd->data);
}
static const char *const at_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",
};
static void at_pin_enable(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 = cb_data_new(cb, data);
char buf[64];
int ret;
if (!(sd->passwd_type_mask & (1 << passwd_type)))
goto error;
snprintf(buf, sizeof(buf), "AT+CLCK=\"%s\",%i,\"%s\"",
at_clck_cpwd_fac[passwd_type], enable ? 1 : 0, passwd);
ret = g_at_chat_send(sd->chat, buf, none_prefix,
at_lock_unlock_cb, cbd, g_free);
memset(buf, 0, sizeof(buf));
if (ret > 0)
return;
error:
g_free(cbd);
CALLBACK_WITH_FAILURE(cb, data);
}
static void at_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);
char buf[64];
int ret;
if (!(sd->passwd_type_mask & (1 << passwd_type)))
goto error;
snprintf(buf, sizeof(buf), "AT+CPWD=\"%s\",\"%s\",\"%s\"",
at_clck_cpwd_fac[passwd_type], old_passwd, new_passwd);
ret = g_at_chat_send(sd->chat, buf, none_prefix,
at_lock_unlock_cb, cbd, g_free);
memset(buf, 0, sizeof(buf));
if (ret > 0)
return;
error:
g_free(cbd);
CALLBACK_WITH_FAILURE(cb, data);
}
static void at_lock_status_cb(gboolean ok, GAtResult *result,
gpointer user_data)
{
struct cb_data *cbd = user_data;
GAtResultIter iter;
ofono_query_facility_lock_cb_t cb = cbd->cb;
struct ofono_error error;
int locked;
decode_at_error(&error, g_at_result_final_response(result));
if (!ok) {
cb(&error, -1, cbd->data);
return;
}
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+CLCK:")) {
CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
return;
}
g_at_result_iter_next_number(&iter, &locked);
DBG("lock_status_cb: %i", locked);
cb(&error, locked, cbd->data);
}
static void at_query_clck(struct ofono_sim *sim,
enum ofono_sim_password_type passwd_type,
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);
char buf[64];
if (!(sd->passwd_type_mask & (1 << passwd_type)))
goto error;
snprintf(buf, sizeof(buf), "AT+CLCK=\"%s\",2",
at_clck_cpwd_fac[passwd_type]);
if (g_at_chat_send(sd->chat, buf, clck_prefix,
at_lock_status_cb, cbd, g_free) > 0)
return;
error:
g_free(cbd);
CALLBACK_WITH_FAILURE(cb, -1, data);
}
static void at_clck_query_cb(gboolean ok, GAtResult *result, gpointer user)
{
struct ofono_sim *sim = user;
struct sim_data *sd = ofono_sim_get_data(sim);
GAtResultIter iter;
const char *fac;
if (!ok)
goto done;
g_at_result_iter_init(&iter, result);
/* e.g. +CLCK: ("SC","FD","PN","PU","PP","PC","PF") */
if (!g_at_result_iter_next(&iter, "+CLCK:") ||
!g_at_result_iter_open_list(&iter))
goto done;
/* Clear the default mask */
sd->passwd_type_mask = 0;
/* Set the bits for <fac>s that are actually supported */
while (g_at_result_iter_next_string(&iter, &fac)) {
unsigned int i;
/* Find it in the list of known <fac>s */
for (i = 0; i < ARRAY_SIZE(at_clck_cpwd_fac); i++) {
if (!g_strcmp0(at_clck_cpwd_fac[i], fac)) {
sd->passwd_type_mask |= (1 << i);
DBG("found %s", fac);
break;
}
}
}
done:
ofono_sim_register(sim);
}
static int at_sim_probe(struct ofono_sim *sim, unsigned int vendor,
void *data)
{
GAtChat *chat = data;
struct sim_data *sd;
unsigned int i;
sd = g_new0(struct sim_data, 1);
sd->chat = g_at_chat_clone(chat);
sd->vendor = vendor;
if (sd->vendor == OFONO_VENDOR_MBM)
g_at_chat_send(sd->chat, "AT*EPEE=1", NULL, NULL, NULL, NULL);
ofono_sim_set_data(sim, sd);
/* <fac>s supported by default */
for (i = 0; i < ARRAY_SIZE(at_clck_cpwd_fac); i++)
if (at_clck_cpwd_fac[i])
sd->passwd_type_mask |= (1 << i);
/* Query supported <fac>s */
return g_at_chat_send(sd->chat, "AT+CLCK=?", clck_prefix,
at_clck_query_cb, sim, NULL) ? 0 : -1;
}
static void at_sim_remove(struct ofono_sim *sim)
{
struct sim_data *sd = ofono_sim_get_data(sim);
g_idle_remove_by_data(sim);
/* Cleanup potential SIM state polling */
at_util_sim_state_query_free(sd->sim_state_query);
ofono_sim_set_data(sim, NULL);
g_at_chat_unref(sd->chat);
g_free(sd);
}
static struct ofono_sim_driver driver = {
.name = "atmodem",
.probe = at_sim_probe,
.remove = at_sim_remove,
.read_file_info = at_sim_read_info,
.read_file_transparent = at_sim_read_binary,
.read_file_linear = at_sim_read_record,
.read_file_cyclic = at_sim_read_record,
.write_file_transparent = at_sim_update_binary,
.write_file_linear = at_sim_update_record,
.write_file_cyclic = at_sim_update_cyclic,
.read_imsi = at_read_imsi,
.query_passwd_state = at_pin_query,
.query_pin_retries = at_pin_retries_query,
.send_passwd = at_pin_send,
.reset_passwd = at_pin_send_puk,
.lock = at_pin_enable,
.change_passwd = at_change_passwd,
.query_facility_lock = at_query_clck,
};
static struct ofono_sim_driver driver_noef = {
.name = "atmodem-noef",
.probe = at_sim_probe,
.remove = at_sim_remove,
.read_imsi = at_read_imsi,
.query_passwd_state = at_pin_query,
.query_pin_retries = at_pin_retries_query,
.send_passwd = at_pin_send,
.reset_passwd = at_pin_send_puk,
.lock = at_pin_enable,
.change_passwd = at_change_passwd,
.query_facility_lock = at_query_clck,
};
void at_sim_init(void)
{
ofono_sim_driver_register(&driver);
ofono_sim_driver_register(&driver_noef);
}
void at_sim_exit(void)
{
ofono_sim_driver_unregister(&driver);
ofono_sim_driver_unregister(&driver_noef);
}