diff options
Diffstat (limited to 'network.c')
-rw-r--r-- | network.c | 664 |
1 files changed, 664 insertions, 0 deletions
diff --git a/network.c b/network.c new file mode 100644 index 0000000..e0488c0 --- /dev/null +++ b/network.c @@ -0,0 +1,664 @@ +/* + * 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> +#include <ctype.h> + +#define LOG_TAG "RIL" +#include <utils/Log.h> + +#include <qmi-ril.h> +#include <plmn_list.h> + +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; +} |