20b92e35040327ab9d3697434ea3fa33e1a18849
[platform/core/telephony/tel-plugin-imc.git] / src / s_ps.c
1 /*
2  * tel-plugin-imc
3  *
4  * Copyright (c) 2012 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact: Arun Shukla <arun.shukla@samsung.com>
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25
26 #include <glib.h>
27 #include <fcntl.h>
28 #include <sys/ioctl.h>
29
30 #include <tcore.h>
31 #include <hal.h>
32 #include <core_object.h>
33 #include <plugin.h>
34 #include <queue.h>
35 #include <co_ps.h>
36 #include <co_context.h>
37 #include <storage.h>
38 #include <server.h>
39 #include <at.h>
40 #include <util.h>
41 #include <type/ps.h>
42
43 #include "s_common.h"
44 #include "s_ps.h"
45
46 /*Invalid Session ID*/
47 #define PS_INVALID_CID  999 /*Need to check */
48
49 /*Maximum String length Of the Command*/
50 #define MAX_AT_CMD_STR_LEN  150
51
52 /*Command for PDP activation and Deactivation*/
53 #define AT_PDP_ACTIVATE 1
54 #define AT_PDP_DEACTIVATE 0
55
56 #define AT_XDNS_ENABLE 1
57 #define AT_XDNS_DISABLE 0
58 #define AT_SESSION_DOWN 0
59
60 static void _ps_free(void *ptr)
61 {
62         dbg("Entry");
63         if (ptr) {
64                 (void) free(ptr);
65                 ptr = NULL;
66         }
67         dbg("Exit");
68         return;
69 }
70 static void _unable_to_get_pending(CoreObject *co_ps, CoreObject *ps_context)
71 {
72         struct tnoti_ps_call_status data_resp = {0};
73         dbg("Entry");
74
75         data_resp.context_id = tcore_context_get_id(ps_context);
76         data_resp.state = PS_DATA_CALL_NOT_CONNECTED;
77         dbg("Sending Call Status Notification - Context ID: [%d] Context State: [NOT CONNECTED]",
78                                         data_resp.context_id);
79
80         /* Send CALL Status Notification */
81         tcore_server_send_notification(tcore_plugin_ref_server(tcore_object_ref_plugin(co_ps)),
82                                 co_ps, TNOTI_PS_CALL_STATUS, sizeof(data_resp), &data_resp);
83
84         /* Set PS State to Deactivated */
85         (void) tcore_context_set_state(ps_context, CONTEXT_STATE_DEACTIVATED);
86         dbg("Exit");
87 }
88
89 static gboolean on_event_dun_call_notification(CoreObject *o, const void *data, void *user_data)
90 {
91         GSList *tokens = NULL;
92         const char *line = NULL;
93         int value = 0;
94         GSList *lines = NULL;
95         dbg("Entry");
96
97         lines = (GSList *) data;
98         if (g_slist_length(lines) != 1) {
99                 dbg("Unsolicited message BUT multiple lines");
100                 goto OUT;
101         }
102
103         line = (char *) (lines->data);
104         tokens = tcore_at_tok_new(line);
105         value = atoi(g_slist_nth_data(tokens, 0));
106
107         /*
108          * <status> may be
109          *      0: DUN Activation in progress
110          *      1: DUN Deactivation in progress
111          *      2: DUN Activated
112          *      3: DUN Deactivated
113          */
114         switch (value) {
115         case 0:    /* FALL THROUGH */
116         case 1:
117         {
118                 break;
119         }
120
121         case 2:
122         {
123                 /* TODO:- Fill Data structure: 'data' */
124                 tcore_server_send_notification(tcore_plugin_ref_server(tcore_object_ref_plugin(o)), o,
125                                                                            TNOTI_PS_EXTERNAL_CALL, sizeof(struct tnoti_ps_external_call), &data);
126         }
127
128         case 3:
129         {
130                 /* TODO:- Fill Data structure: 'data' */
131                 tcore_server_send_notification(tcore_plugin_ref_server(tcore_object_ref_plugin(o)), o,
132                                                                            TNOTI_PS_EXTERNAL_CALL, sizeof(struct tnoti_ps_external_call), &data);
133         }
134         break;
135
136         default:
137                 break;
138         }
139
140 OUT:
141         /* Free tokens */
142         tcore_at_tok_free(tokens);
143
144         return TRUE;
145 }
146 static void on_response_undefine_context_cmd(TcorePending *p, int data_len, const void *data, void *user_data)
147 {
148         CoreObject *co_ps = NULL;
149         const TcoreATResponse *resp = data;
150         CoreObject *ps_context = user_data;
151         dbg("Entry");
152
153         co_ps = tcore_pending_ref_core_object(p);
154         if (resp->success) {
155                 dbg("Response Ok");
156                 return;
157         }
158         dbg("Response NOk");
159         _unable_to_get_pending(co_ps, ps_context);
160         return;
161 }
162
163 static void send_undefine_context_cmd(CoreObject *co_ps, CoreObject *ps_context)
164 {
165         TcoreHal *hal = NULL;
166         TcorePending *pending = NULL;
167         char cmd_str[MAX_AT_CMD_STR_LEN];
168         int cid = 0;
169
170         dbg("Entry");
171         memset(cmd_str, 0x0, MAX_AT_CMD_STR_LEN);
172
173         /* FIXME: Before MUX setup, use PHY HAL directly. */
174         hal = tcore_object_get_hal(co_ps);
175
176         /*Getting Context ID from Core Object*/
177         cid = tcore_context_get_id(ps_context);
178
179         (void) sprintf(cmd_str, "AT+CGDCONT=%d", cid);
180         dbg("Command: [%s] Command Length: [%d]", cmd_str, strlen(cmd_str));
181
182         pending = tcore_at_pending_new(co_ps, cmd_str, NULL, TCORE_AT_NO_RESULT,
183                                                                    on_response_undefine_context_cmd, ps_context);
184         if (NULL == pending) {
185                 err("Unable to get the create a AT request ");
186                 goto error;
187         }
188         tcore_hal_send_request(hal, pending);
189         dbg("Exit: Successfully");
190         return;
191 error:
192         {
193                 dbg("Exit: With error");
194                 _unable_to_get_pending(co_ps, ps_context);
195                 return;
196         }
197 }
198
199 static void on_setup_pdp(CoreObject *co_ps, int result,
200                         const char *netif_name, void *user_data)
201 {
202         CoreObject *ps_context = user_data;
203         struct tnoti_ps_call_status data_status = {0};
204         Server *server;
205
206         dbg("Entry");
207
208         if (result < 0) {
209                 /* Deactivate PDP context */
210                 tcore_ps_deactivate_context(co_ps, ps_context, NULL);
211                 return;
212         }
213
214         dbg("Device name: [%s]", netif_name);
215
216         /* Set Device name */
217         tcore_context_set_ipv4_devname(ps_context, netif_name);
218
219         /* Set State - CONNECTED */
220         data_status.context_id = tcore_context_get_id(ps_context);
221         data_status.state = PS_DATA_CALL_CONNECTED;
222         dbg("Sending Call Status Notification - Context ID: [%d] Context State: [CONNECTED]", data_status.context_id);
223
224         /* Send Notification */
225         server = tcore_plugin_ref_server(tcore_object_ref_plugin(co_ps));
226         tcore_server_send_notification(server, co_ps,
227                                         TNOTI_PS_CALL_STATUS,
228                                         sizeof(struct tnoti_ps_call_status),
229                                         &data_status);
230
231         dbg("Exit");
232 }
233
234 static void on_response_get_dns_cmnd(TcorePending *p, int data_len, const void *data, void *user_data)
235 {
236         GSList *tokens = NULL;
237         GSList *pRespData;
238         const char *line = NULL;
239         char *dns_prim = NULL;
240         char *dns_sec = NULL;
241         char *token_dns = NULL;
242         int no_pdp_active = 0;
243         CoreObject *ps_context = user_data;
244         const TcoreATResponse *resp = data;
245         CoreObject *co_ps = tcore_pending_ref_core_object(p);
246         int cid = tcore_context_get_id(ps_context);
247         TcoreHal *h = tcore_object_get_hal(co_ps);
248
249         dbg("Entry");
250
251         if (resp->final_response) {
252                 dbg("Response OK");
253                 if (resp->lines) {
254                         dbg("DNS data present in the Response");
255                         pRespData = (GSList *) resp->lines;
256                         no_pdp_active = g_slist_length(pRespData);
257                         dbg("Total Number of Active PS Context: [%d]", no_pdp_active);
258                         if (0 == no_pdp_active) {
259                                 goto exit_fail;
260                         }
261
262                         while (pRespData) {
263                                 line = (const char *) pRespData->data;
264                                 dbg("Received Data: [%s]", line);
265                                 tokens = tcore_at_tok_new(line);
266
267                                 /* Check if Context ID is matching */
268                                 if (cid == atoi(g_slist_nth_data(tokens, 0))) {
269                                         dbg("Found the DNS details for the Current Context - Context ID: [%d]", cid);
270                                         break;
271                                 }
272
273                                 /* Free tokens */
274                                 tcore_at_tok_free(tokens);
275                                 tokens = NULL;
276
277                                 /* Move to next line */
278                                 pRespData = pRespData->next;
279                         }
280
281                         /* Read primary DNS */
282                         {
283                                 token_dns = g_slist_nth_data(tokens, 1);
284
285                                 /* Strip off starting " and ending " from this token to read actual PDP address */
286                                 dns_prim = util_removeQuotes((void *)token_dns);
287                                 dbg("Primary DNS: [%s]", dns_prim);
288                         }
289
290                         /* Read Secondary DNS */
291                         {
292                                 token_dns = g_slist_nth_data(tokens, 2);
293
294                                 /* Strip off starting " and ending " from this token to read actual PDP address */
295                                 dns_sec = util_removeQuotes((void *)token_dns);
296                                 dbg("Secondary DNS: [%s]", dns_sec);
297                         }
298
299                         if ((g_strcmp0("0.0.0.0", dns_prim) == 0)
300                                         && (g_strcmp0("0.0.0.0", dns_sec) == 0)) {
301                                 dbg("Invalid DNS");
302
303                                 _ps_free(dns_prim);
304                                 _ps_free(dns_sec);
305
306                                 tcore_at_tok_free(tokens);
307                                 tokens = NULL;
308
309                                 goto exit_fail;
310                         }
311
312                         /* Set DNS Address */
313                         tcore_context_set_ipv4_dns(ps_context, dns_prim, dns_sec);
314                         _ps_free(dns_prim);
315                         _ps_free(dns_sec);
316
317                         tcore_at_tok_free(tokens);
318                         tokens = NULL;
319                         goto exit_success;
320                 } else {
321                         dbg("No data present in the Response");
322                 }
323         }
324         dbg("Response NOK");
325
326 exit_fail:
327         dbg("Adding default DNS");
328         tcore_context_set_ipv4_dns(ps_context, "8.8.8.8", "8.8.4.4");
329
330 exit_success:
331         /* Mount network interface */
332         if (tcore_hal_setup_netif(h, co_ps, on_setup_pdp, ps_context, cid, TRUE)
333                         != TCORE_RETURN_SUCCESS) {
334                 err("Setup network interface failed");
335                 return;
336         }
337
338         dbg("EXIT : Without error");
339 }
340
341 static TReturn send_get_dns_cmd(CoreObject *co_ps, CoreObject *ps_context)
342 {
343         TcoreHal *hal = NULL;
344         TcorePending *pending = NULL;
345         char cmd_str[MAX_AT_CMD_STR_LEN];
346
347         memset(cmd_str, 0x0, MAX_AT_CMD_STR_LEN);
348
349         dbg("Entry");
350         hal = tcore_object_get_hal(co_ps);
351
352         (void) sprintf(cmd_str, "AT+XDNS?");
353         dbg("Command: [%s] Command Length: [%d]", cmd_str, strlen(cmd_str));
354
355         pending = tcore_at_pending_new(co_ps, cmd_str, "+XDNS", TCORE_AT_MULTILINE,
356                                                                    on_response_get_dns_cmnd, ps_context);
357         if (TCORE_RETURN_SUCCESS == tcore_hal_send_request(hal, pending)) {
358                 return TCORE_RETURN_SUCCESS;
359         }
360         _unable_to_get_pending(co_ps, ps_context);
361         return TCORE_RETURN_FAILURE;
362 }
363
364 static void on_response_get_pdp_address(TcorePending *p, int data_len, const void *data, void *user_data)
365 {
366         const TcoreATResponse *resp = data;
367         CoreObject *co_ps = tcore_pending_ref_core_object(p);
368         CoreObject *ps_context = user_data;
369         GSList *tokens = NULL;
370         const char *line;
371         char *token_pdp_address;
372         dbg("Enetered");
373         if (resp->final_response) {
374                 dbg("RESPONSE OK");
375                 if (resp->lines != NULL) {
376                         line = (const char *) resp->lines->data;
377                         tokens = tcore_at_tok_new(line);
378                         if (g_slist_length(tokens) < 2) {
379                                 msg("Invalid message");
380                                 goto error;
381                         }
382                         dbg("Received Data: [%s]", line);
383
384                         /* CID is already stored in ps_context, skip over & read PDP address */
385                         token_pdp_address = g_slist_nth_data(tokens, 1);
386                         token_pdp_address = util_removeQuotes((void *)token_pdp_address);
387                         dbg("IP Address: [%s]", token_pdp_address);
388
389                         /* Strip off starting " and ending " from this token to read actual PDP address */
390                         /* Set IP Address */
391                         (void)tcore_context_set_ipv4_addr(ps_context, (const char *)token_pdp_address);
392                 }
393
394                 /* Get DNS Address */
395                 (void) send_get_dns_cmd(co_ps, ps_context);
396         } else {
397                 dbg("Response NOK");
398
399                 /*without PDP address we will not be able to start packet service*/
400                 tcore_ps_deactivate_context(co_ps, ps_context, NULL);
401         }
402 error:
403         tcore_at_tok_free(tokens);
404         return;
405 }
406
407 static TReturn send_get_pdp_address_cmd(CoreObject *co_ps, CoreObject *ps_context)
408 {
409         TcoreHal *hal = NULL;
410         TcorePending *pending = NULL;
411         unsigned int cid = PS_INVALID_CID;
412         char cmd_str[MAX_AT_CMD_STR_LEN] = {0};
413
414         dbg("Entry");
415         hal = tcore_object_get_hal(co_ps);
416
417         cid = tcore_context_get_id(ps_context);
418         (void) sprintf(cmd_str, "AT+CGPADDR=%d", cid);
419         dbg("Command: [%s] Command Length: [%d]", cmd_str, strlen(cmd_str));
420
421         pending = tcore_at_pending_new(co_ps, cmd_str, "+CGPADDR", TCORE_AT_SINGLELINE,
422                                                                    on_response_get_pdp_address, ps_context);
423         if (TCORE_RETURN_SUCCESS == tcore_hal_send_request(hal, pending)) {
424                 return TCORE_RETURN_SUCCESS;
425         }
426         _unable_to_get_pending(co_ps, ps_context);
427         return TCORE_RETURN_FAILURE;
428 }
429
430 static void on_response_send_pdp_activate_cmd(TcorePending *p, int data_len, const void *data, void *user_data)
431 {
432         CoreObject *co_ps = NULL;
433         const TcoreATResponse *resp = data;
434         CoreObject *ps_context = user_data;
435
436         int cid;
437         cid = tcore_context_get_id(ps_context);
438
439
440         dbg("Entry");
441         if (!p) {
442                 goto error;
443         }
444         co_ps = tcore_pending_ref_core_object(p);
445
446         if (resp->success) {
447                 dbg("Response OK");
448
449                 /* Getting the IP address and DNS from the modem */
450                 dbg("Getting IP Address");
451                 (void) send_get_pdp_address_cmd(co_ps, ps_context);
452                 return;
453         } else {
454                 dbg("Unable to activate PDP context - Context ID: [%d]", cid);
455                 dbg("Undefining PDP context");
456                 (void) tcore_context_set_state(ps_context, CONTEXT_STATE_DEACTIVATED);
457                 send_undefine_context_cmd(co_ps, ps_context);
458                 return;
459         }
460 error:
461         {
462                 _unable_to_get_pending(co_ps, ps_context);
463                 return;
464         }
465 }
466
467 static TReturn send_pdp_activate_cmd(CoreObject *co_ps, CoreObject *ps_context)
468 {
469         TcoreHal *hal = NULL;
470         TcorePending *pending = NULL;
471         char cmd_str[MAX_AT_CMD_STR_LEN] = {0};
472         int cid = 0;
473         dbg("Entry");
474         /* FIXME: Before MUX setup, use PHY HAL directly. */
475         hal = tcore_object_get_hal(co_ps);
476
477         /*Getting Context ID from Core Object*/
478         cid = tcore_context_get_id(ps_context);
479         (void) sprintf(cmd_str, "AT+CGACT=%d,%d", AT_PDP_ACTIVATE, cid);
480         dbg("Command: [%s] Command Length: [%d]", cmd_str, strlen(cmd_str));
481
482         pending = tcore_at_pending_new(co_ps, cmd_str, NULL, TCORE_AT_NO_RESULT,
483                                                                    on_response_send_pdp_activate_cmd, ps_context);
484         if (TCORE_RETURN_SUCCESS == tcore_hal_send_request(hal, pending)) {
485                 return TCORE_RETURN_SUCCESS;
486         }
487         _unable_to_get_pending(co_ps, ps_context);
488         return TCORE_RETURN_FAILURE;
489 }
490
491 static TReturn activate_ps_context(CoreObject *co_ps, CoreObject *ps_context, void *user_data)
492 {
493         dbg("Entry");
494         return send_pdp_activate_cmd(co_ps, ps_context);
495 }
496
497 static void on_response_xdns_enable_cmd(TcorePending *p, int data_len, const void *data, void *user_data)
498 {
499         TcoreATResponse *resp = (TcoreATResponse *) data;
500         CoreObject *co_ps = tcore_pending_ref_core_object(p);
501         CoreObject *ps_context = user_data;
502         struct tnoti_ps_call_status noti = {0};
503         int cid = -1;
504
505         dbg("Entry");
506
507         cid = tcore_context_get_id(ps_context);
508
509         if (resp->success) {
510                 dbg("Response OK");
511                 dbg("DNS address getting is Enabled");
512                 noti.context_id = cid;
513                 noti.state = PS_DATA_CALL_CTX_DEFINED;
514         } else {
515                 dbg("Response NOK");
516                 noti.context_id = cid;
517                 noti.state = PS_DATA_CALL_NOT_CONNECTED;
518                 /*If response to enable the DNS NOK then we will use google DNS for the PDP context*/
519         }
520
521         tcore_server_send_notification(tcore_plugin_ref_server(tcore_object_ref_plugin(co_ps)), co_ps,
522                                                                    TNOTI_PS_CALL_STATUS, sizeof(struct tnoti_ps_call_status), &noti);
523         return;
524 }
525
526 static TReturn send_xdns_enable_cmd(CoreObject *co_ps, CoreObject *ps_context)
527 {
528         TcoreHal *hal = NULL;
529         TcorePending *pending = NULL;
530         int cid = -1;
531         char cmd_str[MAX_AT_CMD_STR_LEN];
532
533         dbg("Entry");
534         memset(cmd_str, 0x0, MAX_AT_CMD_STR_LEN);
535
536         hal = tcore_object_get_hal(co_ps);
537         cid = tcore_context_get_id(ps_context);
538
539         (void) sprintf(cmd_str, "AT+XDNS=%d,%d", cid, AT_XDNS_ENABLE);
540         dbg("Command: [%s] Command Length: [%d]", cmd_str, strlen(cmd_str));
541
542         pending = tcore_at_pending_new(co_ps, cmd_str, NULL, TCORE_AT_NO_RESULT,
543                                                                    on_response_xdns_enable_cmd, ps_context);
544         if (TCORE_RETURN_SUCCESS == tcore_hal_send_request(hal, pending)) {
545                 return TCORE_RETURN_SUCCESS;
546         }
547         _unable_to_get_pending(co_ps, ps_context);
548         return TCORE_RETURN_FAILURE;
549 }
550
551 static void on_response_define_pdp_context(TcorePending *p, int data_len, const void *data, void *user_data)
552 {
553         const TcoreATResponse *resp = data;
554         CoreObject *ps_context = (CoreObject *) user_data;
555         CoreObject *co_ps = tcore_pending_ref_core_object(p);
556
557         dbg("Entry");
558         if (resp->success) {
559                 dbg("Response OK");
560                 send_xdns_enable_cmd(co_ps, ps_context);
561         } else {
562                 dbg("response NOK");
563                 _unable_to_get_pending(co_ps, ps_context);
564                 dbg("Exiting");
565         }
566         return;
567 }
568
569 static TReturn define_ps_context(CoreObject *co_ps, CoreObject *ps_context, void *user_data)
570 {
571         TcoreHal *hal = NULL;
572         TcorePending *pending = NULL;
573         char *apn = NULL;
574         char cmd_str[MAX_AT_CMD_STR_LEN] = {0};
575         char pdp_type_str[10] = {0};
576         unsigned int cid = PS_INVALID_CID;
577         enum co_context_type pdp_type;
578         enum co_context_d_comp d_comp;
579         enum co_context_h_comp h_comp;
580
581         dbg("Entry");
582
583         cid = tcore_context_get_id(ps_context);
584         pdp_type = tcore_context_get_type(ps_context);
585         d_comp = tcore_context_get_data_compression(ps_context);
586         h_comp = tcore_context_get_header_compression(ps_context);
587         apn = tcore_context_get_apn(ps_context);
588
589         hal = tcore_object_get_hal(co_ps);
590         switch (pdp_type) {
591         case CONTEXT_TYPE_X25:
592         {
593                 dbg("CONTEXT_TYPE_X25");
594                 strcpy(pdp_type_str, "X.25");
595                 break;
596         }
597
598         case CONTEXT_TYPE_IP:
599         {
600                 dbg("CONTEXT_TYPE_IP");
601                 strcpy(pdp_type_str, "IP");
602         }
603         break;
604
605         case CONTEXT_TYPE_PPP:
606         {
607                 dbg("CONTEXT_TYPE_PPP");
608                 strcpy(pdp_type_str, "PPP");
609         }
610         break;
611
612         case CONTEXT_TYPE_IPV6:
613         {
614                 dbg("CONTEXT_TYPE_IPV6");
615                 strcpy(pdp_type_str, "IPV6");
616                 break;
617         }
618
619         default:
620         {
621                 /*PDP Type not supported supported*/
622                 dbg("Unsupported PDP type: %d returning ", pdp_type);
623                 return TCORE_RETURN_FAILURE;
624         }
625         }
626         dbg("Activating context for CID: %d", cid);
627         (void) sprintf(cmd_str, "AT+CGDCONT=%d,\"%s\",\"%s\",,%d,%d", cid, pdp_type_str, apn, d_comp, h_comp);
628         dbg("Command: [%s] Command Length: [%d]", cmd_str, strlen(cmd_str));
629
630         pending = tcore_at_pending_new(co_ps, cmd_str, NULL, TCORE_AT_NO_RESULT,
631                                                                    on_response_define_pdp_context, ps_context);
632         if (TCORE_RETURN_SUCCESS == tcore_hal_send_request(hal, pending)) {
633                 return TCORE_RETURN_SUCCESS;
634         }
635         _unable_to_get_pending(co_ps, ps_context);
636         return TCORE_RETURN_FAILURE;
637 }
638
639
640 static struct tcore_ps_operations ps_ops = {
641         .define_context = define_ps_context,
642         .activate_context = activate_ps_context,
643         /* Use AT_standard entry point */
644         .deactivate_context = NULL
645 };
646
647 gboolean s_ps_init(TcorePlugin *cp, CoreObject *co_ps)
648 {
649         TcorePlugin *plugin = tcore_object_ref_plugin(co_ps);
650
651         dbg("Entry");
652
653         tcore_ps_override_ops(co_ps, &ps_ops);
654
655         /*
656          * AT_standard handles standard CGEV notifications:
657          * tcore_object_override_callback(co, "+CGEV", on_cgev_notification, NULL);
658          * no need to handle it here.
659          */
660
661         tcore_object_override_callback(co_ps, "+XNOTIFYDUNSTATUS", on_event_dun_call_notification, plugin);
662
663         dbg("Exit");
664
665         return TRUE;
666 }
667
668 void s_ps_exit(TcorePlugin *cp, CoreObject *co_ps)
669 {
670         dbg("Exit");
671 }