diff options
Diffstat (limited to 'sim.c')
-rw-r--r-- | sim.c | 695 |
1 files changed, 695 insertions, 0 deletions
@@ -0,0 +1,695 @@ +/* + * This file is part of QMI-RIL. + * + * Copyright (C) 2017 Wolfgang Wiedmeyer <wolfgit@wiedmeyer.de> + * + * QMI-RIL is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * QMI-RIL 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 QMI-RIL. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> + +#define LOG_TAG "RIL" +#include <utils/Log.h> + +#include <qmi-ril.h> +#include <sim.h> + +RIL_RadioState qmi2ril_pin_status_response(QmiDmsUimPinStatus pin_status, + RIL_CardStatus_v6 *card_status) +{ + RIL_AppStatus app_statuses[] = { + // Absent + { RIL_APPTYPE_UNKNOWN, RIL_APPSTATE_UNKNOWN, RIL_PERSOSUBSTATE_UNKNOWN, NULL, NULL, 0, RIL_PINSTATE_UNKNOWN, RIL_PINSTATE_UNKNOWN }, + // Not ready + { RIL_APPTYPE_USIM, RIL_APPSTATE_DETECTED, RIL_PERSOSUBSTATE_UNKNOWN, NULL, NULL, 0, RIL_PINSTATE_UNKNOWN, RIL_PINSTATE_UNKNOWN }, + // Ready + { RIL_APPTYPE_USIM, RIL_APPSTATE_READY, RIL_PERSOSUBSTATE_READY, NULL, NULL, 0, RIL_PINSTATE_UNKNOWN, RIL_PINSTATE_UNKNOWN }, + // PIN lock + { RIL_APPTYPE_USIM, RIL_APPSTATE_PIN, RIL_PERSOSUBSTATE_UNKNOWN, NULL, NULL, 0, RIL_PINSTATE_ENABLED_NOT_VERIFIED, RIL_PINSTATE_UNKNOWN }, + // PUK lock + { RIL_APPTYPE_USIM, RIL_APPSTATE_PUK, RIL_PERSOSUBSTATE_UNKNOWN, NULL, NULL, 0, RIL_PINSTATE_ENABLED_BLOCKED, RIL_PINSTATE_UNKNOWN }, + // PUK locked + { RIL_APPTYPE_USIM, RIL_APPSTATE_PUK, RIL_PERSOSUBSTATE_UNKNOWN, NULL, NULL, 0, RIL_PINSTATE_ENABLED_PERM_BLOCKED, RIL_PINSTATE_UNKNOWN }, + }; + RIL_RadioState radio_state; + unsigned int index; + unsigned int count; + + count = sizeof(app_statuses) / sizeof(RIL_AppStatus); + + switch (pin_status) { + case QMI_DMS_UIM_PIN_STATUS_NOT_INITIALIZED: + index = 0; + break; + case QMI_DMS_UIM_PIN_STATUS_ENABLED_NOT_VERIFIED: + index = 3; + break; + case QMI_DMS_UIM_PIN_STATUS_ENABLED_VERIFIED: + case QMI_DMS_UIM_PIN_STATUS_DISABLED: + index = 2; + break; + default: + index = 0; + } + + switch (index) { + case 1: + radio_state = RADIO_STATE_SIM_NOT_READY; + break; + case 2: + radio_state = RADIO_STATE_SIM_READY; + break; + default: + radio_state = RADIO_STATE_SIM_LOCKED_OR_ABSENT; + } + + memset(card_status, 0, sizeof(RIL_CardStatus_v6)); + + if (index == 0) + card_status->card_state = RIL_CARDSTATE_ABSENT; + else + card_status->card_state = RIL_CARDSTATE_PRESENT; + + card_status->universal_pin_state = RIL_PINSTATE_UNKNOWN; + + card_status->cdma_subscription_app_index = -1; + card_status->ims_subscription_app_index = -1; + + memcpy((void *) &card_status->applications[0], (void *) &app_statuses[index], sizeof(RIL_AppStatus)); + + card_status->gsm_umts_subscription_app_index = 0; + card_status->num_applications = 1; + + RIL_LOGD("%s: Selecting status application %d on %d", __func__, + index, count); + + return radio_state; +} + +static void uim_get_pin_status_ready(QmiClientDms *client, + GAsyncResult *res, + RIL_Token token) +{ + RIL_CardStatus_v6 card_status; + RIL_RadioState radio_state; + guint8 verify_retries_left; + guint8 unblock_retries_left; + QmiDmsUimPinStatus pin1_status; + QmiDmsUimPinStatus pin2_status; + QmiMessageDmsUimGetPinStatusOutput *output; + GError *error = NULL; + int rc; + + output = qmi_client_dms_uim_get_pin_status_finish(client, res, + &error); + if (!output) { + RIL_LOGE("%s: error: operation failed: %s", __func__, + error->message); + goto error; + } + + if (!qmi_message_dms_uim_get_pin_status_output_get_result(output, &error)) { + RIL_LOGE("error: couldn't get PIN status: %s", error->message); + + // assume not initialized + pin1_status = QMI_DMS_UIM_PIN_STATUS_NOT_INITIALIZED; + goto evaluation; + } + + RIL_LOGD("PIN status retrieved successfully"); + + if (qmi_message_dms_uim_get_pin_status_output_get_pin1_status ( + output, &pin1_status, &verify_retries_left, + &unblock_retries_left, NULL)) { + RIL_LOGD("PIN1:\n" + "\tStatus: %s\n" + "\tVerify: %u\n" + "\tUnblock: %u", + qmi_dms_uim_pin_status_get_string(pin1_status), + verify_retries_left, unblock_retries_left); + } + + if (qmi_message_dms_uim_get_pin_status_output_get_pin2_status( + output, &pin2_status, &verify_retries_left, + &unblock_retries_left, NULL)) { + RIL_LOGD("PIN2:\n" + "\tStatus: %s\n" + "\tVerify: %u\n" + "\tUnblock: %u", + qmi_dms_uim_pin_status_get_string(pin2_status), + verify_retries_left, unblock_retries_left); + } + +evaluation: + radio_state = qmi2ril_pin_status_response(pin1_status, &card_status); + + if (radio_state == 0) + goto error; + + ril_radio_state_update(radio_state); + + ril_request_complete(token, RIL_E_SUCCESS, (void *) &card_status, sizeof(card_status)); + goto complete; + +error: + ril_request_complete(token, RIL_E_GENERIC_FAILURE, NULL, 0); + +complete: + if (error) + g_error_free(error); + if (output) + qmi_message_dms_uim_get_pin_status_output_unref(output); +} + +int ril_request_get_sim_status(void *data, size_t size, RIL_Token token) +{ + struct ril_request *request; + int rc; + + rc = ril_radio_state_check(RADIO_STATE_SIM_NOT_READY); + if (rc < 0) + return RIL_REQUEST_UNHANDLED; + + request = ril_request_find_request_status(RIL_REQUEST_GET_SIM_STATUS, RIL_REQUEST_HANDLED); + if (request != NULL) + return RIL_REQUEST_UNHANDLED; + + qmi_client_dms_uim_get_pin_status(ctx->DmsClient, NULL, 10, + ctx->cancellable, + (GAsyncReadyCallback)uim_get_pin_status_ready, + token); + + return RIL_REQUEST_HANDLED; +} + +int ril_request_query_facility_lock(void *data, size_t size, RIL_Token token) +{ + int active; + int rc; + + if (data == NULL || size < 4 * sizeof(char *)) + goto error; + + rc = ril_radio_state_check(RADIO_STATE_SIM_READY); + if (rc < 0) + return RIL_REQUEST_UNHANDLED; + + RIL_LOGE("%s: TODO: implement me!", __func__); + // let's assume it's inactive + // corresponding libqmi call fails with my SIM card, so I + // can't test it anyway + active = 0; + ril_request_complete(token, RIL_E_SUCCESS, &active, sizeof(active)); + + rc = RIL_REQUEST_COMPLETED; + goto complete; + +error: + ril_request_complete(token, RIL_E_GENERIC_FAILURE, NULL, 0); + + rc = RIL_REQUEST_COMPLETED; + +complete: + return rc; +} + +static void read_transparent_ready(QmiClientUim *client, + GAsyncResult *res, + RIL_Token token) +{ + QmiMessageUimReadTransparentOutput *output; + GError *error = NULL; + guint8 sw1 = 0; + guint8 sw2 = 0; + GArray *read_result = NULL; + gchar *str = NULL; + RIL_SIM_IO_Response response; + + response.simResponse = NULL; + + output = qmi_client_uim_read_transparent_finish(client, res, + &error); + if (!output) { + RIL_LOGE("%s: error: operation failed: %s", __func__, + error->message); + goto error; + } + + if (!qmi_message_uim_read_transparent_output_get_result( + output, &error)) { + RIL_LOGE("error: couldn't read transparent file from the UIM: %s", + error->message); + goto card_result; + } + + RIL_LOGD("successfully read information from the UIM"); + + /* Read result */ + if (qmi_message_uim_read_transparent_output_get_read_result( + output, &read_result, NULL)) { + str = array2string(read_result); + RIL_LOGD("Read result:\n" + "%s", str); + + response.simResponse = str; + } + +card_result: + if (qmi_message_uim_read_transparent_output_get_card_result( + output, &sw1, &sw2, + NULL)) { + RIL_LOGD("Card result:\n" + "\tSW1: '0x%02x'\n" + "\tSW2: '0x%02x'", sw1, sw2); + response.sw1 = sw1; + response.sw2 = sw2; + } else + goto error; + + ril_request_complete(token, RIL_E_SUCCESS, (void *) &response, sizeof(response)); + goto complete; + +error: + ril_request_complete(token, RIL_E_GENERIC_FAILURE, NULL, 0); + +complete: + if (str) + g_free(str); + if (error) + g_error_free(error); + if (output) + qmi_message_uim_read_transparent_output_unref(output); +} + +static void read_record_ready(QmiClientUim *client, GAsyncResult *res, + RIL_Token token) +{ + QmiMessageUimReadRecordOutput *output; + GError *error = NULL; + guint8 sw1 = 0; + guint8 sw2 = 0; + GArray *read_result = NULL; + gchar *str = NULL; + RIL_SIM_IO_Response response; + + response.simResponse = NULL; + + output = qmi_client_uim_read_record_finish(client, res, + &error); + if (!output) { + RIL_LOGE("%s: error: operation failed: %s", __func__, + error->message); + goto error; + } + + if (!qmi_message_uim_read_record_output_get_result( + output, &error)) { + RIL_LOGE("error: couldn't read record file from the UIM: %s", + error->message); + + goto card_result; + } + + RIL_LOGD("successfully read information from the UIM"); + + /* Read result */ + if (qmi_message_uim_read_record_output_get_read_result( + output, &read_result, NULL)) { + str = array2string(read_result); + RIL_LOGD("Read result:\n" + "%s", str); + + response.simResponse = str; + } + +card_result: + if (qmi_message_uim_read_record_output_get_card_result( + output, &sw1, &sw2, NULL)) { + RIL_LOGD("Card result:\n" + "\tSW1: '0x%02x'\n" + "\tSW2: '0x%02x'", sw1, sw2); + response.sw1 = sw1; + response.sw2 = sw2; + } else + goto error; + + ril_request_complete(token, RIL_E_SUCCESS, (void *) &response, sizeof(response)); + goto complete; + +error: + ril_request_complete(token, RIL_E_GENERIC_FAILURE, NULL, 0); + +complete: + if (str) + g_free(str); + if (error) + g_error_free(error); + if (output) + qmi_message_uim_read_record_output_unref(output); +} + +static void get_file_attributes_ready(QmiClientUim *client, + GAsyncResult *res, + RIL_Token token) +{ + QmiMessageUimGetFileAttributesOutput *output; + GError *error = NULL; + guint8 sw1 = 0; + guint8 sw2 = 0; + guint16 file_size, file_id; + QmiUimFileType file_type; + guint16 record_size; + guint16 record_count; + struct sim_file_response sim_file_response; + RIL_SIM_IO_Response response; + + memset(&sim_file_response, 0, sizeof(sim_file_response)); + response.simResponse = NULL; + + output = qmi_client_uim_get_file_attributes_finish(client, res, &error); + if (!output) { + RIL_LOGE("%s: error: operation failed: %s\n", __func__, + error->message); + goto error; + } + + if (!qmi_message_uim_get_file_attributes_output_get_result(output, &error)) { + RIL_LOGE("error: couldn't get file attributes from the UIM: %s", + error->message); + + goto card_result; + } + + RIL_LOGD("Successfully got file attributes from the UIM"); + + if (qmi_message_uim_get_file_attributes_output_get_file_attributes( + output, + &file_size, &file_id, &file_type, &record_size, + &record_count, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL)) { + + RIL_LOGD("File attributes:\n" + "\tFile size: %u\n" + "\tFile ID: %u\n" + "\tFile type: %s\n" + "\tRecord size: %u\n" + "\tRecord count: %u", (guint)file_size, + (guint)file_id, qmi_uim_file_type_get_string(file_type), + (guint)record_size, (guint)record_count); + + sim_file_response.file_size = file_size; + sim_file_response.file_id = file_id; + sim_file_response.access_condition[0] = 0x00; + sim_file_response.access_condition[1] = 0xff; + sim_file_response.access_condition[2] = 0xff; + + sim_file_response.file_status = 0x01; + sim_file_response.file_length = 0x02; + + switch (file_type) { + case QMI_UIM_FILE_TYPE_TRANSPARENT: + sim_file_response.file_type = SIM_FILE_TYPE_EF; + sim_file_response.file_structure = SIM_FILE_STRUCTURE_TRANSPARENT; + break; + case QMI_UIM_FILE_TYPE_DEDICATED_FILE: + sim_file_response.file_type = SIM_FILE_TYPE_DF; + sim_file_response.file_structure = SIM_FILE_STRUCTURE_LINEAR_FIXED; + break; + case QMI_UIM_FILE_TYPE_MASTER_FILE: + sim_file_response.file_type = SIM_FILE_TYPE_MF; + sim_file_response.file_structure = SIM_FILE_STRUCTURE_LINEAR_FIXED; + break; + case QMI_UIM_FILE_TYPE_LINEAR_FIXED: + default: + sim_file_response.file_type = SIM_FILE_TYPE_EF; + sim_file_response.file_structure = SIM_FILE_STRUCTURE_LINEAR_FIXED; + } + + sim_file_response.record_length = record_size; + response.simResponse = data2string((void *) &sim_file_response, sizeof(sim_file_response)); + } + +card_result: + if (qmi_message_uim_get_file_attributes_output_get_card_result( + output, &sw1, &sw2, NULL)) { + RIL_LOGD("Card result:\n" + "\tSW1: '0x%02x'\n" + "\tSW2: '0x%02x'", sw1, sw2); + response.sw1 = sw1; + response.sw2 = sw2; + } else { + // assume file wasn't found + response.sw1 = 0x6a; + response.sw2 = 0x82; + } + + ril_request_complete(token, RIL_E_SUCCESS, (void *) &response, sizeof(response)); + goto complete; + +error: + ril_request_complete(token, RIL_E_GENERIC_FAILURE, NULL, 0); + +complete: + if (response.simResponse != NULL) + free(response.simResponse); + if (error) + g_error_free(error); + if (output) + qmi_message_uim_get_file_attributes_output_unref(output); +} + +int ril2qmi_sim_file_path(const gchar *file_path_str, GArray **file_path) +{ + guint str_len; + guint array_elements; + gchar str_item[4]; + gulong path_item; + guint8 val; + guint i; + + str_len = strlen(file_path_str); + + if (str_len == 0) { + // assume it's 3F00 (root file) + file_path_str = "3F00"; + str_len = 4; + } else if (str_len < 4 || str_len % 2 != 0) { + RIL_LOGE("file path error: len %d string %s", str_len, + file_path_str); + return -1; + } + + array_elements = strlen(file_path_str) / 4; + + *file_path = g_array_sized_new(FALSE, FALSE, sizeof (guint8), + array_elements); + + for (i = 0; i < array_elements; i++) { + strncpy(str_item, &file_path_str[i*4], 4); + path_item = (strtoul(str_item, NULL, 16)) & 0xFFFF; + + val = path_item & 0xFF; + g_array_append_val(*file_path, val); + val = (path_item >> 8) & 0xFF; + g_array_append_val(*file_path, val); + } + + return 0; +} + +int ril2qmi_get_attributes(RIL_SIM_IO_v6 *sim_io, RIL_Token token) +{ + QmiMessageUimGetFileAttributesInput *input; + GArray *file_path = NULL; + int rc; + + rc = ril2qmi_sim_file_path(sim_io->path, &file_path); + if (rc < 0) { + RIL_LOGE("%s: error extracting file path", __func__); + return -1; + } + + input = qmi_message_uim_get_file_attributes_input_new(); + qmi_message_uim_get_file_attributes_input_set_session_information( + input, QMI_UIM_SESSION_TYPE_PRIMARY_GW_PROVISIONING, + "", NULL); + qmi_message_uim_get_file_attributes_input_set_file( + input, sim_io->fileid, file_path, NULL); + + qmi_client_uim_get_file_attributes(ctx->UimClient, input, 10, + ctx->cancellable, + (GAsyncReadyCallback)get_file_attributes_ready, + token); + + qmi_message_uim_get_file_attributes_input_unref(input); + g_array_unref(file_path); + + return 0; +} + +int ril2qmi_read_record(RIL_SIM_IO_v6 *sim_io, RIL_Token token) +{ + QmiMessageUimReadRecordInput *input; + GArray *file_path = NULL; + int rc; + + rc = ril2qmi_sim_file_path(sim_io->path, &file_path); + if (rc < 0) { + RIL_LOGE("%s: error extracting file path", __func__); + return -1; + } + + input = qmi_message_uim_read_record_input_new(); + qmi_message_uim_read_record_input_set_session_information( + input, QMI_UIM_SESSION_TYPE_PRIMARY_GW_PROVISIONING, + "", NULL); + qmi_message_uim_read_record_input_set_file( + input, sim_io->fileid, file_path, NULL); + qmi_message_uim_read_record_input_set_record( + input,sim_io->p1, sim_io->p3, NULL); + g_array_unref(file_path); + + qmi_client_uim_read_record(ctx->UimClient, input, 10, + ctx->cancellable, + (GAsyncReadyCallback)read_record_ready, + token); + + qmi_message_uim_read_record_input_unref(input); + return 0; +} + +int ril2qmi_read_transparent(RIL_SIM_IO_v6 *sim_io, RIL_Token token) +{ + QmiMessageUimReadTransparentInput *input; + GArray *file_path = NULL; + int rc; + + rc = ril2qmi_sim_file_path(sim_io->path, &file_path); + if (rc < 0) { + RIL_LOGE("%s: error extracting file path", __func__); + return -1; + } + + input = qmi_message_uim_read_transparent_input_new(); + qmi_message_uim_read_transparent_input_set_session_information( + input, QMI_UIM_SESSION_TYPE_PRIMARY_GW_PROVISIONING, + "", NULL); + qmi_message_uim_read_transparent_input_set_file(input, + sim_io->fileid, + file_path, NULL); + qmi_message_uim_read_transparent_input_set_read_information( + input, + // p1 is offset high, p2 is offset low, which one is it? + sim_io->p1, + sim_io->p3, NULL); + g_array_unref (file_path); + + qmi_client_uim_read_transparent(ctx->UimClient, input, 10, + ctx->cancellable, + (GAsyncReadyCallback)read_transparent_ready, + token); + + qmi_message_uim_read_transparent_input_unref(input); + return 0; +} + +int ril_request_sim_io(void *data, size_t size, RIL_Token token) +{ + struct ril_request *request; + RIL_SIM_IO_v6 *sim_io = NULL; + int rc; + + if (data == NULL || size < sizeof(RIL_SIM_IO_v6)) + goto error; + + rc = ril_radio_state_check(RADIO_STATE_SIM_READY); + if (rc < 0) { + rc = ril_radio_state_check(RADIO_STATE_SIM_LOCKED_OR_ABSENT); + if (rc < 0) + return RIL_REQUEST_UNHANDLED; + else + RIL_LOGE("%s: SIM is locked or absent.", __func__); + } + + request = ril_request_find_request_status(RIL_REQUEST_SIM_IO, RIL_REQUEST_HANDLED); + if (request != NULL) + return RIL_REQUEST_UNHANDLED; + + sim_io = (RIL_SIM_IO_v6 *) data; + + if (sim_io->data != NULL) { + RIL_LOGE("TODO: implement processing for data %s", + sim_io->data); + } + + if (sim_io->pin2 != NULL) { + RIL_LOGE("TODO: implement PIN2 unlock first for PIN %s", + sim_io->pin2); + } + + switch (sim_io->command) { + case SIM_COMMAND_GET_RESPONSE: + RIL_LOGD("get response command"); + rc = ril2qmi_get_attributes(sim_io, token); + if (rc < 0) + goto error; + break; + case SIM_COMMAND_READ_BINARY: + RIL_LOGD("read binary command"); + rc = ril2qmi_read_transparent(sim_io, token); + if (rc < 0) + goto error; + + break; + case SIM_COMMAND_READ_RECORD: + RIL_LOGD("read record command"); + rc = ril2qmi_read_record(sim_io, token); + if (rc < 0) + goto error; + + break; + case SIM_COMMAND_UPDATE_BINARY: + case SIM_COMMAND_UPDATE_RECORD: + case SIM_COMMAND_SEEK: + default: + RIL_LOGD("%s: command %d not supported", __func__, + sim_io->command); + goto error; + } + + rc = RIL_REQUEST_HANDLED; + goto complete; + +error: + ril_request_complete(token, RIL_E_GENERIC_FAILURE, NULL, 0); + + rc = RIL_REQUEST_COMPLETED; + +complete: + if (sim_io != NULL) { + if (sim_io->path != NULL) + free(sim_io->path); + + if (sim_io->data != NULL) + free(sim_io->data); + + if (sim_io->pin2 != NULL) + free(sim_io->pin2); + + if (sim_io->aidPtr != NULL) + free(sim_io->aidPtr); + } + + return rc; +} |