/* * This file is part of QMI-RIL. * * Copyright (C) 2017 Wolfgang Wiedmeyer * * 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 . */ #include #define LOG_TAG "RIL" #include #include #include 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; }