summaryrefslogtreecommitdiffstats
path: root/qmi-ril.c
diff options
context:
space:
mode:
Diffstat (limited to 'qmi-ril.c')
-rw-r--r--qmi-ril.c1161
1 files changed, 1161 insertions, 0 deletions
diff --git a/qmi-ril.c b/qmi-ril.c
new file mode 100644
index 0000000..eb22b88
--- /dev/null
+++ b/qmi-ril.c
@@ -0,0 +1,1161 @@
+/*
+ * This file is part of QMI-RIL.
+ *
+ * Copyright (C) 2010-2011 Joerie de Gram <j.de.gram@gmail.com>
+ * Copyright (C) 2011-2014 Paul Kocialkowski <contact@paulk.fr>
+ * 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 <telephony/ril.h>
+
+#include <glib-unix.h>
+
+#include <qmi-ril.h>
+
+static GMainLoop *loop;
+
+/*
+ * RIL data
+ */
+
+struct ril_data *ril_data = NULL;
+
+/*
+ * RIL request
+ */
+
+struct ril_request_handler ril_request_handlers[] = {
+ /* Power */
+ {
+ .request = RIL_REQUEST_RADIO_POWER,
+ .handler = ril_request_radio_power,
+ },
+ /* Call */
+ {
+ .request = RIL_REQUEST_GET_CURRENT_CALLS,
+ .handler = ril_request_get_current_calls,
+ },
+ /* SIM */
+ {
+ .request = RIL_REQUEST_GET_SIM_STATUS,
+ .handler = ril_request_get_sim_status,
+ },
+ {
+ .request = RIL_REQUEST_QUERY_FACILITY_LOCK,
+ .handler = ril_request_query_facility_lock,
+ },
+ {
+ .request = RIL_REQUEST_SIM_IO,
+ .handler = ril_request_sim_io,
+ },
+ /* Network */
+ {
+ .request = RIL_REQUEST_SIGNAL_STRENGTH,
+ .handler = ril_request_signal_strength,
+ },
+ {
+ .request = RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE,
+ .handler = ril_request_query_network_selection_mode,
+ },
+ {
+ .request = RIL_REQUEST_OPERATOR,
+ .handler = ril_request_operator,
+ },
+ {
+ .request = RIL_REQUEST_VOICE_REGISTRATION_STATE,
+ .handler = ril_request_registration_state,
+ },
+ {
+ .request = RIL_REQUEST_DATA_REGISTRATION_STATE,
+ .handler = ril_request_registration_state,
+ },
+ {
+ .request = RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE,
+ .handler = ril_request_set_preferred_network_type,
+ },
+ /* Misc */
+ {
+ .request = RIL_REQUEST_BASEBAND_VERSION,
+ .handler = ril_request_baseband_version,
+ },
+ {
+ .request = RIL_REQUEST_GET_IMSI,
+ .handler = ril_request_get_imsi,
+ },
+ {
+ .request = RIL_REQUEST_GET_IMEI,
+ .handler = ril_request_get_imei,
+ },
+ {
+ .request = RIL_REQUEST_GET_IMEISV,
+ .handler = ril_request_get_imeisv,
+ },
+ {
+ .request = RIL_REQUEST_SCREEN_STATE,
+ .handler = ril_request_screen_state,
+ },
+ /* Data */
+ {
+ .request = RIL_REQUEST_SETUP_DATA_CALL,
+ .handler = ril_request_setup_data_call,
+ },
+};
+
+unsigned int ril_request_handlers_count = sizeof(ril_request_handlers) /
+ sizeof(struct ril_request_handler);
+
+static gboolean sigterm_handler(void)
+{
+ RIL_LOGD("caught sigterm");
+
+ if (ctx != NULL && ctx->cancellable) {
+ RIL_LOGD("cancelling the current operation...");
+ g_cancellable_cancel(ctx->cancellable);
+ }
+
+ if (loop && g_main_loop_is_running (loop)) {
+ RIL_LOGD("cancelling the main loop...");
+ g_idle_add((GSourceFunc)g_main_loop_quit, loop);
+ }
+
+ return G_SOURCE_REMOVE;
+}
+
+int ril_request_stats_log(void)
+{
+ struct ril_request *request;
+ struct list_head *list;
+ unsigned int pending = 0;
+ unsigned int handled = 0;
+ unsigned int unhandled = 0;
+ unsigned int count = 0;
+
+ if (ril_data == NULL)
+ return -1;
+
+ RIL_REQUEST_LOCK();
+
+ list = ril_data->requests;
+ while (list != NULL) {
+ if (list->data == NULL)
+ goto list_continue;
+
+ request = (struct ril_request *) list->data;
+
+ switch (request->status) {
+ case RIL_REQUEST_PENDING:
+ pending++;
+ break;
+ case RIL_REQUEST_HANDLED:
+ handled++;
+ break;
+ case RIL_REQUEST_UNHANDLED:
+ unhandled++;
+ break;
+ }
+
+ count++;
+
+ list_continue:
+ list = list->next;
+ }
+
+ RIL_LOGD("%d RIL request%s in the queue (%d pending, %d handled, %d unhandled)",
+ count, count > 1 ? "s" : "", pending, handled, unhandled);
+
+ count = 0;
+
+ list = ril_data->requests_data;
+ while (list != NULL) {
+ count++;
+
+ list = list->next;
+ }
+
+ if (count > 0)
+ RIL_LOGD("%d RIL request%s data in the queue", count, count > 1 ? "s" : "");
+
+ RIL_REQUEST_UNLOCK();
+
+ return 0;
+}
+
+int ril_request_register(int request, void *data, size_t size, RIL_Token token)
+{
+ struct ril_request *ril_request;
+ struct list_head *list_end;
+ struct list_head *list;
+ unsigned int i;
+
+ if (ril_data == NULL)
+ return -1;
+
+ RIL_REQUEST_LOCK();
+
+ ril_request = (struct ril_request *) calloc(1, sizeof(struct ril_request));
+ ril_request->request = request;
+ ril_request->data = NULL;
+ ril_request->size = size;
+ ril_request->token = token;
+ ril_request->status = RIL_REQUEST_PENDING;
+
+ if (size > 0) {
+ ril_request->data = calloc(1, size);
+ memcpy(ril_request->data, data, size);
+ }
+
+ list_end = ril_data->requests;
+ while (list_end != NULL && list_end->next != NULL)
+ list_end = list_end->next;
+
+ list = list_head_alloc(list_end, NULL, (void *) ril_request);
+
+ if (ril_data->requests == NULL)
+ ril_data->requests = list;
+
+ RIL_REQUEST_UNLOCK();
+
+ return 0;
+}
+
+int ril_request_unregister(struct ril_request *request)
+{
+ struct list_head *list;
+ unsigned int i;
+
+ if (request == NULL || ril_data == NULL)
+ return -1;
+
+ RIL_REQUEST_LOCK();
+
+ list = ril_data->requests;
+ while (list != NULL) {
+ if (list->data == (void *) request) {
+ if (request->data != NULL && request->size > 0)
+ free(request->data);
+
+ memset(request, 0, sizeof(struct ril_request));
+ free(request);
+
+ if (list == ril_data->requests)
+ ril_data->requests = list->next;
+
+ list_head_free(list);
+
+ break;
+ }
+
+ list_continue:
+ list = list->next;
+ }
+
+ RIL_REQUEST_UNLOCK();
+
+ return 0;
+}
+
+int ril_request_flush(void)
+{
+ struct ril_request *request;
+ struct list_head *list;
+ struct list_head *list_next;
+
+ if (ril_data == NULL)
+ return -1;
+
+ RIL_REQUEST_LOCK();
+
+ list = ril_data->requests;
+ while (list != NULL) {
+ if (list->data != NULL) {
+ request = (struct ril_request *) list->data;
+
+ if (request->data != NULL && request->size > 0)
+ free(request->data);
+
+ memset(request, 0, sizeof(struct ril_request));
+ free(request);
+ }
+
+ if (list == ril_data->requests)
+ ril_data->requests = list->next;
+
+ list_next = list->next;
+
+ list_head_free(list);
+
+ list_continue:
+ list = list_next;
+ }
+
+ RIL_REQUEST_UNLOCK();
+
+ return 0;
+}
+
+struct ril_request *ril_request_find(void)
+{
+ struct ril_request *request;
+ struct list_head *list;
+
+ if (ril_data == NULL)
+ return NULL;
+
+ RIL_REQUEST_LOCK();
+
+ list = ril_data->requests;
+ while (list != NULL) {
+ if (list->data == NULL)
+ goto list_continue;
+
+ request = (struct ril_request *) list->data;
+
+ RIL_REQUEST_UNLOCK();
+ return request;
+
+ list_continue:
+ list = list->next;
+ }
+
+ RIL_REQUEST_UNLOCK();
+
+ return NULL;
+}
+
+struct ril_request *ril_request_find_request_status(int request, int status)
+{
+ struct ril_request *ril_request;
+ struct list_head *list;
+
+ if (ril_data == NULL)
+ return NULL;
+
+ RIL_REQUEST_LOCK();
+
+ list = ril_data->requests;
+ while (list != NULL) {
+ if (list->data == NULL)
+ goto list_continue;
+
+ ril_request = (struct ril_request *) list->data;
+
+ if (ril_request->request == request && ril_request->status == status) {
+ RIL_REQUEST_UNLOCK();
+ return ril_request;
+ }
+
+ list_continue:
+ list = list->next;
+ }
+
+ RIL_REQUEST_UNLOCK();
+
+ return NULL;
+}
+
+struct ril_request *ril_request_find_request(int request)
+{
+ struct ril_request *ril_request;
+ struct list_head *list;
+
+ if (ril_data == NULL)
+ return NULL;
+
+ RIL_REQUEST_LOCK();
+
+ list = ril_data->requests;
+ while (list != NULL) {
+ if (list->data == NULL)
+ goto list_continue;
+
+ ril_request = (struct ril_request *) list->data;
+
+ if (ril_request->request == request) {
+ RIL_REQUEST_UNLOCK();
+ return ril_request;
+ }
+
+ list_continue:
+ list = list->next;
+ }
+
+ RIL_REQUEST_UNLOCK();
+
+ return NULL;
+}
+
+struct ril_request *ril_request_find_token(RIL_Token token)
+{
+ struct ril_request *request;
+ struct list_head *list;
+
+ if (ril_data == NULL)
+ return NULL;
+
+ RIL_REQUEST_LOCK();
+
+ list = ril_data->requests;
+ while (list != NULL) {
+ if (list->data == NULL)
+ goto list_continue;
+
+ request = (struct ril_request *) list->data;
+
+ if (request->token == token) {
+ RIL_REQUEST_UNLOCK();
+ return request;
+ }
+
+ list_continue:
+ list = list->next;
+ }
+
+ RIL_REQUEST_UNLOCK();
+
+ return NULL;
+}
+
+struct ril_request *ril_request_find_status(int status)
+{
+ struct ril_request *request;
+ struct list_head *list;
+
+ if (ril_data == NULL)
+ return NULL;
+
+ RIL_REQUEST_LOCK();
+
+ list = ril_data->requests;
+ while (list != NULL) {
+ if (list->data == NULL)
+ goto list_continue;
+
+ request = (struct ril_request *) list->data;
+
+ if (request->status == status) {
+ RIL_REQUEST_UNLOCK();
+ return request;
+ }
+
+ list_continue:
+ list = list->next;
+ }
+
+ RIL_REQUEST_UNLOCK();
+
+ return NULL;
+}
+
+int ril_request_complete(RIL_Token token, RIL_Errno error, void *data,
+ size_t size)
+{
+ struct ril_request *request;
+
+ if (ril_data == NULL || ril_data->env == NULL || ril_data->env->OnRequestComplete == NULL)
+ return -1;
+
+ if (token == NULL)
+ return 0;
+
+ request = ril_request_find_token(token);
+ if (request == NULL)
+ goto complete;
+
+ ril_request_unregister(request);
+
+ ril_request_stats_log();
+
+complete:
+ ril_data->env->OnRequestComplete(token, error, data, size);
+
+ return 0;
+}
+
+int ril_request_unsolicited(int request, void *data, size_t size)
+{
+ if (ril_data == NULL || ril_data->env == NULL || ril_data->env->OnUnsolicitedResponse == NULL)
+ return -1;
+
+ ril_data->env->OnUnsolicitedResponse(request, data, size);
+
+ return 0;
+}
+
+int ril_request_timed_callback(RIL_TimedCallback callback, void *data,
+ const struct timeval *time)
+{
+ if (ril_data == NULL || ril_data->env == NULL || ril_data->env->RequestTimedCallback == NULL)
+ return -1;
+
+ ril_data->env->RequestTimedCallback(callback, data, time);
+
+ return 0;
+}
+
+int ril_request_dispatch(struct ril_request *request)
+{
+ unsigned int i;
+ int status;
+ int rc;
+
+ if (request == NULL || ril_data == NULL)
+ return -1;
+
+ for (i = 0; i < ril_request_handlers_count; i++) {
+ if (ril_request_handlers[i].handler == NULL)
+ continue;
+
+ if (ril_request_handlers[i].request == request->request) {
+ status = ril_request_handlers[i].handler(request->data, request->size, request->token);
+ switch (status) {
+ case RIL_REQUEST_PENDING:
+ case RIL_REQUEST_HANDLED:
+ case RIL_REQUEST_UNHANDLED:
+ request->status = status;
+ break;
+ case RIL_REQUEST_COMPLETED:
+ break;
+ default:
+ RIL_LOGE("Handling RIL request %d failed", request->request);
+ return -1;
+ }
+
+ return 0;
+ }
+ }
+
+ RIL_LOGD("Unhandled RIL request: %d", request->request);
+ ril_request_complete(request->token, RIL_E_REQUEST_NOT_SUPPORTED, NULL, 0);
+
+ return 0;
+}
+
+static gboolean ril_request_loop(gpointer data)
+{
+ struct ril_request *request;
+ int rc;
+
+ rc = all_clients_running();
+ if (rc < 0) {
+ RIL_LOGE("error during QMI client allocation");
+ return FALSE;
+ } else if (rc == 0) {
+ return TRUE;
+ }
+
+ rc = ril_radio_state_check(RADIO_STATE_OFF);
+ if (rc < 0)
+ return TRUE;
+
+ do {
+ request = ril_request_find_status(RIL_REQUEST_UNHANDLED);
+ if (request == NULL)
+ break;
+
+ request->status = RIL_REQUEST_PENDING;
+ } while (request != NULL);
+
+ do {
+ request = ril_request_find_status(RIL_REQUEST_PENDING);
+ if (request == NULL)
+ break;
+
+ rc = ril_request_dispatch(request);
+ if (rc < 0)
+ ril_request_unregister(request);
+ } while (request != NULL);
+
+ return TRUE;
+}
+
+void *ril_request_main_thread(void *data)
+{
+ int rc;
+
+ if (ril_data == NULL)
+ return NULL;
+
+ loop = g_main_loop_new(NULL, FALSE);
+
+ g_unix_signal_add(SIGTERM, (GSourceFunc) sigterm_handler, NULL);
+ g_timeout_add(100, ril_request_loop, data);
+ RIL_LOGD("RIL running main loop");
+ g_main_loop_run(loop);
+
+ RIL_LOGD("exiting main thread");
+
+ if (ctx->cancellable)
+ g_object_unref(ctx->cancellable);
+ if (ctx->DmsClient)
+ g_object_unref(ctx->DmsClient);
+ if (ctx->NasClient)
+ g_object_unref(ctx->NasClient);
+ if (ctx->UimClient)
+ g_object_unref(ctx->UimClient);
+ if (ctx->WdsClient)
+ g_object_unref(ctx->WdsClient);
+ if (ctx->device)
+ g_object_unref(ctx->device);
+ g_main_loop_unref(loop);
+ g_object_unref(ctx->file);
+
+ RIL_LOGD("cleaned up");
+ exit(EXIT_SUCCESS);
+ return NULL;
+}
+
+/*
+ * RIL request data
+ */
+
+int ril_request_data_register(int request, void *data, size_t size)
+{
+ struct ril_request_data *request_data;
+ struct list_head *list_end;
+ struct list_head *list;
+ unsigned int i;
+
+ if (data == NULL || ril_data == NULL)
+ return -1;
+
+ request_data = (struct ril_request_data *) calloc(1, sizeof(struct ril_request_data));
+ request_data->request = request;
+ request_data->data = data;
+ request_data->size = size;
+
+ list_end = ril_data->requests_data;
+ while (list_end != NULL && list_end->next != NULL)
+ list_end = list_end->next;
+
+ list = list_head_alloc(list_end, NULL, (void *) request_data);
+
+ if (ril_data->requests_data == NULL)
+ ril_data->requests_data = list;
+
+ return 0;
+}
+
+int ril_request_data_unregister(struct ril_request_data *request_data)
+{
+ struct list_head *list;
+ unsigned int i;
+
+ if (request_data == NULL || ril_data == NULL)
+ return -1;
+
+ list = ril_data->requests_data;
+ while (list != NULL) {
+ if (list->data == (void *) request_data) {
+ memset(request_data, 0, sizeof(struct ril_request_data));
+ free(request_data);
+
+ if (list == ril_data->requests_data)
+ ril_data->requests_data = list->next;
+
+ list_head_free(list);
+
+ break;
+ }
+
+ list_continue:
+ list = list->next;
+ }
+
+ return 0;
+}
+
+int ril_request_data_flush(void)
+{
+ struct ril_request_data *request_data;
+ struct list_head *list;
+ struct list_head *list_next;
+
+ if (ril_data == NULL)
+ return -1;
+
+ list = ril_data->requests_data;
+ while (list != NULL) {
+ if (list->data != NULL) {
+ request_data = (struct ril_request_data *) list->data;
+
+ if (request_data->data != NULL && request_data->size > 0)
+ free(request_data->data);
+
+ memset(request_data, 0, sizeof(struct ril_request_data));
+ free(request_data);
+ }
+
+ if (list == ril_data->requests_data)
+ ril_data->requests_data = list->next;
+
+ list_next = list->next;
+
+ list_head_free(list);
+
+ list_continue:
+ list = list_next;
+ }
+
+ return 0;
+}
+
+struct ril_request_data *ril_request_data_find_request(int request)
+{
+ struct ril_request_data *request_data;
+ struct list_head *list;
+
+ if (ril_data == NULL)
+ return NULL;
+
+ list = ril_data->requests_data;
+ while (list != NULL) {
+ if (list->data == NULL)
+ goto list_continue;
+
+ request_data = (struct ril_request_data *) list->data;
+
+ if (request_data->request == request)
+ return request_data;
+
+ list_continue:
+ list = list->next;
+ }
+
+ return NULL;
+}
+
+int ril_request_data_free(int request)
+{
+ struct ril_request_data *request_data;
+
+ do {
+ request_data = ril_request_data_find_request(request);
+ if (request_data == NULL)
+ break;
+
+ if (request_data->data != NULL && request_data->size > 0)
+ free(request_data->data);
+
+ ril_request_data_unregister(request_data);
+ } while (request_data != NULL);
+
+ return 0;
+}
+
+int ril_request_data_set(int request, void *data, size_t size)
+{
+ void *buffer;
+ int rc;
+
+ if (data == NULL || size == 0)
+ return -1;
+
+ buffer = calloc(1, size);
+ memcpy(buffer, data, size);
+
+ rc = ril_request_data_register(request, buffer, size);
+ if (rc < 0)
+ return -1;
+
+ return 0;
+}
+
+int ril_request_data_set_uniq(int request, void *data, size_t size)
+{
+ int rc;
+
+ ril_request_data_free(request);
+
+ rc = ril_request_data_set(request, data, size);
+ if (rc < 0)
+ return -1;
+
+ return 0;
+}
+
+size_t ril_request_data_size_get(int request)
+{
+ struct ril_request_data *request_data;
+
+ request_data = ril_request_data_find_request(request);
+ if (request_data == NULL)
+ return 0;
+
+ return request_data->size;
+}
+
+void *ril_request_data_get(int request)
+{
+ struct ril_request_data *request_data;
+ void *buffer;
+
+ request_data = ril_request_data_find_request(request);
+ if (request_data == NULL)
+ return NULL;
+
+ buffer = request_data->data;
+
+ ril_request_data_unregister(request_data);
+
+ return buffer;
+}
+
+/*
+ * RIL radio state
+ */
+
+int ril_radio_state_update(RIL_RadioState radio_state)
+{
+ struct ril_request *request;
+ unsigned int i;
+ int rc;
+
+ if (ril_data == NULL)
+ return -1;
+
+ if (ril_data->radio_state == radio_state)
+ return 0;
+
+ RIL_LOGD("Updating RIL radio state to %d", radio_state);
+
+ ril_data->radio_state = radio_state;
+ ril_request_unsolicited(RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED, NULL, 0);
+
+ switch (ril_data->radio_state) {
+ case RADIO_STATE_UNAVAILABLE:
+ do {
+ request = ril_request_find();
+ if (request == NULL)
+ break;
+
+ ril_request_complete(request->token, RIL_E_RADIO_NOT_AVAILABLE, NULL, 0);
+
+ ril_request_unregister(request);
+ } while (request != NULL);
+
+ ril_request_flush();
+ ril_request_data_flush();
+
+ ril_request_stats_log();
+
+ case RADIO_STATE_OFF:
+ RIL_LOGE("TODO: Implement ril_data_connection_flush() here!");
+
+ ril_request_unsolicited(RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED, NULL, 0);
+ ril_request_unsolicited(RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED, NULL, 0);
+ ril_request_unsolicited(RIL_UNSOL_DATA_CALL_LIST_CHANGED, NULL, 0);
+ ril_request_unsolicited(RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED, NULL, 0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+int ril_radio_state_check(RIL_RadioState radio_state)
+{
+ RIL_RadioState radio_states[] = {
+ RADIO_STATE_UNAVAILABLE,
+ RADIO_STATE_OFF,
+ RADIO_STATE_ON,
+ RADIO_STATE_NV_NOT_READY,
+ RADIO_STATE_NV_READY,
+ RADIO_STATE_SIM_NOT_READY,
+ RADIO_STATE_SIM_LOCKED_OR_ABSENT,
+ RADIO_STATE_SIM_READY,
+ };
+ unsigned int index;
+ unsigned int count;
+ unsigned int i;
+
+ if (ril_data == NULL)
+ return -1;
+
+ count = sizeof(radio_states) / sizeof(RIL_RadioState);
+
+ for (i = 0; i < count; i++)
+ if (radio_states[i] == radio_state)
+ break;
+
+ index = i;
+
+ for (i = 0; i < count; i++)
+ if (radio_states[i] == ril_data->radio_state)
+ break;
+
+ if (i < index)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * RIL data
+ */
+
+int ril_data_create(void)
+{
+ ril_data = (struct ril_data *) calloc(1, sizeof(struct ril_data));
+
+ pthread_mutex_init(&ril_data->request_mutex, NULL);
+
+ RIL_LOGD("initializing RIL data");
+
+ ril_data->radio_state = RADIO_STATE_UNAVAILABLE;
+ /* modem is booting in ethernet mode */
+ ril_data->data_connection.raw_ip_mode = FALSE;
+
+ return 0;
+}
+
+int ril_data_destroy(void)
+{
+ RIL_LOGD("destroying RIL data");
+
+ if (ril_data == NULL)
+ return -1;
+
+ pthread_mutex_destroy(&ril_data->request_mutex);
+
+ free(ril_data);
+ ril_data = NULL;
+
+ return 0;
+}
+
+/*
+ * RIL interface
+ */
+
+void ril_on_request(int request, void *data, size_t size, RIL_Token token)
+{
+ struct ril_request *ril_request;
+ void *buffer = NULL;
+ unsigned int strings_count = 0;
+ unsigned int i;
+ char *c;
+
+ ril_request = ril_request_find_token(token);
+ if (ril_request != NULL)
+ ril_request_unregister(ril_request);
+
+ switch (request) {
+ case RIL_REQUEST_DIAL:
+ if (data == NULL || size < sizeof(RIL_Dial))
+ break;
+
+ buffer = calloc(1, size);
+
+ memcpy(buffer, data, size);
+
+ if (((RIL_Dial *) data)->address != NULL)
+ ((RIL_Dial *) buffer)->address = strdup(((RIL_Dial *) data)->address);
+
+ data = buffer;
+ break;
+ case RIL_REQUEST_WRITE_SMS_TO_SIM:
+ if (data == NULL || size < sizeof(RIL_SMS_WriteArgs))
+ break;
+
+
+ buffer = calloc(1, size);
+
+ memcpy(buffer, data, size);
+
+ if (((RIL_SMS_WriteArgs *) data)->pdu != NULL)
+ ((RIL_SMS_WriteArgs *) buffer)->pdu = strdup(((RIL_SMS_WriteArgs *) data)->pdu);
+
+ if (((RIL_SMS_WriteArgs *) data)->smsc != NULL)
+ ((RIL_SMS_WriteArgs *) buffer)->smsc = strdup(((RIL_SMS_WriteArgs *) data)->smsc);
+
+ data = buffer;
+ break;
+ case RIL_REQUEST_SEND_SMS:
+ case RIL_REQUEST_SEND_SMS_EXPECT_MORE:
+ case RIL_REQUEST_QUERY_FACILITY_LOCK:
+ case RIL_REQUEST_SET_FACILITY_LOCK:
+ case RIL_REQUEST_ENTER_SIM_PIN:
+ case RIL_REQUEST_ENTER_SIM_PUK:
+ case RIL_REQUEST_ENTER_SIM_PIN2:
+ case RIL_REQUEST_ENTER_SIM_PUK2:
+ case RIL_REQUEST_CHANGE_SIM_PIN:
+ case RIL_REQUEST_CHANGE_SIM_PIN2:
+ strings_count = size / sizeof(char *);
+ break;
+ case RIL_REQUEST_SIM_IO:
+ if (data == NULL || size < sizeof(RIL_SIM_IO_v6))
+ break;
+
+ buffer = calloc(1, size);
+
+ memcpy(buffer, data, size);
+
+ if (((RIL_SIM_IO_v6 *) data)->path != NULL)
+ ((RIL_SIM_IO_v6 *) buffer)->path = strdup(((RIL_SIM_IO_v6 *) data)->path);
+
+ if (((RIL_SIM_IO_v6 *) data)->data != NULL)
+ ((RIL_SIM_IO_v6 *) buffer)->data = strdup(((RIL_SIM_IO_v6 *) data)->data);
+
+ if (((RIL_SIM_IO_v6 *) data)->pin2 != NULL)
+ ((RIL_SIM_IO_v6 *) buffer)->pin2 = strdup(((RIL_SIM_IO_v6 *) data)->pin2);
+
+ if (((RIL_SIM_IO_v6 *) data)->aidPtr != NULL)
+ ((RIL_SIM_IO_v6 *) buffer)->aidPtr = strdup(((RIL_SIM_IO_v6 *) data)->aidPtr);
+
+ data = buffer;
+ break;
+ case RIL_REQUEST_SETUP_DATA_CALL:
+ case RIL_REQUEST_DEACTIVATE_DATA_CALL:
+ strings_count = size / sizeof(char *);
+ break;
+ default:
+ if (data == NULL || size != sizeof(char *))
+ break;
+
+ c = (char *) data;
+
+ for (i = 0; isprint(c[i]); i++);
+
+ if (i > 0 && c[i] == '\0') {
+ size = i + 1;
+ RIL_LOGD("Detected string with a size of %d byte%s", size, size > 0 ? "s" : "");
+ }
+
+ break;
+ }
+
+ if (strings_count > 0 && data != NULL && size >= strings_count * sizeof(char *)) {
+ buffer = calloc(1, size);
+
+ for (i = 0; i < strings_count; i++) {
+ if (((char **) data)[i] != NULL) {
+ c = strdup(((char **) data)[i]);
+ ((char **) buffer)[i] = c;
+ }
+ }
+
+ data = buffer;
+ }
+
+ ril_request_register(request, data, size, token);
+
+ if (buffer != NULL)
+ free(buffer);
+
+ ril_request_stats_log();
+}
+
+
+RIL_RadioState ril_on_state_request(void)
+{
+ if (ril_data == NULL)
+ return RADIO_STATE_UNAVAILABLE;
+
+ return ril_data->radio_state;
+}
+
+int ril_supports(int request)
+{
+ unsigned int i;
+
+ for (i = 0; i < ril_request_handlers_count; i++) {
+ if (ril_request_handlers[i].handler == NULL)
+ continue;
+
+ if (ril_request_handlers[i].request == request)
+ return 1;
+ }
+
+ return 0;
+}
+
+void ril_on_cancel(RIL_Token token)
+{
+ struct ril_request *request;
+
+ request = ril_request_find_token(token);
+ if (request == NULL)
+ return;
+
+ ril_request_unregister(request);
+
+ ril_request_stats_log();
+}
+
+const char *ril_get_version(void)
+{
+ return RIL_VERSION_STRING;
+}
+
+RIL_RadioFunctions ril_radio_functions = {
+ RIL_VERSION,
+ ril_on_request,
+ ril_on_state_request,
+ ril_supports,
+ ril_on_cancel,
+ ril_get_version
+};
+
+const RIL_RadioFunctions *RIL_Init(const struct RIL_Env *env, int argc,
+ char **argv)
+{
+ RIL_RadioFunctions *radio_functions;
+ pthread_attr_t attr;
+ int rc;
+
+ if (env == NULL)
+ return NULL;
+
+ rc = ril_data_create();
+ if (rc < 0) {
+ RIL_LOGE("Creating RIL data failed");
+ return NULL;
+ }
+
+ ril_data->env = env;
+
+ RIL_LOGD("creating RIL clients");
+
+ rc = create_qmi_clients();
+ if (rc < 0) {
+ RIL_LOGE("Creating QMI clients failed");
+ goto error;
+ }
+
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ rc = pthread_create(&ril_data->request_thread, &attr, ril_request_main_thread, NULL);
+ if (rc != 0) {
+ RIL_LOGE("Starting request main thread failed");
+ goto error;
+ }
+
+ radio_functions = &ril_radio_functions;
+ goto complete;
+
+error:
+ radio_functions = NULL;
+
+complete:
+ return radio_functions;
+}