summaryrefslogtreecommitdiffstats
path: root/qmi-ril.c
diff options
context:
space:
mode:
authorWolfgang Wiedmeyer <wolfgit@wiedmeyer.de>2017-09-06 00:19:36 +0200
committerWolfgang Wiedmeyer <wolfgit@wiedmeyer.de>2017-09-06 00:19:36 +0200
commitf35a9bbd1ebb52433f313c675fbe01bb1ca96d7d (patch)
treec1aff224fd4a2ea2e5e06805c7caff5406b0ab42 /qmi-ril.c
parent06cf4fe68ad096961b4b9c653ba2bc31d270f009 (diff)
downloadqmi-ril-f35a9bbd1ebb52433f313c675fbe01bb1ca96d7d.tar.gz
qmi-ril-f35a9bbd1ebb52433f313c675fbe01bb1ca96d7d.tar.bz2
qmi-ril-f35a9bbd1ebb52433f313c675fbe01bb1ca96d7d.zip
initial version of QMI-RILHEADmaster
The RIL uses libqmi. qmicli and ModemManager were used as references for using libqmi. The request processing is modelled after Samsung-RIL and a lot of code could be reused for QMI-RIL. Establishing a data connection succeeds and mobile data is working in early testing. However, there is not yet a routine for handling interruptions and notifying Android about them. Information about the serving network including signal strength are retrieved and an unlocked SIM card is recognized. Reading data from the SIM and requesting the usual device information should work as well. Support for voice calls and SMS is completely missing at this point. Signed-off-by: Wolfgang Wiedmeyer <wolfgit@wiedmeyer.de>
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;
+}