diff options
Diffstat (limited to 'qmi-client.c')
-rw-r--r-- | qmi-client.c | 347 |
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; +} |