Use key-manager API instead of secure-storage
[platform/core/telephony/tel-plugin-imc.git] / src / imc_modem.c
1 /*
2  * tel-plugin-imc
3  *
4  * Copyright (c) 2012 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact: Harish Bishnoi <hbishnoi@samsung.com>
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25
26 #include <glib.h>
27
28 #include <tcore.h>
29 #include <hal.h>
30 #include <core_object.h>
31 #include <plugin.h>
32 #include <user_request.h>
33 #include <queue.h>
34 #include <co_modem.h>
35 #include <storage.h>
36 #include <server.h>
37 #include <at.h>
38
39 #include "imc_common.h"
40 #include "imc_modem.h"
41 #include "nvm/nvm.h"
42
43 /*
44  * Modem Private data
45  */
46 typedef struct {
47         /* IMEI */
48         gboolean imei_valid;    /**< IMEI validatity flag */
49         char imei[MODEM_DEVICE_IMEI_LEN_MAX];
50
51         /* Version information */
52         gboolean version_valid; /**< Version validatity flag */
53         char software[33];
54         char hardware[33];
55         char calibration[33];
56         char product_code[33];
57 } PrivateData;
58
59 static void on_confirmation_modem_message_send(TcorePending *pending,
60         gboolean result, void *user_data);
61 static void on_response_network_registration(TcorePending *pending,
62         int data_len, const void *data, void *user_data);
63 static void on_response_enable_proactive_command(TcorePending *pending,
64         int data_len, const void *data, void *user_data);
65
66 /* NVM */
67 static gboolean on_event_modem_nvm_update(CoreObject *co_modem,
68         const void *event_info, void *user_data);
69 static void modem_unsuspend_nvm_updates(CoreObject *co_modem);
70 static void modem_send_nvm_update_ack(CoreObject *co_modem);
71 static void modem_send_nvm_update_request_ack(CoreObject *co_modem);
72
73 static void on_confirmation_modem_message_send(TcorePending *pending,
74         gboolean result, void *user_data)
75 {
76         dbg("Request send: [%s]", (result == TRUE ? "Success" : "Fail"));
77 }
78
79 static void on_response_enable_proactive_command(TcorePending *pending,
80         int data_len, const void *data, void *user_data)
81 {
82         const TcoreATResponse *at_resp = data;
83
84         dbg("[Response] Pro-active command enabling - RESPONSE '%s'",
85                 (at_resp->success > 0 ? "OK" : "NOK"));
86 }
87
88 static void on_response_network_registration(TcorePending *pending,
89         int data_len, const void *data, void *user_data)
90 {
91         const TcoreATResponse *at_resp = data;
92
93         dbg("[Response] Network Registration enable - RESPONSE '%s'",
94                 (at_resp->success > 0 ? "OK" : "NOK"));
95 }
96
97 static void on_response_modem_power_off(TcorePending *pending,
98         int data_len, const void *data, void *user_data)
99 {
100         const TcoreATResponse *at_resp = data;
101         struct tresp_modem_power_off modem_power_off_resp;
102         CoreObject *co_modem = 0;
103         UserRequest *ur;
104         TcoreHal *h = 0;
105
106         dbg("[Response] Modem Power OFF - RESPONSE '%s'",
107                 (at_resp->success > 0 ? "OK" : "NOK"));
108
109         co_modem = tcore_pending_ref_core_object(pending);
110         h = tcore_object_get_hal(co_modem);
111
112         if (at_resp->success > 0) {
113                 modem_power_off_resp.result = TCORE_RETURN_SUCCESS;
114
115                 /* Update HAL state */
116                 tcore_hal_set_power_state(h, FALSE);
117         } else {
118                 modem_power_off_resp.result = TCORE_RETURN_FAILURE;
119         }
120
121         /* Send Response */
122         ur = tcore_pending_ref_user_request(pending);
123         tcore_user_request_send_response(ur,
124                 TRESP_MODEM_POWER_OFF,
125                 sizeof(struct tresp_modem_power_off), &modem_power_off_resp);
126 }
127
128 static void on_response_modem_set_flight_mode(TcorePending *pending,
129         int data_len, const void *data, void *user_data)
130 {
131         CoreObject *co_modem = NULL;
132         UserRequest *ur = NULL;
133         const TcoreATResponse *at_resp = data;
134         struct tresp_modem_set_flightmode modem_set_flightmode_resp = {0};
135         int response = 0;
136         struct tnoti_modem_flight_mode modem_flight_mode = {0};
137
138         co_modem = tcore_pending_ref_core_object(pending);
139         ur = tcore_pending_ref_user_request(pending);
140
141         dbg("[Response] Modem Set Flight mode - RESPONSE '%s'",
142                 (at_resp->success > 0 ? "OK" : "NOK"));
143
144         if (at_resp->success > 0) {
145                 modem_set_flightmode_resp.result = TCORE_RETURN_SUCCESS;
146         } else {
147                 GSList *tokens = NULL;
148                 const char *line = NULL;
149
150                 line = (const char *) at_resp->final_response;
151                 tokens = tcore_at_tok_new(line);
152
153                 if (g_slist_length(tokens) < 1) {
154                         dbg("err cause not specified or string corrupted");
155                 } else {
156                         response = atoi(g_slist_nth_data(tokens, 0));
157                         err("error response: %d", response);
158                         /* TODO: CMEE error mapping is required. */
159                 }
160                 tcore_at_tok_free(tokens);
161                 modem_set_flightmode_resp.result = TCORE_RETURN_3GPP_ERROR;
162         }
163
164         if (NULL == ur) {
165                 dbg("Internal request created during boot-up sequence");
166
167                 if (at_resp->success > 0) {
168                         Server *server;
169
170                         modem_flight_mode.enable =
171                                 tcore_modem_get_flight_mode_state(co_modem);
172                         dbg("Sending Flight Mode Notification (%d) to Telephony Server",
173                                 modem_flight_mode.enable);
174
175                         server = tcore_plugin_ref_server(tcore_object_ref_plugin(co_modem));
176
177                         /* Send Notification */
178                         tcore_server_send_notification(server,
179                                 co_modem, TNOTI_MODEM_FLIGHT_MODE,
180                                 sizeof(struct tnoti_modem_flight_mode), &modem_flight_mode);
181                 }
182         } else {
183                 Server *server;
184                 const struct treq_modem_set_flightmode *req_data = NULL;
185
186                 dbg("Sending response for Flight mode operation");
187
188                 req_data = tcore_user_request_ref_data(ur, NULL);
189
190                 if (TCORE_RETURN_SUCCESS == modem_set_flightmode_resp.result) {
191                         if (TRUE == req_data->enable)
192                                 tcore_modem_set_flight_mode_state(co_modem, TRUE);
193                         else
194                                 tcore_modem_set_flight_mode_state(co_modem, FALSE);
195                 }
196
197                 /* Send Response */
198                 tcore_user_request_send_response(ur,
199                         TRESP_MODEM_SET_FLIGHTMODE,
200                         sizeof(struct tresp_modem_set_flightmode), &modem_set_flightmode_resp);
201
202                 modem_flight_mode.enable = tcore_modem_get_flight_mode_state(co_modem);
203
204
205                 server = tcore_plugin_ref_server(tcore_object_ref_plugin(co_modem));
206
207                 /* Send Notification */
208                 tcore_server_send_notification(server,
209                         co_modem, TNOTI_MODEM_FLIGHT_MODE,
210                         sizeof(struct tnoti_modem_flight_mode), &modem_flight_mode);
211
212                 if (req_data->enable == 0) {
213                         dbg("Flight mode is disabled, trigger COPS to register on network");
214
215                         /* Trigger Network registration (for the moment automatic) */
216                         tcore_prepare_and_send_at_request(co_modem,
217                                 "AT+COPS=0", NULL,
218                                 TCORE_AT_NO_RESULT, NULL,
219                                 NULL, NULL,
220                                 NULL, NULL, 0, NULL, NULL);
221                 }
222         }
223 }
224
225 static void on_response_modem_get_imei(TcorePending *pending,
226         int data_len, const void *data, void *user_data)
227 {
228         const TcoreATResponse *at_resp = data;
229         struct tresp_modem_get_imei modem_get_imei_resp;
230         UserRequest *ur = NULL;
231         GSList *tokens = NULL;
232         const char *line;
233
234         memset(&modem_get_imei_resp, 0x0, sizeof(struct tresp_modem_get_imei));
235
236         if (at_resp->success > 0) {
237                 CoreObject *co = NULL;
238                 PrivateData *priv_data = NULL;
239
240                 dbg("RESPONSE OK");
241
242                 if (at_resp->lines) {
243                         line = (const char *) at_resp->lines->data;
244                         tokens = tcore_at_tok_new(line);
245                         if (g_slist_length(tokens) != 1) {
246                                 msg("invalid message");
247                                 goto OUT;
248                         }
249                 }
250
251                 modem_get_imei_resp.result = TCORE_RETURN_SUCCESS;
252                 strncpy(modem_get_imei_resp.imei, g_slist_nth_data(tokens, 0), MODEM_DEVICE_IMEI_LEN_MAX - 1);
253                 dbg("IMEI: [%s]", modem_get_imei_resp.imei);
254
255                 /* Cache IMEI */
256                 co = tcore_pending_ref_core_object(pending);
257                 priv_data = tcore_object_ref_user_data(co);
258                 priv_data->imei_valid = TRUE;
259                 strncpy(priv_data->imei, modem_get_imei_resp.imei, MODEM_DEVICE_IMEI_LEN_MAX - 1);
260         } else {
261                 dbg("RESPONSE NOK");
262
263                 if (at_resp->lines) {
264                         line = (const char *) at_resp->lines->data;
265                         tokens = tcore_at_tok_new(line);
266                 }
267
268                 if (g_slist_length(tokens) < 1) {
269                         dbg("err cause not specified or string corrupted");
270
271                         modem_get_imei_resp.result = TCORE_RETURN_3GPP_ERROR;
272                 } else {
273                         int response = atoi(g_slist_nth_data(tokens, 0));
274                         err("error response: %d", response);
275
276                         /* TODO: CMEE error mapping is required. */
277                         modem_get_imei_resp.result = TCORE_RETURN_3GPP_ERROR;
278                 }
279         }
280
281         /* Send Response */
282         ur = tcore_pending_ref_user_request(pending);
283         tcore_user_request_send_response(ur,
284                 TRESP_MODEM_GET_IMEI,
285                 sizeof(struct tresp_modem_get_imei), &modem_get_imei_resp);
286
287 OUT:
288         tcore_at_tok_free(tokens);
289 }
290
291 static void on_response_modem_get_version(TcorePending *pending,
292         int data_len, const void *data, void *user_data)
293 {
294         const TcoreATResponse *at_resp = data;
295         struct tresp_modem_get_version modem_get_version_resp;
296         UserRequest *ur = NULL;
297         GSList *tokens = NULL;
298         const char *line = NULL;
299
300         memset(&modem_get_version_resp, 0, sizeof(struct tresp_modem_get_version));
301
302         if (at_resp->success > 0) {
303                 CoreObject *co = NULL;
304                 PrivateData *priv_data = NULL;
305
306                 char *software_version = NULL;
307                 char *hardware_version = NULL;
308                 char *calibration_date = NULL;
309                 char *product_code = NULL;
310                 char *model_id = NULL;
311
312                 dbg("RESPONSE OK");
313
314                 if (at_resp->lines) {
315                         line = (const char *) at_resp->lines->data;
316
317                         tokens = tcore_at_tok_new(line);
318                         if (g_slist_length(tokens) == 1) {
319                                 software_version = g_slist_nth_data(tokens, 0);
320                                 dbg("Software version: [%s]", software_version);
321                         } else if (g_slist_length(tokens) == 5) {
322                                 software_version = g_slist_nth_data(tokens, 0);
323                                 hardware_version = g_slist_nth_data(tokens, 1);
324                                 calibration_date = g_slist_nth_data(tokens, 2);
325                                 product_code = g_slist_nth_data(tokens, 3);
326                                 model_id = g_slist_nth_data(tokens, 4);
327
328                                 dbg("Software version: [%s] Hardware version: [%s] " \
329                                         "Calibration: [%s] Product code: [%s] Model ID: [%s]",
330                                         software_version, hardware_version,
331                                         calibration_date, product_code, model_id);
332                         } else {
333                                 err("Invalid message");
334                                 goto OUT;
335                         }
336                 }
337
338                 co = tcore_pending_ref_core_object(pending);
339                 priv_data = tcore_object_ref_user_data(co);
340
341                 /*
342                  * Update response structure and Cache data
343                  */
344                 priv_data->version_valid = TRUE;
345
346                 /* Software version */
347                 if (software_version) {
348                         snprintf(modem_get_version_resp.software,
349                                 33,  "%s", software_version);
350                         snprintf(priv_data->software,
351                                 33,  "%s", software_version);
352                 }
353
354                 /* Hardware version */
355                 if (hardware_version) {
356                         snprintf(modem_get_version_resp.hardware,
357                                 33,  "%s", hardware_version);
358                         snprintf(priv_data->hardware,
359                                 33,  "%s", hardware_version);
360                 }
361
362                 /* Calibration date */
363                 if (calibration_date) {
364                         snprintf(modem_get_version_resp.calibration,
365                                 33,  "%s", calibration_date);
366                         snprintf(priv_data->calibration,
367                                 33,  "%s", calibration_date);
368                 }
369
370                 /* Product code */
371                 if (product_code) {
372                         snprintf(modem_get_version_resp.product_code,
373                                 33,  "%s", product_code);
374                         snprintf(priv_data->product_code,
375                                 33,  "%s", product_code);
376                 }
377         } else {
378                 dbg("RESPONSE NOK");
379                 if (at_resp->lines) {
380                         line = (const char *) at_resp->lines->data;
381                         tokens = tcore_at_tok_new(line);
382                 }
383
384                 if (g_slist_length(tokens) < 1) {
385                         dbg("err cause not specified or string corrupted");
386
387                         modem_get_version_resp.result = TCORE_RETURN_3GPP_ERROR;
388                 } else {
389                         int response = atoi(g_slist_nth_data(tokens, 0));
390                         err("error response: %d", response);
391
392                         /* TODO: CMEE error mapping is required. */
393                         modem_get_version_resp.result = TCORE_RETURN_3GPP_ERROR;
394                 }
395         }
396
397         /* Send Response */
398         ur = tcore_pending_ref_user_request(pending);
399         tcore_user_request_send_response(ur,
400                 TRESP_MODEM_GET_VERSION,
401                 sizeof(struct tresp_modem_get_version), &modem_get_version_resp);
402
403 OUT:
404         tcore_at_tok_free(tokens);
405 }
406
407 static enum tcore_hook_return on_hook_modem_sim_init_status(Server *s,
408         CoreObject *source, enum tcore_notification_command command,
409         unsigned int data_len, void *data, void *user_data)
410 {
411         const struct tnoti_sim_status *noti_sim_status;
412
413         dbg("SIM INIT Status");
414
415         noti_sim_status = data;
416         if (noti_sim_status == NULL) {
417                 err("SIM notification data is NULL");
418                 return TCORE_HOOK_RETURN_CONTINUE;
419         }
420
421         /* If SIM is initialized, Enable STK and and attach to Network */
422         dbg("SIM Status: [%d]", noti_sim_status->sim_status);
423         if (noti_sim_status->sim_status == SIM_STATUS_INIT_COMPLETED) {
424                 TcorePlugin *plugin;
425                 CoreObject *co_network;
426                 CoreObject *co_sat;
427
428                 dbg("SIM ready for attach!!! Enable STK and attach to Network");
429
430                 plugin = tcore_object_ref_plugin(source);
431
432                 co_network = tcore_plugin_ref_core_object(plugin, CORE_OBJECT_TYPE_NETWORK);
433                 if (co_network) {
434                         /* Sending AT+COPS */
435                         tcore_prepare_and_send_at_request(co_network,
436                                 "AT+COPS=0", NULL,
437                                 TCORE_AT_NO_RESULT, NULL,
438                                 on_response_network_registration, NULL,
439                                 NULL, NULL, 0, NULL, NULL);
440                 }
441
442                 co_sat = tcore_plugin_ref_core_object(plugin, CORE_OBJECT_TYPE_SAT);
443                 if (co_sat) {
444                         /* Sending AT+CFUN=6 */
445                         tcore_prepare_and_send_at_request(co_sat,
446                                 "AT+CFUN=6", NULL,
447                                 TCORE_AT_NO_RESULT, NULL,
448                                 on_response_enable_proactive_command, NULL,
449                                 NULL, NULL, 0, NULL, NULL);
450                 }
451
452         }
453
454         return TCORE_HOOK_RETURN_CONTINUE;
455 }
456
457 gboolean modem_power_on(TcorePlugin *plugin)
458 {
459         Server *server;
460         CoreObject *co_modem = NULL;
461         struct treq_modem_set_flightmode flight_mode_set;
462         struct tnoti_modem_power modem_power;
463         Storage *strg = NULL;
464
465         co_modem = tcore_plugin_ref_core_object(plugin, CORE_OBJECT_TYPE_MODEM);
466         if (co_modem == NULL) {
467                 err("Modem Core object is NULL");
468                 return FALSE;
469         }
470
471         /* Set Modem Power State to 'ON' */
472         tcore_modem_set_powered(co_modem, TRUE);
473
474         server = tcore_plugin_ref_server(plugin);
475
476         /* Get Flight mode from VCONFKEY */
477         strg = tcore_server_find_storage(server, "vconf");
478         flight_mode_set.enable = tcore_storage_get_bool(strg, STORAGE_KEY_FLIGHT_MODE_BOOL);
479
480         /*
481          * Set Flight mode as per AP settings
482          */
483         if (flight_mode_set.enable) {           /* Radio OFF */
484                 dbg("Enabling Flight mode");
485
486                 tcore_prepare_and_send_at_request(co_modem,
487                         "AT+CFUN=4", NULL,
488                         TCORE_AT_NO_RESULT, NULL,
489                         on_response_modem_set_flight_mode, NULL,
490                         NULL, NULL, 0, NULL, NULL);
491
492                 /* Set Flight mode TRUE */
493                 tcore_modem_set_flight_mode_state(co_modem, TRUE);
494         } else {                                /* Radio ON */
495                 dbg("Disabling Flight mode");
496
497                 tcore_prepare_and_send_at_request(co_modem,
498                         "AT+CFUN=1", NULL,
499                         TCORE_AT_NO_RESULT, NULL,
500                         on_response_modem_set_flight_mode, NULL,
501                         NULL, NULL, 0, NULL, NULL);
502
503                 /* Set Flight mode FALSE */
504                 tcore_modem_set_flight_mode_state(co_modem, FALSE);
505         }
506
507         /* Get IMEI */
508         tcore_prepare_and_send_at_request(co_modem,
509                 "AT+CGSN", NULL,
510                 TCORE_AT_NUMERIC, NULL,
511                 on_response_modem_get_imei, NULL,
512                 NULL, NULL, 0, NULL, NULL);
513
514         /* Get Version Number  */
515         tcore_prepare_and_send_at_request(co_modem,
516                 "AT+CGMR", NULL,
517                 TCORE_AT_SINGLELINE, NULL,
518                 on_response_modem_get_version, NULL,
519                 NULL, NULL, 0, NULL, NULL);
520
521         /* Send Notification - MODEM_POWER */
522         modem_power.state = MODEM_STATE_ONLINE;
523
524         dbg("Sending notification - Modem Power state: [ONLINE]");
525         tcore_server_send_notification(server, co_modem,
526                 TNOTI_MODEM_POWER,
527                 sizeof(modem_power), &modem_power);
528
529         return TRUE;
530 }
531
532 static TReturn modem_power_off(CoreObject *co_modem, UserRequest *ur)
533 {
534         TcoreHal *hal;
535
536         hal = tcore_object_get_hal(co_modem);
537         if (FALSE == tcore_hal_get_power_state(hal)) {
538                 struct tresp_modem_power_off modem_power_off_resp;
539
540                 err("Modem is in Powered OFF state!");
541
542                 modem_power_off_resp.result = TCORE_RETURN_SUCCESS;
543
544                 tcore_user_request_send_response(ur,
545                         TRESP_MODEM_POWER_OFF,
546                         sizeof(struct tresp_modem_power_off), &modem_power_off_resp);
547
548                 return TCORE_RETURN_SUCCESS;
549         }
550
551         dbg("[Request] Modem Power OFF - Command: [%s]", "AT+CFUN=0");
552
553         return tcore_prepare_and_send_at_request(co_modem,
554                 "AT+CFUN=0", NULL,
555                 TCORE_AT_NO_RESULT, ur,
556                 on_response_modem_power_off, hal,
557                 on_confirmation_modem_message_send, NULL,
558                 0, NULL, NULL);
559 }
560
561 static TReturn modem_get_imei(CoreObject *co_modem, UserRequest *ur)
562 {
563         PrivateData *priv_data = NULL;
564         TcoreHal *hal;
565
566         dbg("Exit");
567
568         hal = tcore_object_get_hal(co_modem);
569         if (FALSE == tcore_hal_get_power_state(hal)) {
570                 err("CP not ready!");
571                 return TCORE_RETURN_ENOSYS;
572         }
573
574         /*
575          * Check if valid IMEI is available in Cache -
576          *      if Yes, then provide form Cache;
577          *      else, fetch from CP
578          */
579         priv_data = tcore_object_ref_user_data(co_modem);
580         if (priv_data && priv_data->imei_valid) {
581                 struct tresp_modem_get_imei modem_get_imei_resp;
582                 TReturn ret;
583
584                 memset(&modem_get_imei_resp, 0x0, sizeof(struct tresp_modem_get_imei));
585
586                 modem_get_imei_resp.result = TCORE_RETURN_SUCCESS;
587                 memcpy(modem_get_imei_resp.imei,
588                         priv_data->imei, MODEM_DEVICE_IMEI_LEN_MAX);
589
590                 dbg("Valid IMEI information present in cache - IMEI: [%s]",
591                         modem_get_imei_resp.imei);
592
593                 /* Send Response */
594                 ret = tcore_user_request_send_response(ur,
595                         TRESP_MODEM_GET_IMEI,
596                         sizeof(struct tresp_modem_get_imei), &modem_get_imei_resp);
597                 if (ret == TCORE_RETURN_SUCCESS)
598                         tcore_user_request_unref(ur);
599
600                 return ret;
601         }
602
603         dbg("[Request] Get IMEI - Command: [%s]", "AT+CGSN");
604
605         return tcore_prepare_and_send_at_request(co_modem,
606                 "AT+CGSN", NULL,
607                 TCORE_AT_NUMERIC, ur,
608                 on_response_modem_get_imei, hal,
609                 on_confirmation_modem_message_send, NULL,
610                 0, NULL, NULL);
611 }
612
613
614 static TReturn modem_get_version(CoreObject *co_modem, UserRequest *ur)
615 {
616         PrivateData *priv_data = NULL;
617         TcoreHal *hal;
618
619         hal = tcore_object_get_hal(co_modem);
620         if (FALSE == tcore_hal_get_power_state(hal)) {
621                 err("CP not ready!");
622                 return TCORE_RETURN_ENOSYS;
623         }
624
625         /*
626          * Check if valid Version information is available in Cache -
627          *      if Yes, then provide form Cache;
628          *      else, fetch from CP
629          */
630         priv_data = tcore_object_ref_user_data(co_modem);
631         if (priv_data && priv_data->version_valid) {
632                 struct tresp_modem_get_version modem_get_version_resp;
633                 TReturn ret;
634
635                 memset(&modem_get_version_resp, 0x0, sizeof(struct tresp_modem_get_version));
636
637                 modem_get_version_resp.result = TCORE_RETURN_SUCCESS;
638                 snprintf(modem_get_version_resp.software,
639                         33, "%s", priv_data->software);
640                 snprintf(modem_get_version_resp.hardware,
641                         33, "%s", priv_data->hardware);
642                 snprintf(modem_get_version_resp.calibration,
643                         33, "%s", priv_data->calibration);
644                 snprintf(modem_get_version_resp.product_code,
645                         33, "%s", priv_data->product_code);
646
647                 dbg("Valid Version information present in cache -" \
648                         "Software: [%s] Hardware: [%s] Calibration: [%s] Product code: [%s]",
649                         modem_get_version_resp.software, modem_get_version_resp.hardware,
650                         modem_get_version_resp.calibration, modem_get_version_resp.product_code);
651
652                 /* Send Response */
653                 ret = tcore_user_request_send_response(ur,
654                         TRESP_MODEM_GET_VERSION,
655                         sizeof(struct tresp_modem_get_version), &modem_get_version_resp);
656                 if (ret == TCORE_RETURN_SUCCESS)
657                         tcore_user_request_unref(ur);
658
659                 return ret;
660         }
661
662         dbg("[Request] Get VERSION - Command: [%s]", "AT+CGMR");
663
664         return tcore_prepare_and_send_at_request(co_modem,
665                 "AT+CGMR", NULL,
666                 TCORE_AT_SINGLELINE, ur,
667                 on_response_modem_get_version, hal,
668                 on_confirmation_modem_message_send, NULL,
669                 0, NULL, NULL);
670 }
671
672 static TReturn modem_set_flight_mode(CoreObject *co_modem, UserRequest *ur)
673 {
674         TcoreHal *hal = NULL;
675         const struct treq_modem_set_flightmode *req_data = NULL;
676         char *cmd_str = NULL;
677
678         hal = tcore_object_get_hal(co_modem);
679         if (FALSE == tcore_hal_get_power_state(hal)) {
680                 err("CP not ready!");
681                 return TCORE_RETURN_ENOSYS;
682         }
683
684         req_data = tcore_user_request_ref_data(ur, NULL);
685         if (req_data->enable)
686                 cmd_str = "AT+CFUN=4";
687         else
688                 cmd_str = "AT+CFUN=1";
689
690         dbg("[Request] Set Modem Flight mode [%s] - Command: [%s]",
691                 (req_data->enable ? "ON" : "OFF"), cmd_str);
692
693         return tcore_prepare_and_send_at_request(co_modem,
694                 (const char *)cmd_str, NULL,
695                 TCORE_AT_NO_RESULT, ur,
696                 on_response_modem_set_flight_mode, hal,
697                 on_confirmation_modem_message_send, NULL,
698                 0, NULL, NULL);
699 }
700
701 static TReturn modem_get_flight_mode(CoreObject *co_modem, UserRequest *ur)
702 {
703         struct tresp_modem_get_flightmode modem_get_flightmode_resp;
704         TReturn ret;
705
706         dbg("[Request] Get Modem Flight mode");
707
708         memset(&modem_get_flightmode_resp, 0x0, sizeof(struct tresp_modem_get_flightmode));
709
710         modem_get_flightmode_resp.result = TCORE_RETURN_SUCCESS;
711         modem_get_flightmode_resp.enable = tcore_modem_get_flight_mode_state(co_modem);
712         dbg("Flight mode: [%s]", (modem_get_flightmode_resp.enable ? "ON" : "OFF"));
713
714         ret = tcore_user_request_send_response(ur,
715                 TRESP_MODEM_GET_FLIGHTMODE,
716                 sizeof(struct tresp_modem_get_flightmode), &modem_get_flightmode_resp);
717         if (ret == TCORE_RETURN_SUCCESS)
718                 tcore_user_request_unref(ur);
719
720         return ret;
721 }
722
723 /* Modem operations */
724 static struct tcore_modem_operations modem_ops = {
725         .power_on = NULL,
726         .power_off = modem_power_off,
727         .power_reset = NULL,
728         .set_flight_mode = modem_set_flight_mode,
729         .get_flight_mode = modem_get_flight_mode,
730         .get_imei = modem_get_imei,
731         .get_version = modem_get_version,
732         .get_sn = NULL,
733         .dun_pin_ctrl = NULL,
734 };
735
736 gboolean imc_modem_init(TcorePlugin *plugin, CoreObject *co_modem)
737 {
738         PrivateData *priv_data = NULL;
739
740         dbg("Enter");
741
742         /* Set operations */
743         tcore_modem_set_ops(co_modem, &modem_ops, TCORE_OPS_TYPE_CP);
744
745         /* Private data */
746         priv_data = g_malloc0(sizeof(PrivateData));
747         priv_data->imei_valid = FALSE;
748         priv_data->version_valid = FALSE;
749         tcore_object_link_user_data(co_modem, priv_data);
750
751         /* Notification hooks */
752         tcore_server_add_notification_hook(tcore_plugin_ref_server(plugin),
753                 TNOTI_SIM_STATUS, on_hook_modem_sim_init_status, NULL);
754
755         dbg("Registering for +XDRVI event");
756         tcore_object_add_callback(co_modem,
757                 "+XDRVI", on_event_modem_nvm_update, NULL);
758
759         dbg("Exit");
760         return TRUE;
761 }
762
763 void imc_modem_exit(TcorePlugin *plugin, CoreObject *co_modem)
764 {
765         PrivateData *priv_data = NULL;
766
767         dbg("Exit");
768
769         priv_data = tcore_object_ref_user_data(co_modem);
770         g_free(priv_data);
771 }
772
773 /*
774  * NV Manager - Support for Remote File System
775  */
776 /* NVM Hook */
777 static gboolean __modem_rfs_hook(const char *data)
778 {
779         if (data && data[NVM_FUNCTION_ID_OFFSET] == XDRV_INDICATION)
780                 return TRUE;
781
782         return FALSE;
783 }
784
785 /* NVM event Notification */
786 static gboolean on_event_modem_nvm_update(CoreObject *co_modem,
787         const void *event_info, void *user_data)
788 {
789         GSList *tokens = NULL;
790         GSList *lines;
791         const char *line;
792         int function_id;
793
794         gboolean ret = TRUE;
795         dbg("Entered");
796
797         lines = (GSList *)event_info;
798         line = lines->data;
799         dbg("Line: [%s]", line);
800
801         function_id = nvm_sum_4_bytes(&line[NVM_FUNCTION_ID_OFFSET]);
802         dbg("Function ID: [%d]", function_id);
803         if (IUFP_UPDATE == function_id) {
804                 dbg("Calling process nvm_update");
805
806                 /*
807                  * Process NV Update indication
808                  *
809                  * +XDRVI: IUFP_GROUP, IUFP_UPDATE, <xdrv_result>, <data>
810                  */
811                 if (NVM_NO_ERR == nvm_process_nv_update(line)) {
812                         dbg("NV data processed successfully");
813
814                         /* Acknowledge NV Update */
815                         modem_send_nvm_update_ack(co_modem);
816
817                         return ret;
818                 } else {
819                         err("NV data processing failed");
820                         ret = FALSE;
821                 }
822         } else {
823                 tokens = tcore_at_tok_new(line);
824                 if (g_slist_length(tokens) < 3) {
825                         err("XDRVI event with less number of tokens, Ignore!!!");
826                         ret = FALSE;
827                 } else if (IUFP_GROUP_ID != atoi(g_slist_nth_data(tokens, 0))) {
828                         err("Group ID mismatch, Ignore!!!");
829                         ret = FALSE;
830                 } else {
831                         int command = atoi(g_slist_nth_data(tokens, 1));
832                         switch (command) {
833                         case IUFP_UPDATE_REQ:
834                                 dbg("NV Update Request");
835
836                                 /* Acknowledge the Update Request */
837                                 modem_send_nvm_update_request_ack(co_modem);
838                         break;
839
840                         case IUFP_NO_PENDING_UPDATE:
841                                 dbg("NO pending NV Update(s)!!!");
842                                 /* Can send FLUSH request to get fresh updates */
843                         break;
844
845                         default:
846                                 err("Unspported Function ID [%d], Ignore", command);
847                                 ret = FALSE;
848                         break;
849                         }
850                 }
851
852                 tcore_at_tok_free(tokens);
853         }
854
855         dbg("Exit");
856         return ret;
857 }
858
859 /* NVM Responses */
860 static gboolean __modem_check_nvm_response(const void *data, int command)
861 {
862         const TcoreATResponse *at_resp = data;
863         const char *line;
864         char *resp_str;
865         GSList *tokens = NULL;
866         gboolean ret = FALSE;
867         dbg("Entered");
868
869         /* +XDRV: <group_id>,<function_id>,<xdrv_result>[,<response_n>] */
870         if (NULL == at_resp) {
871                 err("Input data is NULL");
872                 return FALSE;
873         }
874
875         if (at_resp->success <= 0) {
876                 dbg("Response NOK");
877                 return FALSE;
878         }
879
880         dbg("RESPONSE OK");
881         line = (const char *) (((GSList *) at_resp->lines)->data);
882         tokens = tcore_at_tok_new(line);
883
884         /* Group ID */
885         resp_str = g_slist_nth_data(tokens, 0);
886         if (NULL == resp_str) {
887                 err("Group ID is missing ");
888                 goto OUT;
889         } else if (IUFP_GROUP_ID != atoi(resp_str)) {
890                 err("Group ID mismatch");
891                 goto OUT;
892         }
893
894         /* Function ID */
895         resp_str =  g_slist_nth_data(tokens, 1);
896         if (NULL == resp_str) {
897                 err("Function ID is missing ");
898                 goto OUT;
899         } else if (command != atoi(resp_str)) {
900                 err("Function ID mismatch");
901                 goto OUT;
902         }
903
904         /* XDRV Result */
905         resp_str =  g_slist_nth_data(tokens, 2);
906         if (NULL == resp_str) {
907                 err("XDRV result is missing ");
908                 goto OUT;
909         } else if (XDRV_RESULT_OK != atoi(resp_str)) {
910                 err("XDRV result[%d] ", atoi(resp_str));
911                 goto OUT;
912         }
913
914         /* Result code */
915         resp_str =  g_slist_nth_data(tokens, 3);
916         if (NULL == resp_str) {
917                 err("UTA result is missing ");
918                 goto OUT;
919         } else if (UTA_SUCCESS != atoi(resp_str)) {
920                 err("uta result[%d] ", atoi(resp_str));
921                 goto OUT;
922         }
923
924         ret = TRUE;
925
926 OUT:
927         tcore_at_tok_free(tokens);
928
929         dbg("Exit");
930         return ret;
931 }
932
933 static void on_response_modem_unsuspend_nvm_updates(TcorePending *pending,
934         int data_len, const void *data, void *user_data)
935 {
936         /* Check NVM response */
937         if (TRUE == __modem_check_nvm_response(data, IUFP_SUSPEND)) {
938                 dbg("Priority level is set to get all updates since Boot-up");
939
940                 /* Create NV data file */
941                 if (nvm_create_nvm_data() == FALSE)
942                         err("Failed to Create NV data file");
943
944                 return;
945         }
946
947         err("Response NOT OK");
948 }
949
950 static void on_response_modem_send_nvm_update_ack(TcorePending *pending,
951         int data_len, const void *data, void *user_data)
952 {
953         /* Check NVM response */
954         if (TRUE ==  __modem_check_nvm_response(data, IUFP_UPDATE_ACK)) {
955                 dbg("[UPDATE ACK] OK");
956                 return;
957         }
958
959         err("[UPDATE ACK] NOT OK");
960 }
961
962 static void on_response_modem_send_nvm_update_request_ack(TcorePending *pending,
963         int data_len, const void *data, void *user_data)
964 {
965         /* Check NVM response */
966         if (TRUE == __modem_check_nvm_response(data, IUFP_UPDATE_REQ_ACK)) {
967                 dbg("[REQUEST ACK] OK");
968                 return;
969         }
970
971         err("[REQUEST ACK] NOT OK");
972 }
973
974 static void on_response_modem_register_nvm(TcorePending *pending,
975         int data_len, const void *data, void *user_data)
976 {
977         /* Check NVM response */
978         if (TRUE == __modem_check_nvm_response(data, IUFP_REGISTER)) {
979                 dbg("Registering successful!");
980
981                 /* Send SUSPEND_UPDATE for all UPDATES */
982                 modem_unsuspend_nvm_updates(tcore_pending_ref_core_object(pending));
983
984                 dbg("Exit");
985                 return;
986         }
987
988         err("Response NOT OK");
989 }
990
991 /* NVM Requests */
992 static void modem_unsuspend_nvm_updates(CoreObject *co_modem)
993 {
994         char *cmd_str;
995         TReturn ret;
996
997         dbg("Entered");
998
999         /* Prepare AT-Command */
1000         cmd_str = g_strdup_printf("AT+XDRV=%d, %d, %d, %d",
1001                 IUFP_GROUP_ID, IUFP_SUSPEND,
1002                 0, UTA_FLASH_PLUGIN_PRIO_UNSUSPEND_ALL);
1003
1004         /* Prepare pending request */
1005         ret = tcore_prepare_and_send_at_request(co_modem,
1006                 cmd_str, "+XDRV:",
1007                 TCORE_AT_SINGLELINE, NULL,
1008                 on_response_modem_unsuspend_nvm_updates, NULL,
1009                 NULL, NULL, 0, NULL, NULL);
1010         if (ret != TCORE_RETURN_SUCCESS)
1011                 err("IUFP_SUSPEND - Unable to send AT-Command");
1012         else
1013                 dbg("IUFP_SUSPEND - Successfully sent AT-Command");
1014
1015         g_free(cmd_str);
1016 }
1017
1018 static void modem_send_nvm_update_ack(CoreObject *co_modem)
1019 {
1020         char *cmd_str;
1021         TReturn ret;
1022
1023         dbg("Entered");
1024
1025         /* Prepare AT-Command */
1026         cmd_str = g_strdup_printf("AT+XDRV=%s, %s", IUFP_GROUP, IUFP_UPDATE_ACK_STR);
1027
1028         /* Prepare pending request */
1029         ret = tcore_prepare_and_send_at_request(co_modem,
1030                 cmd_str, "+XDRV:",
1031                 TCORE_AT_SINGLELINE, NULL,
1032                 on_response_modem_send_nvm_update_ack, NULL,
1033                 NULL, NULL, 0, NULL, NULL);
1034         if (ret != TCORE_RETURN_SUCCESS)
1035                 err("IUFP_UPDATE_ACK - Unable to send AT-Command");
1036         else
1037                 dbg("IUFP_UPDATE_ACK - Successfully sent AT-Command");
1038
1039         g_free(cmd_str);
1040 }
1041
1042 static void modem_send_nvm_update_request_ack(CoreObject *co_modem)
1043 {
1044         char *cmd_str;
1045         TReturn ret;
1046
1047         dbg("Entered");
1048
1049         /* Prepare AT-Command */
1050         cmd_str = g_strdup_printf("AT+XDRV=%s, %s", IUFP_GROUP, IUFP_UPDATE_REQ_ACK_STR);
1051
1052         /* Prepare pending request */
1053         ret = tcore_prepare_and_send_at_request(co_modem,
1054                 cmd_str, "+XDRV:",
1055                 TCORE_AT_SINGLELINE, NULL,
1056                 on_response_modem_send_nvm_update_request_ack, NULL,
1057                 NULL, NULL, 0, NULL, NULL);
1058         if (ret != TCORE_RETURN_SUCCESS)
1059                 err("IUFP_UPDATE_REQ_ACK - Unable to send AT-Ccommand");
1060         else
1061                 dbg("IUFP_UPDATE_REQ_ACK - Successfully sent AT-Command");
1062
1063         g_free(cmd_str);
1064 }
1065
1066 void modem_register_nvm(CoreObject *co_modem)
1067 {
1068         char *cmd_str;
1069         TReturn ret;
1070
1071         dbg("Entered");
1072
1073         /* Prepare AT-Command */
1074         cmd_str = g_strdup_printf("AT+XDRV=%s, %s, %s",
1075                 IUFP_GROUP, IUFP_REGISTER_STR, XDRV_ENABLE);
1076
1077         /* Prepare pending request */
1078         ret = tcore_prepare_and_send_at_request(co_modem,
1079                 cmd_str, "+XDRV:",
1080                 TCORE_AT_SINGLELINE, NULL,
1081                 on_response_modem_register_nvm, NULL,
1082                 NULL, NULL, 0, NULL, NULL);
1083         if (ret != TCORE_RETURN_SUCCESS) {
1084                 err("IUFP_REGISTER (Enable) -Unable to send AT-Command");
1085         } else {
1086                 dbg("IUFP_REGISTER (Enable) -Successfully sent AT-Command");
1087
1088                 /* Add RFS hook */
1089                 /* Todo unblock this api */
1090                 tcore_at_add_hook(tcore_object_get_hal(co_modem),
1091                         __modem_rfs_hook);
1092         }
1093
1094         g_free(cmd_str);
1095 }