+/*
+ * tel-plugin-at_standard
+ *
+ * Copyright (c) 2012 Intel Corporation. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#include <tcore.h>
+#include <server.h>
+#include <plugin.h>
+#include <core_object.h>
+#include <hal.h>
+#include <at.h>
+#include <util.h>
+
+#include <co_ps.h>
+#include <co_context.h>
+
+#include "at_ps.h"
+
+enum pdp_context_state {
+ DEACTIVATE = 0,
+ ACTIVATE = 1,
+};
+
+static TReturn set_pdp_context(CoreObject *co_ps, CoreObject *ps_context,
+ enum pdp_context_state state);
+
+static void on_confirmation_ps_message_send(TcorePending *p, gboolean result,
+ void *user_data)
+{
+ dbg("msg out from queue");
+
+ dbg("Sending %s", (result == TRUE) ? "OK" : "FAIL");
+}
+
+static void notify_context_deactivated(CoreObject *co_ps,
+ CoreObject *ps_context)
+{
+ struct tnoti_ps_call_status data_resp = {0};
+ unsigned int cid = tcore_context_get_id(ps_context);
+ Server *server;
+
+ dbg("Enter");
+
+ data_resp.context_id = cid;
+ data_resp.state = TELEPHONY_GPRS_DATA_STATUS_NOCARRIER;
+ data_resp.result = 0;
+
+ server = tcore_plugin_ref_server(tcore_object_ref_plugin(co_ps));
+
+ tcore_server_send_notification(server, co_ps, TNOTI_PS_CALL_STATUS,
+ sizeof(struct tnoti_ps_call_status),
+ &data_resp);
+
+ dbg("Exit");
+}
+
+static void on_setup_netif(CoreObject *co_ps, const char *netif_name,
+ void *user_data)
+{
+ CoreObject *ps_context = user_data;
+ struct tnoti_ps_call_status data_status = {0};
+ Server *server;
+
+ dbg("Enter");
+
+ dbg("devname = [%s]", netif_name);
+
+ if (tcore_util_netif_up(netif_name) != TCORE_RETURN_SUCCESS) {
+ err("util_netif_up() failed.");
+ set_pdp_context(co_ps, ps_context, DEACTIVATE);
+ return;
+ }
+
+ tcore_context_set_ipv4_devname(ps_context, netif_name);
+
+ server = tcore_plugin_ref_server(tcore_object_ref_plugin(co_ps));
+
+ data_status.context_id = tcore_context_get_id(ps_context);
+ data_status.state = TELEPHONY_GPRS_DATA_STATUS_CONNECTED;
+ data_status.result = 0;
+
+ tcore_server_send_notification(server, co_ps,
+ TNOTI_PS_CALL_STATUS,
+ sizeof(struct tnoti_ps_call_status),
+ &data_status);
+
+ dbg("Exit");
+}
+
+static void on_response_get_pdp_address(TcorePending *p, int data_len,
+ const void *data, void *user_data)
+{
+ CoreObject *co_ps = tcore_pending_ref_core_object(p);
+ CoreObject *ps_context = user_data;
+ TcoreHal *h = tcore_object_get_hal(co_ps);
+ GSList *tokens = NULL;
+ const TcoreATResponse *resp = data;
+ const char *line;
+ char *pdp_address;
+ char *real_pdp_address;
+ int cid = tcore_context_get_id(ps_context);
+
+ dbg("Enter");
+
+ if (NULL == resp->final_response) {
+ err("Response NOK");
+ goto error;
+ }
+
+ dbg("Response OK");
+
+ if (NULL == resp->lines) {
+ err("Invalid response line");
+ goto error;
+ }
+
+ line = (const char *)resp->lines->data;
+ tokens = tcore_at_tok_new(line);
+ if (g_slist_length(tokens) < 2) {
+ err("Invalid message");
+ goto error;
+ }
+
+ dbg("Line: %s", line);
+
+ /* Skip CID & read directly IP address */
+ pdp_address = g_slist_nth_data(tokens, 1);
+ real_pdp_address = tcore_at_tok_extract(pdp_address);
+
+ tcore_context_set_ipv4_addr(ps_context, real_pdp_address);
+
+ dbg("PDP address: %s", real_pdp_address);
+
+ g_free(real_pdp_address);
+
+ dbg("Adding default DNS");
+
+ tcore_context_set_ipv4_dns(ps_context, "8.8.8.8", "8.8.4.4");
+
+ if (tcore_hal_setup_netif(h, co_ps, on_setup_netif, ps_context, cid)
+ == TCORE_RETURN_SUCCESS)
+ goto out;
+
+error:
+ err("Failed to get PDP address deactivating context...");
+ set_pdp_context(co_ps, ps_context, DEACTIVATE);
+
+out:
+ if (tokens != NULL)
+ tcore_at_tok_free(tokens);
+
+ dbg("Exit");
+}
+
+static void get_pdp_address(CoreObject *co_ps, CoreObject *ps_context)
+{
+ char *cmd_str = NULL;
+ unsigned int cid = tcore_context_get_id(ps_context);
+
+ dbg("Enter");
+
+ cmd_str = g_strdup_printf("AT+CGPADDR=%d", cid);
+
+ if (tcore_prepare_and_send_at_request(co_ps, cmd_str, NULL,
+ TCORE_AT_NO_RESULT, NULL,
+ on_response_get_pdp_address,
+ ps_context,
+ on_confirmation_ps_message_send, NULL)
+ != TCORE_RETURN_SUCCESS) {
+ err("Failed to prepare and send AT request");
+ set_pdp_context(co_ps, ps_context, DEACTIVATE);
+ }
+
+ g_free(cmd_str);
+
+ dbg("Exit");
+}
+
+static void on_response_set_pdp_context_activate(TcorePending *p, int data_len,
+ const void *data,
+ void *user_data)
+{
+ CoreObject *co_ps = tcore_pending_ref_core_object(p);
+ const TcoreATResponse *resp = data;
+ CoreObject *ps_context = user_data;
+
+ dbg("Enter");
+
+ if (resp->success) {
+ dbg("Response OK");
+ get_pdp_address(co_ps, ps_context);
+ } else {
+ /* TODO: Manage CME errors */
+ err("Response NOK");
+ notify_context_deactivated(co_ps, ps_context);
+ }
+
+ dbg("Exit");
+}
+
+static TReturn set_pdp_context(CoreObject *co_ps, CoreObject *ps_context,
+ enum pdp_context_state state)
+{
+ char *cmd_str = NULL;
+ unsigned int cid = tcore_context_get_id(ps_context);
+ int ret = TCORE_RETURN_SUCCESS;
+ TcorePendingResponseCallback cb = NULL;
+ gboolean activate = (state == ACTIVATE);
+
+ dbg("Enter");
+
+ dbg("CID %d %s", cid, activate ? "activation" : "deactivation");
+
+ if (activate == TRUE)
+ cb = on_response_set_pdp_context_activate;
+ else
+ return TCORE_RETURN_ENOSYS;
+
+ cmd_str = g_strdup_printf("AT+CGACT=%d,%d", activate ? 1 : 0, cid);
+
+ if (tcore_prepare_and_send_at_request(co_ps, cmd_str, NULL,
+ TCORE_AT_NO_RESULT, NULL,
+ cb, ps_context,
+ on_confirmation_ps_message_send, NULL)
+ != TCORE_RETURN_SUCCESS) {
+ err("Failed to prepare and send AT request");
+ notify_context_deactivated(co_ps, ps_context);
+ ret = TCORE_RETURN_FAILURE;
+ }
+
+ g_free(cmd_str);
+
+ dbg("Exit");
+
+ return ret;
+}
+
+static void on_response_define_pdp_context(TcorePending *p, int data_len,
+ const void *data,
+ void *user_data)
+{
+ const TcoreATResponse *resp = data;
+ CoreObject *ps_context = user_data;
+ CoreObject *co_ps = tcore_pending_ref_core_object(p);
+
+ dbg("Enter");
+
+ if (resp->success) {
+ dbg("Response OK");
+ set_pdp_context(co_ps, ps_context, ACTIVATE);
+ } else {
+ err("Response NOK");
+ notify_context_deactivated(co_ps, ps_context);
+ }
+
+ dbg("Exit");
+}
+
+static TReturn define_pdp_context(CoreObject *co_ps, CoreObject *ps_context)
+{
+ char *apn = NULL;
+ char *cmd_str = NULL;
+ char *pdp_type_str = NULL;
+ unsigned int cid;
+ enum co_context_type pdp_type;
+ enum co_context_d_comp d_comp;
+ enum co_context_h_comp h_comp;
+ int ret = TCORE_RETURN_FAILURE;
+
+ dbg("Enter");
+
+ pdp_type = tcore_context_get_type(ps_context);
+ cid = tcore_context_get_id(ps_context);
+
+ switch (pdp_type) {
+ case CONTEXT_TYPE_X25:
+ dbg("CONTEXT_TYPE_X25");
+ pdp_type_str = g_strdup("X.25");
+ break;
+
+ case CONTEXT_TYPE_IP:
+ dbg("CONTEXT_TYPE_IP");
+ pdp_type_str = g_strdup("IP");
+ break;
+
+ case CONTEXT_TYPE_PPP:
+ dbg("CONTEXT_TYPE_PPP");
+ pdp_type_str = g_strdup("PPP");
+ break;
+
+ case CONTEXT_TYPE_IPV6:
+ dbg("CONTEXT_TYPE_IPV6");
+ pdp_type_str = g_strdup("IPV6");
+ break;
+
+ default:
+ /* PDP Type not supported supported */
+ err("Unsupported PDP type %d ", pdp_type);
+
+ goto error;
+ }
+
+ d_comp = tcore_context_get_data_compression(ps_context);
+ h_comp = tcore_context_get_header_compression(ps_context);
+ apn = tcore_context_get_apn(ps_context);
+
+ cmd_str = g_strdup_printf("AT+CGDCONT=%d,\"%s\",\"%s\",,%d,%d", cid,
+ pdp_type_str, apn, d_comp, h_comp);
+
+ ret = tcore_prepare_and_send_at_request(co_ps, cmd_str, NULL,
+ TCORE_AT_NO_RESULT, NULL,
+ on_response_define_pdp_context,
+ ps_context,
+ on_confirmation_ps_message_send, NULL);
+
+ g_free(pdp_type_str);
+ g_free(cmd_str);
+ g_free(apn);
+
+ if (ret == TCORE_RETURN_SUCCESS)
+ goto out;
+
+ err("Failed to prepare and send AT request");
+
+error:
+ notify_context_deactivated(co_ps, ps_context);
+
+out:
+
+ dbg("Exit");
+
+ return ret;
+}
+
+static TReturn activate_ps_context(CoreObject *co_ps, CoreObject *ps_context,
+ void *user_data)
+{
+ return define_pdp_context(co_ps, ps_context);
+}
+
+static TReturn deactivate_ps_context(CoreObject *co_ps, CoreObject *ps_context,
+ void *user_data)
+{
+ return TCORE_RETURN_ENOSYS;
+}
+
+static struct tcore_ps_operations ps_ops = {
+ .activate_context = activate_ps_context,
+ .deactivate_context = deactivate_ps_context
+};
+
+gboolean at_ps_init(TcorePlugin *p)
+{
+ CoreObject *co_ps;
+
+ co_ps = tcore_ps_new(p, "umts_ps", &ps_ops, NULL);
+ if (NULL == co_ps)
+ return FALSE;
+
+ return TRUE;
+}
+
+void at_ps_exit(TcorePlugin *p)
+{
+ CoreObject *co_ps;
+
+ co_ps = tcore_plugin_ref_core_object(p, "umts_ps");
+ if (NULL == co_ps)
+ return;
+
+ tcore_ps_free(co_ps);
+}