4 * Copyright (c) 2013 Samsung Electronics Co. Ltd. All rights reserved.
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
28 #include <core_object.h>
37 #include "imc_common.h"
40 #define STATUS_INCOMING 4
41 #define STATUS_WAITING 5
42 #define STATUS_CONNECTED 7
44 #define find_call_object(co, call_id, call_obj) \
46 call_obj = tcore_call_object_find_by_id(co, call_id); \
48 err("unable to find call object"); \
53 struct imc_set_volume_info {
58 static gchar *xdrv_set_volume[] = {
66 /*Forward Declarations*/
67 static void on_response_imc_call_default(TcorePending *p,
68 guint data_len, const void *data, void *user_data);
70 static TelReturn __call_list_get(CoreObject *co, gboolean flag);
73 static TelCallType __call_type(int type)
79 return TEL_CALL_TYPE_VOICE;
82 return TEL_CALL_TYPE_VIDEO;
85 err("invalid call type, returing default call type as voice");
86 return TEL_CALL_TYPE_VOICE;
90 static TelCallState __call_state(int state)
96 return TEL_CALL_STATE_ACTIVE;
99 return TEL_CALL_STATE_HELD;
102 return TEL_CALL_STATE_DIALING;
105 return TEL_CALL_STATE_ALERT;
109 return TEL_CALL_STATE_INCOMING;
112 return TEL_CALL_STATE_IDLE;
116 static void __call_branch_by_status(CoreObject *co, CallObject *call_obj, TelCallState call_state)
118 unsigned int call_id;
119 TelCallType call_type;
121 TcoreNotification command = TCORE_NOTIFICATION_UNKNOWN;
122 dbg("Call State[%d]", call_state);
124 if (tcore_call_object_get_state(call_obj, &state) == FALSE) {
125 err("unable to get call status");
129 if (call_state == state) {
130 dbg("current call state and existing call state are same");
134 if (tcore_call_object_get_call_type(call_obj, &call_type) == FALSE) {
135 err("unable to get call type");
139 if (tcore_call_object_get_id(call_obj, &call_id) == FALSE) {
140 err("unable to get call id");
145 tcore_call_object_set_state(call_obj, call_state);
147 if (call_type == TEL_CALL_TYPE_VOICE) {
148 /* voice call notification */
149 switch (call_state) {
150 case TEL_CALL_STATE_ACTIVE:
151 command = TCORE_NOTIFICATION_CALL_STATUS_ACTIVE;
154 case TEL_CALL_STATE_HELD:
155 command = TCORE_NOTIFICATION_CALL_STATUS_HELD;
158 case TEL_CALL_STATE_DIALING:
159 command = TCORE_NOTIFICATION_CALL_STATUS_DIALING;
162 case TEL_CALL_STATE_ALERT:
163 command = TCORE_NOTIFICATION_CALL_STATUS_ALERT;
166 case TEL_CALL_STATE_INCOMING:
167 case TEL_CALL_STATE_WAITING: {
168 TelCallIncomingInfo incoming = {0,};
169 command = TCORE_NOTIFICATION_CALL_STATUS_INCOMING;
170 incoming.call_id = call_id;
171 tcore_call_object_get_cli_validity(call_obj, &incoming.cli_validity);
172 tcore_call_object_get_number(call_obj, incoming.number);
173 tcore_call_object_get_cni_validity(call_obj, &incoming.cni_validity);
174 tcore_call_object_get_name(call_obj, incoming.name);
175 tcore_call_object_get_mt_forward(call_obj, &incoming.forward);
176 tcore_call_object_get_active_line(call_obj, &incoming.active_line);
178 /* Send notification */
179 tcore_object_send_notification(co, command, sizeof(TelCallIncomingInfo), &incoming);
183 case TEL_CALL_STATE_IDLE: {
184 TelCallStatusIdleNoti idle;
185 command = TCORE_NOTIFICATION_CALL_STATUS_IDLE;
186 idle.call_id = call_id;
188 /* TODO - get proper call end cause. */
189 idle.cause = TEL_CALL_END_CAUSE_NONE;
191 /* Send notification */
192 tcore_object_send_notification(co, command,
193 sizeof(TelCallStatusIdleNoti), &idle);
195 /* Free Call object */
196 tcore_call_object_free(co, call_obj);
202 else if (call_type == TEL_CALL_TYPE_VIDEO) {
203 /* video call notification */
204 switch (call_state) {
205 case TEL_CALL_STATE_ACTIVE:
206 command = TCORE_NOTIFICATION_VIDEO_CALL_STATUS_ACTIVE;
209 case TEL_CALL_STATE_HELD:
210 err("invalid state");
213 case TEL_CALL_STATE_DIALING:
214 command = TCORE_NOTIFICATION_VIDEO_CALL_STATUS_DIALING;
217 case TEL_CALL_STATE_ALERT:
218 command = TCORE_NOTIFICATION_VIDEO_CALL_STATUS_ALERT;
221 case TEL_CALL_STATE_INCOMING:
222 case TEL_CALL_STATE_WAITING:
223 command = TCORE_NOTIFICATION_VIDEO_CALL_STATUS_INCOMING;
226 case TEL_CALL_STATE_IDLE: {
227 TelCallStatusIdleNoti idle;
228 command = TCORE_NOTIFICATION_VIDEO_CALL_STATUS_IDLE;
229 idle.call_id = call_id;
231 /* TODO - get proper call end cause. */
232 idle.cause = TEL_CALL_END_CAUSE_NONE;
234 /* Send notification */
235 tcore_object_send_notification(co, command,
236 sizeof(TelCallStatusIdleNoti), &idle);
238 /* Free Call object */
239 tcore_call_object_free(co, call_obj);
245 err("Unknown Call type: [%d]", call_type);
248 if (command != TCORE_NOTIFICATION_UNKNOWN)
249 tcore_object_send_notification(co, command, sizeof(call_id), &call_id);
252 static void __handle_call_list_get(CoreObject *co, gboolean flag, void *data)
260 GSList *tokens = NULL;
265 char number[TEL_CALL_CALLING_NUMBER_LEN_MAX +1] = {0,};
266 GSList *lines = data;
267 CallObject *call_obj = NULL;
269 while (lines != NULL) {
270 line = (char *)lines->data;
271 /* point to next node */
273 /* free previous tokens*/
274 tcore_at_tok_free(tokens);
276 tokens = tcore_at_tok_new(line);
279 resp = g_slist_nth_data(tokens, 0);
281 err("Invalid call_id");
284 call_id = atoi(resp);
287 resp = g_slist_nth_data(tokens, 1);
289 err("Invalid direction");
292 direction = (atoi(resp) == 0) ? 1 : 0;
295 resp = g_slist_nth_data(tokens, 2);
297 err("Invalid state");
300 state = __call_state(atoi(resp));
303 resp = g_slist_nth_data(tokens, 3);
305 err("Invalid call_type");
308 call_type = __call_type(atoi(resp));
311 resp = g_slist_nth_data(tokens, 4);
319 resp = g_slist_nth_data(tokens, 5);
321 err("Number is NULL");
323 // Strike off double quotes
324 num = tcore_at_tok_extract(resp);
325 dbg("Number: [%s]", num);
328 resp = g_slist_nth_data(tokens, 6);
330 err("Invalid num type");
332 num_type = atoi(resp);
333 /* check number is international or national. */
334 ton = ((num_type) >> 4) & 0x07;
335 if (ton == 1 && num[0] != '+') {
336 /* international number */
338 memcpy(&number[1], num, strlen(num));
340 memcpy(number, num, strlen(num));
346 dbg("Call ID: [%d] Direction: [%s] Call Type: [%d] Multi-party: [%s] "
347 "Number: [%s] TON: [%d] State: [%d]",
348 call_id, (direction ? "Outgoing" : "Incoming"), call_type,
349 (mpty ? "YES" : "NO"), number, ton, state);
351 call_obj = tcore_call_object_find_by_id(co, call_id);
352 if (NULL == call_obj) {
353 call_obj = tcore_call_object_new(co, call_id);
354 if (NULL == call_obj) {
355 err("unable to create call object");
360 /* Set Call parameters */
361 tcore_call_object_set_type(call_obj, call_type);
362 tcore_call_object_set_direction(call_obj, direction);
363 tcore_call_object_set_multiparty_state(call_obj, mpty);
364 tcore_call_object_set_cli_info(call_obj, TEL_CALL_CLI_VALIDITY_VALID, number);
365 tcore_call_object_set_active_line(call_obj, TEL_CALL_ACTIVE_LINE1);
367 __call_branch_by_status(co, call_obj, state);
369 tcore_call_object_set_state(call_obj, state);
373 /* internal notification operation */
374 static void __on_notification_imc_call_incoming(CoreObject *co, unsigned int call_id,
378 CallObject *call_obj = NULL;
381 /* check call with incoming status already exist */
382 list = tcore_call_object_find_by_status(co, TEL_CALL_STATE_INCOMING);
384 err("Incoming call already exist! Skip...");
390 call_obj = tcore_call_object_find_by_id(co, call_id);
391 if (call_obj != NULL) {
392 err("Call object for Call ID [%d] already exist! Skip...", call_id);
396 /* Create new Call object */
397 call_obj = tcore_call_object_new(co, (unsigned int)call_id);
398 if (NULL == call_obj) {
399 err("Failed to create Call object");
403 /* Make request to get current Call list */
404 __call_list_get(co, TRUE);
407 static void __on_notification_imc_call_status(CoreObject *co, unsigned int call_id,
408 unsigned int call_state, void *user_data)
410 CallObject *call_obj = NULL;
413 state = __call_state(call_state);
414 dbg("state [%d]", state);
417 case TEL_CALL_STATE_ACTIVE: {
418 find_call_object(co, call_id, call_obj);
419 /* Send notification to application */
420 __call_branch_by_status(co, call_obj, state);
424 case TEL_CALL_STATE_HELD: {
425 find_call_object(co, call_id, call_obj);
426 /* Send notification to application */
427 __call_branch_by_status(co, call_obj, state);
431 case TEL_CALL_STATE_DIALING: {
432 call_obj = tcore_call_object_find_by_id(co, call_id);
434 call_obj = tcore_call_object_new(co, call_id);
436 err("unable to create call object");
440 /* Make request to get current call list.Update CallObject with <number>
441 * and send notification to application */
442 __call_list_get(co, TRUE);
446 case TEL_CALL_STATE_ALERT: {
447 find_call_object(co, call_id, call_obj);
448 /* Send notification to application */
449 __call_branch_by_status(co, call_obj, TEL_CALL_STATE_ALERT);
453 case TEL_CALL_STATE_IDLE: {
454 find_call_object(co, call_id, call_obj);
455 /* Send notification to application */
456 __call_branch_by_status(co, call_obj, state);
461 err("invalid call status");
466 /*internal response operation */
467 static void __on_response_imc_call_list_get(TcorePending *p, guint data_len, const void *data,
470 const TcoreAtResponse *at_resp = data;
471 CoreObject *co = tcore_pending_ref_core_object(p);
472 ImcRespCbData *resp_cb_data = user_data;
473 GSList *lines = NULL;
474 TelCallResult result = TEL_CALL_RESULT_FAILURE; //TODO - CME error mapping required
475 gboolean *flag = IMC_GET_DATA_FROM_RESP_CB_DATA(resp_cb_data);
479 tcore_check_return_assert(co != NULL);
480 tcore_check_return_assert(resp_cb_data != NULL);
482 if (at_resp && at_resp->success) {
483 result = TEL_CALL_RESULT_SUCCESS;
484 if (NULL == at_resp->lines) {
485 err("invalid response received");
489 lines = (GSList *) at_resp->lines;
490 count = g_slist_length(lines);
491 dbg("Total records : %d", g_slist_length(lines));
493 err("Call count is zero");
499 /* parse +CLCC notification parameter */
500 __handle_call_list_get(co, *flag, lines);
507 /*internal request operation */
508 static TelReturn __send_call_request(CoreObject *co, TcoreObjectResponseCallback cb,
509 void *cb_data, gchar *at_cmd, gchar *func_name)
511 ImcRespCbData *resp_cb_data;
514 /* Response callback data */
515 resp_cb_data = imc_create_resp_cb_data(cb, cb_data, func_name, strlen(func_name) + 1);
517 /* Send Request to modem */
518 ret = tcore_at_prepare_and_send_request(co,
520 TCORE_AT_COMMAND_TYPE_NO_RESULT,
521 TCORE_PENDING_PRIORITY_DEFAULT,
523 on_response_imc_call_default, resp_cb_data,
524 on_send_imc_request, NULL,
527 IMC_CHECK_REQUEST_RET(ret, resp_cb_data, func_name);
535 * Operation - Get current call list.
538 * AT-Command: AT+CLCC
542 *[+CLCC: <id1>, <dir>, <stat>, <mode>,<mpty>[,<number>,<type>[,<alpha>[,<priority>]]]
543 *[<CR><LF> +CLCC: <id2>,<dir>,<stat>,<mode>,<mpty>[,<number>,<type>[,<alpha>[,<priority>]]][
\85]]]
546 * +CME ERROR: <error>
548 static TelReturn __call_list_get(CoreObject *co, gboolean flag)
550 ImcRespCbData *resp_cb_data;
551 TelReturn ret =TEL_RETURN_FAILURE;
555 err("Core Object is NULL");
559 /* Response callback data */
560 resp_cb_data = imc_create_resp_cb_data(NULL, NULL, &flag, sizeof(gboolean));
562 /* Send Request to modem */
563 ret = tcore_at_prepare_and_send_request(co,
565 TCORE_AT_COMMAND_TYPE_MULTILINE,
566 TCORE_PENDING_PRIORITY_DEFAULT,
568 __on_response_imc_call_list_get, resp_cb_data,
569 on_send_imc_request, NULL,
571 IMC_CHECK_REQUEST_RET(ret, resp_cb_data, "Get current call list");
579 * Operation - call status notification from network.
580 * notification message format:
581 * +XCALLSTAT: <call_id><stat>
584 * indicates the call identification (GSM02.30 4.5.5.1)
588 * 2 dialling (MO call)
589 * 3 alerting (MO call; ringing for the remote party)
590 * 4 ringing (MT call)
591 * 5 waiting (MT call)
593 * 7 connected (indicates the completion of a call setup first time for MT and MO calls
\96 this is reported in
594 addition to state active)
596 static gboolean on_notification_imc_call_status(CoreObject *co, const void *data,
599 GSList *tokens = NULL;
600 GSList *lines = NULL;
601 const char *line = NULL;
602 char *state = NULL, *call_handle = NULL;
603 unsigned int status, call_id;
607 lines = (GSList *) data;
609 err("Invalid response received");
612 line = (char *) (lines->data);
613 tokens = tcore_at_tok_new(line);
615 call_handle = g_slist_nth_data(tokens, 0);
616 if (NULL == call_handle) {
617 err("call_id missing from %XCALLSTAT indiaction");
620 call_id = atoi(call_handle);
621 state = g_slist_nth_data(tokens, 1);
623 err("State is missing from %XCALLSTAT indication");
626 status = atoi(state);
627 dbg("call_id[%d], status[%d]", call_id, status);
630 case STATUS_INCOMING:
632 __on_notification_imc_call_incoming(co, call_id, user_data);
635 case STATUS_CONNECTED: /* ignore Connected state. */
636 dbg("ignore connected state");
640 __on_notification_imc_call_status(co, call_id, status, user_data);
645 tcore_at_tok_free(tokens);
650 * Operation - SS network initiated notification.
652 * notification message format:
653 * +CSSU: <code2>[<index> [,<number>,<type>]]
655 * (it is manufacturer specific, which of these codes are supported):
656 * 0 this is a forwarded call (MT call setup)
657 * 1 this is a CUG call (<index> present) (MT call setup)
658 * 2 call has been put on hold (during a voice call)
659 * 3 call has been retrieved (during a voice call)
660 * 4 multiparty call entered (during a voice call)
661 * 5 Call has been released - not a SS notification (during a voice call)
662 * 6 forward check SS message received (can be received whenever)
663 * 7 call is being connected (alerting) with the remote party in alerting state
664 * in explicit call transfer operation
665 * (during a voice call)
666 * 8 call has been connected with the other remote party in explicit call transfer
667 * operation (during a voice call or MT call setup)
668 * 9 this is a deflected call (MT call setup)
669 * 10 additional incoming call forwarded
671 * refer Closed user group +CCUG
673 * string type phone of format specified by <type>
675 * type of address octet in integer format.
677 static gboolean on_notification_imc_call_ss_cssu_info(CoreObject *co, const void *event_data,
680 GSList *tokens = NULL;
681 TcoreNotification command = TCORE_NOTIFICATION_UNKNOWN;
686 char number[TEL_CALL_CALLING_NUMBER_LEN_MAX + 1] = {'\0',};
690 if (1 != g_slist_length((GSList *) event_data)) {
691 err("unsolicited msg but multiple line");
695 cmd = (char *) ((GSList *) event_data)->data;
696 dbg("ss notification message[%s]", cmd);
698 tokens = tcore_at_tok_new(cmd);
701 resp = g_slist_nth_data(tokens, 0);
703 err("Code2 is missing from %CSSU indiaction");
704 tcore_at_tok_free(tokens);
710 /* parse [ <index>, <number> <type>] */
711 if ((resp = g_slist_nth_data(tokens, 1)))
714 if ((resp = g_slist_nth_data(tokens, 2))) {
715 /* Strike off double quotes */
716 int len = strlen(resp) - 2;
717 memcpy(number, resp + 1, len);
721 dbg("+CSSU: <code2> = %d <index> = %d <number> = %s ", code2, index, number);
723 /* <code2> - other values will be ignored */
726 command = TCORE_NOTIFICATION_CALL_INFO_MT_FORWARDED;
729 command = TCORE_NOTIFICATION_CALL_INFO_HELD;
732 command = TCORE_NOTIFICATION_CALL_INFO_ACTIVE;
735 command = TCORE_NOTIFICATION_CALL_INFO_JOINED;
739 command = TCORE_NOTIFICATION_CALL_INFO_TRANSFERED;
742 command = TCORE_NOTIFICATION_CALL_INFO_MT_DEFLECTED;
745 dbg("Unsupported +CSSU notification : %d", code2);
749 if (command != TCORE_NOTIFICATION_UNKNOWN)
750 tcore_object_send_notification(co, command, 0, NULL);
751 tcore_at_tok_free(tokens);
757 * Operation - SS network initiated notification.
758 * notification message format:
759 * +CSSI : <code1>[,<index>]
762 * 0 unconditional call forwarding is active
763 * 1 some of the conditional call forwarding are active
764 * 2 call has been forwarded
766 * 4 this is a CUG call (also <index> present)
767 * 5 outgoing calls are barred
768 * 6 incoming calls are barred
769 * 7 CLIR suppression rejected
770 * 8 call has been deflected
773 * refer Closed user group +CCUG.
775 static gboolean on_notification_imc_call_ss_cssi_info(CoreObject *co, const void *event_data,
778 GSList *tokens = NULL;
779 TcoreNotification command = TCORE_NOTIFICATION_UNKNOWN;
787 if (1 != g_slist_length((GSList *) event_data)) {
788 err("unsolicited msg but multiple line");
791 cmd = (char *) ((GSList *) event_data)->data;
792 dbg("ss notification message[%s]", cmd);
794 tokens = tcore_at_tok_new(cmd);
796 resp = g_slist_nth_data(tokens, 0);
798 err("<code1> is missing from %CSSI indiaction");
799 tcore_at_tok_free(tokens);
805 /* parse [ <index>] */
806 if ((resp = g_slist_nth_data(tokens, 1)))
809 dbg("+CSSI: <code1> = %d <index> = %d ", code1, index);
811 /* <code1> - other values will be ignored */
814 command = TCORE_NOTIFICATION_CALL_INFO_MO_FORWARD_UNCONDITIONAL;
817 command = TCORE_NOTIFICATION_CALL_INFO_MO_FORWARD_CONDITIONAL;
820 command = TCORE_NOTIFICATION_CALL_INFO_MO_FORWARDED;
823 command = TCORE_NOTIFICATION_CALL_INFO_MO_WAITING;
826 command = TCORE_NOTIFICATION_CALL_INFO_MO_BARRED_OUTGOING;
829 command = TCORE_NOTIFICATION_CALL_INFO_MO_BARRED_INCOMING;
832 command = TCORE_NOTIFICATION_CALL_INFO_MO_DEFLECTED;
835 dbg("Unsupported +CSSI notification : %d", code1);
839 if (command != TCORE_NOTIFICATION_UNKNOWN)
840 tcore_object_send_notification(co, command, 0, NULL);
841 tcore_at_tok_free(tokens);
846 static gboolean on_notification_imc_call_clip_info(CoreObject *co, const void *data,
850 /* TODO - handle +CLIP notification*/
855 static void on_response_imc_call_default(TcorePending *p,
856 guint data_len, const void *data, void *user_data)
858 const TcoreAtResponse *at_resp = data;
859 CoreObject *co = tcore_pending_ref_core_object(p);
860 ImcRespCbData *resp_cb_data = user_data;
862 TelCallResult result;
865 tcore_check_return_assert(co != NULL);
866 tcore_check_return_assert(resp_cb_data != NULL);
868 if (at_resp && at_resp->success) {
869 result = TEL_CALL_RESULT_SUCCESS;
871 err("ERROR[%s]",at_resp->final_response);
872 result = TEL_CALL_RESULT_FAILURE;
873 /*TODO - need to map CME error and final response error to TelCallResult */
876 dbg("%s: [%s]", IMC_GET_DATA_FROM_RESP_CB_DATA(resp_cb_data),
877 (result == TEL_CALL_RESULT_SUCCESS ? "SUCCESS" : "FAIL"));
879 /* Invoke callback */
880 if (resp_cb_data->cb)
881 resp_cb_data->cb(co, (gint)result, NULL, resp_cb_data->cb_data);
883 /* Free callback data */
884 imc_destroy_resp_cb_data(resp_cb_data);
887 static void on_response_imc_call_set_volume_info(TcorePending *p,
888 guint data_len, const void *data, void *user_data)
890 const TcoreAtResponse *at_resp = data;
891 CoreObject *co = tcore_pending_ref_core_object(p);
892 ImcRespCbData *resp_cb_data = user_data;
893 GSList *tokens = NULL;
895 char *resp_str = NULL;
898 TelCallResult result = TEL_CALL_RESULT_FAILURE; // TODO: XDRV error mapping is required
901 tcore_check_return_assert(co != NULL);
902 tcore_check_return_assert(resp_cb_data != NULL);
904 if (at_resp && at_resp->success) {
905 line = at_resp->lines;
906 tokens = tcore_at_tok_new(line->data);
908 if (!g_slist_nth_data(tokens, 0)) {
909 err("group_id is missing");
913 if (!g_slist_nth_data(tokens, 1)) {
914 err(" function_id is missing");
918 resp_str = g_slist_nth_data(tokens, 2);
920 err("xdrv result missing");
923 struct imc_set_volume_info *volume_info;
928 error = atoi(resp_str);
934 /* Fetch from resp_cb_data */
935 volume_info = (struct imc_set_volume_info *)IMC_GET_DATA_FROM_RESP_CB_DATA(resp_cb_data);
936 dbg("volume info index[%d]", volume_info->next_index);
938 if (xdrv_set_volume[volume_info->next_index] == NULL) {
939 /*Processing of xdrv commands completed */
941 result = TEL_CALL_RESULT_SUCCESS;
943 } else if (volume_info->next_index == 3) {
944 switch ((volume_info->volume) / 10) {
978 at_cmd = g_strdup_printf("%s%s",
979 xdrv_set_volume[volume_info->next_index], vol);
981 /* Increament index to point to next command */
982 volume_info->next_index += 1;
984 /* Send Request to modem */
985 ret = tcore_at_prepare_and_send_request(co,
987 TCORE_AT_COMMAND_TYPE_SINGLELINE,
988 TCORE_PENDING_PRIORITY_DEFAULT,
990 on_response_imc_call_set_volume_info, resp_cb_data,
991 on_send_imc_request, NULL,
992 (guint)0, NULL, NULL);
994 IMC_CHECK_REQUEST_RET(ret, resp_cb_data, "imc_call_set_volume_info");
1002 dbg("Set Volume Info: [%s]",
1003 (result == TEL_CALL_RESULT_SUCCESS ? "SUCCESS" : "FAIL"));
1005 /* Invoke callback */
1006 if (resp_cb_data->cb)
1007 resp_cb_data->cb(co, (gint)result, NULL, resp_cb_data->cb_data);
1009 imc_destroy_resp_cb_data(resp_cb_data);
1010 tcore_at_tok_free(tokens);
1013 static void on_response_imc_call_set_sound_path(TcorePending *p,
1014 guint data_len, const void *data, void *user_data)
1016 const TcoreAtResponse *at_resp = data;
1017 CoreObject *co = tcore_pending_ref_core_object(p);
1018 ImcRespCbData *resp_cb_data = user_data;
1019 GSList *tokens = NULL;
1020 GSList *line = NULL;
1021 char *resp_str = NULL;
1023 gint xdrv_func_id = -1;
1025 TelCallResult result = TEL_CALL_RESULT_FAILURE; // TODO: XDRV error mapping is required
1028 tcore_check_return_assert(co != NULL);
1029 tcore_check_return_assert(resp_cb_data != NULL);
1031 if (at_resp && at_resp->success) {
1032 line = at_resp->lines;
1033 tokens = tcore_at_tok_new(line->data);
1034 if (!g_slist_nth_data(tokens, 0)) {
1035 err("group_id is missing");
1039 if (!(resp_str = g_slist_nth_data(tokens, 1))) {
1040 err(" function_id is missing");
1044 xdrv_func_id = atoi(resp_str);
1046 resp_str = g_slist_nth_data(tokens, 2);
1048 error = atoi(resp_str);
1050 err("RESPONSE NOK");
1053 if (xdrv_func_id == 4) {
1054 /* Send next command to configure destination device type */
1057 gint *device_type = IMC_GET_DATA_FROM_RESP_CB_DATA(resp_cb_data);
1059 at_cmd = g_strdup_printf("AT+XDRV=40,5,2,0,0,0,0,0,1,0,1,0,%d",
1062 ret = tcore_at_prepare_and_send_request(co,
1064 TCORE_AT_COMMAND_TYPE_SINGLELINE,
1065 TCORE_PENDING_PRIORITY_DEFAULT,
1067 on_response_imc_call_set_sound_path, resp_cb_data,
1068 on_send_imc_request, NULL,
1069 (guint)0, NULL, NULL);
1071 IMC_CHECK_REQUEST_RET(ret, resp_cb_data, "imc_call_set_sound_path");
1077 result = TEL_CALL_RESULT_SUCCESS;
1083 dbg("Set Sound Path: [%s]",
1084 (result == TEL_CALL_RESULT_SUCCESS ? "SUCCESS" : "FAIL"));
1086 tcore_at_tok_free(tokens);
1088 /* Invoke callback */
1089 if (resp_cb_data->cb)
1090 resp_cb_data->cb(co, (gint)result, NULL, resp_cb_data->cb_data);
1092 imc_destroy_resp_cb_data(resp_cb_data);
1095 static void on_response_imc_call_set_mute(TcorePending *p, guint data_len,
1096 const void *data, void *user_data)
1098 const TcoreAtResponse *at_resp = data;
1099 CoreObject *co = tcore_pending_ref_core_object(p);
1100 ImcRespCbData *resp_cb_data = user_data;
1101 GSList *tokens = NULL;
1102 const char *line = NULL;
1103 char *resp_str = NULL;
1106 TelCallResult result = TEL_CALL_RESULT_FAILURE; // TODO: XDRV error mapping is required
1109 tcore_check_return_assert(co != NULL);
1110 tcore_check_return_assert(resp_cb_data != NULL);
1112 if (at_resp && at_resp->success) {
1113 result = TEL_CALL_RESULT_SUCCESS;
1114 line = (((GSList *)at_resp->lines)->data);
1115 tokens = tcore_at_tok_new(line);
1117 resp_str = g_slist_nth_data(tokens, 0);
1118 if (!g_slist_nth_data(tokens, 0)) {
1119 err("group_id is missing");
1120 result = TEL_CALL_RESULT_FAILURE;
1124 if (!g_slist_nth_data(tokens, 1)) {
1125 err(" function_id is missing");
1126 result = TEL_CALL_RESULT_FAILURE;
1130 resp_str = g_slist_nth_data(tokens, 2);
1132 error = atoi(resp_str);
1134 result = TEL_CALL_RESULT_FAILURE;
1137 result = TEL_CALL_RESULT_SUCCESS;
1143 dbg("Set Mute: [%s]",
1144 (result == TEL_CALL_RESULT_SUCCESS ? "SUCCESS" : "FAIL"));
1145 tcore_at_tok_free(tokens);
1147 /* Invoke callback */
1148 if (resp_cb_data->cb)
1149 resp_cb_data->cb(co, (gint)result, NULL, resp_cb_data->cb_data);
1151 imc_destroy_resp_cb_data(resp_cb_data);
1160 * AT-Command: ATD <num> [I] [G] [;]
1161 * <num> - dialed number
1162 * [I][i] - CLI presentation(supression or invocation)
1163 * [G] - control the CUG supplementary service information for this call.
1174 * +CME ERROR: <error>
1176 static TelReturn imc_call_dial(CoreObject *co, const TelCallDial *dial_info,
1177 TcoreObjectResponseCallback cb, void *cb_data)
1185 if (dial_info->call_type == TEL_CALL_TYPE_VIDEO) {
1186 err("Video call is not supported in imc modem");
1187 return TEL_RETURN_OPERATION_NOT_SUPPORTED;
1190 if (!strncmp(dial_info->number, "*31#", 4)) {
1191 dbg("clir suppression");
1193 num = (gchar *)&(dial_info->number[4]);
1194 } else if (!strncmp(dial_info->number, "#31#", 4)) {
1195 dbg("clir invocation");
1197 num = (gchar *)&(dial_info->number[4]);
1201 dbg("no clir string in number");
1203 /* it will be removed when setting application use tapi_ss_set_cli()
1204 * instead of his own vconfkey. (0 : By network, 1 : Show, 2 : Hide)
1206 vconf_get_int("db/ciss/show_my_number", &cli);
1208 dbg("clir invocation from setting application");
1211 dbg("set clir state to default");
1214 num = (gchar *)dial_info->number;
1218 at_cmd = g_strdup_printf("ATD%s%s;", num, clir);
1219 dbg(" at command : %s", at_cmd);
1221 return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_dial");
1225 * Operation - Answer/Reject/Replace/hold(current call) & accept incoming call.
1229 * 1. AT-Command: ATA
1234 * +CME ERROR: <error>
1236 * 2. AT-Command: AT+CHLD=[<n>]
1238 * 0 - (deafult)release all held calls or set User Determined User Busy for a waiting/incoming
1239 * call; if both exists then only the waiting call will be rejected.
1240 * 1 - release all active calls and accepts the other (held or waiting)
1241 * Note: In the scenario: An active call, a waiting call and held call, when the active call is
1242 * terminated, we will make the Waiting call as active.
1243 * 2 - place all active calls (if exist) on hold and accepts the other call (held or waiting/in-coming).
1244 * If only one call exists which is active, place it on hold and if only held call exists make it active call.
1249 * +CME ERROR: <error>
1250 * For more informatiion refer 3GPP TS 27.007.
1252 static TelReturn imc_call_answer(CoreObject *co, TelCallAnswerType ans_type,
1253 TcoreObjectResponseCallback cb, void *cb_data)
1258 if (ans_type == TEL_CALL_ANSWER_ACCEPT) {
1260 at_cmd = g_strdup_printf("%s", "ATA");
1261 }else if (ans_type == TEL_CALL_ANSWER_REJECT) {
1263 at_cmd = g_strdup_printf("%s", "AT+CHLD=0");
1264 } else if (ans_type == TEL_CALL_ANSWER_REPLACE) {
1266 at_cmd = g_strdup_printf("%s", "AT+CHLD=1");
1267 } else if (ans_type == TEL_CALL_ANSWER_HOLD_AND_ACCEPT) {
1269 at_cmd = g_strdup_printf("%s", "AT+CHLD=2");
1271 err("Unsupported call answer type");
1272 return TEL_RETURN_FAILURE;
1275 dbg("at command : %s", at_cmd);
1277 return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_answer");
1281 * Operation - release all calls/release specific call/release all active call/release all held calls.
1284 * 1. AT-Command: AT+CHLD=[<n>]
1286 * 0 - (defualt)release all held calls or set User Determined User Busy for a waiting/incoming.
1287 * call; if both exists then only the waiting call will be rejected.
1288 * 1 - release all active calls and accepts the other (held or waiting).
1289 * 1x - release a specific call (x specific call number as indicated by call id).
1290 * 8 - release all calls.
1295 * +CME ERROR: <error>
1297 static TelReturn imc_call_end(CoreObject *co, const TelCallEnd *end_info,
1298 TcoreObjectResponseCallback cb, void *cb_data)
1303 if (end_info->end_type == TEL_CALL_END_ALL) {
1305 at_cmd = g_strdup_printf("%s", "AT+CHLD=8");
1306 }else if (end_info->end_type == TEL_CALL_END) {
1308 at_cmd = g_strdup_printf("%s%d", "AT+CHLD=1",end_info->call_id);
1309 } else if (end_info->end_type == TEL_CALL_END_ACTIVE_ALL) {
1311 at_cmd = g_strdup_printf("%s", "AT+CHLD=1");
1312 } else if (end_info->end_type == TEL_CALL_END_HOLD_ALL) {
1314 at_cmd = g_strdup_printf("%s", "AT+CHLD=0");
1316 err("Unsupported call end type");
1317 return TEL_RETURN_FAILURE;
1320 dbg("at command : %s", at_cmd);
1322 return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_end");
1326 * Operation - send dtmf.
1329 * 1. AT-Command: AT+VTS=<DTMF>,{<DTMF>,<duration>}.
1332 * is a single ASCII character in the set 0-9, #, *, A-D. Even it will support string DTMF.
1334 * integer in range 0-255, meaning 1/10(10 millisec) seconds multiples. The string parameter
1335 * of the command consists of combinations of the following separated by commas:
1336 * NOTE : There is a limit of 50 dtmf tones can be requested through a single VTS command.
1341 * +CME ERROR: <error>
1343 static TelReturn imc_call_send_dtmf(CoreObject *co, const char *dtmf_str,
1344 TcoreObjectResponseCallback cb, void *cb_data)
1347 char *tmp_dtmf = NULL, *dtmf;
1352 //(void) _set_dtmf_tone_duration(o, dup);
1353 tmp_dtmf = tcore_malloc0((strlen(dtmf_str) * 2) + 1); // DTMF digits + comma for each dtmf digit.
1354 tcore_check_return_value_assert(tmp_dtmf != NULL, TEL_RETURN_FAILURE);
1355 /* Save initial pointer */
1358 for (count = 0; count < strlen(dtmf_str); count++) {
1359 *tmp_dtmf = dtmf_str[count];
1366 // last digit is having COMMA , overwrite it with '\0' .
1367 *(--tmp_dtmf) = '\0';
1369 // AT+VTS = <d1>,<d2>,<d3>,<d4>,<d5>,<d6>, ..... <d32>
1370 at_cmd = g_strdup_printf("AT+VTS=%s", dtmf);
1371 dbg("at command : %s", at_cmd);
1375 return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_send_dtmf");
1379 * Operation - call hold.
1382 * 1. AT-Command: AT+CHLD=[<n>]
1385 * 2 - place all active calls (if exist) on hold and accepts the other call (held or waiting/incoming).
1386 * If only one call exists which is active, place it on hold and if only held call exists
1387 * make it active call
1392 * +CME ERROR: <error>
1394 static TelReturn imc_call_hold(CoreObject *co, TcoreObjectResponseCallback cb,
1401 at_cmd = g_strdup_printf("%s", "AT+CHLD=2");
1402 dbg("at command : %s", at_cmd);
1404 return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_hold");
1408 * Operation - call active.
1411 * 1. AT-Command: AT+CHLD=[<n>]
1414 * 2 - place all active calls (if exist) on hold and accepts the other call (held or waiting/incoming).
1415 * If only one call exists which is active, place it on hold and if only held call exists
1416 * make it active call
1421 * +CME ERROR: <error>
1423 static TelReturn imc_call_active(CoreObject *co, TcoreObjectResponseCallback cb,
1429 at_cmd = g_strdup_printf("%s", "AT+CHLD=2");
1430 dbg("at command : %s", at_cmd);
1432 return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_active");
1436 * Operation - call swap.
1439 * 1. AT-Command: AT+CHLD=[<n>]
1442 * 2 - place all active calls (if exist) on hold and accepts the other call (held or waiting/incoming).
1443 * If only one call exists which is active, place it on hold and if only held call exists
1444 * make it active call
1449 * +CME ERROR: <error>
1451 static TelReturn imc_call_swap(CoreObject *co, TcoreObjectResponseCallback cb,
1457 at_cmd = g_strdup_printf("%s", "AT+CHLD=2");
1458 dbg("at command : %s", at_cmd);
1460 return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_swap");
1464 * Operation - call join.
1467 * 1. AT-Command: AT+CHLD=[<n>]
1470 * 3 - adds a held call to the conversation
1475 * +CME ERROR: <error>
1477 static TelReturn imc_call_join(CoreObject *co, TcoreObjectResponseCallback cb,
1483 at_cmd = g_strdup_printf("%s", "AT+CHLD=3");
1484 dbg("at command : %s", at_cmd);
1486 return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_join");
1490 * Operation - call split.
1493 * 1. AT-Command: AT+CHLD=[<n>]
1496 * 2x - place all active calls on hold except call x with which communication is supported
1501 * +CME ERROR: <error>
1503 static TelReturn imc_call_split(CoreObject *co, unsigned int call_id,
1504 TcoreObjectResponseCallback cb, void *cb_data)
1509 at_cmd = g_strdup_printf("%s%d", "AT+CHLD=2", call_id);
1510 dbg("at command : %s", at_cmd);
1512 return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_split");
1516 * Operation - call transfer.
1519 * 1. AT-Command: AT+CHLD=[<n>]
1522 * 4 connects the two calls and disconnects the subscriber from both calls (Explicit Call Transfer)
1527 * +CME ERROR: <error>
1529 static TelReturn imc_call_transfer(CoreObject *co, TcoreObjectResponseCallback cb,
1535 at_cmd = g_strdup_printf("%s", "AT+CHLD=4");
1536 dbg("at command : %s", at_cmd);
1538 return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_transfer");
1542 * Operation - call transfer.
1545 * 1. AT-Command: AT+CTFR= <number>[,<type>]
1548 * string type phone number
1550 * type of address octet in integer format. It is optional parameter.
1556 * +CME ERROR: <error>
1558 static TelReturn imc_call_deflect(CoreObject *co, const char *deflect_to,
1559 TcoreObjectResponseCallback cb, void *cb_data)
1564 at_cmd = g_strdup_printf("AT+CTFR=%s", deflect_to);
1565 dbg("at command : %s", at_cmd);
1567 return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_deflect");
1570 static TelReturn imc_call_set_active_line(CoreObject *co, TelCallActiveLine active_line,
1571 TcoreObjectResponseCallback cb, void *cb_data)
1576 return TEL_RETURN_OPERATION_NOT_SUPPORTED;
1579 static TelReturn imc_call_get_active_line(CoreObject *co, TcoreObjectResponseCallback cb,
1585 return TEL_RETURN_OPERATION_NOT_SUPPORTED;
1589 * Operation - Set voule info.
1592 * AT-Command: AT+XDRV=<group_id>,<function_id>[,<param_n>]
1593 * The first command parameter defines the involved driver group.
1594 * The second command parameter defines a certain function in the selected driver group.
1595 * Other parameters are dependent on the first two parameters.
1596 * Nearly all parameters are integer values, also if they are represented by literals.
1597 * Only very few are strings or
1601 * +XDRV: <group_id>,<function_id>,<xdrv_result>[,<response_n>]
1602 * The first response parameter defines the involved driver group.
1603 * The second response parameter defines the current function in the selected driver group.
1604 * The third response parameter defines the xdrv_result of the operation.
1605 * Additional response parameters dependent on the first two parameters.
1607 static TelReturn imc_call_set_volume_info(CoreObject *co, const TelCallVolumeInfo *volume_info,
1608 TcoreObjectResponseCallback cb, void *cb_data)
1610 ImcRespCbData *resp_cb_data = NULL;
1613 struct imc_set_volume_info cb_volume_info;
1617 cb_volume_info.next_index = 1;
1618 cb_volume_info.volume = volume_info->volume;
1620 /* Response callback data */
1621 resp_cb_data = imc_create_resp_cb_data(cb, cb_data,
1622 &cb_volume_info, sizeof(struct imc_set_volume_info));
1624 at_cmd = g_strdup_printf("%s", xdrv_set_volume[0]);
1626 /* Send Request to modem */
1627 ret = tcore_at_prepare_and_send_request(co,
1629 TCORE_AT_COMMAND_TYPE_SINGLELINE,
1630 TCORE_PENDING_PRIORITY_DEFAULT,
1632 on_response_imc_call_set_volume_info, resp_cb_data,
1633 on_send_imc_request, NULL,
1634 (guint)0, NULL, NULL);
1636 IMC_CHECK_REQUEST_RET(ret, resp_cb_data, "imc_call_set_volume_info");
1643 static TelReturn imc_call_get_volume_info(CoreObject *co, TelCallSoundDevice sound_device,
1644 TcoreObjectResponseCallback cb, void *cb_data)
1649 return TEL_RETURN_OPERATION_NOT_SUPPORTED;
1653 * Operation - Set sound path.
1656 * AT-Command: AT+XDRV=<group_id>,<function_id>[,<param_n>]
1657 * The first command parameter defines the involved driver group.
1658 * The second command parameter defines a certain function in the selected driver group.
1659 * Other parameters are dependent on the first two parameters.
1660 * Nearly all parameters are integer values, also if they are represented by literals.
1661 * Only very few are strings or
1665 * +XDRV: <group_id>,<function_id>,<xdrv_result>[,<response_n>]
1666 * The first response parameter defines the involved driver group.
1667 * The second response parameter defines the current function in the selected driver group.
1668 * The third response parameter defines the xdrv_result of the operation.
1669 * Additional response parameters dependent on the first two parameters.
1672 static TelReturn imc_call_set_sound_path(CoreObject *co, const TelCallSoundPathInfo *sound_path_info,
1673 TcoreObjectResponseCallback cb, void *cb_data)
1675 ImcRespCbData *resp_cb_data = NULL;
1678 gint device_type = -1;
1680 dbg("audio device type - 0x%x", sound_path_info->path);
1682 switch (sound_path_info->path) {
1683 case TEL_SOUND_PATH_HANDSET:
1686 case TEL_SOUND_PATH_HEADSET:
1689 case TEL_SOUND_PATH_HEADSET_3_5PI:
1692 case TEL_SOUND_PATH_SPK_PHONE:
1695 case TEL_SOUND_PATH_HANDSFREE:
1698 case TEL_SOUND_PATH_HEADSET_HAC:
1701 case TEL_SOUND_PATH_BLUETOOTH:
1702 case TEL_SOUND_PATH_STEREO_BLUETOOTH:
1705 case TEL_SOUND_PATH_BT_NSEC_OFF:
1706 case TEL_SOUND_PATH_MIC1:
1707 case TEL_SOUND_PATH_MIC2:
1709 dbg("unsupported device type");
1710 return TEL_RETURN_INVALID_PARAMETER;
1713 /* Response callback data */
1714 resp_cb_data = imc_create_resp_cb_data(cb, cb_data, &device_type, sizeof(gint));
1716 at_cmd = g_strdup_printf("AT+XDRV=40,4,3,0,0,0,0,0,1,0,1,0,%d", device_type);
1718 ret = tcore_at_prepare_and_send_request(co,
1720 TCORE_AT_COMMAND_TYPE_SINGLELINE,
1721 TCORE_PENDING_PRIORITY_DEFAULT,
1723 on_response_imc_call_set_sound_path, resp_cb_data,
1724 on_send_imc_request, NULL,
1725 (guint)0, NULL, NULL);
1727 IMC_CHECK_REQUEST_RET(ret, NULL, "imc_call_set_sound_path");
1734 * Operation - Set/Unset mute status.
1737 * AT-Command: AT+XDRV=<group_id>,<function_id>[,<param_n>]
1738 * The first command parameter defines the involved driver group.
1739 * The second command parameter defines a certain function in the selected driver group.
1740 * Other parameters are dependent on the first two parameters.
1741 * Nearly all parameters are integer values, also if they are represented by literals.
1742 * Only very few are strings or
1746 * +XDRV: <group_id>,<function_id>,<xdrv_result>[,<response_n>]
1747 * The first response parameter defines the involved driver group.
1748 * The second response parameter defines the current function in the selected driver group.
1749 * The third response parameter defines the xdrv_result of the operation.
1750 * Additional response parameters dependent on the first two parameters.
1752 static TelReturn imc_call_set_mute(CoreObject *co, gboolean mute, TcoreObjectResponseCallback cb,
1755 ImcRespCbData *resp_cb_data = NULL;
1761 /* Response callback data */
1762 resp_cb_data = imc_create_resp_cb_data(cb, cb_data, NULL, 0);
1766 at_cmd = g_strdup_printf("%s", "AT+XDRV=40,8,0,0,0"); /*MUTE*/
1768 at_cmd = g_strdup_printf("%s", "AT+XDRV=40,8,0,0,88"); /*UNMUTE*/
1770 /* Send Request to modem */
1771 ret = tcore_at_prepare_and_send_request(co,
1773 TCORE_AT_COMMAND_TYPE_SINGLELINE,
1774 TCORE_PENDING_PRIORITY_DEFAULT,
1776 on_response_imc_call_set_mute, resp_cb_data,
1777 on_send_imc_request, NULL,
1778 (guint)0, NULL, NULL);
1780 IMC_CHECK_REQUEST_RET(ret, resp_cb_data, "imc_call_set_mute");
1787 static TelReturn imc_call_get_mute_status(CoreObject *co, TcoreObjectResponseCallback cb,
1793 return TEL_RETURN_OPERATION_NOT_SUPPORTED;
1797 static TelReturn imc_call_set_sound_recording(CoreObject *co, TelCallSoundRecording sound_rec,
1798 TcoreObjectResponseCallback cb, void *cb_data)
1803 return TEL_RETURN_OPERATION_NOT_SUPPORTED;
1806 static TelReturn imc_call_set_sound_equalization(CoreObject *co, const TelCallSoundEqualization *sound_eq,
1807 TcoreObjectResponseCallback cb, void *cb_data)
1812 return TEL_RETURN_OPERATION_NOT_SUPPORTED;
1815 /* Call Operations */
1816 static TcoreCallOps imc_call_ops = {
1817 .dial = imc_call_dial,
1818 .answer = imc_call_answer,
1819 .end = imc_call_end,
1820 .send_dtmf = imc_call_send_dtmf,
1821 .hold = imc_call_hold,
1822 .active = imc_call_active,
1823 .swap = imc_call_swap,
1824 .join = imc_call_join,
1825 .split = imc_call_split,
1826 .transfer = imc_call_transfer,
1827 .deflect = imc_call_deflect,
1828 .set_active_line = imc_call_set_active_line,
1829 .get_active_line = imc_call_get_active_line,
1830 .set_volume_info = imc_call_set_volume_info,
1831 .get_volume_info = imc_call_get_volume_info,
1832 .set_sound_path = imc_call_set_sound_path,
1833 .set_mute = imc_call_set_mute,
1834 .get_mute_status = imc_call_get_mute_status,
1835 .set_sound_recording = imc_call_set_sound_recording,
1836 .set_sound_equalization = imc_call_set_sound_equalization,
1839 gboolean imc_call_init(TcorePlugin *p, CoreObject *co)
1843 /* Set operations */
1844 tcore_call_set_ops(co, &imc_call_ops);
1847 tcore_object_add_callback(co, "+XCALLSTAT", on_notification_imc_call_status, NULL);
1848 tcore_object_add_callback(co, "+CLIP", on_notification_imc_call_clip_info, NULL);
1849 tcore_object_add_callback(co, "+CSSU", on_notification_imc_call_ss_cssu_info, NULL);
1850 tcore_object_add_callback(co, "+CSSI", on_notification_imc_call_ss_cssi_info, NULL);
1855 void imc_call_exit(TcorePlugin *p, CoreObject *co)