Update plugin
[platform/core/telephony/tel-plugin-at_standard.git] / src / at_ps.c
1 /*
2  * tel-plugin-at_standard
3  *
4  * Copyright (c) 2012 Intel Corporation. All rights reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23
24 #include <glib.h>
25
26 #include <tcore.h>
27 #include <server.h>
28 #include <plugin.h>
29 #include <core_object.h>
30 #include <hal.h>
31 #include <at.h>
32 #include <util.h>
33
34 #include <co_ps.h>
35 #include <co_context.h>
36
37 #include "at_ps.h"
38
39 enum pdp_context_state {
40         DEACTIVATE      = 0,
41         ACTIVATE        = 1,
42 };
43
44 static TReturn set_pdp_context(CoreObject *co_ps, CoreObject *ps_context,
45                                         enum pdp_context_state state);
46
47 static void on_confirmation_ps_message_send(TcorePending *p, gboolean result,
48                                                 void *user_data)
49 {
50         dbg("msg out from queue");
51
52         dbg("Sending %s", (result == TRUE) ? "OK" : "FAIL");
53 }
54
55 static void notify_context_status_changed(CoreObject *co_ps, unsigned int cid,
56                                                 enum ps_data_call_status status)
57 {
58         struct tnoti_ps_call_status data_resp = {0};
59         TcorePlugin *p = tcore_object_ref_plugin(co_ps);
60         Server *server = tcore_plugin_ref_server(p);
61
62         dbg("Enter");
63
64         data_resp.context_id = cid;
65         data_resp.state = status;
66
67         tcore_server_send_notification(server, co_ps, TNOTI_PS_CALL_STATUS,
68                                         sizeof(struct tnoti_ps_call_status),
69                                         &data_resp);
70
71         dbg("Exit");
72 }
73
74 static void on_unmount_netif(CoreObject *co_ps, const char *netif_name,
75                                 void *user_data)
76 {
77         CoreObject *ps_context = user_data;
78         char *ifname;
79
80         dbg("Enter");
81
82         ifname = tcore_context_get_ipv4_devname(ps_context);
83
84         if (tcore_util_netif(ifname, FALSE) != TCORE_RETURN_SUCCESS)
85                 dbg("disabling network interface failed");
86
87         dbg("Exit");
88 }
89
90 static void on_response_set_pdp_context_deactivate(TcorePending *p,
91                                                         int data_len,
92                                                         const void *data,
93                                                         void *user_data)
94 {
95         CoreObject *co_ps = tcore_pending_ref_core_object(p);
96         TcoreHal *h = tcore_object_get_hal(co_ps);
97         const TcoreATResponse *resp = data;
98         CoreObject *ps_context = user_data;
99         unsigned int cid = tcore_context_get_id(ps_context);
100
101         dbg("Enter");
102
103         /*
104          * AT+CGACT = 0 is returning NO CARRIER or an error. Just test if the
105          * response contains NO CARRIER else decode CME error.
106          */
107         if (resp->lines != NULL) {
108                 const char *line;
109
110                 line = (const char *)resp->lines->data;
111                 if (g_strcmp0(line, "NO CARRIER") != 0) {
112                         /* TODO: Decode CME error */
113                         err("%s", line);
114                         err("Context %d has not been deactivated", cid);
115
116                         goto out;
117                 }
118
119         }
120
121         notify_context_status_changed(co_ps, cid, PS_DATA_CALL_NOT_CONNECTED);
122
123         if (tcore_hal_setup_netif(h, co_ps, on_unmount_netif, ps_context, cid,
124                                         FALSE) != TCORE_RETURN_SUCCESS)
125                 err("Failed to disable network interface");
126
127 out:
128         dbg("Exit");
129 }
130
131 static void on_mount_netif(CoreObject *co_ps, const char *netif_name,
132                                 void *user_data)
133 {
134         CoreObject *ps_context = user_data;
135         struct tnoti_ps_call_status data_status = {0};
136         Server *server;
137
138         dbg("Enter");
139
140         dbg("devname = [%s]", netif_name);
141
142         tcore_context_set_ipv4_devname(ps_context, netif_name);
143         if (tcore_util_netif(netif_name, TRUE) != TCORE_RETURN_SUCCESS)
144                 dbg("disabling network interface failed");
145
146         server = tcore_plugin_ref_server(tcore_object_ref_plugin(co_ps));
147
148         data_status.context_id = tcore_context_get_id(ps_context);
149         data_status.state = PS_DATA_CALL_CONNECTED;
150
151         tcore_server_send_notification(server, co_ps,
152                                         TNOTI_PS_CALL_STATUS,
153                                         sizeof(struct tnoti_ps_call_status),
154                                         &data_status);
155
156         dbg("Exit");
157 }
158
159 static void on_response_get_pdp_address(TcorePending *p, int data_len,
160                                         const void *data, void *user_data)
161 {
162         CoreObject *co_ps = tcore_pending_ref_core_object(p);
163         CoreObject *ps_context = user_data;
164         TcoreHal *h = tcore_object_get_hal(co_ps);
165         GSList *tokens = NULL;
166         const TcoreATResponse *resp = data;
167         const char *line;
168         char *pdp_address;
169         char *real_pdp_address;
170         int cid = tcore_context_get_id(ps_context);
171
172         dbg("Enter");
173
174         if (resp->final_response == NULL) {
175                 err("Response NOK");
176                 goto error;
177         }
178
179         dbg("Response OK");
180
181         if (resp->lines == NULL) {
182                 err("Invalid response line");
183                 goto error;
184         }
185
186         line = (const char *)resp->lines->data;
187         tokens = tcore_at_tok_new(line);
188         if (g_slist_length(tokens) < 2) {
189                 err("Invalid message");
190                 goto error;
191         }
192
193         dbg("Line: %s", line);
194
195         /* Skip CID & read directly IP address */
196         pdp_address = g_slist_nth_data(tokens, 1);
197         real_pdp_address = tcore_at_tok_extract(pdp_address);
198
199         tcore_context_set_ipv4_addr(ps_context, real_pdp_address);
200
201         dbg("PDP address: %s", real_pdp_address);
202
203         g_free(real_pdp_address);
204
205         dbg("Adding default DNS");
206
207         tcore_context_set_ipv4_dns(ps_context, "8.8.8.8", "8.8.4.4");
208
209         if (tcore_hal_setup_netif(h, co_ps, on_mount_netif, ps_context, cid,
210                                         TRUE) == TCORE_RETURN_SUCCESS)
211                 goto out;
212
213 error:
214         err("Failed to get PDP address deactivating context...");
215         set_pdp_context(co_ps, ps_context, DEACTIVATE);
216
217 out:
218         tcore_at_tok_free(tokens);
219         dbg("Exit");
220 }
221
222 static void get_pdp_address(CoreObject *co_ps, CoreObject *ps_context)
223 {
224         char *cmd_str;
225         unsigned int cid = tcore_context_get_id(ps_context);
226
227         dbg("Enter");
228
229         cmd_str = g_strdup_printf("AT+CGPADDR=%d", cid);
230
231         if (tcore_prepare_and_send_at_request(co_ps, cmd_str, NULL,
232                                         TCORE_AT_NO_RESULT, NULL,
233                                         on_response_get_pdp_address,
234                                         ps_context,
235                                         on_confirmation_ps_message_send, NULL)
236                                         != TCORE_RETURN_SUCCESS) {
237                 err("Failed to prepare and send AT request");
238                 set_pdp_context(co_ps, ps_context, DEACTIVATE);
239         }
240
241         g_free(cmd_str);
242
243         dbg("Exit");
244 }
245
246 static void on_response_set_pdp_context_activate(TcorePending *p, int data_len,
247                                                         const void *data,
248                                                         void *user_data)
249 {
250         CoreObject *co_ps = tcore_pending_ref_core_object(p);
251         const TcoreATResponse *resp = data;
252         CoreObject *ps_context = user_data;
253
254         dbg("Enter");
255
256         if (resp->success) {
257                 dbg("Response OK");
258                 get_pdp_address(co_ps, ps_context);
259         } else {
260                 unsigned int cid = tcore_context_get_id(ps_context);
261
262                 /* TODO: Manage CME errors */
263                 err("Response NOK");
264                 notify_context_status_changed(co_ps, cid, PS_DATA_CALL_NOT_CONNECTED);
265         }
266
267         dbg("Exit");
268 }
269
270 static TReturn set_pdp_context(CoreObject *co_ps, CoreObject *ps_context,
271                                         enum pdp_context_state state)
272 {
273         char *cmd_str;
274         unsigned int cid = tcore_context_get_id(ps_context);
275         int ret = TCORE_RETURN_SUCCESS;
276         TcorePendingResponseCallback cb = NULL;
277         gboolean activate = (state == ACTIVATE);
278
279         dbg("Enter");
280
281         dbg("CID %d %s", cid, activate ? "activation" : "deactivation");
282
283         if (activate == TRUE)
284                 cb = on_response_set_pdp_context_activate;
285         else
286                 cb = on_response_set_pdp_context_deactivate;
287
288         cmd_str = g_strdup_printf("AT+CGACT=%d,%d", activate ? 1 : 0, cid);
289
290         if (tcore_prepare_and_send_at_request(co_ps, cmd_str, NULL,
291                                         TCORE_AT_NO_RESULT, NULL,
292                                         cb, ps_context,
293                                         on_confirmation_ps_message_send, NULL)
294                                         != TCORE_RETURN_SUCCESS) {
295                 err("Failed to prepare and send AT request");
296                 notify_context_status_changed(co_ps, cid, PS_DATA_CALL_NOT_CONNECTED);
297                 ret =  TCORE_RETURN_FAILURE;
298         }
299
300         g_free(cmd_str);
301
302         dbg("Exit");
303
304         return ret;
305 }
306
307 static void on_response_define_pdp_context(TcorePending *p, int data_len,
308                                                 const void *data,
309                                                 void *user_data)
310 {
311         const TcoreATResponse *resp = data;
312         CoreObject *ps_context = user_data;
313         unsigned int cid = tcore_context_get_id(ps_context);
314         CoreObject *co_ps = tcore_pending_ref_core_object(p);
315
316         dbg("Enter");
317
318         if (resp->success) {
319                 dbg("Response OK");
320                 notify_context_status_changed(co_ps, cid, PS_DATA_CALL_CTX_DEFINED);
321         } else {
322                 err("Response NOK");
323                 notify_context_status_changed(co_ps, cid, PS_DATA_CALL_NOT_CONNECTED);
324         }
325
326         dbg("Exit");
327 }
328
329 static TReturn define_pdp_context(CoreObject *co_ps, CoreObject *ps_context,
330                                         void *user_data)
331 {
332         char *apn = NULL;
333         char *cmd_str = NULL;
334         char *pdp_type_str = NULL;
335         unsigned int cid;
336         enum co_context_type pdp_type;
337         enum co_context_d_comp d_comp;
338         enum co_context_h_comp h_comp;
339         int ret = TCORE_RETURN_FAILURE;
340
341         dbg("Enter");
342
343         pdp_type = tcore_context_get_type(ps_context);
344         cid = tcore_context_get_id(ps_context);
345
346         switch (pdp_type) {
347         case CONTEXT_TYPE_X25:
348                 dbg("CONTEXT_TYPE_X25");
349                 pdp_type_str = g_strdup("X.25");
350                 break;
351
352         case CONTEXT_TYPE_IP:
353                 dbg("CONTEXT_TYPE_IP");
354                 pdp_type_str = g_strdup("IP");
355                 break;
356
357         case CONTEXT_TYPE_PPP:
358                 dbg("CONTEXT_TYPE_PPP");
359                 pdp_type_str = g_strdup("PPP");
360                 break;
361
362         case CONTEXT_TYPE_IPV6:
363                 dbg("CONTEXT_TYPE_IPV6");
364                 pdp_type_str = g_strdup("IPV6");
365                 break;
366
367         default:
368                 /* PDP Type not supported supported */
369                 err("Unsupported PDP type %d ", pdp_type);
370
371                 goto error;
372         }
373
374         d_comp = tcore_context_get_data_compression(ps_context);
375         h_comp = tcore_context_get_header_compression(ps_context);
376         apn = tcore_context_get_apn(ps_context);
377
378         cmd_str = g_strdup_printf("AT+CGDCONT=%d,\"%s\",\"%s\",,%d,%d", cid,
379                                         pdp_type_str, apn, d_comp, h_comp);
380
381         ret = tcore_prepare_and_send_at_request(co_ps, cmd_str, NULL,
382                                         TCORE_AT_NO_RESULT, NULL,
383                                         on_response_define_pdp_context,
384                                         ps_context,
385                                         on_confirmation_ps_message_send, NULL);
386
387         g_free(pdp_type_str);
388         g_free(cmd_str);
389         g_free(apn);
390
391         if (ret == TCORE_RETURN_SUCCESS)
392                 goto out;
393
394         err("Failed to prepare and send AT request");
395
396 error:
397         notify_context_status_changed(co_ps, cid, PS_DATA_CALL_NOT_CONNECTED);
398
399 out:
400         dbg("Exit");
401
402         return ret;
403 }
404
405
406 static gboolean on_cgev_notification(CoreObject *co_ps, const void *data,
407                                         void *user_data)
408 {
409         GSList *tokens = NULL;
410         GSList *lines = (GSList *)data;
411         const char *line = lines->data;
412         char *noti_data;
413         unsigned int cid;
414
415         dbg("Enter");
416
417         if (line == NULL)
418                 goto out;
419
420         dbg("Lines->data :%s", line);
421
422         tokens = tcore_at_tok_new(line);
423         if (g_slist_length(tokens) != 3)
424                 goto out;
425
426         noti_data = g_slist_nth_data(tokens, 0);
427
428         /* Only care about NW context deactivation */
429         if (g_str_has_prefix(noti_data, "NW DEACT") == FALSE)
430                 goto out;
431
432         noti_data = g_slist_nth_data(tokens, 1);
433         dbg("PDP Address: %s", noti_data);
434
435         noti_data = g_slist_nth_data(tokens, 2);
436         cid = (unsigned int)atoi(noti_data);
437         dbg("Context %d deactivated", cid);
438
439         notify_context_status_changed(co_ps, cid, PS_DATA_CALL_NOT_CONNECTED);
440
441         /*
442          * TODO Implement core object PS API to retreive context by ID to get
443          * network interface name and disable it.
444          */
445
446 out:
447         tcore_at_tok_free(tokens);
448
449         dbg("Exit");
450
451         return TRUE;
452 }
453
454 static TReturn activate_ps_context(CoreObject *co_ps, CoreObject *ps_context,
455                                         void *user_data)
456 {
457         return set_pdp_context(co_ps, ps_context, ACTIVATE);
458 }
459
460 static TReturn deactivate_ps_context(CoreObject *co_ps, CoreObject *ps_context,
461                                         void *user_data)
462 {
463         return set_pdp_context(co_ps, ps_context, DEACTIVATE);
464 }
465
466 static struct tcore_ps_operations ps_ops = {
467         .define_context = define_pdp_context,
468         .activate_context = activate_ps_context,
469         .deactivate_context = deactivate_ps_context
470 };
471
472 gboolean at_ps_init(TcorePlugin *cp)
473 {
474         CoreObject *co = NULL;
475         Server *server;
476
477         co = tcore_ps_new(cp, &ps_ops, NULL);
478         if (co == NULL)
479                 return FALSE;
480
481         tcore_object_add_callback(co, "+CGEV", on_cgev_notification, NULL);
482
483         server = tcore_plugin_ref_server(cp);
484         tcore_server_add_template_object(server, co);
485
486         return TRUE;
487 }
488
489 void at_ps_exit(TcorePlugin *cp)
490 {
491         CoreObject *co;
492
493         if (cp == NULL)
494                 return;
495
496         co = tcore_plugin_ref_core_object(cp, CORE_OBJECT_TYPE_PS);
497         if (co == NULL)
498                 return;
499
500         tcore_object_free(co);
501 }