summaryrefslogtreecommitdiffstats
path: root/data.c
diff options
context:
space:
mode:
Diffstat (limited to 'data.c')
-rw-r--r--data.c1049
1 files changed, 1049 insertions, 0 deletions
diff --git a/data.c b/data.c
new file mode 100644
index 0000000..1aa69d1
--- /dev/null
+++ b/data.c
@@ -0,0 +1,1049 @@
+/*
+ * This file is part of Samsung-RIL.
+ *
+ * Copyright (C) 2011-2014 Paul Kocialkowski <contact@paulk.fr>
+ * Copyright (C) 2011 Denis 'GNUtoo' Carikli <GNUtoo@no-log.org>
+ *
+ * Samsung-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.
+ *
+ * Samsung-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 Samsung-RIL. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <arpa/inet.h>
+
+#define LOG_TAG "RIL"
+#include <utils/Log.h>
+#include <netutils/ifc.h>
+
+#include <samsung-ril.h>
+#include <utils.h>
+
+int ipc2ril_gprs_fail_cause(unsigned char fail_cause)
+{
+ switch (fail_cause) {
+ case IPC_GPRS_FAIL_CAUSE_NONE:
+ case IPC_GPRS_FAIL_CAUSE_REL_BY_USER:
+ case IPC_GPRS_FAIL_CAUSE_REGULAR_DEACTIVATION:
+ return PDP_FAIL_NONE;
+ case IPC_GPRS_FAIL_CAUSE_INSUFFICIENT_RESOURCE:
+ return PDP_FAIL_INSUFFICIENT_RESOURCES;
+ case IPC_GPRS_FAIL_CAUSE_UNKNOWN_APN:
+ return PDP_FAIL_MISSING_UKNOWN_APN;
+ case IPC_GPRS_FAIL_CAUSE_UNKNOWN_PDP_ADDRESS:
+ return PDP_FAIL_UNKNOWN_PDP_ADDRESS_TYPE;
+ case IPC_GPRS_FAIL_CAUSE_USER_AUTH_FAILED:
+ return PDP_FAIL_USER_AUTHENTICATION;
+ case IPC_GPRS_FAIL_CAUSE_ACT_REJ_GGSN:
+ return PDP_FAIL_ACTIVATION_REJECT_GGSN;
+ case IPC_GPRS_FAIL_CAUSE_ACT_REJ_UNSPECIFIED:
+ return PDP_FAIL_ACTIVATION_REJECT_UNSPECIFIED;
+ case IPC_GPRS_FAIL_CAUSE_SVC_OPTION_NOT_SUPPORTED:
+ return PDP_FAIL_SERVICE_OPTION_NOT_SUPPORTED;
+ case IPC_GPRS_FAIL_CAUSE_SVC_NOT_SUBSCRIBED:
+ return PDP_FAIL_SERVICE_OPTION_NOT_SUBSCRIBED;
+ case IPC_GPRS_FAIL_CAUSE_SVC_OPT_OUT_ORDER:
+ return PDP_FAIL_SERVICE_OPTION_OUT_OF_ORDER;
+ case IPC_GPRS_FAIL_CAUSE_NSAPI_USED:
+ return PDP_FAIL_NSAPI_IN_USE;
+ case IPC_GPRS_FAIL_CAUSE_NETWORK_FAILURE:
+ return PDP_FAIL_DATA_REGISTRATION_FAIL;
+ case IPC_GPRS_FAIL_CAUSE_UNKOWN_PDP_CONTEXT:
+ case IPC_GPRS_FAIL_CAUSE_INVALID_MSG:
+ case IPC_GPRS_FAIL_CAUSE_PROTOCOL_ERROR:
+ return PDP_FAIL_PROTOCOL_ERRORS;
+ case IPC_GPRS_FAIL_CAUSE_MOBILE_FAILURE_ERROR:
+ return PDP_FAIL_SIGNAL_LOST;
+ case IPC_GPRS_FAIL_CAUSE_UNKNOWN_ERROR:
+ default:
+ return PDP_FAIL_ERROR_UNSPECIFIED;
+ }
+}
+
+int ril_data_connection_register(unsigned int cid, char *apn, char *username,
+ char *password, char *iface)
+{
+ struct ril_data_connection *data_connection;
+ struct list_head *list_end;
+ struct list_head *list;
+ unsigned int i;
+
+ if (apn == NULL || ril_data == NULL)
+ return -1;
+
+ data_connection = (struct ril_data_connection *) calloc(1, sizeof(struct ril_data_connection));
+ data_connection->cid = cid;
+ data_connection->enabled = 0;
+ data_connection->apn = apn;
+ data_connection->username = username;
+ data_connection->password = password;
+ data_connection->iface = iface;
+
+ list_end = ril_data->data_connections;
+ while (list_end != NULL && list_end->next != NULL)
+ list_end = list_end->next;
+
+ list = list_head_alloc(list_end, NULL, (void *) data_connection);
+
+ if (ril_data->data_connections == NULL)
+ ril_data->data_connections = list;
+
+ return 0;
+}
+
+int ril_data_connection_unregister(struct ril_data_connection *data_connection)
+{
+ struct list_head *list;
+ unsigned int i;
+
+ if (data_connection == NULL || ril_data == NULL)
+ return -1;
+
+ list = ril_data->data_connections;
+ while (list != NULL) {
+ if (list->data == (void *) data_connection) {
+ memset(data_connection, 0, sizeof(struct ril_data_connection));
+ free(data_connection);
+
+ if (list == ril_data->data_connections)
+ ril_data->data_connections = list->next;
+
+ list_head_free(list);
+
+ break;
+ }
+
+list_continue:
+ list = list->next;
+ }
+
+ return 0;
+}
+
+int ril_data_connection_flush(void)
+{
+ struct ril_data_connection *data_connection;
+ struct list_head *list;
+ struct list_head *list_next;
+
+ if (ril_data == NULL)
+ return -1;
+
+ list = ril_data->data_connections;
+ while (list != NULL) {
+ if (list->data != NULL) {
+ data_connection = (struct ril_data_connection *) list->data;
+
+ ril_data_connection_stop(data_connection);
+
+ memset(data_connection, 0, sizeof(struct ril_data_connection));
+ free(data_connection);
+ }
+
+ if (list == ril_data->data_connections)
+ ril_data->data_connections = list->next;
+
+ list_next = list->next;
+
+ list_head_free(list);
+
+list_continue:
+ list = list_next;
+ }
+
+ return 0;
+}
+
+struct ril_data_connection *ril_data_connection_find_cid(unsigned int cid)
+{
+ struct ril_data_connection *data_connection;
+ struct list_head *list;
+
+ if (ril_data == NULL)
+ return NULL;
+
+ list = ril_data->data_connections;
+ while (list != NULL) {
+ if (list->data == NULL)
+ goto list_continue;
+
+ data_connection = (struct ril_data_connection *) list->data;
+
+ if (data_connection->cid == cid)
+ return data_connection;
+
+list_continue:
+ list = list->next;
+ }
+
+ return NULL;
+}
+
+struct ril_data_connection *ril_data_connection_find_token(RIL_Token token)
+{
+ struct ril_data_connection *data_connection;
+ struct list_head *list;
+
+ if (ril_data == NULL)
+ return NULL;
+
+ list = ril_data->data_connections;
+ while (list != NULL) {
+ if (list->data == NULL)
+ goto list_continue;
+
+ data_connection = (struct ril_data_connection *) list->data;
+
+ if (data_connection->token == token)
+ return data_connection;
+
+list_continue:
+ list = list->next;
+ }
+
+ return NULL;
+}
+
+struct ril_data_connection *ril_data_connection_start(char *apn, char *username,
+ char *password)
+{
+ struct ril_data_connection *data_connection;
+ struct ril_client *client;
+ struct ipc_fmt_data *ipc_fmt_data;
+ struct ipc_client_gprs_capabilities gprs_capabilities;
+ char *iface = NULL;
+ unsigned int cid;
+ unsigned int i;
+ int rc;
+
+ if (apn == NULL)
+ goto error;
+
+ client = ril_client_find_id(RIL_CLIENT_IPC_FMT);
+ if (client == NULL || client->data == NULL)
+ goto error;
+
+ ipc_fmt_data = (struct ipc_fmt_data *) client->data;
+ if (ipc_fmt_data->ipc_client == NULL)
+ goto error;
+
+ rc = ipc_client_gprs_get_capabilities(ipc_fmt_data->ipc_client, &gprs_capabilities);
+ if (rc < 0)
+ goto error;
+
+ for (i = 0; i < gprs_capabilities.cid_count; i++) {
+ data_connection = ril_data_connection_find_cid(i + 1);
+ if (data_connection == NULL)
+ break;
+ }
+
+ cid = i + 1;
+
+ if (cid > gprs_capabilities.cid_count) {
+ RIL_LOGE("%s: No place left for a new data connection", __func__);
+ goto error;
+ }
+
+ iface = ipc_client_gprs_get_iface(ipc_fmt_data->ipc_client, cid);
+ if (iface == NULL)
+ goto error;
+
+ rc = ril_data_connection_register(cid, apn, username, password, iface);
+ if (rc < 0)
+ goto error;
+
+ data_connection = ril_data_connection_find_cid(cid);
+ if (data_connection == NULL)
+ goto error;
+
+ RIL_LOGD("Starting new data connection with cid: %d and iface: %s", cid, iface);
+
+ goto complete;
+
+error:
+ if (iface != NULL)
+ free(iface);
+
+ data_connection = NULL;
+
+complete:
+ return data_connection;
+}
+
+int ril_data_connection_stop(struct ril_data_connection *data_connection)
+{
+ int rc;
+
+ if (data_connection == NULL)
+ return -1;
+
+ if (data_connection->apn != NULL)
+ free(data_connection->apn);
+
+ if (data_connection->username != NULL)
+ free(data_connection->username);
+
+ if (data_connection->password != NULL)
+ free(data_connection->password);
+
+ if (data_connection->iface != NULL)
+ free(data_connection->iface);
+
+ if (data_connection->ip != NULL)
+ free(data_connection->ip);
+
+ if (data_connection->gateway != NULL)
+ free(data_connection->gateway);
+
+ if (data_connection->subnet_mask != NULL)
+ free(data_connection->subnet_mask);
+
+ if (data_connection->dns1 != NULL)
+ free(data_connection->dns1);
+
+ if (data_connection->dns2 != NULL)
+ free(data_connection->dns2);
+
+ rc = ril_data_connection_unregister(data_connection);
+ if (rc < 0)
+ return -1;
+
+ return 0;
+}
+
+int ril_data_connection_enable(struct ril_data_connection *data_connection)
+{
+ struct ril_client *client;
+ struct ipc_fmt_data *ipc_fmt_data;
+ in_addr_t ip_addr;
+ in_addr_t gateway_addr;
+ in_addr_t subnet_mask_addr;
+ in_addr_t dns1_addr;
+ in_addr_t dns2_addr;
+ int rc;
+
+ if (data_connection == NULL || data_connection->iface == NULL || data_connection->ip == NULL || data_connection->gateway == NULL || data_connection->subnet_mask == NULL)
+ return -1;
+
+ client = ril_client_find_id(RIL_CLIENT_IPC_FMT);
+ if (client == NULL || client->data == NULL)
+ return -1;
+
+ ipc_fmt_data = (struct ipc_fmt_data *) client->data;
+ if (ipc_fmt_data->ipc_client == NULL)
+ return -1;
+
+ rc = ipc_client_gprs_activate(ipc_fmt_data->ipc_client, data_connection->cid);
+ if (rc < 0)
+ return -1;
+
+ ip_addr = inet_addr(data_connection->ip);
+ gateway_addr = inet_addr(data_connection->gateway);
+ subnet_mask_addr = inet_addr(data_connection->subnet_mask);
+
+ if (data_connection->dns1 != NULL)
+ dns1_addr = inet_addr(data_connection->dns1);
+ else
+ dns1_addr = 0;
+
+ if (data_connection->dns2 != NULL)
+ dns2_addr = inet_addr(data_connection->dns2);
+ else
+ dns2_addr = 0;
+
+ rc = ifc_configure(data_connection->iface, ip_addr, ipv4NetmaskToPrefixLength(subnet_mask_addr), gateway_addr, dns1_addr, dns2_addr);
+ if (rc < 0)
+ return -1;
+
+ data_connection->enabled = 1;
+
+ RIL_LOGD("%s: Enabled data connection with cid %d", __func__, data_connection->cid);
+
+ return 0;
+}
+
+int ril_data_connection_disable(struct ril_data_connection *data_connection)
+{
+ struct ril_client *client;
+ struct ipc_fmt_data *ipc_fmt_data;
+ int rc;
+
+ if (data_connection == NULL || data_connection->iface == NULL)
+ return -1;
+
+ client = ril_client_find_id(RIL_CLIENT_IPC_FMT);
+ if (client == NULL || client->data == NULL)
+ return -1;
+
+ ipc_fmt_data = (struct ipc_fmt_data *) client->data;
+ if (ipc_fmt_data->ipc_client == NULL)
+ return -1;
+
+ rc = ifc_reset_connections(data_connection->iface, RESET_IPV4_ADDRESSES);
+ if (rc < 0)
+ return -1;
+
+ rc = ifc_disable(data_connection->iface);
+ if (rc < 0)
+ return -1;
+
+ rc = ipc_client_gprs_deactivate(ipc_fmt_data->ipc_client, data_connection->cid);
+ if (rc < 0)
+ return -1;
+
+ data_connection->enabled = 0;
+
+ RIL_LOGD("%s: Disabled data connection with cid %d", __func__, data_connection->cid);
+
+ return 0;;
+}
+
+int ipc_gprs_define_pdp_context_callback(struct ipc_message *message)
+{
+ struct ipc_gprs_pdp_context_request_set_data request_data;
+ struct ril_data_connection *data_connection;
+ struct ipc_gen_phone_res_data *data;
+ int rc;
+
+ if (message == NULL || message->data == NULL || message->size < sizeof(struct ipc_gen_phone_res_data))
+ return -1;
+
+ data = (struct ipc_gen_phone_res_data *) message->data;
+
+ data_connection = ril_data_connection_find_token(ipc_fmt_request_token(message->aseq));
+ if (data_connection == NULL) {
+ ril_request_complete(ipc_fmt_request_token(message->aseq), RIL_E_GENERIC_FAILURE, NULL, 0);
+ return 0;
+ }
+
+ rc = ipc_gen_phone_res_check(data);
+ if (rc < 0)
+ goto error;
+
+ rc = ipc_gprs_pdp_context_request_set_setup(&request_data, 1, data_connection->cid, data_connection->username, data_connection->password);
+ if (rc < 0)
+ goto error;
+
+ rc = ipc_gen_phone_res_expect_callback(message->aseq, IPC_GPRS_PDP_CONTEXT, ipc_gprs_pdp_context_callback);
+ if (rc < 0)
+ goto error;
+
+ rc = ipc_fmt_send(message->aseq, IPC_GPRS_PDP_CONTEXT, IPC_TYPE_SET, (void *) &request_data, sizeof(request_data));
+ if (rc < 0)
+ goto error;
+
+ goto complete;
+
+error:
+ ril_data_connection_stop(data_connection);
+
+ ril_request_complete(ipc_fmt_request_token(message->aseq), RIL_E_GENERIC_FAILURE, NULL, 0);
+
+complete:
+ return 0;
+}
+
+int ril_request_setup_data_call(void *data, size_t size, RIL_Token token)
+{
+ struct ipc_gprs_define_pdp_context_data request_data;
+ struct ril_data_connection *data_connection = NULL;
+ struct ril_request *request;
+ char *apn = NULL;
+ char *username = NULL;
+ char *password = NULL;
+ char **values = NULL;
+ unsigned int i;
+ int rc;
+
+ if (data == NULL || size < 6 * sizeof(char *))
+ goto error;
+
+ rc = ril_radio_state_check(RADIO_STATE_SIM_READY);
+ if (rc < 0) {
+ ril_request_complete(token, RIL_E_OP_NOT_ALLOWED_BEFORE_REG_TO_NW, NULL, 0);
+ return RIL_REQUEST_COMPLETED;
+ }
+
+ request = ril_request_find_request_status(RIL_REQUEST_SETUP_DATA_CALL, RIL_REQUEST_HANDLED);
+ if (request != NULL)
+ return RIL_REQUEST_UNHANDLED;
+
+ values = (char **) data;
+
+ if (values[2] == NULL) {
+ RIL_LOGE("%s: No APN was provided", __func__);
+ goto error;
+ }
+
+ apn = strdup(values[2]);
+
+ if (values[3] != NULL)
+ username = strdup(values[3]);
+
+ if (values[4] != NULL)
+ password = strdup(values[4]);
+
+ strings_array_free(values, size);
+ values = NULL;
+
+ RIL_LOGD("Setting up data connection to APN: %s with username/password: %s/%s", apn, username, password);
+
+ data_connection = ril_data_connection_start(apn, username, password);
+ if (data_connection == NULL)
+ goto error;
+
+ data_connection->token = token;
+
+ rc = ipc_gprs_define_pdp_context_setup(&request_data, 1, data_connection->cid, data_connection->apn);
+ if (rc < 0)
+ goto error;
+
+ rc = ipc_gen_phone_res_expect_callback(ipc_fmt_request_seq(token), IPC_GPRS_DEFINE_PDP_CONTEXT, ipc_gprs_define_pdp_context_callback);
+ if (rc < 0)
+ goto error;
+
+ rc = ipc_fmt_send(ipc_fmt_request_seq(token), IPC_GPRS_DEFINE_PDP_CONTEXT, IPC_TYPE_SET, (void *) &request_data, sizeof(request_data));
+ if (rc < 0)
+ goto error;
+
+ rc = RIL_REQUEST_HANDLED;
+ goto complete;
+
+error:
+ if (values != NULL)
+ strings_array_free(values, size);
+
+ if (data_connection != NULL) {
+ ril_data_connection_stop(data_connection);
+ } else {
+ if (apn != NULL)
+ free(apn);
+
+ if (username != NULL)
+ free(username);
+
+ if (password != NULL)
+ free(password);
+ }
+
+ ril_request_complete(token, RIL_E_GENERIC_FAILURE, NULL, 0);
+
+ rc = RIL_REQUEST_COMPLETED;
+
+complete:
+ return rc;
+}
+
+int ipc_gprs_ps(struct ipc_message *message)
+{
+ struct ipc_gprs_ps_data *data;
+ struct ril_data_connection *data_connection;
+
+ if (message == NULL || message->data == NULL || message->size < sizeof(struct ipc_gprs_ps_data))
+ return -1;
+
+ data = (struct ipc_gprs_ps_data *) message->data;
+
+ data_connection = ril_data_connection_find_cid(data->cid);
+ if (data_connection == NULL)
+ return 0;
+
+ data_connection->attached = !!data->attached;
+
+ return 0;
+}
+
+int ipc_gprs_pdp_context(struct ipc_message *message)
+{
+ struct ipc_gprs_pdp_context_request_get_data *data;
+ struct ril_data_connection *data_connection;
+#if RIL_VERSION >= 6
+ RIL_Data_Call_Response_v6 response[3];
+#else
+ RIL_Data_Call_Response response[3];
+#endif
+ unsigned int entries_count;
+ unsigned int index = 0;
+ size_t size;
+ unsigned int i;
+
+ if (message == NULL || message->data == NULL || message->size < sizeof(struct ipc_gprs_pdp_context_request_get_data))
+ return -1;
+
+ data = (struct ipc_gprs_pdp_context_request_get_data *) message->data;
+
+ entries_count = sizeof(data->entries) / sizeof(struct ipc_gprs_pdp_context_request_get_entry);
+
+ memset(&response, 0, sizeof(response));
+
+ for (i = 0; i < entries_count; i++) {
+ if (!data->entries[i].active)
+ continue;
+
+ data_connection = ril_data_connection_find_cid(data->entries[i].cid);
+ if (data_connection == NULL)
+ continue;
+
+#if RIL_VERSION >= 6
+ response[index].status = PDP_FAIL_NONE;
+#endif
+ response[index].cid = data->entries[i].cid;
+
+ if (data_connection->enabled)
+ response[index].active = 2;
+ else
+ response[index].active = 0;
+
+ response[index].type = strdup("IP");
+#if RIL_VERSION >= 6
+ if (data_connection->iface != NULL)
+ response[index].ifname = strdup(data_connection->iface);
+
+ if (data_connection->ip != NULL)
+ response[index].addresses = strdup(data_connection->ip);
+
+ asprintf(&response[index].dnses, "%s %s", data_connection->dns1, data_connection->dns2);
+
+ if (data_connection->gateway != NULL)
+ response[index].gateways = strdup(data_connection->gateway);
+#else
+ if (data_connection->apn != NULL)
+ response[index].apn = strdup(data_connection->apn);
+
+ if (data_connection->ip != NULL)
+ response[index].address = strdup(data_connection->ip);
+#endif
+
+ index++;
+ }
+
+#if RIL_VERSION >= 6
+ size = index * sizeof(RIL_Data_Call_Response_v6);
+#else
+ size = index * sizeof(RIL_Data_Call_Response);
+#endif
+
+ if (!ipc_seq_valid(message->aseq))
+ ril_request_unsolicited(RIL_UNSOL_DATA_CALL_LIST_CHANGED, &response, size);
+ else
+ ril_request_complete(ipc_fmt_request_token(message->aseq), RIL_E_SUCCESS, &response, size);
+
+ for (i = 0; i < index; i++) {
+ if (response[i].type != NULL)
+ free(response[i].type);
+
+#if RIL_VERSION >= 6
+ if (response[i].ifname != NULL)
+ free(response[i].ifname);
+
+ if (response[i].addresses != NULL)
+ free(response[i].addresses);
+
+ if (response[i].dnses != NULL)
+ free(response[i].dnses);
+
+ if (response[i].gateways != NULL)
+ free(response[i].gateways);
+#else
+ if (response[i].apn != NULL)
+ free(response[i].apn);
+
+ if (response[i].address != NULL)
+ free(response[i].address);
+#endif
+ }
+
+ return 0;
+}
+
+int ril_request_data_call_list(void *data, size_t size, RIL_Token token)
+{
+ int rc;
+
+ rc = ril_radio_state_check(RADIO_STATE_SIM_NOT_READY);
+ if (rc < 0) {
+ ril_request_complete(token, RIL_E_SUCCESS, NULL, 0);
+ return RIL_REQUEST_COMPLETED;
+ }
+
+ rc = ipc_fmt_send(ipc_fmt_request_seq(token), IPC_GPRS_PDP_CONTEXT, IPC_TYPE_GET, NULL, 0);
+ if (rc < 0)
+ goto error;
+
+ rc = RIL_REQUEST_HANDLED;
+ goto complete;
+
+error:
+ ril_request_complete(token, RIL_E_GENERIC_FAILURE, NULL, 0);
+
+ rc = RIL_REQUEST_COMPLETED;
+
+complete:
+ return rc;
+}
+
+int ipc_gprs_pdp_context_callback(struct ipc_message *message)
+{
+ struct ril_data_connection *data_connection;
+ struct ipc_gen_phone_res_data *data;
+ int rc;
+
+ if (message == NULL || message->data == NULL || message->size < sizeof(struct ipc_gen_phone_res_data))
+ return -1;
+
+ data = (struct ipc_gen_phone_res_data *) message->data;
+
+ data_connection = ril_data_connection_find_token(ipc_fmt_request_token(message->aseq));
+ if (data_connection == NULL) {
+ ril_request_complete(ipc_fmt_request_token(message->aseq), RIL_E_GENERIC_FAILURE, NULL, 0);
+ return 0;
+ }
+
+ rc = ipc_gen_phone_res_check(data);
+ if (rc < 0)
+ goto error;
+
+ goto complete;
+
+error:
+ ril_data_connection_stop(data_connection);
+
+ ril_request_complete(ipc_fmt_request_token(message->aseq), RIL_E_GENERIC_FAILURE, NULL, 0);
+
+complete:
+ return 0;
+}
+
+int ril_request_deactivate_data_call(void *data, size_t size, RIL_Token token)
+{
+ struct ipc_gprs_pdp_context_request_set_data request_data;
+ struct ril_data_connection *data_connection = NULL;
+ struct ril_request *request;
+ char **values = NULL;
+ unsigned int cid;
+ unsigned int i;
+ int rc;
+
+ if (data == NULL || size < 2 * sizeof(char *))
+ goto error;
+
+ rc = ril_radio_state_check(RADIO_STATE_SIM_READY);
+ if (rc < 0)
+ return RIL_REQUEST_UNHANDLED;
+
+ request = ril_request_find_request_status(RIL_REQUEST_DEACTIVATE_DATA_CALL, RIL_REQUEST_HANDLED);
+ if (request != NULL)
+ return RIL_REQUEST_UNHANDLED;
+
+ values = (char **) data;
+
+ if (values[0] == NULL) {
+ RIL_LOGE("%s: No cid was provided", __func__);
+ goto error;
+ }
+
+ cid = (unsigned int) atoi(values[0]);
+
+ strings_array_free(values, size);
+ values = NULL;
+
+ data_connection = ril_data_connection_find_cid(cid);
+ if (data_connection == NULL)
+ goto error;
+
+ data_connection->token = token;
+
+ rc = ipc_gprs_pdp_context_request_set_setup(&request_data, 0, data_connection->cid, NULL, NULL);
+ if (rc < 0)
+ goto error;
+
+ rc = ipc_gen_phone_res_expect_callback(ipc_fmt_request_seq(token), IPC_GPRS_PDP_CONTEXT, ipc_gprs_pdp_context_callback);
+ if (rc < 0)
+ goto error;
+
+ rc = ipc_fmt_send(ipc_fmt_request_seq(token), IPC_GPRS_PDP_CONTEXT, IPC_TYPE_SET, (void *) &request_data, sizeof(request_data));
+ if (rc < 0)
+ goto error;
+
+ rc = RIL_REQUEST_HANDLED;
+ goto complete;
+
+error:
+ if (values != NULL)
+ strings_array_free(values, size);
+
+ if (data_connection != NULL)
+ ril_data_connection_stop(data_connection);
+
+ ril_request_complete(token, RIL_E_GENERIC_FAILURE, NULL, 0);
+
+ rc = RIL_REQUEST_COMPLETED;
+
+complete:
+ return rc;
+}
+
+int ipc_gprs_ip_configuration(struct ipc_message *message)
+{
+ struct ipc_gprs_ip_configuration_data *data;
+ struct ril_data_connection *data_connection;
+
+ if (message == NULL || message->data == NULL || message->size < sizeof(struct ipc_gprs_ip_configuration_data))
+ return -1;
+
+ data = (struct ipc_gprs_ip_configuration_data *) message->data;
+
+ data_connection = ril_data_connection_find_cid(data->cid);
+ if (data_connection == NULL) {
+ if (ipc_seq_valid(message->aseq))
+ ril_request_complete(ipc_fmt_request_token(message->aseq), RIL_E_GENERIC_FAILURE, NULL, 0);
+
+ return 0;
+ }
+
+ if (data_connection->ip != NULL)
+ free(data_connection->ip);
+
+ asprintf(&data_connection->ip, "%i.%i.%i.%i", data->ip[0], data->ip[1], data->ip[2], data->ip[3]);
+
+ if (data_connection->gateway != NULL)
+ free(data_connection->gateway);
+
+ asprintf(&data_connection->gateway, "%i.%i.%i.%i", data->ip[0], data->ip[1], data->ip[2], data->ip[3]);
+
+ if (data_connection->subnet_mask != NULL)
+ free(data_connection->subnet_mask);
+
+ asprintf(&data_connection->subnet_mask, "255.255.255.255");
+
+ if (data_connection->dns1 != NULL)
+ free(data_connection->dns1);
+
+ asprintf(&data_connection->dns1, "%i.%i.%i.%i", data->dns1[0], data->dns1[1], data->dns1[2], data->dns1[3]);
+
+ if (data_connection->dns2 != NULL)
+ free(data_connection->dns2);
+
+ asprintf(&data_connection->dns2, "%i.%i.%i.%i", data->dns2[0], data->dns2[1], data->dns2[2], data->dns2[3]);
+
+ return 0;
+}
+
+int ipc_gprs_hsdpa_status(struct ipc_message *message)
+{
+ struct ipc_gprs_hsdpa_status_data *data;
+ struct ril_client *client;
+ struct ipc_fmt_data *ipc_fmt_data;
+
+ if (message == NULL || message->data == NULL || message->size < sizeof(struct ipc_gprs_hsdpa_status_data))
+ return -1;
+
+ client = ril_client_find_id(RIL_CLIENT_IPC_FMT);
+ if (client == NULL || client->data == NULL)
+ return -1;
+
+ data = (struct ipc_gprs_hsdpa_status_data *) message->data;
+
+ ipc_fmt_data = (struct ipc_fmt_data *) client->data;
+
+ if (ipc_fmt_data->hsdpa_status_data.status != data->status) {
+ ipc_fmt_data->hsdpa_status_data.status = data->status;
+
+#if RIL_VERSION >= 6
+ ril_request_unsolicited(RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED, NULL, 0);
+#else
+ ril_request_unsolicited(RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED, NULL, 0);
+#endif
+ }
+
+ return 0;
+}
+
+int ipc_gprs_call_status(struct ipc_message *message)
+{
+#if RIL_VERSION >= 6
+ RIL_Data_Call_Response_v6 response;
+#else
+ char *setup_data_call_response[3];
+#endif
+ struct ipc_gprs_call_status_data *data;
+ struct ril_data_connection *data_connection;
+ struct ril_request *request;
+ int fail_cause;
+ unsigned int i;
+ int rc;
+
+ if (message == NULL || message->data == NULL || message->size < sizeof(struct ipc_gprs_call_status_data))
+ return -1;
+
+ rc = ril_radio_state_check(RADIO_STATE_SIM_READY);
+ if (rc < 0)
+ return 0;
+
+ data = (struct ipc_gprs_call_status_data *) message->data;
+
+ data_connection = ril_data_connection_find_cid(data->cid);
+ if (data_connection == NULL) {
+ RIL_LOGE("%s: Finding data connection with cid: %d failed", __func__, data->cid);
+
+ if (ipc_seq_valid(message->aseq))
+ ril_request_complete(ipc_fmt_request_token(message->aseq), RIL_E_GENERIC_FAILURE, NULL, 0);
+
+ return 0;
+ }
+
+ request = ril_request_find_token(data_connection->token);
+
+ if (data->status == IPC_GPRS_STATUS_ENABLED) {
+ if (data_connection->enabled) {
+ if (request != NULL && request->request == RIL_REQUEST_DEACTIVATE_DATA_CALL)
+ goto error;
+ else
+ RIL_LOGD("%s: Data connection with cid %d is already enabled", __func__, data_connection->cid);
+ } else {
+ rc = ril_data_connection_enable(data_connection);
+ if (rc < 0)
+ goto error;
+
+ if (request != NULL && request->request == RIL_REQUEST_SETUP_DATA_CALL) {
+ memset(&response, 0, sizeof(response));
+#if RIL_VERSION >= 6
+ response.status = ipc2ril_gprs_fail_cause(data->fail_cause);
+ response.cid = data_connection->cid;
+ response.active = 2;
+ response.type = strdup("IP");
+
+ if (data_connection->iface != NULL)
+ response.ifname = strdup(data_connection->iface);
+
+ if (data_connection->ip != NULL)
+ response.addresses = strdup(data_connection->ip);
+
+ asprintf(&response.dnses, "%s %s", data_connection->dns1, data_connection->dns2);
+
+ if (data_connection->gateway != NULL)
+ response.gateways = strdup(data_connection->gateway);
+#else
+ asprintf(&response[0], "%d", gprs_connection->cid);
+
+ if (data_connection->iface != NULL)
+ response[1] = strdup(data_connection->iface);
+
+ if (data_connection->ip != NULL)
+ response[2] = strdup(data_connection->ip);
+#endif
+
+ ril_request_complete(data_connection->token, RIL_E_SUCCESS, &response, sizeof(response));
+ data_connection->token = NULL;
+
+#if RIL_VERSION >= 6
+ if (response.type != NULL)
+ free(response.type);
+
+ if (response.ifname != NULL)
+ free(response.ifname);
+
+ if (response.addresses != NULL)
+ free(response.addresses);
+
+ if (response.gateways != NULL)
+ free(response.gateways);
+
+ if (response.dnses != NULL)
+ free(response.dnses);
+#else
+ for (i = 0; i < 3; i++) {
+ if (reponse[i] != NULL)
+ free(response[i]);
+ }
+#endif
+ } else {
+ RIL_LOGD("%s: Data connection with cid: %d got unexpectedly enabled", __func__, data_connection->cid);
+ }
+ }
+ } else if (data->status == IPC_GPRS_STATUS_DISABLED || data->status == IPC_GPRS_STATUS_NOT_ENABLED) {
+ if (data_connection->enabled) {
+ rc = ril_data_connection_disable(data_connection);
+ if (rc < 0)
+ goto error;
+
+ ril_data_connection_stop(data_connection);
+
+ if (request != NULL && request->request == RIL_REQUEST_DEACTIVATE_DATA_CALL)
+ ril_request_complete(request->token, RIL_E_SUCCESS, NULL, 0);
+ else
+ RIL_LOGD("%s: Data connection with cid: %d got unexpectedly disabled", __func__, data->cid);
+ } else {
+ if (request != NULL && request->request == RIL_REQUEST_SETUP_DATA_CALL) {
+#if RIL_VERSION >= 6
+ memset(&response, 0, sizeof(response));
+ response.status = ipc2ril_gprs_fail_cause(data->fail_cause);
+
+ ril_request_complete(request->token, RIL_E_SUCCESS, (void *) &response, sizeof(response));
+ // Avoid completing the request twice
+ request = NULL;
+#else
+ fail_cause = ipc2ril_gprs_fail_cause(data->fail_cause);
+
+ ril_request_data_set_uniq(RIL_REQUEST_LAST_DATA_CALL_FAIL_CAUSE, (void *) &fail_cause, sizeof(fail_cause));
+#endif
+ goto error;
+ } else {
+ RIL_LOGD("%s: Data connection with cid: %d is already disabled", __func__, data_connection->cid);
+ }
+ }
+ }
+
+ if (request == NULL) {
+ rc = ipc_fmt_send(0, IPC_GPRS_PDP_CONTEXT, IPC_TYPE_GET, NULL, 0);
+ if (rc < 0)
+ goto error;
+ }
+
+ goto complete;
+
+error:
+ ril_data_connection_stop(data_connection);
+
+ if (request != NULL)
+ ril_request_complete(request->token, RIL_E_GENERIC_FAILURE, NULL, 0);
+
+complete:
+ return 0;
+}
+
+int ril_request_last_data_call_fail_cause(void *data, size_t size,
+ RIL_Token token)
+{
+ void *fail_cause_data;
+ size_t fail_cause_size;
+ int fail_cause;
+ int rc;
+
+ rc = ril_radio_state_check(RADIO_STATE_SIM_NOT_READY);
+ if (rc < 0)
+ return RIL_REQUEST_UNHANDLED;
+
+ fail_cause_size = ril_request_data_size_get(RIL_REQUEST_LAST_DATA_CALL_FAIL_CAUSE);
+ fail_cause_data = ril_request_data_get(RIL_REQUEST_LAST_DATA_CALL_FAIL_CAUSE);
+
+ if (fail_cause_data != NULL && fail_cause_size >= sizeof(int)) {
+ fail_cause = *((int *) fail_cause_data);
+ free(fail_cause_data);
+ } else {
+ fail_cause = PDP_FAIL_ERROR_UNSPECIFIED;
+ }
+
+ ril_request_complete(token, RIL_E_SUCCESS, (void *) &fail_cause, sizeof(fail_cause));
+
+ return RIL_REQUEST_COMPLETED;
+}