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