142ede31663a3eb4e4fc00eaa125581d722de4dc
[platform/core/telephony/tel-plugin-imc.git] / src / imc_call.c
1 /*
2  * tel-plugin-imc
3  *
4  * Copyright (c) 2013 Samsung Electronics Co. Ltd. All rights reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22
23 #include <glib.h>
24
25 #include <tcore.h>
26 #include <server.h>
27 #include <plugin.h>
28 #include <core_object.h>
29 #include <hal.h>
30 #include <queue.h>
31 #include <storage.h>
32 #include <at.h>
33 #include <vconf.h>
34 #include <co_call.h>
35
36 #include "imc_call.h"
37 #include "imc_common.h"
38
39 #define COMMA   0X2c
40 #define STATUS_INCOMING    4
41 #define STATUS_WAITING     5
42 #define STATUS_CONNECTED   7
43
44 #define find_call_object(co, call_id, call_obj) \
45         do { \
46                 call_obj = tcore_call_object_find_by_id(co, call_id); \
47                 if (!call_obj) { \
48                         err("unable to find call object"); \
49                         return; \
50                 } \
51         } while (0)
52
53 struct imc_set_volume_info {
54         guint next_index;
55         guint volume;
56 };
57
58 static gchar *xdrv_set_volume[] = {
59         "AT+XDRV=40,7,3,88",
60         "AT+XDRV=40,7,0,88",
61         "AT+XDRV=40,8,0,88",
62         "AT+XDRV=40,8,2,",
63         NULL
64 };
65
66 /*Forward Declarations*/
67 static void on_response_imc_call_default(TcorePending *p,
68         guint data_len, const void *data, void *user_data);
69
70 static TelReturn __call_list_get(CoreObject *co, gboolean flag);
71
72
73 static TelCallType __call_type(int type)
74 {
75         dbg("Entry");
76
77         switch (type) {
78         case 0:
79                 return TEL_CALL_TYPE_VOICE;
80
81         case 1:
82                 return TEL_CALL_TYPE_VIDEO;
83
84         default:
85                 err("invalid call type, returing default call type as voice");
86                 return TEL_CALL_TYPE_VOICE;
87         }
88 }
89
90 static TelCallState __call_state(int state)
91 {
92         dbg("Entry");
93
94         switch (state) {
95         case 0:
96                 return TEL_CALL_STATE_ACTIVE;
97
98         case 1:
99                 return TEL_CALL_STATE_HELD;
100
101         case 2:
102                 return TEL_CALL_STATE_DIALING;
103
104         case 3:
105                 return TEL_CALL_STATE_ALERT;
106
107         case 4:
108         case 5:
109                 return TEL_CALL_STATE_INCOMING;
110
111         default:
112                 return TEL_CALL_STATE_IDLE;
113         }
114 }
115
116 static void __call_branch_by_status(CoreObject *co, CallObject *call_obj, TelCallState call_state)
117 {
118         unsigned int call_id;
119         TelCallType call_type;
120         TelCallState state;
121         TcoreNotification command = TCORE_NOTIFICATION_UNKNOWN;
122         dbg("Call State[%d]", call_state);
123
124         if (tcore_call_object_get_state(call_obj, &state) == FALSE) {
125                 err("unable to get call status");
126                 return;
127         }
128
129         if (call_state == state) {
130                 dbg("current call state and existing call state are same");
131                 return;
132         }
133
134         if (tcore_call_object_get_call_type(call_obj, &call_type) == FALSE) {
135                 err("unable to get call type");
136                 return;
137         }
138
139         if (tcore_call_object_get_id(call_obj, &call_id) == FALSE) {
140                 err("unable to get call id");
141                 return;
142         }
143
144         /* Set Status */
145         tcore_call_object_set_state(call_obj, call_state);
146
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;
152                 break;
153
154                 case TEL_CALL_STATE_HELD:
155                         command = TCORE_NOTIFICATION_CALL_STATUS_HELD;
156                 break;
157
158                 case TEL_CALL_STATE_DIALING:
159                         command = TCORE_NOTIFICATION_CALL_STATUS_DIALING;
160                 break;
161
162                 case TEL_CALL_STATE_ALERT:
163                         command = TCORE_NOTIFICATION_CALL_STATUS_ALERT;
164                 break;
165
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);
177
178                         /* Send notification */
179                         tcore_object_send_notification(co, command, sizeof(TelCallIncomingInfo), &incoming);
180                         return;
181                 }
182
183                 case TEL_CALL_STATE_IDLE: {
184                         TelCallStatusIdleNoti idle;
185                         command = TCORE_NOTIFICATION_CALL_STATUS_IDLE;
186                         idle.call_id = call_id;
187
188                         /* TODO - get proper call end cause. */
189                         idle.cause = TEL_CALL_END_CAUSE_NONE;
190
191                         /* Send notification */
192                         tcore_object_send_notification(co, command,
193                                 sizeof(TelCallStatusIdleNoti), &idle);
194
195                         /* Free Call object */
196                         tcore_call_object_free(co, call_obj);
197                         return;
198                 }
199                 }
200
201         }
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;
207                 break;
208
209                 case TEL_CALL_STATE_HELD:
210                         err("invalid state");
211                 break;
212
213                 case TEL_CALL_STATE_DIALING:
214                         command = TCORE_NOTIFICATION_VIDEO_CALL_STATUS_DIALING;
215                 break;
216
217                 case TEL_CALL_STATE_ALERT:
218                         command = TCORE_NOTIFICATION_VIDEO_CALL_STATUS_ALERT;
219                 break;
220
221                 case TEL_CALL_STATE_INCOMING:
222                 case TEL_CALL_STATE_WAITING:
223                         command = TCORE_NOTIFICATION_VIDEO_CALL_STATUS_INCOMING;
224                 break;
225
226                 case TEL_CALL_STATE_IDLE: {
227                         TelCallStatusIdleNoti idle;
228                         command = TCORE_NOTIFICATION_VIDEO_CALL_STATUS_IDLE;
229                         idle.call_id = call_id;
230
231                         /* TODO - get proper call end cause. */
232                         idle.cause = TEL_CALL_END_CAUSE_NONE;
233
234                         /* Send notification */
235                         tcore_object_send_notification(co, command,
236                                 sizeof(TelCallStatusIdleNoti), &idle);
237
238                         /* Free Call object */
239                         tcore_call_object_free(co, call_obj);
240                         return;
241                 }
242                 }
243         }
244         else {
245                 err("Unknown Call type: [%d]", call_type);
246         }
247
248         if (command != TCORE_NOTIFICATION_UNKNOWN)
249                 tcore_object_send_notification(co, command, sizeof(call_id), &call_id);
250 }
251
252 static void __handle_call_list_get(CoreObject *co, gboolean flag, void *data)
253 {
254         int call_id;
255         int direction;
256         int call_type;
257         int state;
258         int mpty;
259         int ton;
260         GSList *tokens = NULL;
261         char *resp = NULL;
262         char *line;
263         char *num = NULL;
264         int num_type;
265         char number[TEL_CALL_CALLING_NUMBER_LEN_MAX +1] = {0,};
266         GSList *lines = data;
267         CallObject *call_obj = NULL;
268
269          while (lines != NULL) {
270                 line = (char *)lines->data;
271                 /* point to next node */
272                 lines = lines->next;
273                 /* free previous tokens*/
274                 tcore_at_tok_free(tokens);
275
276                 tokens = tcore_at_tok_new(line);
277
278                 /* <id1> */
279                 resp = g_slist_nth_data(tokens, 0);
280                 if (NULL == resp) {
281                         err("Invalid call_id");
282                         continue;
283                 }
284                 call_id = atoi(resp);
285
286                 /* <dir> */
287                 resp = g_slist_nth_data(tokens, 1);
288                 if (NULL == resp) {
289                         err("Invalid direction");
290                         continue;
291                 }
292                 direction = (atoi(resp) == 0) ? 1 : 0;
293
294                 /* <stat> */
295                 resp = g_slist_nth_data(tokens, 2);
296                 if (NULL == resp) {
297                         err("Invalid state");
298                         continue;
299                 }
300                 state = __call_state(atoi(resp));
301
302                 /* <mode> */
303                 resp = g_slist_nth_data(tokens, 3);
304                 if (NULL == resp) {
305                         err("Invalid call_type");
306                         continue;
307                 }
308                 call_type = __call_type(atoi(resp));
309
310                 /* <mpty> */
311                 resp = g_slist_nth_data(tokens, 4);
312                 if (NULL == resp) {
313                         err("Invalid mpty");
314                         continue;
315                 }
316                 mpty = atoi(resp);
317
318                 /* <number> */
319                 resp = g_slist_nth_data(tokens, 5);
320                 if (NULL == resp) {
321                         err("Number is NULL");
322                 } else {
323                         // Strike off double quotes
324                         num = tcore_at_tok_extract(resp);
325                         dbg("Number: [%s]", num);
326
327                         /* <type> */
328                         resp = g_slist_nth_data(tokens, 6);
329                         if (!resp) {
330                                 err("Invalid num type");
331                         } else {
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 */
337                                         number[0] = '+';
338                                         memcpy(&number[1], num, strlen(num));
339                                 } else {
340                                         memcpy(number, num, strlen(num));
341                                 }
342                         }
343                         g_free(num);
344                 }
345
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);
350
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");
356                                 continue;
357                         }
358                 }
359
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);
366                 if (flag == TRUE)
367                         __call_branch_by_status(co, call_obj, state);
368                 else
369                         tcore_call_object_set_state(call_obj, state);
370         }
371 }
372
373 /* internal notification operation */
374 static void __on_notification_imc_call_incoming(CoreObject *co, unsigned int call_id,
375         void *user_data)
376 {
377         GSList *list = NULL;
378         CallObject *call_obj = NULL;
379         dbg("entry");
380
381         /* check call with incoming status already exist */
382         list = tcore_call_object_find_by_status(co, TEL_CALL_STATE_INCOMING);
383         if (list != NULL) {
384                 err("Incoming call already exist! Skip...");
385                 g_slist_free(list);
386                 return;
387         }
388         g_slist_free(list);
389
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);
393                 return;
394         }
395
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");
400                 return;
401         }
402
403         /* Make request to get current Call list */
404         __call_list_get(co, TRUE);
405 }
406
407 static void __on_notification_imc_call_status(CoreObject *co, unsigned int call_id,
408         unsigned int call_state, void *user_data)
409 {
410         CallObject *call_obj = NULL;
411         TelCallState state;
412
413         state = __call_state(call_state);
414         dbg("state [%d]", state);
415
416         switch (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);
421         }
422         break;
423
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);
428         }
429         break;
430
431         case TEL_CALL_STATE_DIALING: {
432                 call_obj = tcore_call_object_find_by_id(co, call_id);
433                 if (!call_obj) {
434                         call_obj = tcore_call_object_new(co, call_id);
435                         if (!call_obj) {
436                                 err("unable to create call object");
437                                 return;
438                         }
439                 }
440                 /* Make request to get current call list.Update CallObject with <number>
441                  * and send notification to application */
442                 __call_list_get(co, TRUE);
443         }
444         break;
445
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);
450         }
451         break;
452
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);
457         }
458         break;
459
460         default:
461                 err("invalid call status");
462                 break;
463         }
464 }
465
466 /*internal response operation */
467 static void __on_response_imc_call_list_get(TcorePending *p, guint data_len, const void *data,
468         void *user_data)
469 {
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);
476         int count;
477         dbg("entry");
478
479         tcore_check_return_assert(co != NULL);
480         tcore_check_return_assert(resp_cb_data != NULL);
481
482         if (at_resp && at_resp->success) {
483                 result = TEL_CALL_RESULT_SUCCESS;
484                 if (NULL == at_resp->lines) {
485                         err("invalid response received");
486                         return;
487                 }
488
489                 lines = (GSList *) at_resp->lines;
490                 count = g_slist_length(lines);
491                 dbg("Total records : %d", g_slist_length(lines));
492                 if (0 == count) {
493                         err("Call count is zero");
494                         return;
495                 }
496
497                 dbg("RESPONSE OK");
498
499                 /* parse +CLCC notification parameter */
500                 __handle_call_list_get(co, *flag, lines);
501
502         } else {
503                 err("RESPONSE NOK");
504         }
505 }
506
507 /*internal request operation */
508 static TelReturn __send_call_request(CoreObject *co, TcoreObjectResponseCallback cb,
509         void *cb_data, gchar *at_cmd, gchar *func_name)
510 {
511         ImcRespCbData *resp_cb_data;
512         TelReturn ret;
513
514         /* Response callback data */
515         resp_cb_data = imc_create_resp_cb_data(cb, cb_data, func_name, strlen(func_name) + 1);
516
517         /* Send Request to modem */
518         ret = tcore_at_prepare_and_send_request(co,
519                 at_cmd, NULL,
520                 TCORE_AT_COMMAND_TYPE_NO_RESULT,
521                 TCORE_PENDING_PRIORITY_DEFAULT,
522                 NULL,
523                 on_response_imc_call_default, resp_cb_data,
524                 on_send_imc_request, NULL,
525                 0, NULL, NULL);
526
527         IMC_CHECK_REQUEST_RET(ret, resp_cb_data, func_name);
528
529         /* Free resources */
530         g_free(at_cmd);
531         return ret;
532 }
533
534  /*
535  * Operation -  Get current call list.
536  *
537  * Request -
538  * AT-Command: AT+CLCC
539  *
540  * Response -
541  * Success:
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]]]
544  * OK
545  * Failure:
546  * +CME ERROR: <error>
547  */
548 static TelReturn __call_list_get(CoreObject *co, gboolean flag)
549 {
550         ImcRespCbData *resp_cb_data;
551         TelReturn ret =TEL_RETURN_FAILURE;
552         dbg("Entry");
553
554         if (NULL == co) {
555                 err("Core Object is NULL");
556                 return ret;
557         }
558
559         /* Response callback data */
560         resp_cb_data = imc_create_resp_cb_data(NULL, NULL, &flag, sizeof(gboolean));
561
562         /* Send Request to modem */
563         ret = tcore_at_prepare_and_send_request(co,
564                 "AT+CLCC","+CLCC",
565                 TCORE_AT_COMMAND_TYPE_MULTILINE,
566                 TCORE_PENDING_PRIORITY_DEFAULT,
567                 NULL,
568                 __on_response_imc_call_list_get, resp_cb_data,
569                 on_send_imc_request, NULL,
570                 0, NULL, NULL);
571         IMC_CHECK_REQUEST_RET(ret, resp_cb_data, "Get current call list");
572
573         return ret;
574 }
575
576 /* Notification */
577
578 /*
579 * Operation -  call status notification from network.
580 * notification message format:
581 * +XCALLSTAT: <call_id><stat>
582 * where
583 * <call_id>
584 * indicates the call identification (GSM02.30 4.5.5.1)
585 * <stat>
586 * 0 active
587 * 1 hold
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)
592 * 6 disconnected
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)
595 */
596 static gboolean on_notification_imc_call_status(CoreObject *co, const void *data,
597         void *user_data)
598 {
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;
604
605         dbg("Entry");
606
607         lines = (GSList *) data;
608         if (lines == NULL) {
609                 err("Invalid response received");
610                 return TRUE;
611         }
612         line = (char *) (lines->data);
613         tokens = tcore_at_tok_new(line);
614
615         call_handle = g_slist_nth_data(tokens, 0);
616         if (NULL == call_handle) {
617                 err("call_id missing from %XCALLSTAT indiaction");
618                 goto OUT;
619         }
620         call_id = atoi(call_handle);
621         state = g_slist_nth_data(tokens, 1);
622         if (NULL == state) {
623                 err("State is missing from %XCALLSTAT indication");
624                 goto OUT;
625         }
626         status = atoi(state);
627         dbg("call_id[%d], status[%d]", call_id, status);
628
629         switch (status) {
630         case STATUS_INCOMING:
631         case STATUS_WAITING:
632                 __on_notification_imc_call_incoming(co, call_id, user_data);
633                 break;
634
635         case STATUS_CONNECTED:     /* ignore Connected state. */
636                 dbg("ignore connected state");
637                 break;
638
639         default:
640                 __on_notification_imc_call_status(co, call_id, status, user_data);
641                 break;
642         }
643 OUT:
644         // Free tokens
645         tcore_at_tok_free(tokens);
646         return TRUE;
647 }
648
649 /*
650  * Operation -  SS network initiated notification.
651  *
652  * notification message format:
653  * +CSSU: <code2>[<index> [,<number>,<type>]]
654  * <code2>
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
670  * <index>
671  * refer Closed user group +CCUG
672  * <number>
673  *  string type phone of format specified by <type>
674  * <type>
675  * type of address octet in integer format.
676  */
677 static gboolean on_notification_imc_call_ss_cssu_info(CoreObject *co, const void *event_data,
678         void *user_data)
679 {
680         GSList *tokens = NULL;
681         TcoreNotification command = TCORE_NOTIFICATION_UNKNOWN;
682         char *resp = NULL;
683         char *cmd = 0;
684         int index = 0;
685         int code2 = -1;
686         char number[TEL_CALL_CALLING_NUMBER_LEN_MAX + 1] = {'\0',};
687
688         dbg("entry");
689
690         if (1 != g_slist_length((GSList *) event_data)) {
691                 err("unsolicited msg but multiple line");
692                 return TRUE;
693         }
694
695         cmd = (char *) ((GSList *) event_data)->data;
696         dbg("ss notification message[%s]", cmd);
697
698         tokens = tcore_at_tok_new(cmd);
699
700         /* parse <code2> */
701         resp = g_slist_nth_data(tokens, 0);
702         if (NULL == resp) {
703                 err("Code2 is missing from %CSSU indiaction");
704                 tcore_at_tok_free(tokens);
705                 return TRUE;
706         }
707
708         code2 = atoi(resp);
709
710         /* parse [ <index>, <number> <type>] */
711         if ((resp = g_slist_nth_data(tokens, 1)))
712                 index = atoi(resp);
713
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);
718                 number[len] = '\0';;
719         }
720
721         dbg("+CSSU: <code2> = %d <index> = %d <number> = %s ", code2, index, number);
722
723         /* <code2> - other values will be ignored */
724         switch (code2) {
725         case 0:
726                 command = TCORE_NOTIFICATION_CALL_INFO_MT_FORWARDED;
727                 break;
728         case 2:
729                 command = TCORE_NOTIFICATION_CALL_INFO_HELD;
730                 break;
731         case 3:
732                 command = TCORE_NOTIFICATION_CALL_INFO_ACTIVE;
733                 break;
734         case 4:
735                 command = TCORE_NOTIFICATION_CALL_INFO_JOINED;
736                 break;
737         case 7:
738         case 8:
739                 command = TCORE_NOTIFICATION_CALL_INFO_TRANSFERED;
740                 break;
741         case 9:
742                 command = TCORE_NOTIFICATION_CALL_INFO_MT_DEFLECTED;
743                 break;
744         default:
745                 dbg("Unsupported +CSSU notification : %d", code2);
746                 break;
747         }
748
749         if (command != TCORE_NOTIFICATION_UNKNOWN)
750                 tcore_object_send_notification(co, command, 0, NULL);
751         tcore_at_tok_free(tokens);
752
753         return TRUE;
754 }
755
756 /*
757 * Operation -  SS network initiated notification.
758 * notification message format:
759 * +CSSI : <code1>[,<index>]
760 * where
761 * <code1>
762 * 0 unconditional call forwarding is active
763 * 1 some of the conditional call forwarding are active
764 * 2 call has been forwarded
765 * 3 call is waiting
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
771
772 * <index>
773 * refer Closed user group +CCUG.
774 */
775 static gboolean on_notification_imc_call_ss_cssi_info(CoreObject *co, const void *event_data,
776         void *user_data)
777 {
778         GSList *tokens = NULL;
779         TcoreNotification command = TCORE_NOTIFICATION_UNKNOWN;
780         char *resp = NULL;
781         char *cmd = 0;
782         int index = 0;
783         int code1 = -1;
784
785         dbg("entry");
786
787         if (1 != g_slist_length((GSList *) event_data)) {
788                 err("unsolicited msg but multiple line");
789                 return TRUE;
790         }
791         cmd = (char *) ((GSList *) event_data)->data;
792         dbg("ss notification message[%s]", cmd);
793
794         tokens = tcore_at_tok_new(cmd);
795         /* parse <code1> */
796         resp = g_slist_nth_data(tokens, 0);
797         if (NULL == resp) {
798                 err("<code1> is missing from %CSSI indiaction");
799                 tcore_at_tok_free(tokens);
800                 return TRUE;
801         }
802
803         code1 = atoi(resp);
804
805         /* parse [ <index>] */
806         if ((resp = g_slist_nth_data(tokens, 1)))
807                 index = atoi(resp);
808
809         dbg("+CSSI: <code1> = %d <index> = %d ", code1, index);
810
811         /* <code1> - other values will be ignored */
812         switch (code1) {
813         case 0:
814                 command = TCORE_NOTIFICATION_CALL_INFO_MO_FORWARD_UNCONDITIONAL;
815                 break;
816         case 1:
817                 command = TCORE_NOTIFICATION_CALL_INFO_MO_FORWARD_CONDITIONAL;
818                 break;
819         case 2:
820                 command = TCORE_NOTIFICATION_CALL_INFO_MO_FORWARDED;
821                 break;
822         case 3:
823                 command = TCORE_NOTIFICATION_CALL_INFO_MO_WAITING;
824                 break;
825         case 5:
826                 command = TCORE_NOTIFICATION_CALL_INFO_MO_BARRED_OUTGOING;
827                 break;
828         case 6:
829                 command = TCORE_NOTIFICATION_CALL_INFO_MO_BARRED_INCOMING;
830                 break;
831         case 8:
832                 command  = TCORE_NOTIFICATION_CALL_INFO_MO_DEFLECTED;
833                 break;
834         default:
835                 dbg("Unsupported +CSSI notification : %d", code1);
836                 break;
837         }
838
839         if (command != TCORE_NOTIFICATION_UNKNOWN)
840                 tcore_object_send_notification(co, command, 0, NULL);
841         tcore_at_tok_free(tokens);
842
843         return TRUE;
844 }
845
846 static gboolean on_notification_imc_call_clip_info(CoreObject *co, const void *data,
847         void *user_data)
848 {
849         dbg("entry");
850         /* TODO - handle +CLIP notification*/
851         return TRUE;
852 }
853
854 /* Response */
855 static void on_response_imc_call_default(TcorePending *p,
856         guint data_len, const void *data, void *user_data)
857 {
858         const TcoreAtResponse *at_resp = data;
859         CoreObject *co = tcore_pending_ref_core_object(p);
860         ImcRespCbData *resp_cb_data = user_data;
861
862         TelCallResult result;
863         dbg("entry");
864
865         tcore_check_return_assert(co != NULL);
866         tcore_check_return_assert(resp_cb_data != NULL);
867
868         if (at_resp && at_resp->success) {
869                 result = TEL_CALL_RESULT_SUCCESS;
870         } else {
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 */
874         }
875
876         dbg("%s: [%s]", IMC_GET_DATA_FROM_RESP_CB_DATA(resp_cb_data),
877                  (result == TEL_CALL_RESULT_SUCCESS ? "SUCCESS" : "FAIL"));
878
879         /* Invoke callback */
880         if (resp_cb_data->cb)
881                 resp_cb_data->cb(co, (gint)result, NULL, resp_cb_data->cb_data);
882
883         /* Free callback data */
884         imc_destroy_resp_cb_data(resp_cb_data);
885 }
886
887 static void on_response_imc_call_set_volume_info(TcorePending *p,
888         guint data_len, const void *data, void *user_data)
889 {
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;
894         GSList *line = NULL;
895         char *resp_str = NULL;
896         gboolean error;
897
898         TelCallResult result = TEL_CALL_RESULT_FAILURE;  // TODO: XDRV error mapping is required
899         dbg("Enter");
900
901         tcore_check_return_assert(co != NULL);
902         tcore_check_return_assert(resp_cb_data != NULL);
903
904         if (at_resp && at_resp->success) {
905                 line = at_resp->lines;
906                 tokens = tcore_at_tok_new(line->data);
907
908                 if (!g_slist_nth_data(tokens, 0)) {
909                         err("group_id is missing");
910                         goto OUT;
911                 }
912
913                 if (!g_slist_nth_data(tokens, 1)) {
914                         err(" function_id is missing");
915                         goto OUT;
916                 }
917
918                 resp_str = g_slist_nth_data(tokens, 2);
919                 if (!resp_str) {
920                         err("xdrv result missing");
921                         goto OUT;
922                 } else {
923                         struct imc_set_volume_info *volume_info;
924                         gchar *vol = "";
925                         gchar *at_cmd;
926                         TelReturn ret;
927
928                         error = atoi(resp_str);
929                         if (error) {
930                                 err("RESPONSE NOK");
931                                 goto OUT;
932                         }
933
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);
937
938                         if (xdrv_set_volume[volume_info->next_index] == NULL) {
939                                 /*Processing of xdrv commands  completed */
940                                 dbg("RESPONSE OK");
941                                 result = TEL_CALL_RESULT_SUCCESS;
942                                 goto OUT;
943                         } else if (volume_info->next_index == 3) {
944                                 switch ((volume_info->volume) / 10) {
945                                 case 0 :
946                                         vol = "0";
947                                 break;
948                                 case 1 :
949                                         vol = "40";
950                                 break;
951                                 case 2 :
952                                         vol = "46";
953                                 break;
954                                 case 3 :
955                                         vol = "52";
956                                 break;
957                                 case 4 :
958                                         vol = "58";
959                                 break;
960                                 case 5 :
961                                         vol = "64";
962                                 break;
963                                 case 6 :
964                                         vol = "70";
965                                 break;
966                                 case 7 :
967                                         vol = "76";
968                                 break;
969                                 case 8 :
970                                         vol = "82";
971                                 break;
972                                 case 9 :
973                                 default :
974                                         vol = "88";
975                                 }
976                         }
977
978                         at_cmd = g_strdup_printf("%s%s",
979                                         xdrv_set_volume[volume_info->next_index], vol);
980
981                         /* Increament index to point to next command */
982                         volume_info->next_index += 1;
983
984                         /* Send Request to modem */
985                         ret = tcore_at_prepare_and_send_request(co,
986                                         at_cmd, "+XDRV",
987                                         TCORE_AT_COMMAND_TYPE_SINGLELINE,
988                                         TCORE_PENDING_PRIORITY_DEFAULT,
989                                         NULL,
990                                         on_response_imc_call_set_volume_info, resp_cb_data,
991                                         on_send_imc_request, NULL,
992                                         (guint)0, NULL, NULL);
993
994                         IMC_CHECK_REQUEST_RET(ret, resp_cb_data, "imc_call_set_volume_info");
995                         g_free(at_cmd);
996
997                         return;
998                 }
999         }
1000
1001 OUT :
1002         dbg("Set Volume Info: [%s]",
1003                         (result == TEL_CALL_RESULT_SUCCESS ? "SUCCESS" : "FAIL"));
1004
1005         /* Invoke callback */
1006         if (resp_cb_data->cb)
1007                 resp_cb_data->cb(co, (gint)result, NULL, resp_cb_data->cb_data);
1008
1009         imc_destroy_resp_cb_data(resp_cb_data);
1010         tcore_at_tok_free(tokens);
1011 }
1012
1013 static void on_response_imc_call_set_sound_path(TcorePending *p,
1014         guint data_len, const void *data, void *user_data)
1015 {
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;
1022         gboolean error;
1023         gint xdrv_func_id = -1;
1024
1025         TelCallResult result = TEL_CALL_RESULT_FAILURE;  // TODO: XDRV error mapping is required
1026         dbg("Enter");
1027
1028         tcore_check_return_assert(co != NULL);
1029         tcore_check_return_assert(resp_cb_data != NULL);
1030
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");
1036                         goto OUT;
1037                 }
1038
1039                 if (!(resp_str = g_slist_nth_data(tokens, 1))) {
1040                         err(" function_id is missing");
1041                         goto OUT;
1042                 }
1043
1044                 xdrv_func_id = atoi(resp_str);
1045
1046                 resp_str = g_slist_nth_data(tokens, 2);
1047                 if (resp_str) {
1048                         error = atoi(resp_str);
1049                         if (error) {
1050                                 err("RESPONSE NOK");
1051                                 goto OUT;
1052                         } else {
1053                                 if (xdrv_func_id == 4) {
1054                                         /* Send next command to configure destination device type */
1055                                         gchar *at_cmd;
1056                                         TelReturn ret;
1057                                         gint *device_type = IMC_GET_DATA_FROM_RESP_CB_DATA(resp_cb_data);
1058
1059                                         at_cmd = g_strdup_printf("AT+XDRV=40,5,2,0,0,0,0,0,1,0,1,0,%d",
1060                                                                 *device_type);
1061
1062                                         ret = tcore_at_prepare_and_send_request(co,
1063                                                         at_cmd, "+XDRV",
1064                                                         TCORE_AT_COMMAND_TYPE_SINGLELINE,
1065                                                         TCORE_PENDING_PRIORITY_DEFAULT,
1066                                                         NULL,
1067                                                         on_response_imc_call_set_sound_path, resp_cb_data,
1068                                                         on_send_imc_request, NULL,
1069                                                         (guint)0, NULL, NULL);
1070
1071                                         IMC_CHECK_REQUEST_RET(ret, resp_cb_data, "imc_call_set_sound_path");
1072                                         g_free(at_cmd);
1073
1074                                         return;
1075                                 }
1076                                 dbg("RESPONSE OK");
1077                                 result = TEL_CALL_RESULT_SUCCESS;
1078                         }
1079                 }
1080         }
1081
1082 OUT :
1083         dbg("Set Sound Path: [%s]",
1084                         (result == TEL_CALL_RESULT_SUCCESS ? "SUCCESS" : "FAIL"));
1085
1086         tcore_at_tok_free(tokens);
1087
1088         /* Invoke callback */
1089         if (resp_cb_data->cb)
1090                 resp_cb_data->cb(co, (gint)result, NULL, resp_cb_data->cb_data);
1091
1092         imc_destroy_resp_cb_data(resp_cb_data);
1093 }
1094
1095 static void on_response_imc_call_set_mute(TcorePending *p, guint data_len,
1096         const void *data, void *user_data)
1097 {
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;
1104         gboolean error;
1105
1106         TelCallResult result = TEL_CALL_RESULT_FAILURE;  // TODO: XDRV error mapping is required
1107         dbg("Enter");
1108
1109         tcore_check_return_assert(co != NULL);
1110         tcore_check_return_assert(resp_cb_data != NULL);
1111
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);
1116
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;
1121                         goto OUT;
1122                 }
1123
1124                 if (!g_slist_nth_data(tokens, 1)) {
1125                         err(" function_id is missing");
1126                         result = TEL_CALL_RESULT_FAILURE;
1127                         goto OUT;
1128                 }
1129
1130                 resp_str = g_slist_nth_data(tokens, 2);
1131                 if (resp_str) {
1132                         error = atoi(resp_str);
1133                         if (error) {
1134                                 result = TEL_CALL_RESULT_FAILURE;
1135                                 goto OUT;
1136                         } else {
1137                                 result = TEL_CALL_RESULT_SUCCESS;
1138                         }
1139                 }
1140         }
1141
1142 OUT :
1143         dbg("Set Mute: [%s]",
1144                         (result == TEL_CALL_RESULT_SUCCESS ? "SUCCESS" : "FAIL"));
1145         tcore_at_tok_free(tokens);
1146
1147         /* Invoke callback */
1148         if (resp_cb_data->cb)
1149                 resp_cb_data->cb(co, (gint)result, NULL, resp_cb_data->cb_data);
1150
1151         imc_destroy_resp_cb_data(resp_cb_data);
1152 }
1153
1154
1155  /* Request */
1156  /*
1157  * Operation - dial
1158  *
1159  * Request -
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.
1164  *
1165  * Response -
1166  * Success:
1167  * OK or CONNECT
1168  * Failure:
1169  * "ERROR"
1170  * "NO ANSWER"
1171  * "NO CARRIER"
1172  * "BUSY"
1173  * "NO DIALTONE"
1174  * +CME ERROR: <error>
1175  */
1176 static TelReturn imc_call_dial(CoreObject *co, const TelCallDial *dial_info,
1177                 TcoreObjectResponseCallback cb, void *cb_data)
1178 {
1179         gchar *at_cmd;
1180         const gchar *clir;
1181         gchar *num;
1182
1183         dbg("entry");
1184
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;
1188         }
1189
1190         if (!strncmp(dial_info->number, "*31#", 4)) {
1191                 dbg("clir suppression");
1192                 clir = "i";
1193                 num = (gchar *)&(dial_info->number[4]);
1194         } else if (!strncmp(dial_info->number, "#31#", 4)) {
1195                 dbg("clir invocation");
1196                 clir = "I";
1197                 num = (gchar *)&(dial_info->number[4]);
1198         } else {
1199                 int cli = 0;
1200
1201                 dbg("no clir string in number");
1202
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)
1205                  */
1206                 vconf_get_int("db/ciss/show_my_number", &cli);
1207                 if(cli == 2){
1208                         dbg("clir invocation from setting application");
1209                         clir = "I";
1210                 } else {
1211                         dbg("set clir state to default");
1212                         clir = "";
1213                 }
1214                 num = (gchar *)dial_info->number;
1215         }
1216
1217         /* AT-Command */
1218         at_cmd = g_strdup_printf("ATD%s%s;", num, clir);
1219         dbg(" at command : %s", at_cmd);
1220
1221         return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_dial");
1222 }
1223
1224 /*
1225  * Operation - Answer/Reject/Replace/hold(current call) & accept incoming call.
1226  *
1227  * Request -
1228  *
1229  * 1. AT-Command: ATA
1230  * Response -
1231  * Success:
1232  * OK
1233  * Failure:
1234  * +CME ERROR: <error>
1235  *
1236  * 2. AT-Command: AT+CHLD=[<n>]
1237  * <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.
1245  * Response -
1246  * Success:
1247  * OK
1248  * Failure:
1249  * +CME ERROR: <error>
1250  * For more informatiion refer 3GPP TS 27.007.
1251  */
1252 static TelReturn imc_call_answer(CoreObject *co, TelCallAnswerType ans_type,
1253         TcoreObjectResponseCallback cb, void *cb_data)
1254 {
1255         gchar *at_cmd;
1256         dbg("entry");
1257
1258         if (ans_type == TEL_CALL_ANSWER_ACCEPT) {
1259                 /* AT-Command */
1260                 at_cmd = g_strdup_printf("%s", "ATA");
1261         }else if (ans_type == TEL_CALL_ANSWER_REJECT) {
1262                 /* AT-Command */
1263                 at_cmd = g_strdup_printf("%s", "AT+CHLD=0");
1264         } else if (ans_type == TEL_CALL_ANSWER_REPLACE) {
1265                 /* AT-Command */
1266                 at_cmd = g_strdup_printf("%s", "AT+CHLD=1");
1267         } else if (ans_type == TEL_CALL_ANSWER_HOLD_AND_ACCEPT) {
1268                 /* AT-Command */
1269                 at_cmd = g_strdup_printf("%s", "AT+CHLD=2");
1270         }else {
1271                 err("Unsupported call answer type");
1272                 return TEL_RETURN_FAILURE;
1273         }
1274
1275         dbg("at command : %s", at_cmd);
1276
1277         return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_answer");
1278 }
1279
1280 /*
1281  * Operation - release all calls/release specific call/release all active call/release all held calls.
1282  *
1283  * Request -
1284  * 1. AT-Command: AT+CHLD=[<n>]
1285  * <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.
1291  * Response -
1292  * Success:
1293  * OK
1294  * Failure:
1295  * +CME ERROR: <error>
1296  */
1297 static TelReturn imc_call_end(CoreObject *co, const TelCallEnd *end_info,
1298         TcoreObjectResponseCallback cb, void *cb_data)
1299 {
1300         gchar *at_cmd;
1301         dbg("entry");
1302
1303         if (end_info->end_type == TEL_CALL_END_ALL) {
1304                 /* AT-Command */
1305                 at_cmd = g_strdup_printf("%s", "AT+CHLD=8");
1306         }else if (end_info->end_type == TEL_CALL_END) {
1307                 /* AT-Command */
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) {
1310                 /* AT-Command */
1311                 at_cmd = g_strdup_printf("%s", "AT+CHLD=1");
1312         } else if (end_info->end_type == TEL_CALL_END_HOLD_ALL) {
1313                 /* AT-Command */
1314                 at_cmd = g_strdup_printf("%s", "AT+CHLD=0");
1315         }else {
1316                 err("Unsupported call end type");
1317                 return TEL_RETURN_FAILURE;
1318         }
1319
1320         dbg("at command : %s", at_cmd);
1321
1322         return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_end");
1323 }
1324
1325 /*
1326  * Operation - send dtmf.
1327  *
1328  * Request -
1329  * 1. AT-Command: AT+VTS=<DTMF>,{<DTMF>,<duration>}.
1330  * where
1331  * <DTMF>:
1332  * is a single ASCII character in the set 0-9, #, *, A-D. Even it will support string DTMF.
1333  * <duration>:
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.
1337  * Response -
1338  * Success:
1339  * OK
1340  * Failure:
1341  * +CME ERROR: <error>
1342  */
1343 static TelReturn imc_call_send_dtmf(CoreObject *co, const char *dtmf_str,
1344         TcoreObjectResponseCallback cb,  void *cb_data)
1345 {
1346         gchar *at_cmd;
1347         char *tmp_dtmf = NULL, *dtmf;
1348         unsigned int count;
1349
1350         dbg("entry");
1351
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 */
1356         dtmf = tmp_dtmf;
1357
1358         for (count = 0; count < strlen(dtmf_str); count++) {
1359                 *tmp_dtmf = dtmf_str[count];
1360                 tmp_dtmf++;
1361
1362                 *tmp_dtmf = COMMA;
1363                 tmp_dtmf++;
1364         }
1365
1366         // last digit is having COMMA , overwrite it with '\0' .
1367         *(--tmp_dtmf) = '\0';
1368
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);
1372
1373         tcore_free(dtmf);
1374
1375         return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_send_dtmf");
1376 }
1377
1378 /*
1379  * Operation - call hold.
1380  *
1381  * Request -
1382  * 1. AT-Command: AT+CHLD=[<n>]
1383  * Where
1384  * <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
1388  * Response -
1389  * Success:
1390  * OK
1391  * Failure:
1392  * +CME ERROR: <error>
1393  */
1394 static TelReturn imc_call_hold(CoreObject *co, TcoreObjectResponseCallback cb,
1395         void *cb_data)
1396
1397 {
1398         gchar *at_cmd;
1399         dbg("entry");
1400
1401         at_cmd = g_strdup_printf("%s", "AT+CHLD=2");
1402         dbg("at command : %s", at_cmd);
1403
1404         return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_hold");
1405 }
1406
1407 /*
1408  * Operation - call active.
1409  *
1410  * Request -
1411  * 1. AT-Command: AT+CHLD=[<n>]
1412  * Where
1413  * <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
1417  * Response -
1418  * Success:
1419  * OK
1420  * Failure:
1421  * +CME ERROR: <error>
1422  */
1423 static TelReturn imc_call_active(CoreObject *co, TcoreObjectResponseCallback cb,
1424         void *cb_data)
1425 {
1426         gchar *at_cmd;
1427         dbg("entry");
1428
1429         at_cmd = g_strdup_printf("%s", "AT+CHLD=2");
1430         dbg("at command : %s", at_cmd);
1431
1432         return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_active");
1433 }
1434
1435 /*
1436  * Operation - call swap.
1437  *
1438  * Request -
1439  * 1. AT-Command: AT+CHLD=[<n>]
1440  * Where
1441  * <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
1445  * Response -
1446  * Success:
1447  * OK
1448  * Failure:
1449  * +CME ERROR: <error>
1450  */
1451 static TelReturn imc_call_swap(CoreObject *co, TcoreObjectResponseCallback cb,
1452         void *cb_data)
1453 {
1454         gchar *at_cmd;
1455         dbg("entry");
1456
1457         at_cmd = g_strdup_printf("%s", "AT+CHLD=2");
1458         dbg("at command : %s", at_cmd);
1459
1460         return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_swap");
1461 }
1462
1463 /*
1464  * Operation - call join.
1465  *
1466  * Request -
1467  * 1. AT-Command: AT+CHLD=[<n>]
1468  * Where
1469  * <n>
1470  * 3 - adds a held call to the conversation
1471  * Response -
1472  * Success:
1473  * OK
1474  * Failure:
1475  * +CME ERROR: <error>
1476  */
1477 static TelReturn imc_call_join(CoreObject *co, TcoreObjectResponseCallback cb,
1478         void *cb_data)
1479 {
1480         gchar *at_cmd;
1481         dbg("entry");
1482
1483         at_cmd = g_strdup_printf("%s", "AT+CHLD=3");
1484         dbg("at command : %s", at_cmd);
1485
1486         return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_join");
1487 }
1488
1489 /*
1490  * Operation - call split.
1491  *
1492  * Request -
1493  * 1. AT-Command: AT+CHLD=[<n>]
1494  * Where
1495  * <n>
1496  * 2x - place all active calls on hold except call x with which communication is supported
1497  * Response -
1498  * Success:
1499  * OK
1500  * Failure:
1501  * +CME ERROR: <error>
1502  */
1503 static TelReturn imc_call_split(CoreObject *co, unsigned int call_id,
1504         TcoreObjectResponseCallback cb, void *cb_data)
1505 {
1506         gchar *at_cmd;
1507         dbg("entry");
1508
1509         at_cmd = g_strdup_printf("%s%d", "AT+CHLD=2", call_id);
1510         dbg("at command : %s", at_cmd);
1511
1512         return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_split");
1513 }
1514
1515 /*
1516  * Operation - call transfer.
1517  *
1518  * Request -
1519  * 1. AT-Command: AT+CHLD=[<n>]
1520  * Where
1521  * <n>
1522  * 4 connects the two calls and disconnects the subscriber from both calls (Explicit Call Transfer)
1523  * Response -
1524  * Success:
1525  * OK
1526  * Failure:
1527  * +CME ERROR: <error>
1528  */
1529 static TelReturn imc_call_transfer(CoreObject *co, TcoreObjectResponseCallback cb,
1530         void *cb_data)
1531 {
1532         gchar *at_cmd;
1533         dbg("entry");
1534
1535         at_cmd = g_strdup_printf("%s", "AT+CHLD=4");
1536         dbg("at command : %s", at_cmd);
1537
1538         return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_transfer");
1539 }
1540
1541 /*
1542  * Operation - call transfer.
1543  *
1544  * Request -
1545  * 1. AT-Command: AT+CTFR= <number>[,<type>]
1546  * Where
1547  * number>
1548  * string type phone number
1549  * <type>
1550  * type of address octet in integer format. It is optional parameter.
1551  *
1552  * Response -
1553  * Success:
1554  * OK
1555  * Failure:
1556  * +CME ERROR: <error>
1557  */
1558 static TelReturn imc_call_deflect(CoreObject *co, const char *deflect_to,
1559         TcoreObjectResponseCallback cb, void *cb_data)
1560 {
1561         gchar *at_cmd;
1562         dbg("entry");
1563
1564         at_cmd = g_strdup_printf("AT+CTFR=%s", deflect_to);
1565         dbg("at command : %s", at_cmd);
1566
1567         return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_deflect");
1568 }
1569
1570 static TelReturn imc_call_set_active_line(CoreObject *co, TelCallActiveLine active_line,
1571         TcoreObjectResponseCallback cb, void *cb_data)
1572 {
1573         dbg("entry");
1574
1575         dbg("exit");
1576         return TEL_RETURN_OPERATION_NOT_SUPPORTED;
1577 }
1578
1579 static TelReturn imc_call_get_active_line(CoreObject *co, TcoreObjectResponseCallback cb,
1580         void *cb_data)
1581 {
1582         dbg("entry");
1583
1584         dbg("exit");
1585         return TEL_RETURN_OPERATION_NOT_SUPPORTED;
1586 }
1587
1588 /*
1589  * Operation - Set voule info.
1590  *
1591  * Request -
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
1598  * hex data strings.
1599  *
1600  * Response -
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.
1606  */
1607 static TelReturn imc_call_set_volume_info(CoreObject *co, const TelCallVolumeInfo *volume_info,
1608         TcoreObjectResponseCallback cb, void *cb_data)
1609 {
1610         ImcRespCbData *resp_cb_data = NULL;
1611         gchar *at_cmd;
1612         TelReturn ret;
1613         struct imc_set_volume_info cb_volume_info;
1614
1615         dbg("entry");
1616
1617         cb_volume_info.next_index = 1;
1618         cb_volume_info.volume = volume_info->volume;
1619
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));
1623
1624         at_cmd = g_strdup_printf("%s", xdrv_set_volume[0]);
1625
1626         /* Send Request to modem */
1627         ret = tcore_at_prepare_and_send_request(co,
1628                         at_cmd, "+XDRV",
1629                         TCORE_AT_COMMAND_TYPE_SINGLELINE,
1630                         TCORE_PENDING_PRIORITY_DEFAULT,
1631                         NULL,
1632                         on_response_imc_call_set_volume_info, resp_cb_data,
1633                         on_send_imc_request, NULL,
1634                         (guint)0, NULL, NULL);
1635
1636         IMC_CHECK_REQUEST_RET(ret, resp_cb_data, "imc_call_set_volume_info");
1637
1638         g_free(at_cmd);
1639         return ret;
1640 }
1641
1642
1643 static TelReturn imc_call_get_volume_info(CoreObject *co, TelCallSoundDevice sound_device,
1644         TcoreObjectResponseCallback cb, void *cb_data)
1645 {
1646         dbg("Entry");
1647
1648         dbg("Exit");
1649         return TEL_RETURN_OPERATION_NOT_SUPPORTED;
1650 }
1651
1652 /*
1653  * Operation - Set sound path.
1654  *
1655  * Request -
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
1662  * hex data strings.
1663  *
1664  * Response -
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.
1670  */
1671
1672 static TelReturn imc_call_set_sound_path(CoreObject *co, const TelCallSoundPathInfo *sound_path_info,
1673         TcoreObjectResponseCallback cb, void *cb_data)
1674 {
1675         ImcRespCbData *resp_cb_data = NULL;
1676         TelReturn ret;
1677         gchar *at_cmd;
1678         gint device_type = -1;
1679
1680         dbg("audio device type - 0x%x", sound_path_info->path);
1681
1682         switch (sound_path_info->path) {
1683                 case TEL_SOUND_PATH_HANDSET:
1684                         device_type = 1;
1685                         break;
1686                 case TEL_SOUND_PATH_HEADSET:
1687                         device_type = 2;
1688                         break;
1689                 case TEL_SOUND_PATH_HEADSET_3_5PI:
1690                         device_type = 3;
1691                         break;
1692                 case TEL_SOUND_PATH_SPK_PHONE:
1693                         device_type = 4;
1694                         break;
1695                 case TEL_SOUND_PATH_HANDSFREE:
1696                         device_type = 5;
1697                         break;
1698                 case TEL_SOUND_PATH_HEADSET_HAC:
1699                         device_type = 6;
1700                         break;
1701                 case TEL_SOUND_PATH_BLUETOOTH:
1702                 case TEL_SOUND_PATH_STEREO_BLUETOOTH:
1703                         device_type = 7;
1704                         break;
1705                 case TEL_SOUND_PATH_BT_NSEC_OFF:
1706                 case TEL_SOUND_PATH_MIC1:
1707                 case TEL_SOUND_PATH_MIC2:
1708                 default:
1709                         dbg("unsupported device type");
1710                         return TEL_RETURN_INVALID_PARAMETER;
1711         }
1712
1713         /* Response callback data */
1714         resp_cb_data = imc_create_resp_cb_data(cb, cb_data, &device_type, sizeof(gint));
1715
1716         at_cmd = g_strdup_printf("AT+XDRV=40,4,3,0,0,0,0,0,1,0,1,0,%d", device_type);
1717
1718         ret = tcore_at_prepare_and_send_request(co,
1719                         at_cmd, "+XDRV",
1720                         TCORE_AT_COMMAND_TYPE_SINGLELINE,
1721                         TCORE_PENDING_PRIORITY_DEFAULT,
1722                         NULL,
1723                         on_response_imc_call_set_sound_path, resp_cb_data,
1724                         on_send_imc_request, NULL,
1725                         (guint)0, NULL, NULL);
1726
1727         IMC_CHECK_REQUEST_RET(ret, NULL, "imc_call_set_sound_path");
1728         g_free(at_cmd);
1729
1730         return ret;
1731 }
1732
1733 /*
1734  * Operation - Set/Unset mute status.
1735  *
1736  * Request -
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
1743  * hex data strings.
1744  *
1745  * Response -
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.
1751  */
1752 static TelReturn imc_call_set_mute(CoreObject *co, gboolean mute, TcoreObjectResponseCallback cb,
1753         void *cb_data)
1754 {
1755         ImcRespCbData *resp_cb_data = NULL;
1756         gchar *at_cmd;
1757         TelReturn ret;
1758
1759         dbg("entry");
1760
1761         /* Response callback data */
1762         resp_cb_data = imc_create_resp_cb_data(cb, cb_data, NULL, 0);
1763
1764         /* AT - Command */
1765         if (mute)
1766                 at_cmd = g_strdup_printf("%s", "AT+XDRV=40,8,0,0,0");  /*MUTE*/
1767         else
1768                 at_cmd = g_strdup_printf("%s", "AT+XDRV=40,8,0,0,88"); /*UNMUTE*/
1769
1770         /* Send Request to modem */
1771         ret = tcore_at_prepare_and_send_request(co,
1772                         at_cmd, "+XDRV",
1773                         TCORE_AT_COMMAND_TYPE_SINGLELINE,
1774                         TCORE_PENDING_PRIORITY_DEFAULT,
1775                         NULL,
1776                         on_response_imc_call_set_mute, resp_cb_data,
1777                         on_send_imc_request, NULL,
1778                         (guint)0, NULL, NULL);
1779
1780         IMC_CHECK_REQUEST_RET(ret, resp_cb_data, "imc_call_set_mute");
1781
1782         g_free(at_cmd);
1783
1784         return ret;
1785 }
1786
1787 static TelReturn imc_call_get_mute_status(CoreObject *co, TcoreObjectResponseCallback cb,
1788         void *cb_data)
1789 {
1790         dbg("entry");
1791
1792         dbg("exit");
1793         return TEL_RETURN_OPERATION_NOT_SUPPORTED;
1794 }
1795
1796
1797 static TelReturn imc_call_set_sound_recording(CoreObject *co, TelCallSoundRecording sound_rec,
1798         TcoreObjectResponseCallback cb, void *cb_data)
1799 {
1800         dbg("entry");
1801
1802         dbg("exit");
1803         return TEL_RETURN_OPERATION_NOT_SUPPORTED;
1804 }
1805
1806 static TelReturn imc_call_set_sound_equalization(CoreObject *co, const TelCallSoundEqualization *sound_eq,
1807         TcoreObjectResponseCallback cb, void *cb_data)
1808 {
1809         dbg("entry");
1810
1811         dbg("exit");
1812         return TEL_RETURN_OPERATION_NOT_SUPPORTED;
1813 }
1814
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,
1837 };
1838
1839 gboolean imc_call_init(TcorePlugin *p, CoreObject *co)
1840 {
1841         dbg("Entry");
1842
1843         /* Set operations */
1844         tcore_call_set_ops(co, &imc_call_ops);
1845
1846         /* Add Callbacks */
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);
1851
1852         return TRUE;
1853 }
1854
1855 void imc_call_exit(TcorePlugin *p, CoreObject *co)
1856 {
1857         dbg("Exit");
1858 }