Code cleanup
[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 static TelCallType __call_type(int type)
73 {
74         dbg("Entry");
75
76         switch (type) {
77         case 0:
78                 return TEL_CALL_TYPE_VOICE;
79
80         case 1:
81                 return TEL_CALL_TYPE_VIDEO;
82
83         default:
84                 err("invalid call type, returning default call type as voice");
85                 return TEL_CALL_TYPE_VOICE;
86         }
87 }
88
89 static TelCallState __call_state(int state)
90 {
91         dbg("Entry");
92
93         switch (state) {
94         case 0:
95                 return TEL_CALL_STATE_ACTIVE;
96
97         case 1:
98                 return TEL_CALL_STATE_HELD;
99
100         case 2:
101                 return TEL_CALL_STATE_DIALING;
102
103         case 3:
104                 return TEL_CALL_STATE_ALERT;
105
106         case 4:
107         case 5:
108                 return TEL_CALL_STATE_INCOMING;
109
110         default:
111                 return TEL_CALL_STATE_IDLE;
112         }
113 }
114
115 static void __call_branch_by_status(CoreObject *co, CallObject *call_obj,
116         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,
180                                 sizeof(TelCallIncomingInfo), &incoming);
181                         return;
182                 }
183
184                 case TEL_CALL_STATE_IDLE: {
185                         TelCallStatusIdleNoti idle;
186                         command = TCORE_NOTIFICATION_CALL_STATUS_IDLE;
187                         idle.call_id = call_id;
188
189                         /* TODO - get proper call end cause. */
190                         idle.cause = TEL_CALL_END_CAUSE_NONE;
191
192                         /* Send notification */
193                         tcore_object_send_notification(co, command,
194                                 sizeof(TelCallStatusIdleNoti), &idle);
195
196                         /* Free Call object */
197                         tcore_call_object_free(co, call_obj);
198                         return;
199                 }
200                 }
201
202         }
203         else if (call_type == TEL_CALL_TYPE_VIDEO) {
204                 /* video call notification */
205                 switch (call_state) {
206                 case TEL_CALL_STATE_ACTIVE:
207                         command = TCORE_NOTIFICATION_VIDEO_CALL_STATUS_ACTIVE;
208                 break;
209
210                 case TEL_CALL_STATE_HELD:
211                         err("invalid state");
212                 break;
213
214                 case TEL_CALL_STATE_DIALING:
215                         command = TCORE_NOTIFICATION_VIDEO_CALL_STATUS_DIALING;
216                 break;
217
218                 case TEL_CALL_STATE_ALERT:
219                         command = TCORE_NOTIFICATION_VIDEO_CALL_STATUS_ALERT;
220                 break;
221
222                 case TEL_CALL_STATE_INCOMING:
223                 case TEL_CALL_STATE_WAITING:
224                         command = TCORE_NOTIFICATION_VIDEO_CALL_STATUS_INCOMING;
225                 break;
226
227                 case TEL_CALL_STATE_IDLE: {
228                         TelCallStatusIdleNoti idle;
229                         command = TCORE_NOTIFICATION_VIDEO_CALL_STATUS_IDLE;
230                         idle.call_id = call_id;
231
232                         /* TODO - get proper call end cause. */
233                         idle.cause = TEL_CALL_END_CAUSE_NONE;
234
235                         /* Send notification */
236                         tcore_object_send_notification(co, command,
237                                 sizeof(TelCallStatusIdleNoti), &idle);
238
239                         /* Free Call object */
240                         tcore_call_object_free(co, call_obj);
241                         return;
242                 }
243                 }
244         }
245         else {
246                 err("Unknown Call type: [%d]", call_type);
247         }
248
249         if (command != TCORE_NOTIFICATION_UNKNOWN)
250                 tcore_object_send_notification(co, command, sizeof(call_id), &call_id);
251 }
252
253 static void __handle_call_list_get(CoreObject *co, gboolean flag, void *data)
254 {
255         int call_id;
256         int direction;
257         int call_type;
258         int state;
259         int mpty;
260         int ton;
261         GSList *tokens = NULL;
262         char *resp = NULL;
263         char *line;
264         char *num = NULL;
265         int num_type;
266         char number[TEL_CALL_CALLING_NUMBER_LEN_MAX +1] = {0,};
267         GSList *lines = data;
268         CallObject *call_obj = NULL;
269
270          while (lines != NULL) {
271                 line = (char *)lines->data;
272                 /* point to next node */
273                 lines = lines->next;
274                 /* free previous tokens*/
275                 tcore_at_tok_free(tokens);
276
277                 tokens = tcore_at_tok_new(line);
278
279                 /* <id1> */
280                 resp = g_slist_nth_data(tokens, 0);
281                 if (NULL == resp) {
282                         err("Invalid call_id");
283                         continue;
284                 }
285                 call_id = atoi(resp);
286
287                 /* <dir> */
288                 resp = g_slist_nth_data(tokens, 1);
289                 if (NULL == resp) {
290                         err("Invalid direction");
291                         continue;
292                 }
293                 direction = (atoi(resp) == 0) ? 1 : 0;
294
295                 /* <stat> */
296                 resp = g_slist_nth_data(tokens, 2);
297                 if (NULL == resp) {
298                         err("Invalid state");
299                         continue;
300                 }
301                 state = __call_state(atoi(resp));
302
303                 /* <mode> */
304                 resp = g_slist_nth_data(tokens, 3);
305                 if (NULL == resp) {
306                         err("Invalid call_type");
307                         continue;
308                 }
309                 call_type = __call_type(atoi(resp));
310
311                 /* <mpty> */
312                 resp = g_slist_nth_data(tokens, 4);
313                 if (NULL == resp) {
314                         err("Invalid mpty");
315                         continue;
316                 }
317                 mpty = atoi(resp);
318
319                 /* <number> */
320                 resp = g_slist_nth_data(tokens, 5);
321                 if (NULL == resp) {
322                         err("Number is NULL");
323                 } else {
324                         /* Strike off double quotes */
325                         num = tcore_at_tok_extract(resp);
326                         dbg("Number: [%s]", num);
327
328                         /* <type> */
329                         resp = g_slist_nth_data(tokens, 6);
330                         if (!resp) {
331                                 err("Invalid num type");
332                         } else {
333                                 num_type = atoi(resp);
334                                 /* check number is international or national. */
335                                 ton = ((num_type) >> 4) & 0x07;
336                                 if (ton == 1 && num[0] != '+') {
337                                         /* international number */
338                                         number[0] = '+';
339                                         memcpy(&number[1], num, strlen(num));
340                                 } else {
341                                         memcpy(number, num, strlen(num));
342                                 }
343                         }
344                         g_free(num);
345                 }
346
347                 dbg("Call ID: [%d] Direction: [%s] Call Type: [%d] Multi-party: [%s] "
348                         "Number: [%s] TON: [%d] State: [%d]",
349                         call_id, (direction ? "Outgoing" : "Incoming"), call_type,
350                         (mpty ? "YES" : "NO"), number, ton, state);
351
352                 call_obj = tcore_call_object_find_by_id(co, call_id);
353                 if (NULL == call_obj) {
354                         call_obj = tcore_call_object_new(co, call_id);
355                         if (NULL == call_obj) {
356                                 err("unable to create call object");
357                                 continue;
358                         }
359                 }
360
361                 /* Set Call parameters */
362                 tcore_call_object_set_type(call_obj, call_type);
363                 tcore_call_object_set_direction(call_obj, direction);
364                 tcore_call_object_set_multiparty_state(call_obj, mpty);
365                 tcore_call_object_set_cli_info(call_obj, TEL_CALL_CLI_VALIDITY_VALID, number);
366                 tcore_call_object_set_active_line(call_obj, TEL_CALL_ACTIVE_LINE1);
367                 if (flag == TRUE)
368                         __call_branch_by_status(co, call_obj, state);
369                 else
370                         tcore_call_object_set_state(call_obj, state);
371         }
372 }
373
374 /* internal notification operation */
375 static void __on_notification_imc_call_incoming(CoreObject *co, unsigned int call_id,
376         void *user_data)
377 {
378         GSList *list = NULL;
379         CallObject *call_obj = NULL;
380         dbg("entry");
381
382         /* check call with incoming status already exist */
383         list = tcore_call_object_find_by_status(co, TEL_CALL_STATE_INCOMING);
384         if (list != NULL) {
385                 err("Incoming call already exist! Skip...");
386                 g_slist_free(list);
387                 return;
388         }
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                 if (NULL == at_resp->lines) {
484                         err("invalid response received");
485                         return;
486                 }
487
488                 lines = (GSList *) at_resp->lines;
489                 count = g_slist_length(lines);
490                 dbg("Total records : %d", count);
491                 if (0 == count) {
492                         err("Call count is zero");
493                         return;
494                 }
495                 result = TEL_CALL_RESULT_SUCCESS;
496                 dbg("RESPONSE OK");
497
498                 /* parse +CLCC notification parameter */
499                 __handle_call_list_get(co, *flag, lines);
500
501         } else {
502                 err("RESPONSE NOK");
503         }
504 }
505
506 /*internal request operation */
507 static TelReturn __send_call_request(CoreObject *co, TcoreObjectResponseCallback cb,
508         void *cb_data, gchar *at_cmd, gchar *func_name)
509 {
510         ImcRespCbData *resp_cb_data;
511         TelReturn ret;
512
513         /* Response callback data */
514         resp_cb_data = imc_create_resp_cb_data(cb, cb_data, func_name,
515                         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  *
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
589 *   this is reported in 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 indication");
613                 goto OUT;
614         }
615         call_id = atoi(call_handle);
616         state = g_slist_nth_data(tokens, 1);
617         if (NULL == state) {
618                 err("call 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 = NULL;
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 indication");
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                 number = tcore_at_tok_extract(resp);
712         }
713
714         dbg("+CSSU: <code2> = %d <index> = %d <number> = %s ", code2, index, number);
715
716         /* <code2> - other values will be ignored */
717         switch (code2) {
718         case 0:
719                 command = TCORE_NOTIFICATION_CALL_INFO_MT_FORWARDED;
720                 break;
721         case 2:
722                 command = TCORE_NOTIFICATION_CALL_INFO_HELD;
723                 break;
724         case 3:
725                 command = TCORE_NOTIFICATION_CALL_INFO_ACTIVE;
726                 break;
727         case 4:
728                 command = TCORE_NOTIFICATION_CALL_INFO_JOINED;
729                 break;
730         case 7:
731         case 8:
732                 command = TCORE_NOTIFICATION_CALL_INFO_TRANSFERED;
733                 break;
734         case 9:
735                 command = TCORE_NOTIFICATION_CALL_INFO_MT_DEFLECTED;
736                 break;
737         default:
738                 dbg("Unsupported +CSSU notification : %d", code2);
739                 break;
740         }
741
742         if (command != TCORE_NOTIFICATION_UNKNOWN)
743                 tcore_object_send_notification(co, command, 0, NULL);
744         tcore_at_tok_free(tokens);
745         g_free(number);
746
747         return TRUE;
748 }
749
750 /*
751 * Operation -  SS network initiated notification.
752 * notification message format:
753 * +CSSI : <code1>[,<index>]
754 * where
755 * <code1>
756 * 0 unconditional call forwarding is active
757 * 1 some of the conditional call forwarding are active
758 * 2 call has been forwarded
759 * 3 call is waiting
760 * 4 this is a CUG call (also <index> present)
761 * 5 outgoing calls are barred
762 * 6 incoming calls are barred
763 * 7 CLIR suppression rejected
764 * 8 call has been deflected
765
766 * <index>
767 * refer Closed user group +CCUG.
768 */
769 static gboolean on_notification_imc_call_ss_cssi_info(CoreObject *co, const void *event_data,
770         void *user_data)
771 {
772         GSList *tokens = NULL;
773         TcoreNotification command = TCORE_NOTIFICATION_UNKNOWN;
774         char *resp = NULL;
775         char *cmd = 0;
776         int index = 0;
777         int code1 = -1;
778
779         dbg("entry");
780
781         if (1 != g_slist_length((GSList *) event_data)) {
782                 err("unsolicited msg but multiple line");
783                 return TRUE;
784         }
785         cmd = (char *) ((GSList *) event_data)->data;
786         dbg("ss notification message[%s]", cmd);
787
788         tokens = tcore_at_tok_new(cmd);
789         /* parse <code1> */
790         resp = g_slist_nth_data(tokens, 0);
791         if (NULL == resp) {
792                 err("<code1> is missing from %CSSI indication");
793                 tcore_at_tok_free(tokens);
794                 return TRUE;
795         }
796
797         code1 = atoi(resp);
798
799         /* parse [ <index>] */
800         if ((resp = g_slist_nth_data(tokens, 1)))
801                 index = atoi(resp);
802
803         dbg("+CSSI: <code1> = %d <index> = %d ", code1, index);
804
805         /* <code1> - other values will be ignored */
806         switch (code1) {
807         case 0:
808                 command = TCORE_NOTIFICATION_CALL_INFO_MO_FORWARD_UNCONDITIONAL;
809                 break;
810         case 1:
811                 command = TCORE_NOTIFICATION_CALL_INFO_MO_FORWARD_CONDITIONAL;
812                 break;
813         case 2:
814                 command = TCORE_NOTIFICATION_CALL_INFO_MO_FORWARDED;
815                 break;
816         case 3:
817                 command = TCORE_NOTIFICATION_CALL_INFO_MO_WAITING;
818                 break;
819         case 5:
820                 command = TCORE_NOTIFICATION_CALL_INFO_MO_BARRED_OUTGOING;
821                 break;
822         case 6:
823                 command = TCORE_NOTIFICATION_CALL_INFO_MO_BARRED_INCOMING;
824                 break;
825         case 8:
826                 command  = TCORE_NOTIFICATION_CALL_INFO_MO_DEFLECTED;
827                 break;
828         default:
829                 dbg("Unsupported +CSSI notification : %d", code1);
830                 break;
831         }
832
833         if (command != TCORE_NOTIFICATION_UNKNOWN)
834                 tcore_object_send_notification(co, command, 0, NULL);
835         tcore_at_tok_free(tokens);
836
837         return TRUE;
838 }
839
840 static gboolean on_notification_imc_call_clip_info(CoreObject *co, const void *data,
841         void *user_data)
842 {
843         dbg("entry");
844         /* TODO - handle +CLIP notification*/
845         return TRUE;
846 }
847
848 /* Response */
849 static void on_response_imc_call_default(TcorePending *p,
850         guint data_len, const void *data, void *user_data)
851 {
852         const TcoreAtResponse *at_resp = data;
853         CoreObject *co = tcore_pending_ref_core_object(p);
854         ImcRespCbData *resp_cb_data = user_data;
855
856         TelCallResult result = TEL_CALL_RESULT_FAILURE;
857         dbg("entry");
858
859         tcore_check_return_assert(co != NULL);
860         tcore_check_return_assert(resp_cb_data != NULL);
861
862         if (at_resp && at_resp->success) {
863                 result = TEL_CALL_RESULT_SUCCESS;
864         } else {
865                 err("ERROR[%s] CME error[%s]",at_resp->final_response,
866                         at_resp->lines->data);
867                 /*
868                  * TODO - need to map CME error and final
869                  * response error to TelCallResult
870                  */
871         }
872
873         dbg("%s: [%s]", IMC_GET_DATA_FROM_RESP_CB_DATA(resp_cb_data),
874                  (result == TEL_CALL_RESULT_SUCCESS ? "SUCCESS" : "FAIL"));
875
876         /* Invoke callback */
877         if (resp_cb_data->cb)
878                 resp_cb_data->cb(co, (gint)result, NULL, resp_cb_data->cb_data);
879
880         /* Free callback data */
881         imc_destroy_resp_cb_data(resp_cb_data);
882 }
883
884 static void on_response_imc_call_set_volume_info(TcorePending *p,
885         guint data_len, const void *data, void *user_data)
886 {
887         const TcoreAtResponse *at_resp = data;
888         CoreObject *co = tcore_pending_ref_core_object(p);
889         ImcRespCbData *resp_cb_data = user_data;
890         GSList *tokens = NULL;
891         GSList *line = NULL;
892         char *resp_str = NULL;
893         gboolean error;
894
895         /* TODO: XDRV error mapping required */
896         TelCallResult result = TEL_CALL_RESULT_FAILURE;
897         dbg("Enter");
898
899         tcore_check_return_assert(co != NULL);
900         tcore_check_return_assert(resp_cb_data != NULL);
901
902         if (at_resp && at_resp->success) {
903                 line = at_resp->lines;
904                 tokens = tcore_at_tok_new(line->data);
905
906                 if (!g_slist_nth_data(tokens, 0)) {
907                         err("group_id  missing");
908                         goto OUT;
909                 }
910
911                 if (!g_slist_nth_data(tokens, 1)) {
912                         err(" function_id  missing");
913                         goto OUT;
914                 }
915
916                 resp_str = g_slist_nth_data(tokens, 2);
917                 if (!resp_str) {
918                         err("xdrv result missing");
919                         goto OUT;
920                 } else {
921                         struct imc_set_volume_info *volume_info;
922                         gchar *vol = "";
923                         gchar *at_cmd;
924                         TelReturn ret;
925
926                         error = atoi(resp_str);
927                         if (error) {
928                                 err("RESPONSE NOK");
929                                 goto OUT;
930                         }
931
932                         /* Fetch volume_info from resp_cb_data */
933                         volume_info = (struct imc_set_volume_info *)
934                                         IMC_GET_DATA_FROM_RESP_CB_DATA(resp_cb_data);
935                         dbg("volume info index[%d]", volume_info->next_index);
936
937                         if (xdrv_set_volume[volume_info->next_index] == NULL) {
938                                 /*Processing of xdrv commands  completed */
939                                 dbg("RESPONSE OK");
940                                 result = TEL_CALL_RESULT_SUCCESS;
941                                 goto OUT;
942                         } else if (volume_info->next_index == 3) {
943                                 switch ((volume_info->volume) / 10) {
944                                 case 0 :
945                                         vol = "0";
946                                 break;
947                                 case 1 :
948                                         vol = "40";
949                                 break;
950                                 case 2 :
951                                         vol = "46";
952                                 break;
953                                 case 3 :
954                                         vol = "52";
955                                 break;
956                                 case 4 :
957                                         vol = "58";
958                                 break;
959                                 case 5 :
960                                         vol = "64";
961                                 break;
962                                 case 6 :
963                                         vol = "70";
964                                 break;
965                                 case 7 :
966                                         vol = "76";
967                                 break;
968                                 case 8 :
969                                         vol = "82";
970                                 break;
971                                 case 9 :
972                                 default :
973                                         vol = "88";
974                                 }
975                         }
976
977                         at_cmd = g_strdup_printf("%s%s",
978                                         xdrv_set_volume[volume_info->next_index], vol);
979
980                         /* Increment index to point to next xdrv command */
981                         volume_info->next_index += 1;
982
983                         /* Send Request to modem */
984                         ret = tcore_at_prepare_and_send_request(co,
985                                         at_cmd, "+XDRV",
986                                         TCORE_AT_COMMAND_TYPE_SINGLELINE,
987                                         NULL,
988                                         on_response_imc_call_set_volume_info,
989                                         resp_cb_data, on_send_imc_request, NULL);
990                         IMC_CHECK_REQUEST_RET(ret, resp_cb_data, "imc_call_set_volume_info");
991                         g_free(at_cmd);
992
993                         return;
994                 }
995         }
996
997 OUT :
998         dbg("Set Volume Info: [%s]",
999                         (result == TEL_CALL_RESULT_SUCCESS ? "SUCCESS" : "FAIL"));
1000
1001         /* Invoke callback */
1002         if (resp_cb_data->cb)
1003                 resp_cb_data->cb(co, (gint)result, NULL, resp_cb_data->cb_data);
1004
1005         imc_destroy_resp_cb_data(resp_cb_data);
1006         tcore_at_tok_free(tokens);
1007 }
1008
1009 static void on_response_imc_call_set_sound_path(TcorePending *p,
1010         guint data_len, const void *data, void *user_data)
1011 {
1012         const TcoreAtResponse *at_resp = data;
1013         CoreObject *co = tcore_pending_ref_core_object(p);
1014         ImcRespCbData *resp_cb_data = user_data;
1015         GSList *tokens = NULL;
1016         GSList *line = NULL;
1017         char *resp_str = NULL;
1018         gboolean error;
1019         gint xdrv_func_id = -1;
1020
1021         TelCallResult result = TEL_CALL_RESULT_FAILURE;  // TODO: XDRV error mapping is required
1022         dbg("Enter");
1023
1024         tcore_check_return_assert(co != NULL);
1025         tcore_check_return_assert(resp_cb_data != NULL);
1026
1027         if (at_resp && at_resp->success) {
1028                 line = at_resp->lines;
1029                 tokens = tcore_at_tok_new(line->data);
1030                 if (!g_slist_nth_data(tokens, 0)) {
1031                         err("group_id  missing");
1032                         goto OUT;
1033                 }
1034
1035                 if (!(resp_str = g_slist_nth_data(tokens, 1))) {
1036                         err("function_id  missing");
1037                         goto OUT;
1038                 }
1039                 xdrv_func_id = atoi(resp_str);
1040
1041                 if (!(resp_str = g_slist_nth_data(tokens, 2))) {
1042                         err("RESPONSE NOK");
1043                         goto OUT;
1044                 }
1045                 error = atoi(resp_str);
1046                 if (error) {
1047                         err("RESPONSE NOK");
1048                         goto OUT;
1049                 }
1050
1051                 if (xdrv_func_id == 4) {
1052                         /* Send next command to configure destination device type */
1053                         gchar *at_cmd;
1054                         TelReturn ret;
1055                         gint *device_type = IMC_GET_DATA_FROM_RESP_CB_DATA(resp_cb_data);
1056
1057                         at_cmd = g_strdup_printf("AT+XDRV=40,5,2,0,0,0,0,0,1,0,1,0,%d",
1058                                                 *device_type);
1059
1060                         ret = tcore_at_prepare_and_send_request(co,
1061                                         at_cmd, "+XDRV",
1062                                         TCORE_AT_COMMAND_TYPE_SINGLELINE,
1063                                         NULL,
1064                                         on_response_imc_call_set_sound_path, resp_cb_data,
1065                                         on_send_imc_request, NULL);
1066                         IMC_CHECK_REQUEST_RET(ret, resp_cb_data, "imc_call_set_sound_path");
1067                         g_free(at_cmd);
1068
1069                         return;
1070                 }
1071                 dbg("RESPONSE OK");
1072                 result = TEL_CALL_RESULT_SUCCESS;
1073         }
1074
1075 OUT :
1076         dbg("Set Sound Path: [%s]",
1077                         (result == TEL_CALL_RESULT_SUCCESS ? "SUCCESS" : "FAIL"));
1078
1079         tcore_at_tok_free(tokens);
1080
1081         /* Invoke callback */
1082         if (resp_cb_data->cb)
1083                 resp_cb_data->cb(co, (gint)result, NULL, resp_cb_data->cb_data);
1084
1085         imc_destroy_resp_cb_data(resp_cb_data);
1086 }
1087
1088 static void on_response_set_sound_path(TcorePending *p, guint data_len,
1089                                         const void *data, void *user_data)
1090 {
1091         const TcoreAtResponse *at_resp = data;
1092         ImcRespCbData *resp_cb_data = user_data;
1093         TelCallResult result = TEL_CALL_RESULT_FAILURE;
1094         CoreObject *co_call = tcore_pending_ref_core_object(p);
1095
1096         if (at_resp && at_resp->success)
1097                         result = TEL_CALL_RESULT_SUCCESS;
1098
1099         if(resp_cb_data->cb)
1100                 resp_cb_data->cb(co_call, (gint)result, NULL, resp_cb_data->cb_data);
1101
1102         imc_destroy_resp_cb_data(resp_cb_data);
1103 }
1104
1105 static void on_response_imc_call_set_mute(TcorePending *p, guint data_len,
1106         const void *data, void *user_data)
1107 {
1108         const TcoreAtResponse *at_resp = data;
1109         CoreObject *co = tcore_pending_ref_core_object(p);
1110         ImcRespCbData *resp_cb_data = user_data;
1111         GSList *tokens = NULL;
1112         const char *line = NULL;
1113         char *resp_str = NULL;
1114         gboolean error;
1115
1116         TelCallResult result = TEL_CALL_RESULT_FAILURE;  // TODO: XDRV error mapping is required
1117         dbg("Enter");
1118
1119         tcore_check_return_assert(co != NULL);
1120         tcore_check_return_assert(resp_cb_data != NULL);
1121
1122         if (at_resp && at_resp->success) {
1123                 line = (((GSList *)at_resp->lines)->data);
1124                 tokens = tcore_at_tok_new(line);
1125
1126                 resp_str = g_slist_nth_data(tokens, 0);
1127                 if (!g_slist_nth_data(tokens, 0)) {
1128                         err("group_id  missing");
1129                         goto OUT;
1130                 }
1131
1132                 if (!g_slist_nth_data(tokens, 1)) {
1133                         err("function_id  missing");
1134                         goto OUT;
1135                 }
1136
1137                 resp_str = g_slist_nth_data(tokens, 2);
1138                 if (!(resp_str = g_slist_nth_data(tokens, 1))) {
1139                         err("xdrv_result missing");
1140                         goto OUT;
1141                 }
1142                 error = atoi(resp_str);
1143                 if (!error) {
1144                         err(" RESPONSE NOK [%d]", error);
1145                         goto OUT;
1146                 }
1147                 result = TEL_CALL_RESULT_SUCCESS;
1148         }
1149
1150 OUT :
1151         dbg("Set Mute: [%s]",
1152                         (result == TEL_CALL_RESULT_SUCCESS ? "SUCCESS" : "FAIL"));
1153         tcore_at_tok_free(tokens);
1154
1155         /* Invoke callback */
1156         if (resp_cb_data->cb)
1157                 resp_cb_data->cb(co, (gint)result, NULL, resp_cb_data->cb_data);
1158
1159         imc_destroy_resp_cb_data(resp_cb_data);
1160 }
1161
1162
1163  /* Request */
1164  /*
1165  * Operation - dial
1166  *
1167  * Request -
1168  * AT-Command: ATD <num> [I] [G] [;]
1169  * <num> - dialed number
1170  * [I][i] - CLI presentation(supression or invocation)
1171  * [G] - control the CUG supplementary service information for this call.
1172  *
1173  * Response -
1174  * Success:
1175  * OK or CONNECT
1176  * Failure:
1177  * "ERROR"
1178  * "NO ANSWER"
1179  * "NO CARRIER"
1180  * "BUSY"
1181  * "NO DIALTONE"
1182  * +CME ERROR: <error>
1183  */
1184 static TelReturn imc_call_dial(CoreObject *co, const TelCallDial *dial_info,
1185                 TcoreObjectResponseCallback cb, void *cb_data)
1186 {
1187         gchar *at_cmd;
1188         const gchar *clir;
1189         gchar *num;
1190
1191         dbg("entry");
1192
1193         if (dial_info->call_type == TEL_CALL_TYPE_VIDEO) {
1194                 err("Video call is not supported in imc modem");
1195                  return TEL_RETURN_OPERATION_NOT_SUPPORTED;
1196         }
1197
1198         if (!strncmp(dial_info->number, "*31#", 4)) {
1199                 dbg("clir suppression");
1200                 clir = "i";
1201                 num = (gchar *)&(dial_info->number[4]);
1202         } else if (!strncmp(dial_info->number, "#31#", 4)) {
1203                 dbg("clir invocation");
1204                 clir = "I";
1205                 num = (gchar *)&(dial_info->number[4]);
1206         } else {
1207                 int cli = 0;
1208
1209                 dbg("no clir string in number");
1210
1211                 /* it will be removed when setting application use tapi_ss_set_cli()
1212                  * instead of his own vconfkey. (0 : By network, 1 : Show, 2 : Hide)
1213                  */
1214                 vconf_get_int("db/ciss/show_my_number", &cli);
1215                 if (cli == 2){
1216                         dbg("clir invocation from setting application");
1217                         clir = "I";
1218                 } else {
1219                         dbg("set clir state to default");
1220                         clir = "";
1221                 }
1222                 num = (gchar *)dial_info->number;
1223         }
1224
1225         /* AT-Command */
1226         at_cmd = g_strdup_printf("ATD%s%s;", num, clir);
1227         dbg(" at command : %s", at_cmd);
1228
1229         return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_dial");
1230 }
1231
1232 /*
1233  * Operation - Answer/Reject/Replace/hold(current call) & accept incoming call.
1234  *
1235  * Request -
1236  *
1237  * 1. AT-Command: ATA
1238  * Response -
1239  * Success:
1240  * OK
1241  * Failure:
1242  * +CME ERROR: <error>
1243  *
1244  * 2. AT-Command: AT+CHLD=[<n>]
1245  * <n>
1246  * 0 - (deafult)release all held calls or set User Determined User Busy for a waiting/incoming
1247  * call; if both exists then only the waiting call will be rejected.
1248  * 1 -  release all active calls and accepts the other (held or waiting)
1249  * Note: In the scenario: An active call, a waiting call and held call, when the active call is
1250  * terminated, we will make the Waiting call as active.
1251  * 2 -  place all active calls (if exist) on hold and accepts the other call (held or waiting/in-coming).
1252  * If only one call exists which is active, place it on hold and if only held call exists make it active call.
1253  * Response -
1254  * Success:
1255  * OK
1256  * Failure:
1257  * +CME ERROR: <error>
1258  * For more informatiion refer 3GPP TS 27.007.
1259  */
1260 static TelReturn imc_call_answer(CoreObject *co, TelCallAnswerType ans_type,
1261         TcoreObjectResponseCallback cb, void *cb_data)
1262 {
1263         gchar *at_cmd;
1264         dbg("entry");
1265
1266         if (ans_type == TEL_CALL_ANSWER_ACCEPT) {
1267                 /* AT-Command */
1268                 at_cmd = g_strdup_printf("%s", "ATA");
1269         }else if (ans_type == TEL_CALL_ANSWER_REJECT) {
1270                 /* AT-Command */
1271                 at_cmd = g_strdup_printf("%s", "AT+CHLD=0");
1272         } else if (ans_type == TEL_CALL_ANSWER_REPLACE) {
1273                 /* AT-Command */
1274                 at_cmd = g_strdup_printf("%s", "AT+CHLD=1");
1275         } else if (ans_type == TEL_CALL_ANSWER_HOLD_AND_ACCEPT) {
1276                 /* AT-Command */
1277                 at_cmd = g_strdup_printf("%s", "AT+CHLD=2");
1278         }else {
1279                 err("Unsupported call answer type");
1280                 return TEL_RETURN_FAILURE;
1281         }
1282
1283         dbg("at command : %s", at_cmd);
1284
1285         return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_answer");
1286 }
1287
1288 /*
1289  * Operation - release all calls/release specific call/release all active
1290  * call/release all held calls.
1291  *
1292  * Request -
1293  * 1. AT-Command: AT+CHLD=[<n>]
1294  * <n>
1295  * 0  - (defualt)release all held calls or set User Determined User -
1296  *       Busy for a waiting/incoming.
1297  * call; if both exists then only the waiting call will be rejected.
1298  * 1  - release all active calls and accepts the other (held or waiting).
1299  * 1x - release a specific call (x specific call number as indicated by call id).
1300  * 8  - release all calls.
1301  * Response -
1302  * Success:
1303  * OK
1304  * Failure:
1305  * +CME ERROR: <error>
1306  */
1307 static TelReturn imc_call_end(CoreObject *co, const TelCallEnd *end_info,
1308         TcoreObjectResponseCallback cb, void *cb_data)
1309 {
1310         gchar *at_cmd;
1311         dbg("entry");
1312
1313         if (end_info->end_type == TEL_CALL_END_ALL) {
1314                 /* AT-Command */
1315                 at_cmd = g_strdup_printf("%s", "AT+CHLD=8");
1316         }else if (end_info->end_type == TEL_CALL_END) {
1317                 /* AT-Command */
1318                 at_cmd = g_strdup_printf("%s%d", "AT+CHLD=1",end_info->call_id);
1319         } else if (end_info->end_type == TEL_CALL_END_ACTIVE_ALL) {
1320                 /* AT-Command */
1321                 at_cmd = g_strdup_printf("%s", "AT+CHLD=1");
1322         } else if (end_info->end_type == TEL_CALL_END_HOLD_ALL) {
1323                 /* AT-Command */
1324                 at_cmd = g_strdup_printf("%s", "AT+CHLD=0");
1325         }else {
1326                 err("Unsupported call end type");
1327                 return TEL_RETURN_FAILURE;
1328         }
1329
1330         dbg("at command : %s", at_cmd);
1331
1332         return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_end");
1333 }
1334
1335 /*
1336  * Operation - send dtmf.
1337  *
1338  * Request -
1339  * 1. AT-Command: AT+VTS=<DTMF>,{<DTMF>,<duration>}.
1340  * where
1341  * <DTMF>:
1342  * is a single ASCII character in the set 0-9, #, *, A-D. Even it will support string DTMF.
1343  * <duration>:
1344  * integer in range 0-255, meaning 1/10(10 millisec) seconds multiples. The string parameter
1345  * of the command consists of combinations of the following separated by commas:
1346  * NOTE : There is a limit of 50 dtmf tones can be requested through a single VTS command.
1347  * Response -
1348  * Success:
1349  * OK
1350  * Failure:
1351  * +CME ERROR: <error>
1352  */
1353 static TelReturn imc_call_send_dtmf(CoreObject *co, const char *dtmf_str,
1354         TcoreObjectResponseCallback cb,  void *cb_data)
1355 {
1356         gchar *at_cmd;
1357         char *tmp_dtmf = NULL, *dtmf;
1358         unsigned int count;
1359
1360         dbg("entry");
1361
1362         //(void) _set_dtmf_tone_duration(o, dup);
1363
1364         /* DTMF digits + comma for each dtmf digit */
1365         tmp_dtmf = tcore_malloc0((strlen(dtmf_str) * 2) + 1);
1366         tcore_check_return_value_assert(tmp_dtmf != NULL, TEL_RETURN_FAILURE);
1367         /* Save initial pointer */
1368         dtmf = tmp_dtmf;
1369
1370         for (count = 0; count < strlen(dtmf_str); count++) {
1371                 *tmp_dtmf = dtmf_str[count];
1372                 tmp_dtmf++;
1373
1374                 *tmp_dtmf = COMMA;
1375                 tmp_dtmf++;
1376         }
1377
1378         /* last digit is having COMMA , overwrite it with '\0'*/
1379         *(--tmp_dtmf) = '\0';
1380
1381         /* (AT+VTS = <d1>,<d2>,<d3>,<d4>,<d5>,<d6>, ..... <d32> */
1382         at_cmd = g_strdup_printf("AT+VTS=%s", dtmf);
1383         dbg("at command : %s", at_cmd);
1384
1385         tcore_free(dtmf);
1386
1387         return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_send_dtmf");
1388 }
1389
1390 /*
1391  * Operation - call hold.
1392  *
1393  * Request -
1394  * 1. AT-Command: AT+CHLD=[<n>]
1395  * Where
1396  * <n>
1397  * 2 - place all active calls (if exist) on hold and accepts the other call (held or waiting/incoming).
1398  * If only one call exists which is active, place it on hold and if only held call exists
1399  * make it active call
1400  * Response -
1401  * Success:
1402  * OK
1403  * Failure:
1404  * +CME ERROR: <error>
1405  */
1406 static TelReturn imc_call_hold(CoreObject *co, TcoreObjectResponseCallback cb,
1407         void *cb_data)
1408
1409 {
1410         gchar *at_cmd;
1411         dbg("entry");
1412
1413         at_cmd = g_strdup_printf("%s", "AT+CHLD=2");
1414         dbg("at command : %s", at_cmd);
1415
1416         return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_hold");
1417 }
1418
1419 /*
1420  * Operation - call active.
1421  *
1422  * Request -
1423  * 1. AT-Command: AT+CHLD=[<n>]
1424  * Where
1425  * <n>
1426  * 2 - place all active calls (if exist) on hold and accepts the other call (held or waiting/incoming).
1427  * If only one call exists which is active, place it on hold and if only held call exists
1428  * make it active call
1429  * Response -
1430  * Success:
1431  * OK
1432  * Failure:
1433  * +CME ERROR: <error>
1434  */
1435 static TelReturn imc_call_active(CoreObject *co, TcoreObjectResponseCallback cb,
1436         void *cb_data)
1437 {
1438         gchar *at_cmd;
1439         dbg("entry");
1440
1441         at_cmd = g_strdup_printf("%s", "AT+CHLD=2");
1442         dbg("at command : %s", at_cmd);
1443
1444         return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_active");
1445 }
1446
1447 /*
1448  * Operation - call swap.
1449  *
1450  * Request -
1451  * 1. AT-Command: AT+CHLD=[<n>]
1452  * Where
1453  * <n>
1454  * 2 - place all active calls (if exist) on hold and accepts the other call (held or waiting/incoming).
1455  * If only one call exists which is active, place it on hold and if only held call exists
1456  * make it active call
1457  * Response -
1458  * Success:
1459  * OK
1460  * Failure:
1461  * +CME ERROR: <error>
1462  */
1463 static TelReturn imc_call_swap(CoreObject *co, TcoreObjectResponseCallback cb,
1464         void *cb_data)
1465 {
1466         gchar *at_cmd;
1467         dbg("entry");
1468
1469         at_cmd = g_strdup_printf("%s", "AT+CHLD=2");
1470         dbg("at command : %s", at_cmd);
1471
1472         return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_swap");
1473 }
1474
1475 /*
1476  * Operation - call join.
1477  *
1478  * Request -
1479  * 1. AT-Command: AT+CHLD=[<n>]
1480  * Where
1481  * <n>
1482  * 3 - adds a held call to the conversation
1483  * Response -
1484  * Success:
1485  * OK
1486  * Failure:
1487  * +CME ERROR: <error>
1488  */
1489 static TelReturn imc_call_join(CoreObject *co, TcoreObjectResponseCallback cb,
1490         void *cb_data)
1491 {
1492         gchar *at_cmd;
1493         dbg("entry");
1494
1495         at_cmd = g_strdup_printf("%s", "AT+CHLD=3");
1496         dbg("at command : %s", at_cmd);
1497
1498         return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_join");
1499 }
1500
1501 /*
1502  * Operation - call split.
1503  *
1504  * Request -
1505  * 1. AT-Command: AT+CHLD=[<n>]
1506  * Where
1507  * <n>
1508  * 2x - place all active calls on hold except call x with which communication is supported
1509  * Response -
1510  * Success:
1511  * OK
1512  * Failure:
1513  * +CME ERROR: <error>
1514  */
1515 static TelReturn imc_call_split(CoreObject *co, unsigned int call_id,
1516         TcoreObjectResponseCallback cb, void *cb_data)
1517 {
1518         gchar *at_cmd;
1519         dbg("entry");
1520
1521         at_cmd = g_strdup_printf("%s%d", "AT+CHLD=2", call_id);
1522         dbg("at command : %s", at_cmd);
1523
1524         return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_split");
1525 }
1526
1527 /*
1528  * Operation - call transfer.
1529  *
1530  * Request -
1531  * 1. AT-Command: AT+CHLD=[<n>]
1532  * Where
1533  * <n>
1534  * 4 connects the two calls and disconnects the subscriber from both calls (Explicit Call Transfer)
1535  * Response -
1536  * Success:
1537  * OK
1538  * Failure:
1539  * +CME ERROR: <error>
1540  */
1541 static TelReturn imc_call_transfer(CoreObject *co, TcoreObjectResponseCallback cb,
1542         void *cb_data)
1543 {
1544         gchar *at_cmd;
1545         dbg("entry");
1546
1547         at_cmd = g_strdup_printf("%s", "AT+CHLD=4");
1548         dbg("at command : %s", at_cmd);
1549
1550         return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_transfer");
1551 }
1552
1553 /*
1554  * Operation - call deflect.
1555  *
1556  * Request -
1557  * 1. AT-Command: AT+CTFR= <number>[,<type>]
1558  * Where
1559  * number>
1560  * string type phone number
1561  * <type>
1562  * type of address octet in integer format. It is optional parameter.
1563  *
1564  * Response -
1565  * Success:
1566  * OK
1567  * Failure:
1568  * +CME ERROR: <error>
1569  */
1570 static TelReturn imc_call_deflect(CoreObject *co, const char *deflect_to,
1571         TcoreObjectResponseCallback cb, void *cb_data)
1572 {
1573         gchar *at_cmd;
1574         dbg("entry");
1575
1576         at_cmd = g_strdup_printf("AT+CTFR=%s", deflect_to);
1577         dbg("at command : %s", at_cmd);
1578
1579         return __send_call_request(co, cb, cb_data, at_cmd, "imc_call_deflect");
1580 }
1581
1582 static TelReturn imc_call_set_active_line(CoreObject *co, TelCallActiveLine active_line,
1583         TcoreObjectResponseCallback cb, void *cb_data)
1584 {
1585         dbg("entry");
1586
1587         dbg("exit");
1588         return TEL_RETURN_OPERATION_NOT_SUPPORTED;
1589 }
1590
1591 static TelReturn imc_call_get_active_line(CoreObject *co, TcoreObjectResponseCallback cb,
1592         void *cb_data)
1593 {
1594         dbg("entry");
1595
1596         dbg("exit");
1597         return TEL_RETURN_OPERATION_NOT_SUPPORTED;
1598 }
1599
1600 /*
1601  * Operation - Set voule info.
1602  *
1603  * Request -
1604  * AT-Command: AT+XDRV=<group_id>,<function_id>[,<param_n>]
1605  * The first command parameter defines the involved driver group.
1606  * The second command parameter defines a certain function in the selected driver group.
1607  * Other parameters are dependent on the first two parameters.
1608  * Nearly all parameters are integer values, also if they are represented by literals.
1609  * Only very few are strings or
1610  * hex data strings.
1611  *
1612  * Response -
1613  * +XDRV: <group_id>,<function_id>,<xdrv_result>[,<response_n>]
1614  * The first response parameter defines the involved driver group.
1615  * The second response parameter defines the current function in the selected driver group.
1616  * The third response parameter defines the xdrv_result of the operation.
1617  * Additional response parameters dependent on the first two parameters.
1618  */
1619 static TelReturn imc_call_set_volume_info(CoreObject *co, const TelCallVolumeInfo *volume_info,
1620         TcoreObjectResponseCallback cb, void *cb_data)
1621 {
1622         ImcRespCbData *resp_cb_data = NULL;
1623         gchar *at_cmd;
1624         TelReturn ret;
1625         struct imc_set_volume_info cb_volume_info;
1626
1627         dbg("entry");
1628
1629         cb_volume_info.next_index = 1;
1630         cb_volume_info.volume = volume_info->volume;
1631
1632         /* Response callback data */
1633         resp_cb_data = imc_create_resp_cb_data(cb, cb_data,
1634                                 &cb_volume_info, sizeof(struct imc_set_volume_info));
1635
1636         at_cmd = g_strdup_printf("%s", xdrv_set_volume[0]);
1637
1638         /* Send Request to modem */
1639         ret = tcore_at_prepare_and_send_request(co,
1640                         at_cmd, "+XDRV",
1641                         TCORE_AT_COMMAND_TYPE_SINGLELINE,
1642                         NULL,
1643                         on_response_imc_call_set_volume_info, resp_cb_data,
1644                         on_send_imc_request, NULL);
1645         IMC_CHECK_REQUEST_RET(ret, resp_cb_data, "imc_call_set_volume_info");
1646
1647         g_free(at_cmd);
1648         return ret;
1649 }
1650
1651
1652 static TelReturn imc_call_get_volume_info(CoreObject *co, TelCallSoundDevice sound_device,
1653         TcoreObjectResponseCallback cb, void *cb_data)
1654 {
1655         dbg("Entry");
1656
1657         dbg("Exit");
1658         return TEL_RETURN_OPERATION_NOT_SUPPORTED;
1659 }
1660
1661 /*
1662  * Operation - Set sound path.
1663  *
1664  * Request -
1665  * AT-Command: AT+XDRV=<group_id>,<function_id>[,<param_n>]
1666  * The first command parameter defines the involved driver group.
1667  * The second command parameter defines a certain function in the selected driver group.
1668  * Other parameters are dependent on the first two parameters.
1669  * Nearly all parameters are integer values, also if they are represented by literals.
1670  * Only very few are strings or
1671  * hex data strings.
1672  *
1673  * Response -
1674  * +XDRV: <group_id>,<function_id>,<xdrv_result>[,<response_n>]
1675  * The first response parameter defines the involved driver group.
1676  * The second response parameter defines the current function in the selected driver group.
1677  * The third response parameter defines the xdrv_result of the operation.
1678  * Additional response parameters dependent on the first two parameters.
1679  */
1680
1681 static TelReturn imc_call_set_sound_path(CoreObject *co, const TelCallSoundPathInfo *sound_path_info,
1682         TcoreObjectResponseCallback cb, void *cb_data)
1683 {
1684         ImcRespCbData *resp_cb_data = NULL;
1685         TelReturn ret;
1686         gchar *at_cmd;
1687         gint device_type = -1;
1688         TcorePlugin *plugin = tcore_object_ref_plugin(co);
1689         const char *cp_name = tcore_server_get_cp_name_by_plugin(plugin);
1690
1691         dbg("audio device type - 0x%x", sound_path_info->path);
1692
1693         switch (sound_path_info->path) {
1694                 case TEL_SOUND_PATH_HANDSET:
1695                         device_type = 1;
1696                         break;
1697                 case TEL_SOUND_PATH_HEADSET:
1698                         device_type = 2;
1699                         break;
1700                 case TEL_SOUND_PATH_HEADSET_3_5PI:
1701                         device_type = 3;
1702                         break;
1703                 case TEL_SOUND_PATH_SPK_PHONE:
1704                         device_type = 4;
1705                         break;
1706                 case TEL_SOUND_PATH_HANDSFREE:
1707                         device_type = 5;
1708                         break;
1709                 case TEL_SOUND_PATH_HEADSET_HAC:
1710                         device_type = 6;
1711                         break;
1712                 case TEL_SOUND_PATH_BLUETOOTH:
1713                 case TEL_SOUND_PATH_STEREO_BLUETOOTH:
1714                         device_type = 7;
1715                         break;
1716                 case TEL_SOUND_PATH_BT_NSEC_OFF:
1717                 case TEL_SOUND_PATH_MIC1:
1718                 case TEL_SOUND_PATH_MIC2:
1719                 default:
1720                         dbg("unsupported device type");
1721                         return TEL_RETURN_INVALID_PARAMETER;
1722         }
1723
1724         if (g_str_has_prefix(cp_name, "imcmodem")) {
1725                 /* Response callback data */
1726                 resp_cb_data = imc_create_resp_cb_data(cb, cb_data, &device_type, sizeof(gint));
1727
1728                 at_cmd = g_strdup_printf("AT+XDRV=40,4,3,0,0,0,0,0,1,0,1,0,%d", device_type);
1729
1730                 ret = tcore_at_prepare_and_send_request(co,
1731                         at_cmd, "+XDRV",
1732                         TCORE_AT_COMMAND_TYPE_SINGLELINE,
1733                         NULL,
1734                         on_response_imc_call_set_sound_path, resp_cb_data,
1735                         on_send_imc_request, NULL);
1736                 IMC_CHECK_REQUEST_RET(ret, NULL, "imc_call_set_sound_path");
1737                 g_free(at_cmd);
1738         } else {
1739                 /* Response callback data */
1740                 resp_cb_data = imc_create_resp_cb_data(cb, cb_data, NULL, 0);
1741
1742                 /* Configure modem I2S1 to 8khz, mono, PCM if routing to bluetooth */
1743                 if (sound_path_info->path == TEL_SOUND_PATH_BLUETOOTH ||
1744                                 sound_path_info->path == TEL_SOUND_PATH_STEREO_BLUETOOTH) {
1745                         tcore_at_prepare_and_send_request(co,
1746                                         "AT+XDRV=40,4,3,0,1,0,0,0,0,0,0,0,21",
1747                                         NULL, TCORE_AT_COMMAND_TYPE_NO_RESULT,
1748                                         NULL, NULL, NULL, NULL, NULL);
1749
1750                         tcore_at_prepare_and_send_request(co,
1751                                         "AT+XDRV=40,5,2,0,1,0,0,0,0,0,0,0,22",
1752                                         NULL, TCORE_AT_COMMAND_TYPE_NO_RESULT,
1753                                         NULL, NULL, NULL, NULL, NULL);
1754                 } else {
1755                         tcore_at_prepare_and_send_request(co,
1756                                         "AT+XDRV=40,4,3,0,1,0,8,0,1,0,2,0,21",
1757                                         NULL, TCORE_AT_COMMAND_TYPE_NO_RESULT,
1758                                         NULL, NULL, NULL, NULL, NULL);
1759
1760                         tcore_at_prepare_and_send_request(co,
1761                                         "AT+XDRV=40,5,2,0,1,0,8,0,1,0,2,0,22",
1762                                         NULL, TCORE_AT_COMMAND_TYPE_NO_RESULT,
1763                                         NULL, NULL, NULL, NULL, NULL);
1764                 }
1765
1766                 /* Configure modem I2S2 and do the modem routing */
1767                 tcore_at_prepare_and_send_request(co,
1768                                 "AT+XDRV=40,4,4,0,0,0,8,0,1,0,2,0,21",
1769                                 NULL, TCORE_AT_COMMAND_TYPE_NO_RESULT,
1770                                 NULL, NULL, NULL, NULL, NULL);
1771
1772                 tcore_at_prepare_and_send_request(co,
1773                                 "AT+XDRV=40,5,3,0,0,0,8,0,1,0,2,0,22",
1774                                 NULL, TCORE_AT_COMMAND_TYPE_NO_RESULT,
1775                                 NULL, NULL, NULL, NULL, NULL);
1776
1777                 tcore_at_prepare_and_send_request(co, "AT+XDRV=40,6,0,4",
1778                                 NULL, TCORE_AT_COMMAND_TYPE_NO_RESULT,
1779                                 NULL, NULL, NULL, NULL, NULL);
1780
1781                 tcore_at_prepare_and_send_request(co, "AT+XDRV=40,6,3,0",
1782                                 NULL, TCORE_AT_COMMAND_TYPE_NO_RESULT,
1783                                 NULL, NULL, NULL, NULL, NULL);
1784
1785                 tcore_at_prepare_and_send_request(co, "AT+XDRV=40,6,4,2",
1786                                 NULL, TCORE_AT_COMMAND_TYPE_NO_RESULT,
1787                                 NULL, NULL, NULL, NULL, NULL);
1788
1789                 tcore_at_prepare_and_send_request(co, "AT+XDRV=40,6,5,2",
1790                                 NULL, TCORE_AT_COMMAND_TYPE_NO_RESULT,
1791                                 NULL, NULL, NULL, NULL, NULL);
1792
1793                 /* amc enable */
1794                 tcore_at_prepare_and_send_request(co, "AT+XDRV=40,2,4",
1795                                 NULL, TCORE_AT_COMMAND_TYPE_NO_RESULT,
1796                                 NULL, NULL, NULL, NULL, NULL);
1797
1798                 tcore_at_prepare_and_send_request(co, "AT+XDRV=40,2,3",
1799                                 NULL, TCORE_AT_COMMAND_TYPE_NO_RESULT,
1800                                 NULL, NULL, NULL, NULL, NULL);
1801
1802                 /* amc route: AMC_RADIO_RX => AMC_I2S1_TX */
1803                 ret = tcore_at_prepare_and_send_request(co, "AT+XDRV=40,6,0,2",
1804                                 "+XDRV", TCORE_AT_COMMAND_TYPE_SINGLELINE, NULL,
1805                                 on_response_set_sound_path, resp_cb_data, NULL, NULL);
1806
1807                 IMC_CHECK_REQUEST_RET(ret, NULL, "imc_call_set_sound_path");
1808         }
1809
1810         return ret;
1811 }
1812
1813 /*
1814  * Operation - Set/Unset mute status.
1815  *
1816  * Request -
1817  * AT-Command: AT+XDRV=<group_id>,<function_id>[,<param_n>]
1818  * The first command parameter defines the involved driver group.
1819  * The second command parameter defines a certain function in the selected driver group.
1820  * Other parameters are dependent on the first two parameters.
1821  * Nearly all parameters are integer values, also if they are represented by literals.
1822  * Only very few are strings or
1823  * hex data strings.
1824  *
1825  * Response -
1826  * +XDRV: <group_id>,<function_id>,<xdrv_result>[,<response_n>]
1827  * The first response parameter defines the involved driver group.
1828  * The second response parameter defines the current function in the selected driver group.
1829  * The third response parameter defines the xdrv_result of the operation.
1830  * Additional response parameters dependent on the first two parameters.
1831  */
1832 static TelReturn imc_call_set_mute(CoreObject *co, gboolean mute, TcoreObjectResponseCallback cb,
1833         void *cb_data)
1834 {
1835         ImcRespCbData *resp_cb_data = NULL;
1836         gchar *at_cmd;
1837         TelReturn ret;
1838
1839         dbg("entry");
1840
1841         /* Response callback data */
1842         resp_cb_data = imc_create_resp_cb_data(cb, cb_data, NULL, 0);
1843
1844         /* AT - Command */
1845         if (mute)
1846                 at_cmd = g_strdup_printf("%s", "AT+XDRV=40,8,0,0,0");  /*MUTE*/
1847         else
1848                 at_cmd = g_strdup_printf("%s", "AT+XDRV=40,8,0,0,88"); /*UNMUTE*/
1849
1850         /* Send Request to modem */
1851         ret = tcore_at_prepare_and_send_request(co,
1852                         at_cmd, "+XDRV",
1853                         TCORE_AT_COMMAND_TYPE_SINGLELINE,
1854                         NULL,
1855                         on_response_imc_call_set_mute, resp_cb_data,
1856                         on_send_imc_request, NULL);
1857         IMC_CHECK_REQUEST_RET(ret, resp_cb_data, "imc_call_set_mute");
1858
1859         g_free(at_cmd);
1860
1861         return ret;
1862 }
1863
1864 static TelReturn imc_call_get_mute_status(CoreObject *co, TcoreObjectResponseCallback cb,
1865         void *cb_data)
1866 {
1867         dbg("entry");
1868
1869         dbg("exit");
1870         return TEL_RETURN_OPERATION_NOT_SUPPORTED;
1871 }
1872
1873
1874 static TelReturn imc_call_set_sound_recording(CoreObject *co, TelCallSoundRecording sound_rec,
1875         TcoreObjectResponseCallback cb, void *cb_data)
1876 {
1877         dbg("entry");
1878
1879         dbg("exit");
1880         return TEL_RETURN_OPERATION_NOT_SUPPORTED;
1881 }
1882
1883 static TelReturn imc_call_set_sound_equalization(CoreObject *co, const TelCallSoundEqualization *sound_eq,
1884         TcoreObjectResponseCallback cb, void *cb_data)
1885 {
1886         dbg("entry");
1887
1888         dbg("exit");
1889         return TEL_RETURN_OPERATION_NOT_SUPPORTED;
1890 }
1891
1892 /* Call Operations */
1893 static TcoreCallOps imc_call_ops = {
1894         .dial = imc_call_dial,
1895         .answer = imc_call_answer,
1896         .end = imc_call_end,
1897         .send_dtmf = imc_call_send_dtmf,
1898         .hold = imc_call_hold,
1899         .active = imc_call_active,
1900         .swap = imc_call_swap,
1901         .join = imc_call_join,
1902         .split = imc_call_split,
1903         .transfer = imc_call_transfer,
1904         .deflect = imc_call_deflect,
1905         .set_active_line = imc_call_set_active_line,
1906         .get_active_line = imc_call_get_active_line,
1907         .set_volume_info = imc_call_set_volume_info,
1908         .get_volume_info = imc_call_get_volume_info,
1909         .set_sound_path = imc_call_set_sound_path,
1910         .set_mute = imc_call_set_mute,
1911         .get_mute_status = imc_call_get_mute_status,
1912         .set_sound_recording = imc_call_set_sound_recording,
1913         .set_sound_equalization = imc_call_set_sound_equalization,
1914 };
1915
1916 gboolean imc_call_init(TcorePlugin *p, CoreObject *co)
1917 {
1918         dbg("Entry");
1919
1920         /* Set operations */
1921         tcore_call_set_ops(co, &imc_call_ops);
1922
1923         /* Add Callbacks */
1924         tcore_object_add_callback(co, "+XCALLSTAT", on_notification_imc_call_status, NULL);
1925         tcore_object_add_callback(co, "+CLIP", on_notification_imc_call_clip_info, NULL);
1926         tcore_object_add_callback(co, "+CSSU", on_notification_imc_call_ss_cssu_info, NULL);
1927         tcore_object_add_callback(co, "+CSSI", on_notification_imc_call_ss_cssi_info, NULL);
1928
1929         return TRUE;
1930 }
1931
1932 void imc_call_exit(TcorePlugin *p, CoreObject *co)
1933 {
1934         dbg("Exit");
1935 }