diff options
Diffstat (limited to 'drivers/isimodem/sim.c')
-rw-r--r-- | drivers/isimodem/sim.c | 584 |
1 files changed, 520 insertions, 64 deletions
diff --git a/drivers/isimodem/sim.c b/drivers/isimodem/sim.c index bfecbc9f..3ffdcebf 100644 --- a/drivers/isimodem/sim.c +++ b/drivers/isimodem/sim.c @@ -37,6 +37,8 @@ #include <ofono/log.h> #include <ofono/modem.h> #include <ofono/sim.h> + +#include "ofono.h" #include "simutil.h" #include "isimodem.h" @@ -48,7 +50,10 @@ struct sim_data { GIsiClient *client; - gboolean registered; + GIsiClient *sec_client; + enum ofono_sim_password_type passwd_state; + ofono_bool_t ready; + ofono_bool_t notify_ready; }; struct sim_imsi { @@ -75,6 +80,40 @@ struct file_info { uint8_t file_status; }; +static int sim_resp_status(const GIsiMessage *msg, uint8_t msgid, + uint8_t service) +{ + uint8_t type = 0; + uint8_t status; + + if (g_isi_msg_error(msg) < 0) { + DBG("Error: %s", strerror(-g_isi_msg_error(msg))); + return -1; + } + + if (g_isi_msg_id(msg) != msgid) { + DBG("Unexpected msg: %s", + sim_message_id_name(g_isi_msg_id(msg))); + return -1; + } + + if (!g_isi_msg_data_get_byte(msg, 1, &status) || + !g_isi_msg_data_get_byte(msg, 0, &type)) { + DBG("Runt msg: %s", sim_message_id_name(msgid)); + return -1; + } + + if (status != SIM_SERV_OK) + DBG("Request failed: %s", sim_isi_cause_name(status)); + + if (type != service) { + DBG("Unexpected service: 0x%02X", type); + return -1; + } + + return status; +} + /* Returns file info */ static gboolean fake_file_info(gpointer user) { @@ -115,30 +154,7 @@ static void isi_read_file_info(struct ofono_sim *sim, int fileid, static gboolean check_response_status(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", strerror(-g_isi_msg_error(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 != SIM_SERV_OK) { - DBG("Request failed: %s", sim_isi_cause_name(cause)); - return FALSE; - } - - if (!g_isi_msg_data_get_byte(msg, 0, &type) || type != service) { - DBG("Unexpected service: 0x%02X", type); - return FALSE; - } - return TRUE; + return sim_resp_status(msg, msgid, service) == SIM_SERV_OK; } static void spn_resp_cb(const GIsiMessage *msg, void *data) @@ -191,9 +207,6 @@ static gboolean isi_read_spn(struct ofono_sim *sim, struct isi_cb_data *cbd) 0 }; - if (sd == NULL) - return FALSE; - return g_isi_client_send(sd->client, msg, sizeof(msg), spn_resp_cb, cbd, g_free); } @@ -227,9 +240,6 @@ static gboolean isi_read_iccid(struct ofono_sim *sim, struct isi_cb_data *cbd) ICC, }; - if (sd == NULL) - return FALSE; - return g_isi_client_send(sd->client, req, sizeof(req), read_iccid_resp_cb, cbd, g_free); } @@ -315,7 +325,7 @@ static void imsi_resp_cb(const GIsiMessage *msg, void *data) struct isi_cb_data *cbd = data; ofono_sim_imsi_cb_t cb = cbd->cb; - struct sim_imsi *resp; + const struct sim_imsi *resp; size_t len = sizeof(struct sim_imsi); char imsi[SIM_MAX_IMSI_LENGTH + 1]; @@ -370,78 +380,504 @@ error: g_free(cbd); } -static void isi_sim_register(struct ofono_sim *sim) +static void isi_query_passwd_state(struct ofono_sim *sim, + ofono_sim_passwd_cb_t cb, void *data) { struct sim_data *sd = ofono_sim_get_data(sim); - if (sd && !sd->registered) { - sd->registered = TRUE; - ofono_sim_register(sim); - ofono_sim_inserted_notify(sim, TRUE); + DBG("passwd_state %u", sd->passwd_state); + + sd->notify_ready = TRUE; + + switch (sd->passwd_state) { + case OFONO_SIM_PASSWORD_NONE: + if (sd->ready) + CALLBACK_WITH_SUCCESS(cb, sd->passwd_state, data); + else + CALLBACK_WITH_FAILURE(cb, -1, data); + break; + + case OFONO_SIM_PASSWORD_INVALID: + CALLBACK_WITH_FAILURE(cb, -1, data); + break; + + default: + CALLBACK_WITH_SUCCESS(cb, sd->passwd_state, data); } } -static void read_hplmn_resp_cb(const GIsiMessage *msg, void *data) +static void sim_set_passwd_state(struct ofono_sim *sim, + enum ofono_sim_password_type pin_type) { - struct ofono_sim *sim = data; + struct sim_data *sd = ofono_sim_get_data(sim); + int inserted; + int previous; + + if (pin_type == sd->passwd_state) + return; + + DBG("new state \"%s\"", sim_password_name(pin_type)); + + inserted = pin_type != OFONO_SIM_PASSWORD_INVALID; + previous = sd->passwd_state != OFONO_SIM_PASSWORD_INVALID; + + sd->passwd_state = pin_type; + + if (pin_type != OFONO_SIM_PASSWORD_NONE) { + sd->ready = FALSE; + sd->notify_ready = FALSE; + } + + if (inserted != previous) + ofono_sim_inserted_notify(sim, inserted); +} + +static void check_sec_response(const GIsiMessage *msg, void *opaque, + uint8_t success, uint8_t failure) +{ + struct isi_cb_data *cbd = opaque; + ofono_sim_lock_unlock_cb_t cb = cbd->cb; + struct ofono_sim *sim = cbd->user; + uint8_t id; + uint8_t cause; + + if (g_isi_msg_error(msg) < 0) { + DBG("Error: %s", strerror(-g_isi_msg_error(msg))); + goto failure; + } + + id = g_isi_msg_id(msg); + + if (id == success) { + DBG("%s", sec_message_id_name(id)); + sim_set_passwd_state(sim, OFONO_SIM_PASSWORD_NONE); + CALLBACK_WITH_SUCCESS(cb, cbd->data); + return; + } + + if (id == failure && g_isi_msg_data_get_byte(msg, 0, &cause)) { + DBG("%s(cause=%02x)", sec_message_id_name(id), cause); + + if (cause == SEC_CAUSE_CODE_BLOCKED) + sim_set_passwd_state(sim, OFONO_SIM_PASSWORD_SIM_PUK); + } else + DBG("Error msg: %s", sec_message_id_name(id)); + +failure: + CALLBACK_WITH_FAILURE(cb, cbd->data); +} + +static void sec_code_verify_resp(const GIsiMessage *msg, void *opaque) +{ + check_sec_response(msg, opaque, + SEC_CODE_VERIFY_OK_RESP, SEC_CODE_VERIFY_FAIL_RESP); +} + +static void isi_send_passwd(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 isi_cb_data *cbd = isi_cb_data_new(sim, cb, data); + unsigned char msg[2 + SEC_CODE_MAX_LENGTH + 1] = { + SEC_CODE_VERIFY_REQ, + SEC_CODE_PIN, + }; + int len = 2 + strlen(passwd) + 1; + + DBG(""); + + if (!cbd) + goto error; - if (!check_response_status(msg, SIM_NETWORK_INFO_RESP, READ_HPLMN)) + strcpy((char *) msg + 2, passwd); + + if (g_isi_client_send(sd->sec_client, msg, len, + sec_code_verify_resp, cbd, g_free)) return; - isi_sim_register(sim); +error: + g_free(cbd); + CALLBACK_WITH_FAILURE(cb, data); } +static void isi_reset_passwd(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 isi_cb_data *cbd = isi_cb_data_new(sim, cb, data); + enum ofono_sim_password_type passwd_type = OFONO_SIM_PASSWORD_SIM_PIN; + unsigned char msg[2 + 2 * (SEC_CODE_MAX_LENGTH + 1)] = { + SEC_CODE_VERIFY_REQ, + }; + size_t len = sizeof(msg); + + DBG(""); + + if (!cbd) + goto error; + + if (passwd_type == OFONO_SIM_PASSWORD_SIM_PIN) + msg[1] = SEC_CODE_PUK; + else if (passwd_type == OFONO_SIM_PASSWORD_SIM_PIN2) + msg[1] = SEC_CODE_PUK2; + else + goto error; + + strcpy((char *) &msg[2], puk); + strcpy((char *) &msg[2 + SEC_CODE_MAX_LENGTH + 1], passwd); + + if (g_isi_client_send(sd->sec_client, msg, len, + sec_code_verify_resp, cbd, g_free)) + return; -static void isi_read_hplmn(struct ofono_sim *sim) +error: + g_free(cbd); + CALLBACK_WITH_FAILURE(cb, data); +} + + +/* ISI callback: Enable/disable PIN */ +static void pin_enable_resp_cb(const GIsiMessage *msg, void *opaque) +{ + check_sec_response(msg, opaque, + SEC_CODE_STATE_OK_RESP, SEC_CODE_STATE_FAIL_RESP); +} + +static void isi_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 isi_cb_data *cbd = isi_cb_data_new(sim, cb, data); - const uint8_t req[] = { - SIM_NETWORK_INFO_REQ, - READ_HPLMN, 0 + unsigned char req[3 + SEC_CODE_MAX_LENGTH + 1] = { + SEC_CODE_STATE_REQ, }; - size_t len = sizeof(req); - if (sd == NULL) + if (!cbd) + goto error; + + DBG("enable %d pintype %d pass %s", enable, passwd_type, passwd); + + if (passwd_type == OFONO_SIM_PASSWORD_SIM_PIN) + req[1] = SEC_CODE_PIN; + else if (passwd_type == OFONO_SIM_PASSWORD_SIM_PIN2) + req[1] = SEC_CODE_PIN2; + else + goto error; + + if (enable) + req[2] = SEC_CODE_ENABLE; + else + req[2] = SEC_CODE_DISABLE; + + strcpy((char *) &req[3], passwd); + + if (g_isi_client_send(sd->sec_client, req, sizeof(req), + pin_enable_resp_cb, cbd, g_free)) return; - g_isi_client_send(sd->client, req, len, read_hplmn_resp_cb, sim, NULL); +error: + g_free(cbd); + CALLBACK_WITH_FAILURE(cb, data); } -static void sim_ind_cb(const GIsiMessage *msg, void *data) + +/* ISI callback: PIN state (enabled/disabled) query */ +static void sec_code_change_resp(const GIsiMessage *msg, void *opaque) +{ + check_sec_response(msg, opaque, + SEC_CODE_CHANGE_OK_RESP, SEC_CODE_CHANGE_FAIL_RESP); +} + + +static void isi_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) { - struct ofono_sim *sim = data; struct sim_data *sd = ofono_sim_get_data(sim); + struct isi_cb_data *cbd = isi_cb_data_new(sim, cb, data); + unsigned char msg[2 + 2 * (SEC_CODE_MAX_LENGTH + 1)] = { + SEC_CODE_CHANGE_REQ, + }; + + DBG("passwd_type %d", passwd_type); + + if (!cbd) + goto error; + + if (passwd_type == OFONO_SIM_PASSWORD_SIM_PIN) + msg[1] = SEC_CODE_PIN; + else if (passwd_type == OFONO_SIM_PASSWORD_SIM_PIN2) + msg[1] = SEC_CODE_PIN2; + else + goto error; + + strcpy((char *) &msg[2], old); + strcpy((char *) &msg[2 + SEC_CODE_MAX_LENGTH + 1], new); + + if (g_isi_client_send(sd->sec_client, msg, sizeof(msg), + sec_code_change_resp, cbd, g_free)) + return; + +error: + g_free(cbd); + CALLBACK_WITH_FAILURE(cb, data); +} + + +/* ISI callback: PIN state (enabled/disabled) query */ +static void sec_code_state_resp_cb(const GIsiMessage *msg, void *opaque) +{ + check_sec_response(msg, opaque, + SEC_CODE_STATE_OK_RESP, SEC_CODE_STATE_FAIL_RESP); +} + +static void isi_query_locked(struct ofono_sim *sim, + enum ofono_sim_password_type passwd_type, + ofono_sim_locked_cb_t cb, void *data) +{ + struct sim_data *sd = ofono_sim_get_data(sim); + struct isi_cb_data *cbd = isi_cb_data_new(sim, cb, data); + + unsigned char msg[] = { + SEC_CODE_STATE_REQ, + 0, + SEC_CODE_STATE_QUERY + }; + + DBG(""); + + if (!cbd) + goto error; + + if (passwd_type == OFONO_SIM_PASSWORD_SIM_PIN) + msg[1] = SEC_CODE_PIN; + else if (passwd_type == OFONO_SIM_PASSWORD_SIM_PIN2) + msg[1] = SEC_CODE_PIN2; + else + goto error; + + if (g_isi_client_send(sd->sec_client, msg, sizeof(msg), + sec_code_state_resp_cb, cbd, g_free)) + return; + +error: + g_free(cbd); + CALLBACK_WITH_FAILURE(cb, -1, data); +} + +static void sim_ind_cb(const GIsiMessage *msg, void *opaque) +{ + struct ofono_sim *sim = opaque; + uint8_t service; uint8_t status; - if (sd == NULL || g_isi_msg_id(msg) != SIM_IND || sd->registered) + DBG(""); + + if (g_isi_msg_id(msg) != SIM_IND || + !g_isi_msg_data_get_byte(msg, 0, &service) || + !g_isi_msg_data_get_byte(msg, 1, &status)) return; - if (!g_isi_msg_data_get_byte(msg, 0, &status)) + if (status == SIM_SERV_PIN_VERIFY_REQUIRED && service == SIM_ST_PIN) + sim_set_passwd_state(sim, OFONO_SIM_PASSWORD_SIM_PIN); + else if (status == SIM_SERV_SIM_BLOCKED) + sim_set_passwd_state(sim, OFONO_SIM_PASSWORD_SIM_PUK); + else if (status == SIM_SERV_INIT_OK && service == SIM_ST_INFO) + sim_set_passwd_state(sim, OFONO_SIM_PASSWORD_NONE); + else if (status == SIM_SERV_SIM_DISCONNECTED) + sim_set_passwd_state(sim, OFONO_SIM_PASSWORD_INVALID); +} + +static void sim_server_ready_ind_cb(const GIsiMessage *msg, void *opaque) +{ + struct ofono_sim *sim = opaque; + struct sim_data *sd = ofono_sim_get_data(sim); + + DBG(""); + + if (sd == NULL || g_isi_msg_id(msg) != SIM_SERVER_READY_IND) return; - switch (status) { - case SIM_ST_PIN: - isi_sim_register(sim); + sd->ready = TRUE; + + if (sd->notify_ready) + __ofono_sim_recheck_pin(sim); +} + +static void read_dyn_flags_cb(const GIsiMessage *msg, void *opaque) +{ + struct ofono_sim *sim = opaque; + struct sim_data *sd = ofono_sim_get_data(sim); + int status; + + status = sim_resp_status(msg, SIM_DYNAMIC_FLAGS_RESP, READ_DYN_FLAGS); + + if (status < 0 || status == SIM_SERV_NOTREADY) + return; + + sim_set_passwd_state(sim, OFONO_SIM_PASSWORD_NONE); + + sd->ready = TRUE; + + if (sd->notify_ready) + __ofono_sim_recheck_pin(sim); +} + +static void read_dyn_flags_req(struct ofono_sim *sim) +{ + struct sim_data *sd = ofono_sim_get_data(sim); + + unsigned char req[] = { + SIM_DYNAMIC_FLAGS_REQ, + READ_DYN_FLAGS, + 0 + }; + + g_isi_client_send(sd->client, req, sizeof(req), + read_dyn_flags_cb, sim, NULL); +} + +static void sec_state_resp_cb(const GIsiMessage *msg, void *opaque) +{ + struct ofono_sim *sim = opaque; + uint8_t msgid; + uint8_t cause; + + if (g_isi_msg_error(msg) < 0) { + DBG("Error: %s", strerror(-g_isi_msg_error(msg))); + return; + } + + msgid = g_isi_msg_id(msg); + + if (msgid != SEC_STATE_RESP) { + DBG("Unexpected msg: %s", sec_message_id_name(msgid)); + return; + } + + if (!g_isi_msg_data_get_byte(msg, 0, &cause)) { + DBG("Runt msg: %s", sec_message_id_name(msgid)); + return; + } + + DBG("%s(cause=0x%0x)", sec_message_id_name(msgid), cause); + + switch (cause) { + case SEC_STARTUP_OK: + DBG("SEC_STARTUP_OK"); + sim_set_passwd_state(sim, OFONO_SIM_PASSWORD_NONE); + /* Check if SIM server is already ready */ + read_dyn_flags_req(sim); + break; + + case SEC_CAUSE_PIN_REQUIRED: + DBG("SEC_CAUSE_PIN_REQUIRED"); + sim_set_passwd_state(sim, OFONO_SIM_PASSWORD_SIM_PIN); + break; + + case SEC_CAUSE_PUK_REQUIRED: + DBG("SEC_CAUSE_PUK_REQUIRED"); + sim_set_passwd_state(sim, OFONO_SIM_PASSWORD_SIM_PIN); + break; + + case SEC_CAUSE_NO_SIM: + DBG("SEC_CAUSE_NO_SIM"); break; - case SIM_ST_INFO: - isi_read_hplmn(sim); + case SEC_CAUSE_INVALID_SIM: + DBG("SEC_CAUSE_INVALID_SIM"); break; + + case SEC_CAUSE_SIM_REJECTED: + DBG("SEC_CAUSE_SIM_REJECTED"); + break; + + default: + break; + } +} + +static void isi_sec_state_req(struct ofono_sim *sim) +{ + struct sim_data *sd = ofono_sim_get_data(sim); + + unsigned char req[] = { + SEC_STATE_REQ, + 0, + 0 + }; + + g_isi_client_send(sd->sec_client, req, sizeof(req), + sec_state_resp_cb, sim, NULL); +} + +static void sim_status_resp_cb(const GIsiMessage *msg, void *opaque) +{ + struct ofono_sim *sim = opaque; + struct sim_data *sd = ofono_sim_get_data(sim); + int status = sim_resp_status(msg, SIM_STATUS_RESP, SIM_ST_CARD_STATUS); + + if (status < 0 || status == SIM_SERV_SIM_DISCONNECTED) + return; + + /* We probably have a SIM. */ + if (sd->sec_client) + isi_sec_state_req(sim); + else + read_dyn_flags_req(sim); +} + +static void isi_sim_status_req(struct ofono_sim *sim) +{ + struct sim_data *sd = ofono_sim_get_data(sim); + const unsigned char req[] = { + SIM_STATUS_REQ, + SIM_ST_CARD_STATUS + }; + + g_isi_client_send(sd->client, req, sizeof(req), + sim_status_resp_cb, sim, NULL); +} + +static void sec_reachable_cb(const GIsiMessage *msg, void *data) +{ + struct ofono_sim *sim = data; + struct sim_data *sd = ofono_sim_get_data(sim); + + if (g_isi_msg_error(msg) < 0) { + DBG("PN_SECURITY: %s", strerror(-g_isi_msg_error(msg))); + DBG("PIN code handling not available"); + g_isi_client_destroy(sd->sec_client); + sd->sec_client = NULL; } + + g_isi_client_ind_subscribe(sd->client, SIM_IND, sim_ind_cb, sim); + g_isi_client_ind_subscribe(sd->client, SIM_SERVER_READY_IND, + sim_server_ready_ind_cb, sim); + /* Check if we have a SIM */ + isi_sim_status_req(sim); + + ofono_sim_register(sim); } static void sim_reachable_cb(const GIsiMessage *msg, void *data) { struct ofono_sim *sim = data; + struct sim_data *sd = ofono_sim_get_data(sim); - if (g_isi_msg_error(msg) < 0) + if (g_isi_msg_error(msg) < 0) { + DBG("PN_SIM: %s", strerror(-g_isi_msg_error(msg))); return; + } ISI_VERSION_DBG(msg); - /* Check if SIM is ready by reading HPLMN */ - isi_read_hplmn(sim); + g_isi_client_verify(sd->sec_client, sec_reachable_cb, sim, NULL); } static int isi_sim_probe(struct ofono_sim *sim, unsigned int vendor, @@ -454,11 +890,18 @@ static int isi_sim_probe(struct ofono_sim *sim, unsigned int vendor, if (sd == NULL) return -ENOMEM; + sd->passwd_state = OFONO_SIM_PASSWORD_INVALID; + sd->client = g_isi_client_create(modem, PN_SIM); - if (sd->client == NULL) { - g_free(sd); - return -ENOMEM; - } + if (sd->client == NULL) + goto error; + + sd->sec_client = g_isi_client_create(modem, PN_SECURITY); + if (sd->sec_client == NULL) + goto error; + + g_isi_client_set_timeout(sd->client, SIM_TIMEOUT); + g_isi_client_set_timeout(sd->sec_client, SIM_TIMEOUT); ofono_sim_set_data(sim, sd); @@ -466,6 +909,12 @@ static int isi_sim_probe(struct ofono_sim *sim, unsigned int vendor, g_isi_client_verify(sd->client, sim_reachable_cb, sim, NULL); return 0; + +error: + g_isi_client_destroy(sd->client); + g_isi_client_destroy(sd->sec_client); + + return -ENOMEM; } static void isi_sim_remove(struct ofono_sim *sim) @@ -478,6 +927,7 @@ static void isi_sim_remove(struct ofono_sim *sim) return; g_isi_client_destroy(data->client); + g_isi_client_destroy(data->sec_client); g_free(data); } @@ -493,6 +943,12 @@ static struct ofono_sim_driver driver = { .write_file_linear = isi_write_file_linear, .write_file_cyclic = isi_write_file_cyclic, .read_imsi = isi_read_imsi, + .query_passwd_state = isi_query_passwd_state, + .send_passwd = isi_send_passwd, + .reset_passwd = isi_reset_passwd, + .lock = isi_lock, + .change_passwd = isi_change_passwd, + .query_locked = isi_query_locked, }; void isi_sim_init(void) |