summaryrefslogtreecommitdiffstats
path: root/qmi-client.c
diff options
context:
space:
mode:
Diffstat (limited to 'qmi-client.c')
-rw-r--r--qmi-client.c347
1 files changed, 347 insertions, 0 deletions
diff --git a/qmi-client.c b/qmi-client.c
new file mode 100644
index 0000000..ab47325
--- /dev/null
+++ b/qmi-client.c
@@ -0,0 +1,347 @@
+/*
+ * 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 <telephony/ril.h>
+
+#include <gio/gio.h>
+
+#include <qmi-ril.h>
+
+#define CDC_PATH "/dev/cdc-wdm2"
+
+static GCancellable *cancellable;
+static GFile *file;
+static QmiDevice *device;
+
+QmiService qmi_services[] = {
+ QMI_SERVICE_DMS,
+ QMI_SERVICE_NAS,
+ QMI_SERVICE_UIM,
+ QMI_SERVICE_WDS,
+ QMI_SERVICE_WDA,
+};
+
+unsigned int qmi_service_count = sizeof(qmi_services)
+ / sizeof(QmiService);
+unsigned int qmi_indications_count = 3;
+
+static unsigned int qmi_client_count = 0;
+static unsigned int qmi_client_failures = 0;
+static unsigned int qmi_indications_ready = 0;
+
+static void all_indications_ready()
+{
+ if (qmi_indications_ready < qmi_indications_count) {
+ RIL_LOGD("%d indications ready", qmi_indications_ready);
+ return;
+ } else {
+ RIL_LOGD("registered indications");
+ // initialization finished, main loop can now process
+ // requests
+ ril_radio_state_update(RADIO_STATE_OFF);
+ }
+}
+
+static void nas_set_event_report_ready(QmiClientNas *client,
+ GAsyncResult *res)
+{
+ QmiMessageNasSetEventReportOutput *output;
+ GError *error = NULL;
+
+ output = qmi_client_nas_set_event_report_finish(client, res, &error);
+ if (!output) {
+ RIL_LOGE("%s: error: operation failed: %s\n", __func__,
+ error->message);
+ g_error_free(error);
+ return;
+ }
+
+ if (!qmi_message_nas_set_event_report_output_get_result(output, &error)) {
+ RIL_LOGE("Couldn't set nas event report: %s", error->message);
+ g_error_free(error);
+ qmi_message_nas_set_event_report_output_unref(output);
+
+ return;
+ }
+
+ qmi_message_nas_set_event_report_output_unref(output);
+
+ ctx->nas_event_report_indication_id =
+ g_signal_connect(ctx->NasClient, "event-report",
+ G_CALLBACK(nas_event_report_indication_cb),
+ NULL);
+
+ qmi_indications_ready++;
+ all_indications_ready();
+}
+
+static void nas_register_indications_ready(QmiClientNas *client,
+ GAsyncResult *res)
+{
+ QmiMessageNasRegisterIndicationsOutput *output;
+ GError *error = NULL;
+
+ output = qmi_client_nas_register_indications_finish(client, res, &error);
+ if (!output) {
+ RIL_LOGE("%s: error: operation failed: %s", __func__,
+ error->message);
+ g_error_free(error);
+ return;
+ }
+
+ if (!qmi_message_nas_register_indications_output_get_result(output, &error)) {
+ RIL_LOGE("Couldn't register nas indications: %s", error->message);
+ g_error_free(error);
+ qmi_message_nas_register_indications_output_unref(output);
+
+ return;
+ }
+
+ qmi_message_nas_register_indications_output_unref(output);
+
+ ctx->serving_system_indication_id =
+ g_signal_connect(ctx->NasClient, "serving-system",
+ G_CALLBACK(serving_system_indication_cb),
+ NULL);
+
+ qmi_indications_ready++;
+ all_indications_ready();
+}
+
+static void dms_set_event_report_ready(QmiClientDms *client,
+ GAsyncResult *res)
+{
+ QmiMessageDmsSetEventReportOutput *output;
+ GError *error = NULL;
+
+ output = qmi_client_dms_set_event_report_finish(client, res, &error);
+
+ if (!output) {
+ RIL_LOGE("%s: error: operation failed: %s", __func__,
+ error->message);
+ g_error_free(error);
+ return;
+ }
+
+ if (!qmi_message_dms_set_event_report_output_get_result(output, &error)) {
+ RIL_LOGE("Couldn't set dms event report: %s", error->message);
+ g_error_free(error);
+ qmi_message_dms_set_event_report_output_unref(output);
+ return;
+ }
+
+ qmi_message_dms_set_event_report_output_unref(output);
+
+ ctx->dms_event_report_indication_id = g_signal_connect(
+ ctx->DmsClient,
+ "event-report",
+ G_CALLBACK(dms_event_report_indication_cb),
+ NULL);
+
+ qmi_indications_ready++;
+ all_indications_ready();
+}
+
+static void setup_indications()
+{
+ QmiMessageDmsSetEventReportInput *dms_event_report_input;
+ QmiMessageNasRegisterIndicationsInput *nas_register_input;
+ QmiMessageNasSetEventReportInput *nas_event_report_input;
+ static const gint8 thresholds_data[] = {-80, -40, 0, 40, 80};
+ GArray *thresholds;
+
+ dms_event_report_input = qmi_message_dms_set_event_report_input_new();
+ qmi_message_dms_set_event_report_input_set_operating_mode_reporting(
+ dms_event_report_input, TRUE, NULL);
+ qmi_client_dms_set_event_report(ctx->DmsClient,
+ dms_event_report_input, 5, NULL,
+ (GAsyncReadyCallback)dms_set_event_report_ready,
+ NULL);
+ qmi_message_dms_set_event_report_input_unref(dms_event_report_input);
+
+ nas_register_input = qmi_message_nas_register_indications_input_new();
+ qmi_message_nas_register_indications_input_set_serving_system_events(
+ nas_register_input, TRUE, NULL);
+ qmi_client_nas_register_indications(ctx->NasClient,
+ nas_register_input, 5, NULL,
+ (GAsyncReadyCallback)nas_register_indications_ready,
+ NULL);
+ qmi_message_nas_register_indications_input_unref(nas_register_input);
+
+ nas_event_report_input = qmi_message_nas_set_event_report_input_new();
+ thresholds = g_array_sized_new(FALSE, FALSE, sizeof (gint8),
+ G_N_ELEMENTS(thresholds_data));
+ g_array_append_vals(thresholds, thresholds_data,
+ G_N_ELEMENTS(thresholds_data));
+ qmi_message_nas_set_event_report_input_set_signal_strength_indicator(
+ nas_event_report_input, TRUE, thresholds, NULL);
+ g_array_unref(thresholds);
+ qmi_client_nas_set_event_report(ctx->NasClient,
+ nas_event_report_input, 5, NULL,
+ (GAsyncReadyCallback)nas_set_event_report_ready,
+ NULL);
+ qmi_message_nas_set_event_report_input_unref(nas_event_report_input);
+}
+
+static void allocate_client_ready(QmiDevice *dev, GAsyncResult *res)
+{
+ QmiClient *client;
+ QmiService service;
+ GError *error = NULL;
+
+ client = qmi_device_allocate_client_finish(dev, res, &error);
+ if (!client) {
+ RIL_LOGE("%s: error: couldn't create client: %s", __func__,
+ error->message);
+ qmi_client_failures++;
+ RIL_LOGE("increased failure counter to %d", qmi_client_failures);
+ return;
+ }
+
+ service = qmi_client_get_service(client);
+
+ switch (service) {
+ case QMI_SERVICE_DMS:
+ ctx->DmsClient = g_object_ref(QMI_CLIENT_DMS(client));
+ break;
+ case QMI_SERVICE_NAS:
+ ctx->NasClient = g_object_ref(QMI_CLIENT_NAS(client));
+ break;
+ case QMI_SERVICE_UIM:
+ ctx->UimClient = g_object_ref(QMI_CLIENT_UIM(client));
+ break;
+ case QMI_SERVICE_WDS:
+ ctx->WdsClient = g_object_ref(QMI_CLIENT_WDS(client));
+ break;
+ case QMI_SERVICE_WDA:
+ ctx->WdaClient = g_object_ref(QMI_CLIENT_WDA(client));
+ // configure it right away
+ qmi_set_raw_ip_mode();
+ break;
+ default:
+ RIL_LOGE("unknown service");
+ return;
+ }
+
+ RIL_LOGD("QMI client for service %s allocated",
+ qmi_service_get_string(service));
+ RIL_LOGD("%d of %d clients allocated", qmi_client_count,
+ qmi_service_count);
+
+ qmi_client_count++;
+
+ if (qmi_client_failures)
+ RIL_LOGD("%d failures", qmi_client_failures);
+
+ if (qmi_client_count == qmi_service_count)
+ setup_indications();
+}
+
+static void device_allocate_client(QmiDevice *dev)
+{
+ guint8 cid = QMI_CID_NONE;
+ unsigned int i;
+
+ ctx = g_slice_new(Context);
+ ctx->file = g_object_ref(file);
+ ctx->device = g_object_ref(device);
+ if (cancellable)
+ ctx->cancellable = g_object_ref(cancellable);
+
+ for (i = 0; i < qmi_service_count; i++)
+ qmi_device_allocate_client(dev, qmi_services[i], cid,
+ 10, cancellable,
+ (GAsyncReadyCallback)allocate_client_ready,
+ NULL);
+
+ RIL_LOGD("Started allocating QMI clients");
+}
+
+static void device_open_ready(QmiDevice *dev, GAsyncResult *res)
+{
+ GError *error = NULL;
+
+ if (!qmi_device_open_finish(dev, res, &error)) {
+ RIL_LOGE("error: couldn't open the QmiDevice: %s",
+ error->message);
+ qmi_client_failures++;
+ return;
+ }
+
+ RIL_LOGD("QMI Device at '%s' ready",
+ qmi_device_get_path_display(dev));
+
+ device_allocate_client(dev);
+}
+
+static void device_new_ready(GObject *unused, GAsyncResult *res)
+{
+ QmiDeviceOpenFlags open_flags = QMI_DEVICE_OPEN_FLAGS_NONE;
+ GError *error = NULL;
+
+ device = qmi_device_new_finish(res, &error);
+ if (!device) {
+ RIL_LOGE("error: couldn't create QmiDevice: %s",
+ error->message);
+ qmi_client_failures++;
+ return;
+ }
+
+ open_flags |= QMI_DEVICE_OPEN_FLAGS_AUTO;
+
+ /* Open the device */
+ qmi_device_open(device, open_flags, 15, cancellable,
+ (GAsyncReadyCallback)device_open_ready, NULL);
+}
+
+int create_qmi_clients(void)
+{
+ cancellable = g_cancellable_new();
+
+ file = g_file_new_for_path(CDC_PATH);
+
+ if(g_file_query_exists(file, NULL))
+ qmi_device_new(file, cancellable,
+ (GAsyncReadyCallback)device_new_ready,
+ NULL);
+ else {
+ RIL_LOGE("error opening file path");
+ return -1;
+ }
+
+ RIL_LOGD("started creating QMI clients");
+
+ return 0;
+}
+
+int all_clients_running(void)
+{
+ if (qmi_client_failures) {
+ return -1;
+ } else if (qmi_client_count < qmi_service_count
+ || qmi_indications_ready < qmi_indications_count
+ || ril_data->data_connection.raw_ip_mode == FALSE)
+ return 0;
+ else
+ return 1;
+}