/* * 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 #include #define LOG_TAG "RIL" #include #include #include void serving_system_indication_cb(QmiClientNas *client, QmiIndicationNasServingSystemOutput *output) { RIL_LOGD("Got serving system indication"); ril_request_unsolicited(RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED, NULL, 0); } int qmi2ril_signal_strength(gint8 qmi_strength) { int ril_strength; ril_strength = (int) qmi_strength / 2 + 56; if (ril_strength < 0) ril_strength = 0; else if (ril_strength > 31) ril_strength = 31; return ril_strength; } void nas_event_report_indication_cb(QmiClientNas *client, QmiIndicationNasEventReportOutput *output) { RIL_SignalStrength_v6 ril_strength; gint8 qmi_strength, rsrq; gint16 rsrp, snr; int rc; rc = ril_radio_state_check(RADIO_STATE_SIM_NOT_READY); if (rc < 0) return; qmi_indication_nas_event_report_output_get_signal_strength( output, &qmi_strength, NULL, NULL); RIL_LOGD("Successfully got signal strength through indication\n" "Current: '%d dBm'", qmi_strength); /* RSRQ */ if (qmi_indication_nas_event_report_output_get_rsrq( output, &rsrq, NULL, NULL)) { RIL_LOGD("RSRQ: '%d dB'", rsrq); } else return; /* LTE SNR */ if (qmi_indication_nas_event_report_output_get_lte_snr( output, &snr, NULL)) { RIL_LOGD("SNR: '%.1lf dB'", (0.1) * ((gdouble)snr)); } else return; /* LTE RSRP */ if (qmi_indication_nas_event_report_output_get_lte_rsrp( output, &rsrp, NULL)) { RIL_LOGD("RSRP: '%d dBm'", rsrp); } else return; memset(&ril_strength, -1, sizeof(RIL_SignalStrength_v6)); ril_strength.LTE_SignalStrength.signalStrength = qmi2ril_signal_strength(qmi_strength); ril_strength.LTE_SignalStrength.rsrp = -rsrp; ril_strength.LTE_SignalStrength.rsrq = -rsrq; ril_strength.LTE_SignalStrength.rssnr = snr; ril_strength.LTE_SignalStrength.cqi = INT_MAX; ril_request_unsolicited(RIL_UNSOL_SIGNAL_STRENGTH, (void *) &ril_strength, sizeof(ril_strength)); RIL_LOGD("reported signal strength"); } static void get_signal_strength_ready(QmiClientNas *client, GAsyncResult *res, RIL_Token token) { RIL_SignalStrength_v6 ril_strength; QmiMessageNasGetSignalStrengthOutput *output; GError *error = NULL; gint8 qmi_strength, rsrq; gint16 rsrp, snr; int rc; output = qmi_client_nas_get_signal_strength_finish(client, res, &error); if (!output) { RIL_LOGE("%s: error: operation failed: %s", __func__, error->message); goto error; } if (!qmi_message_nas_get_signal_strength_output_get_result(output, &error)) { RIL_LOGE("%s: error: couldn't get signal strength: %s", __func__, error->message); goto error; } qmi_message_nas_get_signal_strength_output_get_signal_strength( output, &qmi_strength, NULL, NULL); RIL_LOGD("Successfully got signal strength\n" "Current: '%d dBm'", qmi_strength); /* RSRQ */ if (qmi_message_nas_get_signal_strength_output_get_rsrq( output, &rsrq, NULL, NULL)) { RIL_LOGD("RSRQ: '%d dB'", rsrq); } else goto error; /* LTE SNR */ if (qmi_message_nas_get_signal_strength_output_get_lte_snr( output, &snr, NULL)) { RIL_LOGD("SNR: '%.1lf dB'", (0.1) * ((gdouble)snr)); } else goto error; /* LTE RSRP */ if (qmi_message_nas_get_signal_strength_output_get_lte_rsrp( output, &rsrp, NULL)) { RIL_LOGD("RSRP: '%d dBm'", rsrp); } else goto error; memset(&ril_strength, -1, sizeof(RIL_SignalStrength_v6)); ril_strength.LTE_SignalStrength.signalStrength = qmi2ril_signal_strength(qmi_strength); ril_strength.LTE_SignalStrength.rsrp = -rsrp; ril_strength.LTE_SignalStrength.rsrq = -rsrq; ril_strength.LTE_SignalStrength.rssnr = snr; ril_strength.LTE_SignalStrength.cqi = INT_MAX; ril_request_complete(token, RIL_E_SUCCESS, (void *) &ril_strength, sizeof(ril_strength)); goto complete; error: ril_request_complete(token, RIL_E_GENERIC_FAILURE, NULL, 0); complete: if (error) g_error_free(error); if (output) qmi_message_nas_get_signal_strength_output_unref(output); } static QmiMessageNasGetSignalStrengthInput *ril2qmi_signal_strength_input_create(void) { GError *error = NULL; QmiMessageNasGetSignalStrengthInput *input; QmiNasSignalStrengthRequest mask; mask = (QMI_NAS_SIGNAL_STRENGTH_REQUEST_RSRQ | QMI_NAS_SIGNAL_STRENGTH_REQUEST_LTE_SNR | QMI_NAS_SIGNAL_STRENGTH_REQUEST_LTE_RSRP); input = qmi_message_nas_get_signal_strength_input_new(); if (!qmi_message_nas_get_signal_strength_input_set_request_mask( input, mask, &error)) { RIL_LOGE("error: couldn't create input data bundle: '%s'", error->message); g_error_free(error); qmi_message_nas_get_signal_strength_input_unref(input); input = NULL; } return input; } int ril_request_signal_strength(void *data, size_t size, RIL_Token token) { QmiMessageNasGetSignalStrengthInput *input; int rc; rc = ril_radio_state_check(RADIO_STATE_SIM_NOT_READY); if (rc < 0) return RIL_REQUEST_UNHANDLED; input = ril2qmi_signal_strength_input_create(); qmi_client_nas_get_signal_strength(ctx->NasClient, input, 10, ctx->cancellable, (GAsyncReadyCallback)get_signal_strength_ready, token); qmi_message_nas_get_signal_strength_input_unref(input); return RIL_REQUEST_HANDLED; } static void get_system_selection_preference_ready(QmiClientNas *client, GAsyncResult *res, RIL_Token token) { QmiMessageNasGetSystemSelectionPreferenceOutput *output; GError *error = NULL; QmiNasNetworkSelectionPreference qmi_network_selection; int ril_network_selection; output = qmi_client_nas_get_system_selection_preference_finish( client, res, &error); if (!output) { RIL_LOGE("%s: error: operation failed: %s", __func__, error->message); goto error; } if (!qmi_message_nas_get_system_selection_preference_output_get_result(output, &error)) { RIL_LOGE("error: couldn't get system_selection preference: %s", error->message); goto error; } RIL_LOGD("Successfully got system selection preference"); if (qmi_message_nas_get_system_selection_preference_output_get_network_selection_preference( output, &qmi_network_selection, NULL)) { RIL_LOGD("Network selection preference: '%s'", qmi_nas_network_selection_preference_get_string(qmi_network_selection)); switch (qmi_network_selection) { case QMI_NAS_NETWORK_SELECTION_PREFERENCE_AUTOMATIC: ril_network_selection = 0; case QMI_NAS_NETWORK_SELECTION_PREFERENCE_MANUAL: ril_network_selection = 1; } } else goto error; ril_request_complete(token, RIL_E_SUCCESS, (void *) &ril_network_selection, sizeof(ril_network_selection)); goto complete; error: ril_request_complete(token, RIL_E_GENERIC_FAILURE, NULL, 0); complete: if (error) g_error_free(error); if (output) qmi_message_nas_get_system_selection_preference_output_unref(output); } int ril_request_query_network_selection_mode(void *data, size_t size, RIL_Token token) { int rc; rc = ril_radio_state_check(RADIO_STATE_SIM_NOT_READY); if (rc < 0) return RIL_REQUEST_UNHANDLED; qmi_client_nas_get_system_selection_preference(ctx->NasClient, NULL, 10, ctx->cancellable, (GAsyncReadyCallback)get_system_selection_preference_ready, token); return RIL_REQUEST_HANDLED; } int ril_request_set_preferred_network_type(void *data, size_t size, RIL_Token token) { int type; int rc; if (data == NULL || size < sizeof(int)) goto error; type = *((int *) data); RIL_LOGD("request for setting %d as preferred network type", type); RIL_LOGE("%s: TODO: implement me!", __func__); ril_request_complete(token, RIL_E_SUCCESS, NULL, 0); rc = RIL_REQUEST_COMPLETED; goto complete; error: ril_request_complete(token, RIL_E_GENERIC_FAILURE, NULL, 0); rc = RIL_REQUEST_COMPLETED; complete: return rc; } int qmi2ril_net_operator(QmiMessageNasGetOperatorNameOutputOperatorPlmnListElement *element, char **operator_long, char **operator_short, char **plmn) { char buffer[7] = { 0 }; unsigned int mcc = 0; unsigned int mnc = 0; unsigned int i; int rc; *plmn = NULL; strncpy(buffer, element->mcc, 3); strncpy(&buffer[3], element->mnc, 3); for (i = 0; i < 7; i++) { if (!isdigit(buffer[i])) { buffer[i] = '\0'; break; } } if (buffer[0] == '\0') goto error; *plmn = strdup(buffer); *operator_long = NULL; *operator_short = NULL; if (buffer[5] == '\0') rc = sscanf((char *) &buffer, "%3u%2u", &mcc, &mnc); else rc = sscanf((char *) &buffer, "%3u%3u", &mcc, &mnc); if (rc < 2) goto error; for (i = 0 ; i < plmn_list_count ; i++) { if (plmn_list[i].mcc == mcc && plmn_list[i].mnc == mnc) { *operator_long = strdup(plmn_list[i].operator_long); *operator_short = strdup(plmn_list[i].operator_short); } } if (*operator_long == NULL || *operator_short == NULL) { RIL_LOGE("%s: Finding operator with PLMN %d%d failed", __func__, mcc, mnc); goto error; } else RIL_LOGD("%s: Found operator with PLMN %d%d", __func__, mcc, mnc); rc = 0; goto complete; error: if (*plmn != NULL) { free(*plmn); *plmn = NULL; } if (operator_long != NULL && *operator_long != NULL) { free(*operator_long); *operator_long = NULL; } if (operator_short != NULL && *operator_short != NULL) { free(*operator_short); *operator_short = NULL; } rc = -1; complete: return rc; } static void get_operator_name_ready(QmiClientNas *client, GAsyncResult *res, RIL_Token token) { QmiMessageNasGetOperatorNameOutput *output; QmiMessageNasGetOperatorNameOutputOperatorPlmnListElement *element; GError *error = NULL; GArray *array; char *operator[3] = { NULL }; output = qmi_client_nas_get_operator_name_finish(client, res, &error); if (!output) { RIL_LOGE("%s: error: operation failed: %s", __func__, error->message); goto error; } if (!qmi_message_nas_get_operator_name_output_get_result(output, &error)) { RIL_LOGE("error: couldn't get operator name data: %s", error->message); goto error; } RIL_LOGD("Successfully got operator name data"); if (qmi_message_nas_get_operator_name_output_get_operator_plmn_list( output, &array, NULL)) { element = &g_array_index(array, QmiMessageNasGetOperatorNameOutputOperatorPlmnListElement, 0); if(qmi2ril_net_operator(element, &operator[0], &operator[1], &operator[2])) goto error; RIL_LOGD("MCC/MNC: '%s', name '%s' '%s'", operator[2], operator[0], operator[1]); } error: ril_request_complete(token, RIL_E_SUCCESS, (void *) &operator, sizeof(operator)); if (error) g_error_free(error); if (output) qmi_message_nas_get_operator_name_output_unref(output); } int ril_request_operator(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_OPERATOR, RIL_REQUEST_HANDLED); if (request != NULL) return RIL_REQUEST_UNHANDLED; RIL_LOGD("requesting operator name"); qmi_client_nas_get_operator_name(ctx->NasClient, NULL, 10, ctx->cancellable, (GAsyncReadyCallback)get_operator_name_ready, token); return RIL_REQUEST_HANDLED; } static void get_serving_system_ready(QmiClientNas *client, GAsyncResult *res, RIL_Token token) { QmiMessageNasGetServingSystemOutput *output; QmiNasRegistrationState registration_state; QmiNasRoamingIndicatorStatus roaming; QmiNasCallBarringStatus cs_call_barring, ps_call_barring; GArray *data_service_capability; QmiNasDataCapability data_cap; guint16 lac; guint32 cid; unsigned char ril_status_voice = 10; unsigned char ril_status_data; RIL_RadioTechnology ril_technology; char **voice_registration[15] = { NULL }; char *data_registration[5] = { NULL }; char **registration; size_t registration_size; struct ril_request *request; GError *error = NULL; output = qmi_client_nas_get_serving_system_finish(client, res, &error); if (!output) { RIL_LOGE("%s: error: operation failed: %s", __func__, error->message); goto error; } if (!qmi_message_nas_get_serving_system_output_get_result(output, &error)) { RIL_LOGE("error: couldn't get serving system: %s", error->message); goto error; } RIL_LOGD("Successfully got serving system"); qmi_message_nas_get_serving_system_output_get_serving_system( output, ®istration_state, NULL, NULL, NULL, NULL, NULL); qmi_message_nas_get_serving_system_output_get_data_service_capability( output, &data_service_capability, NULL); qmi_message_nas_get_serving_system_output_get_roaming_indicator( output, &roaming, NULL); qmi_message_nas_get_serving_system_output_get_lac_3gpp( output, &lac, NULL); qmi_message_nas_get_serving_system_output_get_cid_3gpp( output, &cid, NULL); if(qmi_message_nas_get_serving_system_output_get_call_barring_status( output, &cs_call_barring, &ps_call_barring, NULL)) { if ((cs_call_barring == QMI_NAS_CALL_BARRING_STATUS_NORMAL_ONLY || cs_call_barring == QMI_NAS_CALL_BARRING_STATUS_NO_CALLS) && (ps_call_barring == QMI_NAS_CALL_BARRING_STATUS_NORMAL_ONLY || ps_call_barring == QMI_NAS_CALL_BARRING_STATUS_NO_CALLS)) ril_status_voice = 0; } switch (registration_state) { case QMI_NAS_REGISTRATION_STATE_NOT_REGISTERED: ril_status_voice += 0; break; case QMI_NAS_REGISTRATION_STATE_REGISTERED: switch (roaming) { case QMI_NAS_ROAMING_INDICATOR_STATUS_ON: ril_status_voice = 5; break; case QMI_NAS_ROAMING_INDICATOR_STATUS_OFF: ril_status_voice = 1; break; } break; case QMI_NAS_REGISTRATION_STATE_NOT_REGISTERED_SEARCHING: ril_status_voice += 2; break; case QMI_NAS_REGISTRATION_STATE_REGISTRATION_DENIED: ril_status_voice += 3; break; case QMI_NAS_REGISTRATION_STATE_UNKNOWN: default: ril_status_voice += 4; } // status for DATA_REGISTRATION_STATE doesn't include // emergency status and is only 0 <= ril_status_data <= 5 if (ril_status_voice > 5) ril_status_data = ril_status_voice - 10; else ril_status_data = ril_status_voice; if (data_service_capability->len > 0) data_cap = g_array_index(data_service_capability, QmiNasDataCapability, 0); else data_cap = QMI_NAS_DATA_CAPABILITY_NONE; switch (data_cap) { case QMI_NAS_DATA_CAPABILITY_GPRS: ril_technology = RADIO_TECH_GPRS; break; case QMI_NAS_DATA_CAPABILITY_EDGE: ril_technology = RADIO_TECH_EDGE; break; case QMI_NAS_DATA_CAPABILITY_HSDPA: ril_technology = RADIO_TECH_HSDPA; break; case QMI_NAS_DATA_CAPABILITY_HSUPA: ril_technology = RADIO_TECH_HSUPA; break; case QMI_NAS_DATA_CAPABILITY_WCDMA: ril_technology = RADIO_TECH_UMTS; break; case QMI_NAS_DATA_CAPABILITY_EVDO_REV_0: ril_technology = RADIO_TECH_EVDO_0; break; case QMI_NAS_DATA_CAPABILITY_EVDO_REV_A: ril_technology = RADIO_TECH_EVDO_A; break; case QMI_NAS_DATA_CAPABILITY_GSM: ril_technology = RADIO_TECH_GSM; break; case QMI_NAS_DATA_CAPABILITY_EVDO_REV_B: ril_technology = RADIO_TECH_EVDO_B; break; case QMI_NAS_DATA_CAPABILITY_LTE: ril_technology = RADIO_TECH_LTE; break; case QMI_NAS_DATA_CAPABILITY_HSDPA_PLUS: case QMI_NAS_DATA_CAPABILITY_DC_HSDPA_PLUS: ril_technology = RADIO_TECH_HSPAP; break; case QMI_NAS_DATA_CAPABILITY_NONE: default: ril_technology = RADIO_TECH_UNKNOWN; } request = ril_request_find_token(token); if (request != NULL && request->request == RIL_REQUEST_VOICE_REGISTRATION_STATE) { registration = (char **) &voice_registration; registration_size = sizeof(voice_registration); asprintf(®istration[0], "%d", ril_status_voice); asprintf(®istration[1], "%x", lac); asprintf(®istration[2], "%x", cid); asprintf(®istration[3], "%d", ril_technology); } else if (request != NULL && request->request == RIL_REQUEST_DATA_REGISTRATION_STATE) { registration = (char **) &data_registration; registration_size = sizeof(data_registration); asprintf(®istration[0], "%d", ril_status_data); asprintf(®istration[1], "%x", lac); asprintf(®istration[2], "%x", cid); asprintf(®istration[3], "%d", ril_technology); /* * number of simultanious data connections * TODO: make it possible to setup more using multiple * devices */ asprintf(®istration[4], "%d", 1); } else goto error; ril_request_complete(token, RIL_E_SUCCESS, (void *) registration, registration_size); goto complete; error: ril_request_complete(token, RIL_E_GENERIC_FAILURE, NULL, 0); complete: if (error) g_error_free(error); if (output) qmi_message_nas_get_serving_system_output_unref(output); } int ril_request_registration_state(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_VOICE_REGISTRATION_STATE, RIL_REQUEST_HANDLED); if (request != NULL) return RIL_REQUEST_UNHANDLED; request = ril_request_find_request_status(RIL_REQUEST_DATA_REGISTRATION_STATE, RIL_REQUEST_HANDLED); if (request != NULL) return RIL_REQUEST_UNHANDLED; qmi_client_nas_get_serving_system(ctx->NasClient, NULL, 10, ctx->cancellable, (GAsyncReadyCallback)get_serving_system_ready, token); return RIL_REQUEST_HANDLED; }