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