d18215e35b91288f0c36bece5ba3ece7ab3886d2
[platform/core/telephony/tel-plugin-imc.git] / src / imc_modem.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
34 #include <co_modem.h>
35
36 #include "imc_modem.h"
37 #include "imc_common.h"
38 #include "nvm/nvm.h"
39
40 static gboolean on_event_imc_nvm_update(CoreObject *co,
41         const void *event_info, void *user_data);
42
43 static TelModemResult
44 __imc_modem_convert_cme_error_tel_modem_result(const TcoreAtResponse *at_resp)
45 {
46         TelModemResult result = TEL_MODEM_RESULT_FAILURE;
47         const gchar *line;
48         GSList *tokens = NULL;
49
50         dbg("Entry");
51
52         if (!at_resp || !at_resp->lines) {
53                 err("Invalid response data");
54                 return result;
55         }
56
57         line = (const gchar *)at_resp->lines->data;
58         tokens = tcore_at_tok_new(line);
59         if (g_slist_length(tokens) > 0) {
60                 gchar *resp_str;
61                 gint cme_err;
62
63                 resp_str = g_slist_nth_data(tokens, 0);
64                 if (!resp_str) {
65                         err("Invalid CME Error data");
66                         tcore_at_tok_free(tokens);
67                         return result;
68                 }
69                 cme_err = atoi(resp_str);
70                 dbg("CME error[%d]", cme_err);
71
72                 switch (cme_err) {
73                 case 3:
74                         result = TEL_MODEM_RESULT_OPERATION_NOT_PERMITTED;
75                 break;
76
77                 case 4:
78                         result = TEL_MODEM_RESULT_OPERATION_NOT_SUPPORTED;
79                 break;
80
81                 case 20:
82                         result = TEL_MODEM_RESULT_MEMORY_FAILURE;
83                 break;
84
85                 case 50:
86                         result = TEL_MODEM_RESULT_INVALID_PARAMETER;
87                 break;
88
89                 case 100:
90                         result = TEL_MODEM_RESULT_UNKNOWN_FAILURE;
91                 break;
92
93                 default:
94                         result = TEL_MODEM_RESULT_FAILURE;
95                 }
96         }
97         tcore_at_tok_free(tokens);
98
99         return result;
100 }
101
102 /* NVM Req/Response */
103 static gboolean __imc_modem_check_nvm_response(const void *data, int command)
104 {
105         const TcoreAtResponse *at_resp = data;
106         const char *line;
107         char *resp_str;
108         GSList *tokens = NULL;
109         gboolean ret = FALSE;
110
111         dbg("Entered");
112
113         /* +XDRV: <group_id>,<function_id>,<xdrv_result>[,<response_n>] */
114         if (NULL == at_resp) {
115                 err("Input data is NULL");
116                 return FALSE;
117         }
118
119         if (at_resp->success > 0) {
120                 dbg("RESPONSE OK");
121                 line = (const char *) (((GSList *) at_resp->lines)->data);
122                 tokens = tcore_at_tok_new(line);
123
124                 /* Group ID */
125                 resp_str = g_slist_nth_data(tokens, 0);
126                 if (NULL == resp_str) {
127                         err("Group ID is missing ");
128                         goto OUT;
129                 }
130                 else if (IUFP_GROUP_ID != atoi(resp_str)) {
131                         err("Group ID mismatch");
132                         goto OUT;
133                 }
134
135                 /* Function ID */
136                 resp_str =  g_slist_nth_data(tokens, 1);
137                 if (NULL == resp_str) {
138                         err("Function ID is missing ");
139                         goto OUT;
140                 }
141                 else if (command != atoi(resp_str)) {
142                         err("Function ID mismatch");
143                         goto OUT;
144                 }
145
146                 /* XDRV Result */
147                 resp_str =  g_slist_nth_data(tokens, 2);
148                 if (NULL == resp_str) {
149                         err("XDRV result is missing ");
150                         goto OUT;
151                 }
152                 else if (XDRV_RESULT_OK != atoi(resp_str)) {
153                         err("XDRV result[%d] ", atoi(resp_str));
154                         goto OUT;
155                 }
156
157                 /* Result code */
158                 resp_str =  g_slist_nth_data(tokens, 3);
159                 if (NULL == resp_str) {
160                         err("UTA result is missing ");
161                         goto OUT;
162                 }
163                 else if (UTA_SUCCESS != atoi(resp_str)) {
164                         err("uta result[%d] ", atoi(resp_str));
165                         goto OUT;
166                 }
167
168                 ret = TRUE;
169         } else {
170                 dbg("Response NOK");
171         }
172
173 OUT:
174         tcore_at_tok_free(tokens);
175
176         dbg("Exit");
177         return ret;
178 }
179
180 static void __on_response_modem_unsuspend_nvm_updates(TcorePending *p,
181                                                         guint data_len, const void *data, void *user_data)
182 {
183         /* Check NVM response */
184         if (TRUE == __imc_modem_check_nvm_response(data, IUFP_SUSPEND)) {
185                 dbg("Priority level is set to get all updates since Boot-up");
186
187                 /* Create NV data file */
188                 if (nvm_create_nvm_data() == FALSE) {
189                         err("Failed to Create NV data file");
190                 }
191
192                 return;
193         }
194
195         err("Response NOT OK");
196 }
197
198 static void __imc_modem_unsuspend_nvm_updates(CoreObject *co)
199 {
200         char *cmd_str;
201         TelReturn ret;
202
203         /* Prepare AT-Command */
204         cmd_str = g_strdup_printf("AT+XDRV=%d, %d, %d, %d",
205                                         IUFP_GROUP_ID, IUFP_SUSPEND,
206                                         0, UTA_FLASH_PLUGIN_PRIO_UNSUSPEND_ALL);
207
208         /* Send Request to modem */
209         ret = tcore_at_prepare_and_send_request(co,
210                 cmd_str, "+XDRV:",
211                 TCORE_AT_COMMAND_TYPE_SINGLELINE,
212                 NULL,
213                 __on_response_modem_unsuspend_nvm_updates, NULL,
214                 on_send_imc_request, NULL);
215         IMC_CHECK_REQUEST_RET(ret, NULL, "Unsuspend Nvm Updates");
216
217         g_free(cmd_str);
218 }
219
220 static void __on_response_modem_send_nvm_update_ack(TcorePending *p,
221                                                         guint data_len, const void *data, void *user_data)
222 {
223         /* Check NVM response */
224         if (TRUE ==  __imc_modem_check_nvm_response(data, IUFP_UPDATE_ACK)) {
225                 dbg("[UPDATE ACK] OK");
226                 return;
227         }
228
229         err("[UPDATE ACK] NOT OK");
230 }
231
232 static void __imc_modem_send_nvm_update_ack(CoreObject *co)
233 {
234         char *cmd_str;
235         TelReturn ret;
236
237         /* Prepare AT-Command */
238         cmd_str = g_strdup_printf("AT+XDRV=%s, %s", IUFP_GROUP, IUFP_UPDATE_ACK_STR);
239
240         /* Send Request to modem */
241         ret = tcore_at_prepare_and_send_request(co,
242                 cmd_str, "+XDRV:",
243                 TCORE_AT_COMMAND_TYPE_SINGLELINE,
244                 NULL,
245                 __on_response_modem_send_nvm_update_ack, NULL,
246                 on_send_imc_request, NULL);
247         IMC_CHECK_REQUEST_RET(ret, NULL, "Nvm Update Ack");
248
249         g_free(cmd_str);
250 }
251
252 static void __on_response_modem_send_nvm_update_request_ack(TcorePending *p,
253                                                         guint data_len, const void *data, void *user_data)
254 {
255         /* Check NVM response */
256         if (TRUE == __imc_modem_check_nvm_response(data, IUFP_UPDATE_REQ_ACK)) {
257                 dbg("[REQUEST ACK] OK");
258                 return;
259         }
260
261         err("[REQUEST ACK] NOT OK");
262 }
263
264 static void __imc_modem_send_nvm_update_request_ack(CoreObject *co)
265 {
266         char *cmd_str;
267         TelReturn ret;
268
269         /* Prepare AT-Command */
270         cmd_str = g_strdup_printf("AT+XDRV=%s, %s", IUFP_GROUP, IUFP_UPDATE_REQ_ACK_STR);
271
272         /* Send Request to modem */
273         ret = tcore_at_prepare_and_send_request(co,
274                 cmd_str, "+XDRV:",
275                 TCORE_AT_COMMAND_TYPE_SINGLELINE,
276                 NULL,
277                 __on_response_modem_send_nvm_update_request_ack, NULL,
278                 on_send_imc_request, NULL);
279         IMC_CHECK_REQUEST_RET(ret, NULL, "Nvm Update Request Ack");
280
281         g_free(cmd_str);
282 }
283
284 static void __on_response_modem_register_nvm(TcorePending *p,
285                                                 guint data_len, const void *data, void *user_data)
286 {
287         /* Check NVM response */
288         if (TRUE == __imc_modem_check_nvm_response(data, IUFP_REGISTER)) {
289                 dbg("Registering successful");
290
291                 /* Send SUSPEND_UPDATE for all UPDATES */
292                 __imc_modem_unsuspend_nvm_updates(tcore_pending_ref_core_object(p));
293
294                 dbg("Exit");
295                 return;
296         }
297
298         err("Response NOT OK");
299 }
300
301 /* System function responses */
302 static void on_response_modem_set_flight_mode_internal(TcorePlugin *plugin,
303         gint result, const void *response, void *user_data)
304 {
305         CoreObject *co;
306         gboolean flight_mode;
307         dbg("Enter");
308
309         co = tcore_plugin_ref_core_object(plugin, CORE_OBJECT_TYPE_MODEM);
310         tcore_check_return_assert(co != NULL);
311
312         tcore_check_return(result == TEL_MODEM_RESULT_SUCCESS);
313
314         /* Get Flight mode state */
315         (void)tcore_modem_get_flight_mode_state(co, &flight_mode);
316
317         dbg("Setting Modem Fiight mode (internal) - [%s] - [SUCCESS]",
318                 (flight_mode ? "ON": "OFF"));
319
320         /*
321          * Send notification
322          *
323          * This is an internal request to set Flight mode, which is sent during
324          * boot-up based on AP-side configuration (VCONF).
325          *
326          * Need to notify TAPI through Notiifcation -
327          *      TCORE_NOTIFICATION_MODEM_FLIGHT_MODE
328          */
329         (void)tcore_object_send_notification(co,
330                 TCORE_NOTIFICATION_MODEM_FLIGHT_MODE,
331                 sizeof(gboolean), &flight_mode);
332 }
333
334 /* System functions */
335 gboolean imc_modem_power_on_modem(TcorePlugin *plugin)
336 {
337         CoreObject *co;
338         TcoreStorage *strg;
339         gboolean flight_mode;
340         TelModemPowerStatus power_status;
341
342         co = tcore_plugin_ref_core_object(plugin, CORE_OBJECT_TYPE_MODEM);
343         tcore_check_return_value_assert(co != NULL, FALSE);
344
345         /* Set Modem Power State to 'ON' */
346         tcore_modem_set_powered(co, TRUE);
347
348         /*
349          * Set Flight mode (as per AP settings -VCONF)
350          */
351         /* Get Flight mode from VCONFKEY */
352         strg = tcore_server_find_storage(tcore_plugin_ref_server(plugin), "vconf");
353         tcore_check_return_value_assert(strg != NULL, FALSE);
354
355         flight_mode = tcore_storage_get_bool(strg, STORAGE_KEY_FLIGHT_MODE);
356
357         /*
358          * Set Flight mode request is dispatched to Core Object (Modem)
359          * to ensure that 'Request Hooks' get executed.
360          */
361         (void)tcore_object_dispatch_request(co, TRUE,
362                 TCORE_COMMAND_MODEM_SET_FLIGHTMODE,
363                 &flight_mode, sizeof(gboolean),
364                 on_response_modem_set_flight_mode_internal, NULL);
365
366         /*
367          * Send notification
368          *
369          * Need to notify Modem is Powered UP through Notiifcation -
370          *      TCORE_NOTIFICATION_MODEM_POWER
371          */
372         power_status = TEL_MODEM_POWER_ON;
373         (void)tcore_object_send_notification(co,
374                 TCORE_NOTIFICATION_MODEM_POWER,
375                 sizeof(TelModemPowerStatus), &power_status);
376
377         return TRUE;
378 }
379
380 /* Modem Responses */
381 static void on_response_imc_modem_set_power_status(TcorePending *p,
382         guint data_len, const void *data, void *user_data)
383 {
384         const TcoreAtResponse *at_resp = data;
385         CoreObject *co = tcore_pending_ref_core_object(p);
386         ImcRespCbData *resp_cb_data = user_data;
387         TelModemPowerStatus *status;
388         gboolean powered = FALSE;
389
390         TelModemResult result = TEL_MODEM_RESULT_FAILURE;
391         dbg("Enter");
392
393         tcore_check_return_assert(co != NULL);
394         tcore_check_return_assert(resp_cb_data != NULL);
395
396         if (at_resp && at_resp->success){
397                 dbg("RESPONSE OK");
398                 result = TEL_MODEM_RESULT_SUCCESS;
399         } else{
400                 err("RESPONSE NOK");
401                 result = __imc_modem_convert_cme_error_tel_modem_result(at_resp);
402                 goto END;
403         }
404
405         status = (TelModemPowerStatus *)
406                 IMC_GET_DATA_FROM_RESP_CB_DATA(resp_cb_data);
407
408         /* Update Core Object */
409         switch (*status) {
410         case TEL_MODEM_POWER_ON:
411                 dbg("Setting Modem Power status [ON] - [%s]",
412                         (result == TEL_MODEM_RESULT_SUCCESS ? "SUCCESS" : "FAIL"));
413                 powered = TRUE;
414         break;
415         case TEL_MODEM_POWER_OFF:
416                 dbg("Setting Modem Power status [OFF] - [%s]",
417                         (result == TEL_MODEM_RESULT_SUCCESS ? "SUCCESS" : "FAIL"));
418                 powered = FALSE;
419         break;
420         default:
421                 warn("Unexpected - Setting Modem Power status [RESET] - [%s]",
422                         (result == TEL_MODEM_RESULT_SUCCESS ? "SUCCESS" : "FAIL"));
423         break;
424         }
425         tcore_modem_set_powered(co, powered);
426
427 END:
428         /* Invoke callback */
429         if (resp_cb_data->cb)
430                 resp_cb_data->cb(co, (gint)result, NULL, resp_cb_data->cb_data);
431
432         /* Free callback data */
433         imc_destroy_resp_cb_data(resp_cb_data);
434 }
435
436 static void on_response_imc_modem_set_flight_mode(TcorePending *p,
437         guint data_len, const void *data, void *user_data)
438 {
439         const TcoreAtResponse *at_resp = data;
440         CoreObject *co = tcore_pending_ref_core_object(p);
441         ImcRespCbData *resp_cb_data = user_data;
442         gboolean *enable;
443
444         TelModemResult result = TEL_MODEM_RESULT_FAILURE;
445         dbg("Enter");
446
447         tcore_check_return_assert(co != NULL);
448         tcore_check_return_assert(resp_cb_data != NULL);
449
450         if (at_resp && at_resp->success){
451                 dbg("RESPONSE OK");
452                 result = TEL_MODEM_RESULT_SUCCESS;
453         } else{
454                 err("RESPONSE NOK");
455                 result = __imc_modem_convert_cme_error_tel_modem_result(at_resp);
456                 goto END;
457         }
458
459         enable = (gboolean *)IMC_GET_DATA_FROM_RESP_CB_DATA(resp_cb_data);
460
461         dbg("Setting Modem Fiight mode - [%s] - [%s]",
462                 (*enable ? "ON": "OFF"),
463                 (result == TEL_MODEM_RESULT_SUCCESS ? "SUCCESS" : "FAIL"));
464
465         /* Update Core Object */
466         (void)tcore_modem_set_flight_mode_state(co, *enable);
467
468 END:
469         /* Invoke callback */
470         if (resp_cb_data->cb)
471                 resp_cb_data->cb(co, (gint)result, NULL, resp_cb_data->cb_data);
472
473         /* Free callback data */
474         imc_destroy_resp_cb_data(resp_cb_data);
475
476         /*
477          * In case Flight mode is set to OFF, we need to trigger
478          * Network Registration.
479          *
480          * This is taken care by Network module which hooks on
481          * Set Flight mode Request of Modem module.
482          */
483 }
484
485 /* Current modem does not support this operation */
486 #if 0
487 static void on_response_imc_modem_get_version(TcorePending *p,
488         guint data_len, const void *data, void *user_data)
489 {
490         const TcoreAtResponse *at_resp = data;
491         CoreObject *co = tcore_pending_ref_core_object(p);
492         ImcRespCbData *resp_cb_data = user_data;
493         TelModemVersion version = {{0}, {0}, {0}, {0}};
494
495         TelModemResult result = TEL_MODEM_RESULT_FAILURE;
496         dbg("Enter");
497
498         tcore_check_return_assert(co != NULL);
499         tcore_check_return_assert(resp_cb_data != NULL);
500
501         if (at_resp) {
502                 if (at_resp->lines) {
503                         const gchar *line;
504                         GSList *tokens = NULL;
505
506                         line = (const gchar *)at_resp->lines->data;
507                         tokens = tcore_at_tok_new(line);
508                         if (g_slist_length(tokens) > 0) {
509                                 if (at_resp->success) {
510                                         gchar *sw_ver = NULL, *hw_ver = NULL;
511                                         gchar *calib_date = NULL, *p_code = NULL;
512
513                                         sw_ver = g_slist_nth_data(tokens, 0);
514                                         hw_ver = g_slist_nth_data(tokens, 1);
515                                         calib_date = g_slist_nth_data(tokens, 2);
516                                         p_code = g_slist_nth_data(tokens, 3);
517                                         if (sw_ver != NULL){
518                                                 g_strlcpy(version.software_version,
519                                                         sw_ver,
520                                                         TEL_MODEM_VERSION_LENGTH_MAX + 1);
521                                         }
522                                         if (hw_ver != NULL){
523                                                 g_strlcpy(version.hardware_version,
524                                                         hw_ver,
525                                                         TEL_MODEM_VERSION_LENGTH_MAX + 1);
526                                         }
527                                         if (calib_date != NULL){
528                                                 g_strlcpy(version.calibration_date,
529                                                         calib_date,
530                                                         TEL_MODEM_VERSION_LENGTH_MAX + 1);
531                                         }
532                                         if (p_code != NULL){
533                                                 g_strlcpy(version.product_code,
534                                                         p_code,
535                                                         TEL_MODEM_VERSION_LENGTH_MAX + 1);
536                                         }
537                                         dbg("Version - Software: [%s] Hardware: [%s] "
538                                                 "Calibration date: [%s] Product "
539                                                 "Code: [%s]", sw_ver, hw_ver,
540                                                 calib_date, p_code);
541
542                                         result = TEL_MODEM_RESULT_SUCCESS;
543                                 } else {
544                                         err("RESPONSE - [NOK]");
545                                         err("[%s]", g_slist_nth_data(tokens, 0));
546                                 }
547                         } else {
548                                 err("Invalid response message");
549                                 result = TEL_MODEM_RESULT_UNKNOWN_FAILURE;
550                         }
551                         tcore_at_tok_free(tokens);
552                 }
553         }
554
555         /* Invoke callback */
556         if (resp_cb_data->cb)
557                 resp_cb_data->cb(co, (gint)result, &version, resp_cb_data->cb_data);
558
559         /* Free callback data */
560         imc_destroy_resp_cb_data(resp_cb_data);
561 }
562 #endif
563
564 static void on_response_imc_modem_get_imei(TcorePending *p,
565         guint data_len, const void *data, void *user_data)
566 {
567         const TcoreAtResponse *at_resp = data;
568         CoreObject *co = tcore_pending_ref_core_object(p);
569         ImcRespCbData *resp_cb_data = user_data;
570         gchar imei[TEL_MODEM_IMEI_LENGTH_MAX +1] = {0};
571
572         TelModemResult result = TEL_MODEM_RESULT_FAILURE;
573         dbg("Enter");
574
575         tcore_check_return_assert(co != NULL);
576         tcore_check_return_assert(resp_cb_data != NULL);
577
578         if (at_resp) {
579                 if (at_resp->lines) {
580                         const gchar *line;
581                         GSList *tokens = NULL;
582
583                         line = (const gchar *)at_resp->lines->data;
584                         tokens = tcore_at_tok_new(line);
585                         if (g_slist_length(tokens) == 1) {
586                                 if (at_resp->success) {
587                                         dbg("RESPONSE - [OK]");
588                                         g_strlcpy(imei,
589                                                 (const gchar *)g_slist_nth_data(tokens, 0),
590                                                 TEL_MODEM_IMEI_LENGTH_MAX+1);
591                                         dbg("IMEI: [%s]", imei);
592
593                                         result = TEL_MODEM_RESULT_SUCCESS;
594                                 } else {
595                                         err("RESPONSE - [NOK]");
596                                         err("[%s]", g_slist_nth_data(tokens, 0));
597                                         result = __imc_modem_convert_cme_error_tel_modem_result(at_resp);
598                                 }
599                         }  else {
600                                 err("Invalid response message");
601                                 result = TEL_MODEM_RESULT_UNKNOWN_FAILURE;
602                         }
603                         tcore_at_tok_free(tokens);
604                 }
605         }
606
607         /* Invoke callback */
608         if (resp_cb_data->cb)
609                 resp_cb_data->cb(co, (gint)result, imei, resp_cb_data->cb_data);
610
611         /* Free callback data */
612         imc_destroy_resp_cb_data(resp_cb_data);
613 }
614
615 /* Modem Operations */
616 /*
617  * Operation - set_power_status
618  *
619  * Request -
620  * AT-Command: AT+CFUN=<fun>
621  * where,
622  * <fun>
623  * 0    Mode to switch off MS
624  * ...  Other modes are available for other oprations
625  *
626  * Response -
627  * Success: (No Result)
628  *      OK
629  * Failure:
630  *      +CME ERROR: <error>
631  */
632 static TelReturn imc_modem_set_power_status(CoreObject *co,
633         TelModemPowerStatus status,
634         TcoreObjectResponseCallback cb, void *cb_data)
635 {
636         gchar *at_cmd;
637         guint power_mode;
638
639         ImcRespCbData *resp_cb_data;
640         TelReturn ret;
641
642         if (status == TEL_MODEM_POWER_ON) {
643                 warn("Modem Power ON - Not supported by CP");
644                 return TEL_RETURN_OPERATION_NOT_SUPPORTED;
645         } else if (status == TEL_MODEM_POWER_ERROR) {
646                 err("Modem Power ERROR - Invalid mode");
647                 return TEL_RETURN_INVALID_PARAMETER;
648         } else {
649                 dbg("Modem Power OFF");
650                 power_mode = 0;
651         }
652
653         /* AT-Command */
654         at_cmd = g_strdup_printf("AT+CFUN=%d", power_mode);
655
656         /* Response callback data */
657         resp_cb_data = imc_create_resp_cb_data(cb, cb_data,
658                                 &status, sizeof(TelModemPowerStatus));
659
660         /* Send Request to modem */
661         ret = tcore_at_prepare_and_send_request(co,
662                 at_cmd, NULL,
663                 TCORE_AT_COMMAND_TYPE_NO_RESULT,
664                 NULL,
665                 on_response_imc_modem_set_power_status, resp_cb_data,
666                 on_send_imc_request, NULL);
667         IMC_CHECK_REQUEST_RET(ret, resp_cb_data, "Set Power Status");
668
669         /* Free resources */
670         g_free(at_cmd);
671
672         return ret;
673 }
674
675 /*
676  * Operation - set_flight_mode
677  *
678  * Request -
679  * AT-Command: AT+CFUN=<fun>
680  * where,
681  * <fun>
682  * 0    Mode to switch off MS
683  * 1    Full functionality
684  * 4    Mode to disable phone both transmit and receive
685  *      RF circuits. Airplane mode.
686  * ...  Other modes are available for other oprations
687  *
688  * Response -
689  * Success: (No Result)
690  *      OK
691  * Failure:
692  *      +CME ERROR: <error>
693  */
694 static TelReturn imc_modem_set_flight_mode(CoreObject *co, gboolean enable,
695         TcoreObjectResponseCallback cb, void *cb_data)
696 {
697         gchar *at_cmd;
698         guint power_mode;
699
700         ImcRespCbData *resp_cb_data;
701         TelReturn ret;
702
703         if (enable) {
704                 dbg("Flight mode - [ON]");
705                 power_mode = 4;
706         } else {
707                 dbg("Flight mode - [OFF]");
708                 power_mode = 1;
709         }
710
711         /* AT-Command */
712         at_cmd = g_strdup_printf("AT+CFUN=%d", power_mode);
713
714         /* Response callback data */
715         resp_cb_data = imc_create_resp_cb_data(cb, cb_data,
716                                 &enable, sizeof(gboolean));
717
718         /* Send Request to modem */
719         ret = tcore_at_prepare_and_send_request(co,
720                 at_cmd, NULL,
721                 TCORE_AT_COMMAND_TYPE_NO_RESULT,
722                 NULL,
723                 on_response_imc_modem_set_flight_mode, resp_cb_data,
724                 on_send_imc_request, NULL);
725         IMC_CHECK_REQUEST_RET(ret, resp_cb_data, "Set Flight mode");
726
727         /* Free resources */
728         g_free(at_cmd);
729
730         return ret;
731 }
732
733 /*
734  * Operation - get_flight_mode
735  *
736  * Request -
737  * AT-Command: None
738  *      Fetch information from Core Object
739  *
740  * Response - flight_mode (gboolean)
741  */
742 static TelReturn imc_modem_get_flight_mode(CoreObject *co,
743         TcoreObjectResponseCallback cb, void *cb_data)
744 {
745         gboolean flight_mode;
746
747         /* Fetch Flight mode from Core Object */
748         (void)tcore_modem_get_flight_mode_state(co, &flight_mode);
749         dbg("Modem Flight mode - [%s]", (flight_mode ? "ON": "OFF"));
750
751         /* Invoke response callback */
752         if (cb)
753                 cb(co, (gint)TEL_MODEM_RESULT_SUCCESS, &flight_mode, cb_data);
754
755         return TEL_RETURN_SUCCESS;
756 }
757
758 /*
759  * Operation - get_version
760  *
761  * Request -
762  * AT-Command: AT+CGMR
763  *
764  * Response - version (TelModemVersion)
765  * Success: (Single line) -
766  *      <sw_ver>, <hw_ver>, <calib_date>, <p_code>
767  *      OK
768  * Note:
769  *      Success Response is different from standard 3GPP AT-Command (+CGMR)
770  * Failure:
771  *      +CME ERROR: <error>
772  */
773 static TelReturn imc_modem_get_version(CoreObject *co,
774         TcoreObjectResponseCallback cb, void *cb_data)
775 {
776         dbg("entry");
777
778         /* Current modem does not support this operation */
779 #if 0
780         ImcRespCbData *resp_cb_data;
781         TelReturn ret;
782
783         /* Response callback data */
784         resp_cb_data = imc_create_resp_cb_data(cb, cb_data,
785                                 NULL, 0);
786
787         /* Send Request to modem */
788         ret = tcore_at_prepare_and_send_request(co,
789                 "AT+CGMR", NULL,
790                 TCORE_AT_COMMAND_TYPE_SINGLELINE,
791                 TCORE_PENDING_PRIORITY_DEFAULT,
792                 NULL,
793                 on_response_imc_modem_get_version, resp_cb_data,
794                 on_send_imc_request, NULL,
795                 0, NULL, NULL);
796         IMC_CHECK_REQUEST_RET(ret, resp_cb_data, "Get Version");
797
798         return ret;
799 #endif
800
801         dbg("exit");
802         return TEL_RETURN_OPERATION_NOT_SUPPORTED;
803 }
804
805 /*
806  * Operation - get_imei
807  *
808  * Request -
809  * AT-Command: AT+CGSN
810  *
811  * Response - imei (gchar array of length 20+'\0' bytes)
812  * Success: (Single line)
813  *      <IMEI>
814  *      OK
815  * Failure:
816  *      +CME ERROR: <error>
817  */
818 static TelReturn imc_modem_get_imei(CoreObject *co,
819         TcoreObjectResponseCallback cb, void *cb_data)
820 {
821         ImcRespCbData *resp_cb_data;
822         TelReturn ret;
823
824         dbg("Enter");
825
826         /* Response callback data */
827         resp_cb_data = imc_create_resp_cb_data(cb, cb_data,
828                                 NULL, 0);
829
830         /* Send Request to modem */
831         ret = tcore_at_prepare_and_send_request(co,
832                 "AT+CGSN", NULL,
833                 TCORE_AT_COMMAND_TYPE_NUMERIC,
834                 NULL,
835                 on_response_imc_modem_get_imei, resp_cb_data,
836                 on_send_imc_request, NULL);
837         IMC_CHECK_REQUEST_RET(ret, resp_cb_data, "Get IMEI");
838
839         return ret;
840 }
841
842 /* Modem Operations */
843 static TcoreModemOps imc_modem_ops = {
844         .set_power_status = imc_modem_set_power_status,
845         .set_flight_mode = imc_modem_set_flight_mode,
846         .get_flight_mode = imc_modem_get_flight_mode,
847         .get_version = imc_modem_get_version,
848         .get_imei = imc_modem_get_imei
849 };
850
851 gboolean imc_modem_init(TcorePlugin *p, CoreObject *co)
852 {
853         dbg("Enter");
854
855         /* Set operations */
856         tcore_modem_set_ops(co, &imc_modem_ops);
857
858         /* Add Callbacks */
859         tcore_object_add_callback(co, "+XDRVI:", on_event_imc_nvm_update, NULL);
860
861         dbg("Exit");
862         return TRUE;
863 }
864
865 void imc_modem_exit(TcorePlugin *p, CoreObject *co)
866 {
867         dbg("Exit");
868 }
869
870 /*
871  * NV Manager - Support for Remote File System
872  */
873 /* NVM Hook */
874 static gboolean __imc_nvm_modem_rfs_hook(const char *data)
875 {
876         if (data != NULL)
877                 if (data[NVM_FUNCTION_ID_OFFSET] == XDRV_INDICATION)
878                         return TRUE;
879
880         return FALSE;
881 }
882
883 /* NVM Event */
884 gboolean on_event_imc_nvm_update(CoreObject *co,
885         const void *event_info, void *user_data)
886 {
887         GSList *tokens = NULL;
888         GSList *lines;
889         const char *line;
890         int function_id;
891
892         gboolean ret = TRUE;
893         dbg("Entered");
894
895         lines = (GSList *)event_info;
896         line = lines->data;
897         dbg("Line: [%s]", line);
898
899         function_id = nvm_sum_4_bytes(&line[NVM_FUNCTION_ID_OFFSET]);
900         dbg("Function ID: [%d]", function_id);
901         if (IUFP_UPDATE == function_id) {
902                 dbg("Calling process nvm_update");
903
904                 /*
905                  * Process NV Update indication
906                  *
907                  * +XDRVI: IUFP_GROUP, IUFP_UPDATE, <xdrv_result>, <data>
908                  */
909                 if (NVM_NO_ERR == nvm_process_nv_update(line)) {
910                         dbg("NV data processed successfully");
911
912                         /* Acknowledge NV Update */
913                         __imc_modem_send_nvm_update_ack(co);
914
915                         return ret;
916                 } else {
917                         err("NV data processing failed");
918                         ret = FALSE;
919                 }
920         } else {
921                 tokens = tcore_at_tok_new(line);
922                 if (g_slist_length(tokens) < 3) {
923                         err("XDRVI event with less number of tokens, Ignore!!!");
924                         ret = FALSE;
925                 }
926                 else if (IUFP_GROUP_ID != atoi(g_slist_nth_data(tokens, 0))) {
927                         err("Group ID mismatch, Ignore!!!");
928                         ret = FALSE;
929                 }
930                 else {
931                         switch (atoi(g_slist_nth_data(tokens, 1))) {
932                                 case IUFP_UPDATE_REQ:
933                                         dbg("NV Update Request");
934
935                                         /* Acknowledge the Update Request */
936                                         __imc_modem_send_nvm_update_request_ack(co);
937                                 break;
938
939                                 case IUFP_NO_PENDING_UPDATE:
940                                         dbg("NO pending NV Update(s)!!!");
941                                         /* Can send FLUSH request to get fresh updates */
942                                 break;
943
944                                 default:
945                                         err("Unspported Function ID [%d], Ignore", atoi(g_slist_nth_data(tokens, 1)));
946                                         ret = FALSE;
947                         }
948                 }
949
950                 tcore_at_tok_free(tokens);
951         }
952
953         dbg("Exit");
954         return ret;
955 }
956
957 /* NVM Register */
958 void imc_modem_register_nvm(CoreObject *co)
959 {
960         char *cmd_str;
961         TelReturn ret;
962
963         /* Prepare AT-Command */
964         cmd_str = g_strdup_printf("AT+XDRV=%s, %s, %s",
965                                         IUFP_GROUP, IUFP_REGISTER_STR, XDRV_ENABLE);
966
967         /* Send Request to modem */
968         ret = tcore_at_prepare_and_send_request(co,
969                 cmd_str, "+XDRV:",
970                 TCORE_AT_COMMAND_TYPE_SINGLELINE,
971                 NULL,
972                 __on_response_modem_register_nvm, NULL,
973                 on_send_imc_request, NULL);
974         if (ret != TEL_RETURN_SUCCESS) {
975                 err("Failed to process request - [Register NVM]");
976         }
977         else {
978                 /* Add RFS hook */
979                 dbg("Adding NVM hook");
980                 tcore_at_add_hook(tcore_object_get_hal(co), __imc_nvm_modem_rfs_hook);
981         }
982
983         g_free(cmd_str);
984 }