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