add merrifield and clovertrail audio control support
[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                 NULL,
522                 on_response_imc_call_default, resp_cb_data,
523                 on_send_imc_request, NULL);
524         IMC_CHECK_REQUEST_RET(ret, resp_cb_data, func_name);
525
526         /* Free resources */
527         g_free(at_cmd);
528         return ret;
529 }
530
531  /*
532  * Operation -  Get current call list.
533  *
534  * Request -
535  * AT-Command: AT+CLCC
536  *
537  * Response -
538  * Success:
539  *[+CLCC: <id1>, <dir>, <stat>, <mode>,<mpty>[,<number>,<type>[,<alpha>[,<priority>]]]
540  *[<CR><LF> +CLCC: <id2>,<dir>,<stat>,<mode>,<mpty>[,<number>,<type>[,<alpha>[,<priority>]]][\85]]]
541  * OK
542  * Failure:
543  * +CME ERROR: <error>
544  */
545 static TelReturn __call_list_get(CoreObject *co, gboolean flag)
546 {
547         ImcRespCbData *resp_cb_data;
548         TelReturn ret =TEL_RETURN_FAILURE;
549         dbg("Entry");
550
551         if (NULL == co) {
552                 err("Core Object is NULL");
553                 return ret;
554         }
555
556         /* Response callback data */
557         resp_cb_data = imc_create_resp_cb_data(NULL, NULL, &flag, sizeof(gboolean));
558
559         /* Send Request to modem */
560         ret = tcore_at_prepare_and_send_request(co,
561                 "AT+CLCC","+CLCC",
562                 TCORE_AT_COMMAND_TYPE_MULTILINE,
563                 NULL,
564                 __on_response_imc_call_list_get, resp_cb_data,
565                 on_send_imc_request, NULL);
566         IMC_CHECK_REQUEST_RET(ret, resp_cb_data, "Get current call list");
567
568         return ret;
569 }
570
571 /* Notification */
572
573 /*
574 * Operation -  call status notification from network.
575 * notification message format:
576 * +XCALLSTAT: <call_id><stat>
577 * where
578 * <call_id>
579 * indicates the call identification (GSM02.30 4.5.5.1)
580 * <stat>
581 * 0 active
582 * 1 hold
583 * 2 dialling (MO call)
584 * 3 alerting (MO call; ringing for the remote party)
585 * 4 ringing (MT call)
586 * 5 waiting (MT call)
587 * 6 disconnected
588 * 7 connected (indicates the completion of a call setup first time for MT and MO calls \96 this is reported in
589 addition to state active)
590 */
591 static gboolean on_notification_imc_call_status(CoreObject *co, const void *data,
592         void *user_data)
593 {
594         GSList *tokens = NULL;
595         GSList *lines = NULL;
596         const char *line = NULL;
597         char *state = NULL, *call_handle = NULL;
598         unsigned int status, call_id;
599
600         dbg("Entry");
601
602         lines = (GSList *) data;
603         if (lines == NULL) {
604                 err("Invalid response received");
605                 return TRUE;
606         }
607         line = (char *) (lines->data);
608         tokens = tcore_at_tok_new(line);
609
610         call_handle = g_slist_nth_data(tokens, 0);
611         if (NULL == call_handle) {
612                 err("call_id missing from %XCALLSTAT indiaction");
613                 goto OUT;
614         }
615         call_id = atoi(call_handle);
616         state = g_slist_nth_data(tokens, 1);
617         if (NULL == state) {
618                 err("State is missing from %XCALLSTAT indication");
619                 goto OUT;
620         }
621         status = atoi(state);
622         dbg("call_id[%d], status[%d]", call_id, status);
623
624         switch (status) {
625         case STATUS_INCOMING:
626         case STATUS_WAITING:
627                 __on_notification_imc_call_incoming(co, call_id, user_data);
628                 break;
629
630         case STATUS_CONNECTED:     /* ignore Connected state. */
631                 dbg("ignore connected state");
632                 break;
633
634         default:
635                 __on_notification_imc_call_status(co, call_id, status, user_data);
636                 break;
637         }
638 OUT:
639         // Free tokens
640         tcore_at_tok_free(tokens);
641         return TRUE;
642 }
643
644 /*
645  * Operation -  SS network initiated notification.
646  *
647  * notification message format:
648  * +CSSU: <code2>[<index> [,<number>,<type>]]
649  * <code2>
650  * (it is manufacturer specific, which of these codes are supported):
651  * 0 this is a forwarded call (MT call setup)
652  * 1 this is a CUG call (<index> present) (MT call setup)
653  * 2 call has been put on hold (during a voice call)
654  * 3 call has been retrieved (during a voice call)
655  * 4 multiparty call entered (during a voice call)
656  * 5 Call has been released - not a SS notification (during a voice call)
657  * 6 forward check SS message received (can be received whenever)
658  * 7 call is being connected (alerting) with the remote party in alerting state
659  *   in explicit call transfer operation
660  *   (during a voice call)
661  * 8 call has been connected with the other remote party in explicit call transfer
662  *   operation (during a voice call or MT call setup)
663  * 9 this is a deflected call (MT call setup)
664  * 10 additional incoming call forwarded
665  * <index>
666  * refer Closed user group +CCUG
667  * <number>
668  *  string type phone of format specified by <type>
669  * <type>
670  * type of address octet in integer format.
671  */
672 static gboolean on_notification_imc_call_ss_cssu_info(CoreObject *co, const void *event_data,
673         void *user_data)
674 {
675         GSList *tokens = NULL;
676         TcoreNotification command = TCORE_NOTIFICATION_UNKNOWN;
677         char *resp = NULL;
678         char *cmd = 0;
679         int index = 0;
680         int code2 = -1;
681         char number[TEL_CALL_CALLING_NUMBER_LEN_MAX + 1] = {'\0',};
682
683         dbg("entry");
684
685         if (1 != g_slist_length((GSList *) event_data)) {
686                 err("unsolicited msg but multiple line");
687                 return TRUE;
688         }
689
690         cmd = (char *) ((GSList *) event_data)->data;
691         dbg("ss notification message[%s]", cmd);
692
693         tokens = tcore_at_tok_new(cmd);
694
695         /* parse <code2> */
696         resp = g_slist_nth_data(tokens, 0);
697         if (NULL == resp) {
698                 err("Code2 is missing from %CSSU indiaction");
699                 tcore_at_tok_free(tokens);
700                 return TRUE;
701         }
702
703         code2 = atoi(resp);
704
705         /* parse [ <index>, <number> <type>] */
706         if ((resp = g_slist_nth_data(tokens, 1)))
707                 index = atoi(resp);
708
709         if ((resp = g_slist_nth_data(tokens, 2))) {
710                 /* Strike off double quotes */
711                 int len = strlen(resp) - 2;
712                 memcpy(number, resp + 1, len);
713                 number[len] = '\0';;
714         }
715
716         dbg("+CSSU: <code2> = %d <index> = %d <number> = %s ", code2, index, number);
717
718         /* <code2> - other values will be ignored */
719         switch (code2) {
720         case 0:
721                 command = TCORE_NOTIFICATION_CALL_INFO_MT_FORWARDED;
722                 break;
723         case 2:
724                 command = TCORE_NOTIFICATION_CALL_INFO_HELD;
725                 break;
726         case 3:
727                 command = TCORE_NOTIFICATION_CALL_INFO_ACTIVE;
728                 break;
729         case 4:
730                 command = TCORE_NOTIFICATION_CALL_INFO_JOINED;
731                 break;
732         case 7:
733         case 8:
734                 command = TCORE_NOTIFICATION_CALL_INFO_TRANSFERED;
735                 break;
736         case 9:
737                 command = TCORE_NOTIFICATION_CALL_INFO_MT_DEFLECTED;
738                 break;
739         default:
740                 dbg("Unsupported +CSSU notification : %d", code2);
741                 break;
742         }
743
744         if (command != TCORE_NOTIFICATION_UNKNOWN)
745                 tcore_object_send_notification(co, command, 0, NULL);
746         tcore_at_tok_free(tokens);
747
748         return TRUE;
749 }
750
751 /*
752 * Operation -  SS network initiated notification.
753 * notification message format:
754 * +CSSI : <code1>[,<index>]
755 * where
756 * <code1>
757 * 0 unconditional call forwarding is active
758 * 1 some of the conditional call forwarding are active
759 * 2 call has been forwarded
760 * 3 call is waiting
761 * 4 this is a CUG call (also <index> present)
762 * 5 outgoing calls are barred
763 * 6 incoming calls are barred
764 * 7 CLIR suppression rejected
765 * 8 call has been deflected
766
767 * <index>
768 * refer Closed user group +CCUG.
769 */
770 static gboolean on_notification_imc_call_ss_cssi_info(CoreObject *co, const void *event_data,
771         void *user_data)
772 {
773         GSList *tokens = NULL;
774         TcoreNotification command = TCORE_NOTIFICATION_UNKNOWN;
775         char *resp = NULL;
776         char *cmd = 0;
777         int index = 0;
778         int code1 = -1;
779
780         dbg("entry");
781
782         if (1 != g_slist_length((GSList *) event_data)) {
783                 err("unsolicited msg but multiple line");
784                 return TRUE;
785         }
786         cmd = (char *) ((GSList *) event_data)->data;
787         dbg("ss notification message[%s]", cmd);
788
789         tokens = tcore_at_tok_new(cmd);
790         /* parse <code1> */
791         resp = g_slist_nth_data(tokens, 0);
792         if (NULL == resp) {
793                 err("<code1> is missing from %CSSI indiaction");
794                 tcore_at_tok_free(tokens);
795                 return TRUE;
796         }
797
798         code1 = atoi(resp);
799
800         /* parse [ <index>] */
801         if ((resp = g_slist_nth_data(tokens, 1)))
802                 index = atoi(resp);
803
804         dbg("+CSSI: <code1> = %d <index> = %d ", code1, index);
805
806         /* <code1> - other values will be ignored */
807         switch (code1) {
808         case 0:
809                 command = TCORE_NOTIFICATION_CALL_INFO_MO_FORWARD_UNCONDITIONAL;
810                 break;
811         case 1:
812                 command = TCORE_NOTIFICATION_CALL_INFO_MO_FORWARD_CONDITIONAL;
813                 break;
814         case 2:
815                 command = TCORE_NOTIFICATION_CALL_INFO_MO_FORWARDED;
816                 break;
817         case 3:
818                 command = TCORE_NOTIFICATION_CALL_INFO_MO_WAITING;
819                 break;
820         case 5:
821                 command = TCORE_NOTIFICATION_CALL_INFO_MO_BARRED_OUTGOING;
822                 break;
823         case 6:
824                 command = TCORE_NOTIFICATION_CALL_INFO_MO_BARRED_INCOMING;
825                 break;
826         case 8:
827                 command  = TCORE_NOTIFICATION_CALL_INFO_MO_DEFLECTED;
828                 break;
829         default:
830                 dbg("Unsupported +CSSI notification : %d", code1);
831                 break;
832         }
833
834         if (command != TCORE_NOTIFICATION_UNKNOWN)
835                 tcore_object_send_notification(co, command, 0, NULL);
836         tcore_at_tok_free(tokens);
837
838         return TRUE;
839 }
840
841 static gboolean on_notification_imc_call_clip_info(CoreObject *co, const void *data,
842         void *user_data)
843 {
844         dbg("entry");
845         /* TODO - handle +CLIP notification*/
846         return TRUE;
847 }
848
849 /* Response */
850 static void on_response_imc_call_default(TcorePending *p,
851         guint data_len, const void *data, void *user_data)
852 {
853         const TcoreAtResponse *at_resp = data;
854         CoreObject *co = tcore_pending_ref_core_object(p);
855         ImcRespCbData *resp_cb_data = user_data;
856
857         TelCallResult result;
858         dbg("entry");
859
860         tcore_check_return_assert(co != NULL);
861         tcore_check_return_assert(resp_cb_data != NULL);
862
863         if (at_resp && at_resp->success) {
864                 result = TEL_CALL_RESULT_SUCCESS;
865         } else {
866                 err("ERROR[%s]",at_resp->final_response);
867                 result = TEL_CALL_RESULT_FAILURE;
868                 /*TODO - need to map CME error and final response error to TelCallResult */
869         }
870
871         dbg("%s: [%s]", IMC_GET_DATA_FROM_RESP_CB_DATA(resp_cb_data),
872                  (result == TEL_CALL_RESULT_SUCCESS ? "SUCCESS" : "FAIL"));
873
874         /* Invoke callback */
875         if (resp_cb_data->cb)
876                 resp_cb_data->cb(co, (gint)result, NULL, resp_cb_data->cb_data);
877
878         /* Free callback data */
879         imc_destroy_resp_cb_data(resp_cb_data);
880 }
881
882 static void on_response_imc_call_set_volume_info(TcorePending *p,
883         guint data_len, const void *data, void *user_data)
884 {
885         const TcoreAtResponse *at_resp = data;
886         CoreObject *co = tcore_pending_ref_core_object(p);
887         ImcRespCbData *resp_cb_data = user_data;
888         GSList *tokens = NULL;
889         GSList *line = NULL;
890         char *resp_str = NULL;
891         gboolean error;
892
893         TelCallResult result = TEL_CALL_RESULT_FAILURE;  // TODO: XDRV error mapping is required
894         dbg("Enter");
895
896         tcore_check_return_assert(co != NULL);
897         tcore_check_return_assert(resp_cb_data != NULL);
898
899         if (at_resp && at_resp->success) {
900                 line = at_resp->lines;
901                 tokens = tcore_at_tok_new(line->data);
902
903                 if (!g_slist_nth_data(tokens, 0)) {
904                         err("group_id is missing");
905                         goto OUT;
906                 }
907
908                 if (!g_slist_nth_data(tokens, 1)) {
909                         err(" function_id is missing");
910                         goto OUT;
911                 }
912
913                 resp_str = g_slist_nth_data(tokens, 2);
914                 if (!resp_str) {
915                         err("xdrv result missing");
916                         goto OUT;
917                 } else {
918                         struct imc_set_volume_info *volume_info;
919                         gchar *vol = "";
920                         gchar *at_cmd;
921                         TelReturn ret;
922
923                         error = atoi(resp_str);
924                         if (error) {
925                                 err("RESPONSE NOK");
926                                 goto OUT;
927                         }
928
929                         /* Fetch from resp_cb_data */
930                         volume_info = (struct imc_set_volume_info *)IMC_GET_DATA_FROM_RESP_CB_DATA(resp_cb_data);
931                         dbg("volume info index[%d]", volume_info->next_index);
932
933                         if (xdrv_set_volume[volume_info->next_index] == NULL) {
934                                 /*Processing of xdrv commands  completed */
935                                 dbg("RESPONSE OK");
936                                 result = TEL_CALL_RESULT_SUCCESS;
937                                 goto OUT;
938                         } else if (volume_info->next_index == 3) {
939                                 switch ((volume_info->volume) / 10) {
940                                 case 0 :
941                                         vol = "0";
942                                 break;
943                                 case 1 :
944                                         vol = "40";
945                                 break;
946                                 case 2 :
947                                         vol = "46";
948                                 break;
949                                 case 3 :
950                                         vol = "52";
951                                 break;
952                                 case 4 :
953                                         vol = "58";
954                                 break;
955                                 case 5 :
956                                         vol = "64";
957                                 break;
958                                 case 6 :
959                                         vol = "70";
960                                 break;
961                                 case 7 :
962                                         vol = "76";
963                                 break;
964                                 case 8 :
965                                         vol = "82";
966                                 break;
967                                 case 9 :
968                                 default :
969                                         vol = "88";
970                                 }
971                         }
972
973                         at_cmd = g_strdup_printf("%s%s",
974                                         xdrv_set_volume[volume_info->next_index], vol);
975
976                         /* Increament index to point to next command */
977                         volume_info->next_index += 1;
978
979                         /* Send Request to modem */
980                         ret = tcore_at_prepare_and_send_request(co,
981                                         at_cmd, "+XDRV",
982                                         TCORE_AT_COMMAND_TYPE_SINGLELINE,
983                                         NULL,
984                                         on_response_imc_call_set_volume_info, resp_cb_data,
985                                         on_send_imc_request, NULL);
986                         IMC_CHECK_REQUEST_RET(ret, resp_cb_data, "imc_call_set_volume_info");
987                         g_free(at_cmd);
988
989                         return;
990                 }
991         }
992
993 OUT :
994         dbg("Set Volume Info: [%s]",
995                         (result == TEL_CALL_RESULT_SUCCESS ? "SUCCESS" : "FAIL"));
996
997         /* Invoke callback */
998         if (resp_cb_data->cb)
999                 resp_cb_data->cb(co, (gint)result, NULL, resp_cb_data->cb_data);
1000
1001         imc_destroy_resp_cb_data(resp_cb_data);
1002         tcore_at_tok_free(tokens);
1003 }
1004
1005 static void on_response_imc_call_set_sound_path(TcorePending *p,
1006         guint data_len, const void *data, void *user_data)
1007 {
1008         const TcoreAtResponse *at_resp = data;
1009         CoreObject *co = tcore_pending_ref_core_object(p);
1010         ImcRespCbData *resp_cb_data = user_data;
1011         GSList *tokens = NULL;
1012         GSList *line = NULL;
1013         char *resp_str = NULL;
1014         gboolean error;
1015         gint xdrv_func_id = -1;
1016
1017         TelCallResult result = TEL_CALL_RESULT_FAILURE;  // TODO: XDRV error mapping is required
1018         dbg("Enter");
1019
1020         tcore_check_return_assert(co != NULL);
1021         tcore_check_return_assert(resp_cb_data != NULL);
1022
1023         if (at_resp && at_resp->success) {
1024                 line = at_resp->lines;
1025                 tokens = tcore_at_tok_new(line->data);
1026                 if (!g_slist_nth_data(tokens, 0)) {
1027                         err("group_id is missing");
1028                         goto OUT;
1029                 }
1030
1031                 if (!(resp_str = g_slist_nth_data(tokens, 1))) {
1032                         err(" function_id is missing");
1033                         goto OUT;
1034                 }
1035
1036                 xdrv_func_id = atoi(resp_str);
1037
1038                 resp_str = g_slist_nth_data(tokens, 2);
1039                 if (resp_str) {
1040                         error = atoi(resp_str);
1041                         if (error) {
1042                                 err("RESPONSE NOK");
1043                                 goto OUT;
1044                         } else {
1045                                 if (xdrv_func_id == 4) {
1046                                         /* Send next command to configure destination device type */
1047                                         gchar *at_cmd;
1048                                         TelReturn ret;
1049                                         gint *device_type = IMC_GET_DATA_FROM_RESP_CB_DATA(resp_cb_data);
1050
1051                                         at_cmd = g_strdup_printf("AT+XDRV=40,5,2,0,0,0,0,0,1,0,1,0,%d",
1052                                                                 *device_type);
1053
1054                                         ret = tcore_at_prepare_and_send_request(co,
1055                                                         at_cmd, "+XDRV",
1056                                                         TCORE_AT_COMMAND_TYPE_SINGLELINE,
1057                                                         NULL,
1058                                                         on_response_imc_call_set_sound_path, resp_cb_data,
1059                                                         on_send_imc_request, NULL);
1060                                         IMC_CHECK_REQUEST_RET(ret, resp_cb_data, "imc_call_set_sound_path");
1061                                         g_free(at_cmd);
1062
1063                                         return;
1064                                 }
1065                                 dbg("RESPONSE OK");
1066                                 result = TEL_CALL_RESULT_SUCCESS;
1067                         }
1068                 }
1069         }
1070
1071 OUT :
1072         dbg("Set Sound Path: [%s]",
1073                         (result == TEL_CALL_RESULT_SUCCESS ? "SUCCESS" : "FAIL"));
1074
1075         tcore_at_tok_free(tokens);
1076
1077         /* Invoke callback */
1078         if (resp_cb_data->cb)
1079                 resp_cb_data->cb(co, (gint)result, NULL, resp_cb_data->cb_data);
1080
1081         imc_destroy_resp_cb_data(resp_cb_data);
1082 }
1083
1084 static void on_response_set_sound_path(TcorePending *p, guint data_len,
1085                                         const void *data, void *user_data)
1086 {
1087         const TcoreAtResponse *at_resp = data;
1088         ImcRespCbData *resp_cb_data = user_data;
1089         TelCallResult result = TEL_CALL_RESULT_FAILURE;
1090         CoreObject *co_call = tcore_pending_ref_core_object(p);
1091
1092         if (at_resp && at_resp->success)
1093                         result = TEL_CALL_RESULT_SUCCESS;
1094
1095         if(resp_cb_data->cb)
1096                 resp_cb_data->cb(co_call, (gint)result, NULL, resp_cb_data->cb_data);
1097
1098         imc_destroy_resp_cb_data(resp_cb_data);
1099 }
1100
1101 static void on_response_imc_call_set_mute(TcorePending *p, guint data_len,
1102         const void *data, void *user_data)
1103 {
1104         const TcoreAtResponse *at_resp = data;
1105         CoreObject *co = tcore_pending_ref_core_object(p);
1106         ImcRespCbData *resp_cb_data = user_data;
1107         GSList *tokens = NULL;
1108         const char *line = NULL;
1109         char *resp_str = NULL;
1110         gboolean error;
1111
1112         TelCallResult result = TEL_CALL_RESULT_FAILURE;  // TODO: XDRV error mapping is required
1113         dbg("Enter");
1114
1115         tcore_check_return_assert(co != NULL);
1116         tcore_check_return_assert(resp_cb_data != NULL);
1117
1118         if (at_resp && at_resp->success) {
1119                 result = TEL_CALL_RESULT_SUCCESS;
1120                 line = (((GSList *)at_resp->lines)->data);
1121                 tokens = tcore_at_tok_new(line);
1122
1123                 resp_str = g_slist_nth_data(tokens, 0);
1124                 if (!g_slist_nth_data(tokens, 0)) {
1125                         err("group_id is missing");
1126                         result = TEL_CALL_RESULT_FAILURE;
1127                         goto OUT;
1128                 }
1129
1130                 if (!g_slist_nth_data(tokens, 1)) {
1131                         err(" function_id is missing");
1132                         result = TEL_CALL_RESULT_FAILURE;
1133                         goto OUT;
1134                 }
1135
1136                 resp_str = g_slist_nth_data(tokens, 2);
1137                 if (resp_str) {
1138                         error = atoi(resp_str);
1139                         if (error) {
1140                                 result = TEL_CALL_RESULT_FAILURE;
1141                                 goto OUT;
1142                         } else {
1143                                 result = TEL_CALL_RESULT_SUCCESS;
1144                         }
1145                 }
1146         }
1147
1148 OUT :
1149         dbg("Set Mute: [%s]",
1150                         (result == TEL_CALL_RESULT_SUCCESS ? "SUCCESS" : "FAIL"));
1151         tcore_at_tok_free(tokens);
1152
1153         /* Invoke callback */
1154         if (resp_cb_data->cb)
1155                 resp_cb_data->cb(co, (gint)result, NULL, resp_cb_data->cb_data);
1156
1157         imc_destroy_resp_cb_data(resp_cb_data);
1158 }
1159
1160
1161  /* Request */
1162  /*
1163  * Operation - dial
1164  *
1165  * Request -
1166  * AT-Command: ATD <num> [I] [G] [;]
1167  * <num> - dialed number
1168  * [I][i] - CLI presentation(supression or invocation)
1169  * [G] - control the CUG supplementary service information for this call.
1170  *
1171  * Response -
1172  * Success:
1173  * OK or CONNECT
1174  * Failure:
1175  * "ERROR"
1176  * "NO ANSWER"
1177  * "NO CARRIER"
1178  * "BUSY"
1179  * "NO DIALTONE"
1180  * +CME ERROR: <error>
1181  */
1182 static TelReturn imc_call_dial(CoreObject *co, const TelCallDial *dial_info,
1183                 TcoreObjectResponseCallback cb, void *cb_data)
1184 {
1185         gchar *at_cmd;
1186         const gchar *clir;
1187         gchar *num;
1188
1189         dbg("entry");
1190
1191         if (dial_info->call_type == TEL_CALL_TYPE_VIDEO) {
1192                 err("Video call is not supported in imc modem");
1193                  return TEL_RETURN_OPERATION_NOT_SUPPORTED;
1194         }
1195
1196         if (!strncmp(dial_info->number, "*31#", 4)) {
1197                 dbg("clir suppression");
1198                 clir = "i";
1199                 num = (gchar *)&(dial_info->number[4]);
1200         } else if (!strncmp(dial_info->number, "#31#", 4)) {
1201                 dbg("clir invocation");
1202                 clir = "I";
1203                 num = (gchar *)&(dial_info->number[4]);
1204         } else {
1205                 int cli = 0;
1206
1207                 dbg("no clir string in number");
1208
1209                 /* it will be removed when setting application use tapi_ss_set_cli()
1210                  * instead of his own vconfkey. (0 : By network, 1 : Show, 2 : Hide)
1211                  */
1212                 vconf_get_int("db/ciss/show_my_number", &cli);
1213                 if(cli == 2){
1214                         dbg("clir invocation from setting application");
1215                         clir = "I";
1216                 } else {
1217                         dbg("set clir state to default");
1218                         clir = "";
1219                 }
1220                 num = (gchar *)dial_info->number;
1221         }
1222
1223         /* AT-Command */
1224         at_cmd = g_strdup_printf("ATD%s%s;", num, clir);
1225         dbg(" at command : %s", at_cmd);
1226
1227         return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_dial");
1228 }
1229
1230 /*
1231  * Operation - Answer/Reject/Replace/hold(current call) & accept incoming call.
1232  *
1233  * Request -
1234  *
1235  * 1. AT-Command: ATA
1236  * Response -
1237  * Success:
1238  * OK
1239  * Failure:
1240  * +CME ERROR: <error>
1241  *
1242  * 2. AT-Command: AT+CHLD=[<n>]
1243  * <n>
1244  * 0 - (deafult)release all held calls or set User Determined User Busy for a waiting/incoming
1245  * call; if both exists then only the waiting call will be rejected.
1246  * 1 -  release all active calls and accepts the other (held or waiting)
1247  * Note: In the scenario: An active call, a waiting call and held call, when the active call is
1248  * terminated, we will make the Waiting call as active.
1249  * 2 -  place all active calls (if exist) on hold and accepts the other call (held or waiting/in-coming).
1250  * If only one call exists which is active, place it on hold and if only held call exists make it active call.
1251  * Response -
1252  * Success:
1253  * OK
1254  * Failure:
1255  * +CME ERROR: <error>
1256  * For more informatiion refer 3GPP TS 27.007.
1257  */
1258 static TelReturn imc_call_answer(CoreObject *co, TelCallAnswerType ans_type,
1259         TcoreObjectResponseCallback cb, void *cb_data)
1260 {
1261         gchar *at_cmd;
1262         dbg("entry");
1263
1264         if (ans_type == TEL_CALL_ANSWER_ACCEPT) {
1265                 /* AT-Command */
1266                 at_cmd = g_strdup_printf("%s", "ATA");
1267         }else if (ans_type == TEL_CALL_ANSWER_REJECT) {
1268                 /* AT-Command */
1269                 at_cmd = g_strdup_printf("%s", "AT+CHLD=0");
1270         } else if (ans_type == TEL_CALL_ANSWER_REPLACE) {
1271                 /* AT-Command */
1272                 at_cmd = g_strdup_printf("%s", "AT+CHLD=1");
1273         } else if (ans_type == TEL_CALL_ANSWER_HOLD_AND_ACCEPT) {
1274                 /* AT-Command */
1275                 at_cmd = g_strdup_printf("%s", "AT+CHLD=2");
1276         }else {
1277                 err("Unsupported call answer type");
1278                 return TEL_RETURN_FAILURE;
1279         }
1280
1281         dbg("at command : %s", at_cmd);
1282
1283         return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_answer");
1284 }
1285
1286 /*
1287  * Operation - release all calls/release specific call/release all active call/release all held calls.
1288  *
1289  * Request -
1290  * 1. AT-Command: AT+CHLD=[<n>]
1291  * <n>
1292  * 0  - (defualt)release all held calls or set User Determined User Busy for a waiting/incoming.
1293  * call; if both exists then only the waiting call will be rejected.
1294  * 1  - release all active calls and accepts the other (held or waiting).
1295  * 1x - release a specific call (x specific call number as indicated by call id).
1296  * 8  - release all calls.
1297  * Response -
1298  * Success:
1299  * OK
1300  * Failure:
1301  * +CME ERROR: <error>
1302  */
1303 static TelReturn imc_call_end(CoreObject *co, const TelCallEnd *end_info,
1304         TcoreObjectResponseCallback cb, void *cb_data)
1305 {
1306         gchar *at_cmd;
1307         dbg("entry");
1308
1309         if (end_info->end_type == TEL_CALL_END_ALL) {
1310                 /* AT-Command */
1311                 at_cmd = g_strdup_printf("%s", "AT+CHLD=8");
1312         }else if (end_info->end_type == TEL_CALL_END) {
1313                 /* AT-Command */
1314                 at_cmd = g_strdup_printf("%s%d", "AT+CHLD=1",end_info->call_id);
1315         } else if (end_info->end_type == TEL_CALL_END_ACTIVE_ALL) {
1316                 /* AT-Command */
1317                 at_cmd = g_strdup_printf("%s", "AT+CHLD=1");
1318         } else if (end_info->end_type == TEL_CALL_END_HOLD_ALL) {
1319                 /* AT-Command */
1320                 at_cmd = g_strdup_printf("%s", "AT+CHLD=0");
1321         }else {
1322                 err("Unsupported call end type");
1323                 return TEL_RETURN_FAILURE;
1324         }
1325
1326         dbg("at command : %s", at_cmd);
1327
1328         return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_end");
1329 }
1330
1331 /*
1332  * Operation - send dtmf.
1333  *
1334  * Request -
1335  * 1. AT-Command: AT+VTS=<DTMF>,{<DTMF>,<duration>}.
1336  * where
1337  * <DTMF>:
1338  * is a single ASCII character in the set 0-9, #, *, A-D. Even it will support string DTMF.
1339  * <duration>:
1340  * integer in range 0-255, meaning 1/10(10 millisec) seconds multiples. The string parameter
1341  * of the command consists of combinations of the following separated by commas:
1342  * NOTE : There is a limit of 50 dtmf tones can be requested through a single VTS command.
1343  * Response -
1344  * Success:
1345  * OK
1346  * Failure:
1347  * +CME ERROR: <error>
1348  */
1349 static TelReturn imc_call_send_dtmf(CoreObject *co, const char *dtmf_str,
1350         TcoreObjectResponseCallback cb,  void *cb_data)
1351 {
1352         gchar *at_cmd;
1353         char *tmp_dtmf = NULL, *dtmf;
1354         unsigned int count;
1355
1356         dbg("entry");
1357
1358         //(void) _set_dtmf_tone_duration(o, dup);
1359         tmp_dtmf = tcore_malloc0((strlen(dtmf_str) * 2) + 1); // DTMF digits + comma for each dtmf digit.
1360         tcore_check_return_value_assert(tmp_dtmf != NULL, TEL_RETURN_FAILURE);
1361         /* Save initial pointer */
1362         dtmf = tmp_dtmf;
1363
1364         for (count = 0; count < strlen(dtmf_str); count++) {
1365                 *tmp_dtmf = dtmf_str[count];
1366                 tmp_dtmf++;
1367
1368                 *tmp_dtmf = COMMA;
1369                 tmp_dtmf++;
1370         }
1371
1372         // last digit is having COMMA , overwrite it with '\0' .
1373         *(--tmp_dtmf) = '\0';
1374
1375         // AT+VTS = <d1>,<d2>,<d3>,<d4>,<d5>,<d6>, ..... <d32>
1376         at_cmd = g_strdup_printf("AT+VTS=%s", dtmf);
1377         dbg("at command : %s", at_cmd);
1378
1379         tcore_free(dtmf);
1380
1381         return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_send_dtmf");
1382 }
1383
1384 /*
1385  * Operation - call hold.
1386  *
1387  * Request -
1388  * 1. AT-Command: AT+CHLD=[<n>]
1389  * Where
1390  * <n>
1391  * 2 - place all active calls (if exist) on hold and accepts the other call (held or waiting/incoming).
1392  * If only one call exists which is active, place it on hold and if only held call exists
1393  * make it active call
1394  * Response -
1395  * Success:
1396  * OK
1397  * Failure:
1398  * +CME ERROR: <error>
1399  */
1400 static TelReturn imc_call_hold(CoreObject *co, TcoreObjectResponseCallback cb,
1401         void *cb_data)
1402
1403 {
1404         gchar *at_cmd;
1405         dbg("entry");
1406
1407         at_cmd = g_strdup_printf("%s", "AT+CHLD=2");
1408         dbg("at command : %s", at_cmd);
1409
1410         return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_hold");
1411 }
1412
1413 /*
1414  * Operation - call active.
1415  *
1416  * Request -
1417  * 1. AT-Command: AT+CHLD=[<n>]
1418  * Where
1419  * <n>
1420  * 2 - place all active calls (if exist) on hold and accepts the other call (held or waiting/incoming).
1421  * If only one call exists which is active, place it on hold and if only held call exists
1422  * make it active call
1423  * Response -
1424  * Success:
1425  * OK
1426  * Failure:
1427  * +CME ERROR: <error>
1428  */
1429 static TelReturn imc_call_active(CoreObject *co, TcoreObjectResponseCallback cb,
1430         void *cb_data)
1431 {
1432         gchar *at_cmd;
1433         dbg("entry");
1434
1435         at_cmd = g_strdup_printf("%s", "AT+CHLD=2");
1436         dbg("at command : %s", at_cmd);
1437
1438         return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_active");
1439 }
1440
1441 /*
1442  * Operation - call swap.
1443  *
1444  * Request -
1445  * 1. AT-Command: AT+CHLD=[<n>]
1446  * Where
1447  * <n>
1448  * 2 - place all active calls (if exist) on hold and accepts the other call (held or waiting/incoming).
1449  * If only one call exists which is active, place it on hold and if only held call exists
1450  * make it active call
1451  * Response -
1452  * Success:
1453  * OK
1454  * Failure:
1455  * +CME ERROR: <error>
1456  */
1457 static TelReturn imc_call_swap(CoreObject *co, TcoreObjectResponseCallback cb,
1458         void *cb_data)
1459 {
1460         gchar *at_cmd;
1461         dbg("entry");
1462
1463         at_cmd = g_strdup_printf("%s", "AT+CHLD=2");
1464         dbg("at command : %s", at_cmd);
1465
1466         return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_swap");
1467 }
1468
1469 /*
1470  * Operation - call join.
1471  *
1472  * Request -
1473  * 1. AT-Command: AT+CHLD=[<n>]
1474  * Where
1475  * <n>
1476  * 3 - adds a held call to the conversation
1477  * Response -
1478  * Success:
1479  * OK
1480  * Failure:
1481  * +CME ERROR: <error>
1482  */
1483 static TelReturn imc_call_join(CoreObject *co, TcoreObjectResponseCallback cb,
1484         void *cb_data)
1485 {
1486         gchar *at_cmd;
1487         dbg("entry");
1488
1489         at_cmd = g_strdup_printf("%s", "AT+CHLD=3");
1490         dbg("at command : %s", at_cmd);
1491
1492         return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_join");
1493 }
1494
1495 /*
1496  * Operation - call split.
1497  *
1498  * Request -
1499  * 1. AT-Command: AT+CHLD=[<n>]
1500  * Where
1501  * <n>
1502  * 2x - place all active calls on hold except call x with which communication is supported
1503  * Response -
1504  * Success:
1505  * OK
1506  * Failure:
1507  * +CME ERROR: <error>
1508  */
1509 static TelReturn imc_call_split(CoreObject *co, unsigned int call_id,
1510         TcoreObjectResponseCallback cb, void *cb_data)
1511 {
1512         gchar *at_cmd;
1513         dbg("entry");
1514
1515         at_cmd = g_strdup_printf("%s%d", "AT+CHLD=2", call_id);
1516         dbg("at command : %s", at_cmd);
1517
1518         return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_split");
1519 }
1520
1521 /*
1522  * Operation - call transfer.
1523  *
1524  * Request -
1525  * 1. AT-Command: AT+CHLD=[<n>]
1526  * Where
1527  * <n>
1528  * 4 connects the two calls and disconnects the subscriber from both calls (Explicit Call Transfer)
1529  * Response -
1530  * Success:
1531  * OK
1532  * Failure:
1533  * +CME ERROR: <error>
1534  */
1535 static TelReturn imc_call_transfer(CoreObject *co, TcoreObjectResponseCallback cb,
1536         void *cb_data)
1537 {
1538         gchar *at_cmd;
1539         dbg("entry");
1540
1541         at_cmd = g_strdup_printf("%s", "AT+CHLD=4");
1542         dbg("at command : %s", at_cmd);
1543
1544         return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_transfer");
1545 }
1546
1547 /*
1548  * Operation - call transfer.
1549  *
1550  * Request -
1551  * 1. AT-Command: AT+CTFR= <number>[,<type>]
1552  * Where
1553  * number>
1554  * string type phone number
1555  * <type>
1556  * type of address octet in integer format. It is optional parameter.
1557  *
1558  * Response -
1559  * Success:
1560  * OK
1561  * Failure:
1562  * +CME ERROR: <error>
1563  */
1564 static TelReturn imc_call_deflect(CoreObject *co, const char *deflect_to,
1565         TcoreObjectResponseCallback cb, void *cb_data)
1566 {
1567         gchar *at_cmd;
1568         dbg("entry");
1569
1570         at_cmd = g_strdup_printf("AT+CTFR=%s", deflect_to);
1571         dbg("at command : %s", at_cmd);
1572
1573         return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_deflect");
1574 }
1575
1576 static TelReturn imc_call_set_active_line(CoreObject *co, TelCallActiveLine active_line,
1577         TcoreObjectResponseCallback cb, void *cb_data)
1578 {
1579         dbg("entry");
1580
1581         dbg("exit");
1582         return TEL_RETURN_OPERATION_NOT_SUPPORTED;
1583 }
1584
1585 static TelReturn imc_call_get_active_line(CoreObject *co, TcoreObjectResponseCallback cb,
1586         void *cb_data)
1587 {
1588         dbg("entry");
1589
1590         dbg("exit");
1591         return TEL_RETURN_OPERATION_NOT_SUPPORTED;
1592 }
1593
1594 /*
1595  * Operation - Set voule info.
1596  *
1597  * Request -
1598  * AT-Command: AT+XDRV=<group_id>,<function_id>[,<param_n>]
1599  * The first command parameter defines the involved driver group.
1600  * The second command parameter defines a certain function in the selected driver group.
1601  * Other parameters are dependent on the first two parameters.
1602  * Nearly all parameters are integer values, also if they are represented by literals.
1603  * Only very few are strings or
1604  * hex data strings.
1605  *
1606  * Response -
1607  * +XDRV: <group_id>,<function_id>,<xdrv_result>[,<response_n>]
1608  * The first response parameter defines the involved driver group.
1609  * The second response parameter defines the current function in the selected driver group.
1610  * The third response parameter defines the xdrv_result of the operation.
1611  * Additional response parameters dependent on the first two parameters.
1612  */
1613 static TelReturn imc_call_set_volume_info(CoreObject *co, const TelCallVolumeInfo *volume_info,
1614         TcoreObjectResponseCallback cb, void *cb_data)
1615 {
1616         ImcRespCbData *resp_cb_data = NULL;
1617         gchar *at_cmd;
1618         TelReturn ret;
1619         struct imc_set_volume_info cb_volume_info;
1620
1621         dbg("entry");
1622
1623         cb_volume_info.next_index = 1;
1624         cb_volume_info.volume = volume_info->volume;
1625
1626         /* Response callback data */
1627         resp_cb_data = imc_create_resp_cb_data(cb, cb_data,
1628                                 &cb_volume_info, sizeof(struct imc_set_volume_info));
1629
1630         at_cmd = g_strdup_printf("%s", xdrv_set_volume[0]);
1631
1632         /* Send Request to modem */
1633         ret = tcore_at_prepare_and_send_request(co,
1634                         at_cmd, "+XDRV",
1635                         TCORE_AT_COMMAND_TYPE_SINGLELINE,
1636                         NULL,
1637                         on_response_imc_call_set_volume_info, resp_cb_data,
1638                         on_send_imc_request, NULL);
1639         IMC_CHECK_REQUEST_RET(ret, resp_cb_data, "imc_call_set_volume_info");
1640
1641         g_free(at_cmd);
1642         return ret;
1643 }
1644
1645
1646 static TelReturn imc_call_get_volume_info(CoreObject *co, TelCallSoundDevice sound_device,
1647         TcoreObjectResponseCallback cb, void *cb_data)
1648 {
1649         dbg("Entry");
1650
1651         dbg("Exit");
1652         return TEL_RETURN_OPERATION_NOT_SUPPORTED;
1653 }
1654
1655 /*
1656  * Operation - Set sound path.
1657  *
1658  * Request -
1659  * AT-Command: AT+XDRV=<group_id>,<function_id>[,<param_n>]
1660  * The first command parameter defines the involved driver group.
1661  * The second command parameter defines a certain function in the selected driver group.
1662  * Other parameters are dependent on the first two parameters.
1663  * Nearly all parameters are integer values, also if they are represented by literals.
1664  * Only very few are strings or
1665  * hex data strings.
1666  *
1667  * Response -
1668  * +XDRV: <group_id>,<function_id>,<xdrv_result>[,<response_n>]
1669  * The first response parameter defines the involved driver group.
1670  * The second response parameter defines the current function in the selected driver group.
1671  * The third response parameter defines the xdrv_result of the operation.
1672  * Additional response parameters dependent on the first two parameters.
1673  */
1674
1675 static TelReturn imc_call_set_sound_path(CoreObject *co, const TelCallSoundPathInfo *sound_path_info,
1676         TcoreObjectResponseCallback cb, void *cb_data)
1677 {
1678         ImcRespCbData *resp_cb_data = NULL;
1679         TelReturn ret;
1680         gchar *at_cmd;
1681         gint device_type = -1;
1682         TcorePlugin *plugin = tcore_object_ref_plugin(co);
1683         const char *cp_name = tcore_server_get_cp_name_by_plugin(plugin);
1684
1685         dbg("audio device type - 0x%x", sound_path_info->path);
1686
1687         switch (sound_path_info->path) {
1688                 case TEL_SOUND_PATH_HANDSET:
1689                         device_type = 1;
1690                         break;
1691                 case TEL_SOUND_PATH_HEADSET:
1692                         device_type = 2;
1693                         break;
1694                 case TEL_SOUND_PATH_HEADSET_3_5PI:
1695                         device_type = 3;
1696                         break;
1697                 case TEL_SOUND_PATH_SPK_PHONE:
1698                         device_type = 4;
1699                         break;
1700                 case TEL_SOUND_PATH_HANDSFREE:
1701                         device_type = 5;
1702                         break;
1703                 case TEL_SOUND_PATH_HEADSET_HAC:
1704                         device_type = 6;
1705                         break;
1706                 case TEL_SOUND_PATH_BLUETOOTH:
1707                 case TEL_SOUND_PATH_STEREO_BLUETOOTH:
1708                         device_type = 7;
1709                         break;
1710                 case TEL_SOUND_PATH_BT_NSEC_OFF:
1711                 case TEL_SOUND_PATH_MIC1:
1712                 case TEL_SOUND_PATH_MIC2:
1713                 default:
1714                         dbg("unsupported device type");
1715                         return TEL_RETURN_INVALID_PARAMETER;
1716         }
1717
1718         if (g_str_has_prefix(cp_name, "imcmodem")) {
1719                 /* Response callback data */
1720                 resp_cb_data = imc_create_resp_cb_data(cb, cb_data, &device_type, sizeof(gint));
1721
1722                 at_cmd = g_strdup_printf("AT+XDRV=40,4,3,0,0,0,0,0,1,0,1,0,%d", device_type);
1723
1724                 ret = tcore_at_prepare_and_send_request(co,
1725                         at_cmd, "+XDRV",
1726                         TCORE_AT_COMMAND_TYPE_SINGLELINE,
1727                         NULL,
1728                         on_response_imc_call_set_sound_path, resp_cb_data,
1729                         on_send_imc_request, NULL);
1730                 IMC_CHECK_REQUEST_RET(ret, NULL, "imc_call_set_sound_path");
1731                 g_free(at_cmd);
1732         } else {
1733                 /* Response callback data */
1734                 resp_cb_data = imc_create_resp_cb_data(cb, cb_data, NULL, 0);
1735
1736                 /* Configure modem I2S1 to 8khz, mono, PCM if routing to bluetooth */
1737                 if (sound_path_info->path == TEL_SOUND_PATH_BLUETOOTH ||
1738                                 sound_path_info->path == TEL_SOUND_PATH_STEREO_BLUETOOTH) {
1739                         tcore_at_prepare_and_send_request(co,
1740                                         "AT+XDRV=40,4,3,0,1,0,0,0,0,0,0,0,21",
1741                                         NULL, TCORE_AT_COMMAND_TYPE_NO_RESULT,
1742                                         NULL, NULL, NULL, NULL, NULL);
1743
1744                         tcore_at_prepare_and_send_request(co,
1745                                         "AT+XDRV=40,5,2,0,1,0,0,0,0,0,0,0,22",
1746                                         NULL, TCORE_AT_COMMAND_TYPE_NO_RESULT,
1747                                         NULL, NULL, NULL, NULL, NULL);
1748                 } else {
1749                         tcore_at_prepare_and_send_request(co,
1750                                         "AT+XDRV=40,4,3,0,1,0,8,0,1,0,2,0,21",
1751                                         NULL, TCORE_AT_COMMAND_TYPE_NO_RESULT,
1752                                         NULL, NULL, NULL, NULL, NULL);
1753
1754                         tcore_at_prepare_and_send_request(co,
1755                                         "AT+XDRV=40,5,2,0,1,0,8,0,1,0,2,0,22",
1756                                         NULL, TCORE_AT_COMMAND_TYPE_NO_RESULT,
1757                                         NULL, NULL, NULL, NULL, NULL);
1758                 }
1759
1760                 /* Configure modem I2S2 and do the modem routing */
1761                 tcore_at_prepare_and_send_request(co,
1762                                 "AT+XDRV=40,4,4,0,0,0,8,0,1,0,2,0,21",
1763                                 NULL, TCORE_AT_COMMAND_TYPE_NO_RESULT,
1764                                 NULL, NULL, NULL, NULL, NULL);
1765
1766                 tcore_at_prepare_and_send_request(co,
1767                                 "AT+XDRV=40,5,3,0,0,0,8,0,1,0,2,0,22",
1768                                 NULL, TCORE_AT_COMMAND_TYPE_NO_RESULT,
1769                                 NULL, NULL, NULL, NULL, NULL);
1770
1771                 tcore_at_prepare_and_send_request(co, "AT+XDRV=40,6,0,4",
1772                                 NULL, TCORE_AT_COMMAND_TYPE_NO_RESULT,
1773                                 NULL, NULL, NULL, NULL, NULL);
1774
1775                 tcore_at_prepare_and_send_request(co, "AT+XDRV=40,6,3,0",
1776                                 NULL, TCORE_AT_COMMAND_TYPE_NO_RESULT,
1777                                 NULL, NULL, NULL, NULL, NULL);
1778
1779                 tcore_at_prepare_and_send_request(co, "AT+XDRV=40,6,4,2",
1780                                 NULL, TCORE_AT_COMMAND_TYPE_NO_RESULT,
1781                                 NULL, NULL, NULL, NULL, NULL);
1782
1783                 tcore_at_prepare_and_send_request(co, "AT+XDRV=40,6,5,2",
1784                                 NULL, TCORE_AT_COMMAND_TYPE_NO_RESULT,
1785                                 NULL, NULL, NULL, NULL, NULL);
1786
1787                 /* amc enable */
1788                 tcore_at_prepare_and_send_request(co, "AT+XDRV=40,2,4",
1789                                 NULL, TCORE_AT_COMMAND_TYPE_NO_RESULT,
1790                                 NULL, NULL, NULL, NULL, NULL);
1791
1792                 tcore_at_prepare_and_send_request(co, "AT+XDRV=40,2,3",
1793                                 NULL, TCORE_AT_COMMAND_TYPE_NO_RESULT,
1794                                 NULL, NULL, NULL, NULL, NULL);
1795
1796                 /* amc route: AMC_RADIO_RX => AMC_I2S1_TX */
1797                 ret = tcore_at_prepare_and_send_request(co, "AT+XDRV=40,6,0,2",
1798                                 "+XDRV", TCORE_AT_COMMAND_TYPE_SINGLELINE, NULL,
1799                                 on_response_set_sound_path, resp_cb_data, NULL, NULL);
1800
1801                 IMC_CHECK_REQUEST_RET(ret, NULL, "imc_call_set_sound_path");
1802         }
1803
1804         return ret;
1805 }
1806
1807 /*
1808  * Operation - Set/Unset mute status.
1809  *
1810  * Request -
1811  * AT-Command: AT+XDRV=<group_id>,<function_id>[,<param_n>]
1812  * The first command parameter defines the involved driver group.
1813  * The second command parameter defines a certain function in the selected driver group.
1814  * Other parameters are dependent on the first two parameters.
1815  * Nearly all parameters are integer values, also if they are represented by literals.
1816  * Only very few are strings or
1817  * hex data strings.
1818  *
1819  * Response -
1820  * +XDRV: <group_id>,<function_id>,<xdrv_result>[,<response_n>]
1821  * The first response parameter defines the involved driver group.
1822  * The second response parameter defines the current function in the selected driver group.
1823  * The third response parameter defines the xdrv_result of the operation.
1824  * Additional response parameters dependent on the first two parameters.
1825  */
1826 static TelReturn imc_call_set_mute(CoreObject *co, gboolean mute, TcoreObjectResponseCallback cb,
1827         void *cb_data)
1828 {
1829         ImcRespCbData *resp_cb_data = NULL;
1830         gchar *at_cmd;
1831         TelReturn ret;
1832
1833         dbg("entry");
1834
1835         /* Response callback data */
1836         resp_cb_data = imc_create_resp_cb_data(cb, cb_data, NULL, 0);
1837
1838         /* AT - Command */
1839         if (mute)
1840                 at_cmd = g_strdup_printf("%s", "AT+XDRV=40,8,0,0,0");  /*MUTE*/
1841         else
1842                 at_cmd = g_strdup_printf("%s", "AT+XDRV=40,8,0,0,88"); /*UNMUTE*/
1843
1844         /* Send Request to modem */
1845         ret = tcore_at_prepare_and_send_request(co,
1846                         at_cmd, "+XDRV",
1847                         TCORE_AT_COMMAND_TYPE_SINGLELINE,
1848                         NULL,
1849                         on_response_imc_call_set_mute, resp_cb_data,
1850                         on_send_imc_request, NULL);
1851         IMC_CHECK_REQUEST_RET(ret, resp_cb_data, "imc_call_set_mute");
1852
1853         g_free(at_cmd);
1854
1855         return ret;
1856 }
1857
1858 static TelReturn imc_call_get_mute_status(CoreObject *co, TcoreObjectResponseCallback cb,
1859         void *cb_data)
1860 {
1861         dbg("entry");
1862
1863         dbg("exit");
1864         return TEL_RETURN_OPERATION_NOT_SUPPORTED;
1865 }
1866
1867
1868 static TelReturn imc_call_set_sound_recording(CoreObject *co, TelCallSoundRecording sound_rec,
1869         TcoreObjectResponseCallback cb, void *cb_data)
1870 {
1871         dbg("entry");
1872
1873         dbg("exit");
1874         return TEL_RETURN_OPERATION_NOT_SUPPORTED;
1875 }
1876
1877 static TelReturn imc_call_set_sound_equalization(CoreObject *co, const TelCallSoundEqualization *sound_eq,
1878         TcoreObjectResponseCallback cb, void *cb_data)
1879 {
1880         dbg("entry");
1881
1882         dbg("exit");
1883         return TEL_RETURN_OPERATION_NOT_SUPPORTED;
1884 }
1885
1886 /* Call Operations */
1887 static TcoreCallOps imc_call_ops = {
1888         .dial = imc_call_dial,
1889         .answer = imc_call_answer,
1890         .end = imc_call_end,
1891         .send_dtmf = imc_call_send_dtmf,
1892         .hold = imc_call_hold,
1893         .active = imc_call_active,
1894         .swap = imc_call_swap,
1895         .join = imc_call_join,
1896         .split = imc_call_split,
1897         .transfer = imc_call_transfer,
1898         .deflect = imc_call_deflect,
1899         .set_active_line = imc_call_set_active_line,
1900         .get_active_line = imc_call_get_active_line,
1901         .set_volume_info = imc_call_set_volume_info,
1902         .get_volume_info = imc_call_get_volume_info,
1903         .set_sound_path = imc_call_set_sound_path,
1904         .set_mute = imc_call_set_mute,
1905         .get_mute_status = imc_call_get_mute_status,
1906         .set_sound_recording = imc_call_set_sound_recording,
1907         .set_sound_equalization = imc_call_set_sound_equalization,
1908 };
1909
1910 gboolean imc_call_init(TcorePlugin *p, CoreObject *co)
1911 {
1912         dbg("Entry");
1913
1914         /* Set operations */
1915         tcore_call_set_ops(co, &imc_call_ops);
1916
1917         /* Add Callbacks */
1918         tcore_object_add_callback(co, "+XCALLSTAT", on_notification_imc_call_status, NULL);
1919         tcore_object_add_callback(co, "+CLIP", on_notification_imc_call_clip_info, NULL);
1920         tcore_object_add_callback(co, "+CSSU", on_notification_imc_call_ss_cssu_info, NULL);
1921         tcore_object_add_callback(co, "+CSSI", on_notification_imc_call_ss_cssi_info, NULL);
1922
1923         return TRUE;
1924 }
1925
1926 void imc_call_exit(TcorePlugin *p, CoreObject *co)
1927 {
1928         dbg("Exit");
1929 }