summaryrefslogtreecommitdiffstats
path: root/network.c
diff options
context:
space:
mode:
Diffstat (limited to 'network.c')
-rw-r--r--network.c664
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, &registration_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(&registration[0], "%d", ril_status_voice);
+ asprintf(&registration[1], "%x", lac);
+ asprintf(&registration[2], "%x", cid);
+ asprintf(&registration[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(&registration[0], "%d", ril_status_data);
+ asprintf(&registration[1], "%x", lac);
+ asprintf(&registration[2], "%x", cid);
+ asprintf(&registration[3], "%d", ril_technology);
+ /*
+ * number of simultanious data connections
+ * TODO: make it possible to setup more using multiple
+ * devices
+ */
+ asprintf(&registration[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;
+}