Update plugin to compile with new libtcore
[platform/core/telephony/tel-plugin-imc.git] / src / s_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 #include <mux.h>
39
40 #include "s_common.h"
41 #include "s_modem.h"
42
43
44 #define ID_RESERVED_AT 0x0229
45
46 #define MAX_VERSION_LEN 32
47 #define TAPI_MISC_ME_SN_LEN_MAX             32
48 #define TAPI_MISC_PRODUCT_CODE_LEN_MAX      32
49 #define TAPI_MISC_MODEL_ID_LEN_MAX          17
50 #define TAPI_MISC_PRL_ERI_VER_LEN_MAX       17
51
52 #define CPAS_RES_READY          0
53 #define CPAS_RES_UNAVAIL            1
54 #define CPAS_RES_UNKNOWN            2
55 #define CPAS_RES_RINGING            3
56 #define CPAS_RES_CALL_PROGRESS  4
57 #define CPAS_RES_ASLEEP           5
58 #define AT_VER_LEN 20
59
60
61 enum cp_state {
62         CP_STATE_OFFLINE,
63         CP_STATE_CRASH_RESET,
64         CP_STATE_CRASH_EXIT,
65         CP_STATE_BOOTING,
66         CP_STATE_ONLINE,
67         CP_STATE_NV_REBUILDING,
68         CP_STATE_LOADER_DONE,
69 };
70
71 typedef enum {
72         TAPI_MISC_ME_IMEI = 0x00, /**< 0x00: IMEI, GSM/UMTS device */
73         TAPI_MISC_ME_ESN = 0x01, /**< 0x01: ESN(Electronic Serial Number), It`s essentially run out. CDMA device */
74         TAPI_MISC_ME_MEID = 0x02, /**< 0x02: MEID, This value can have hexa decimal digits. CDMA device */
75         TAPI_MISC_ME_MAX = 0xff /**< 0xff: reserved */
76 } TelMiscSNIndexType_t;
77
78 typedef struct {
79         TelMiscSNIndexType_t sn_index; /**< serial number index */
80         int sn_len; /**< Length */
81         unsigned char szNumber[TAPI_MISC_ME_SN_LEN_MAX]; /**< Number */
82 } TelMiscSNInformation;
83
84 /**
85  * Mobile Equipment Version Information
86  */
87 typedef struct {
88         unsigned char ver_mask; /**< version mask  - 0x01:SW_ver, 0x02:HW_ver, 0x04:RF_CAL_date, 0x08:Product_code, 0x10:Model_ID, 0x20:PRL, 0x04:ERI, 0xff:all */
89         unsigned char szSwVersion[MAX_VERSION_LEN]; /**< Software version, null termination */
90         unsigned char szHwVersion[MAX_VERSION_LEN]; /**< Hardware version, null termination */
91         unsigned char szRfCalDate[MAX_VERSION_LEN]; /**< Calculation Date, null termination */
92         unsigned char szProductCode[TAPI_MISC_PRODUCT_CODE_LEN_MAX]; /**< product code, null termination */
93         unsigned char szModelId[TAPI_MISC_MODEL_ID_LEN_MAX]; /**< model id (only for CDMA), null termination */
94         unsigned char prl_nam_num; /**< number of PRL NAM fields */
95         unsigned char szPrlVersion[TAPI_MISC_PRL_ERI_VER_LEN_MAX * 3]; /**< prl version (only for CDMA), null termination */
96         unsigned char eri_nam_num; /**< number of PRL NAM fields */
97         unsigned char szEriVersion[TAPI_MISC_PRL_ERI_VER_LEN_MAX * 3]; /**< eri version (only for CDMA), null termination */
98 } TelMiscVersionInformation;
99
100
101 static void prepare_and_send_pending_request(CoreObject *co, const char *at_cmd, const char *prefix, enum tcore_at_command_type at_cmd_type, TcorePendingResponseCallback callback);
102 static void on_confirmation_modem_message_send(TcorePending *p, gboolean result, void *user_data);     // from Kernel
103 void on_response_bootup_subscription(TcorePending *p, int data_len, const void *data, void *user_data);
104 void on_response_last_bootup_subscription(TcorePending *p, int data_len, const void *data, void *user_data);
105 static void on_response_enable_proactive_command(TcorePending *p, int data_len, const void *data, void *user_data);
106
107 static void on_confirmation_modem_message_send(TcorePending *p, gboolean result, void *user_data)
108 {
109         dbg("on_confirmation_modem_message_send - msg out from queue.\n");
110
111         if (result == FALSE) {
112                 /* Fail */
113                 dbg("SEND FAIL");
114         } else {
115                 dbg("SEND OK");
116         }
117 }
118
119 static void on_response_enable_proactive_command(TcorePending *p, int data_len, const void *data, void *user_data)
120 {
121         const TcoreATResponse *resp = data;
122
123         if (resp->success > 0) {
124                 dbg("RESPONSE OK proactive command enabled");
125         } else {
126                 dbg("RESPONSE NOK proactive command disabled");
127         }
128 }
129
130 void prepare_and_send_pending_request(CoreObject *co, const char *at_cmd, const char *prefix, enum tcore_at_command_type at_cmd_type, TcorePendingResponseCallback callback)
131 {
132         TcoreATRequest *req = NULL;
133         TcoreHal *hal = NULL;
134         TcorePending *pending = NULL;
135         TReturn ret;
136
137         hal = tcore_object_get_hal(co);
138         dbg("hal: %p", hal);
139
140         pending = tcore_pending_new(co, 0);
141         if (!pending)
142                 dbg("Pending is NULL");
143         req = tcore_at_request_new(at_cmd, prefix, at_cmd_type);
144
145         dbg("cmd : %s, prefix(if any) :%s, cmd_len : %d", req->cmd, req->prefix, strlen(req->cmd));
146
147         tcore_pending_set_request_data(pending, 0, req);
148         tcore_pending_set_response_callback(pending, callback, NULL);
149         tcore_pending_set_send_callback(pending, on_confirmation_modem_message_send, NULL);
150         ret = tcore_hal_send_request(hal, pending);
151 }
152
153 static void on_response_power_off(TcorePending *p, int data_len, const void *data, void *user_data)
154 {
155         CoreObject *o = 0;
156         TcoreHal *h = 0;
157
158         o = tcore_pending_ref_core_object(p);
159         h = tcore_object_get_hal(o);
160
161         dbg("modem power off");
162
163         tcore_hal_set_power_state(h, FALSE);
164 }
165
166 static void on_response_set_flight_mode(TcorePending *p, int data_len, const void *data, void *user_data)
167 {
168         CoreObject *o = NULL;
169         UserRequest *ur = NULL;
170         const TcoreATResponse *ATresp = data;
171         GSList *tokens = NULL;
172         const char *line = NULL;
173         struct tresp_modem_set_flightmode res = {0};
174         int response = 0;
175         struct tnoti_modem_flight_mode modem_flight_mode = {0};
176         const struct treq_modem_set_flightmode *req_data = NULL;
177
178         o = tcore_pending_ref_core_object(p);
179
180         if (ATresp->success > 0) {
181                 dbg("RESPONSE OK - flight mode operation finished");
182                 res.result = TCORE_RETURN_SUCCESS;
183         } else {
184                 dbg("RESPONSE NOK");
185                 line = (const char *) ATresp->final_response;
186                 tokens = tcore_at_tok_new(line);
187
188                 if (g_slist_length(tokens) < 1) {
189                         dbg("err cause not specified or string corrupted");
190                         res.result = TCORE_RETURN_3GPP_ERROR;
191                 } else {
192                         response = atoi(g_slist_nth_data(tokens, 0));
193                         /* TODO: CMEE error mapping is required. */
194                         res.result = TCORE_RETURN_3GPP_ERROR;
195                 }
196         }
197
198         ur = tcore_pending_ref_user_request(p);
199         if (NULL == ur) {
200                 dbg("No user request. Internal request created during boot-up sequence");
201
202                 if (ATresp->success > 0) {
203                         modem_flight_mode.enable = tcore_modem_get_flight_mode_state(o);
204                         dbg("sucess case - Sending Flight Mode Notification (%d) to Telephony Server", modem_flight_mode.enable);
205
206                         tcore_server_send_notification(tcore_plugin_ref_server(tcore_object_ref_plugin(o)), o, TNOTI_MODEM_FLIGHT_MODE,
207                                                                                    sizeof(struct tnoti_modem_flight_mode), &modem_flight_mode);
208                 }
209         } else {
210                 dbg("Sending response for Flight mode operation");
211
212                 req_data = tcore_user_request_ref_data(ur, NULL);
213
214                 if (TCORE_RETURN_SUCCESS == res.result) {
215                         if (TRUE == req_data->enable)
216                                 res.result = 1;
217                         else
218                                 res.result = 2;
219                 } else {
220                         res.result = 3;
221                 }
222
223                 tcore_user_request_send_response(ur, TRESP_MODEM_SET_FLIGHTMODE, sizeof(struct tresp_modem_set_flightmode), &res);
224
225                 if (req_data->enable == 0) {
226                         dbg("Flight mode is disabled, trigger COPS to register on network");
227                         /* Trigger Network registration (for the moment automatic) */
228                         prepare_and_send_pending_request(o, "AT+COPS=0", NULL, TCORE_AT_NO_RESULT, NULL);
229                 }
230         }
231
232         tcore_at_tok_free(tokens);
233 }
234
235 static void on_response_imei(TcorePending *p, int data_len, const void *data, void *user_data)
236 {
237         const TcoreATResponse *resp = data;
238         TcorePlugin *plugin = NULL;
239         struct tresp_modem_get_imei res;
240         TelMiscSNInformation *imei_property = NULL;
241         UserRequest *ur = NULL;
242         GSList *tokens = NULL;
243         const char *line;
244         int response = 0;
245
246         memset(&res, 0, sizeof(struct tresp_modem_get_imei));
247
248         if (resp->success > 0) {
249                 dbg("RESPONSE OK");
250                 if (resp->lines) {
251                         line = (const char *) resp->lines->data;
252                         tokens = tcore_at_tok_new(line);
253                         if (g_slist_length(tokens) != 1) {
254                                 msg("invalid message");
255                                 goto OUT;
256                         }
257                 }
258                 res.result = TCORE_RETURN_SUCCESS;
259                 strncpy(res.imei, g_slist_nth_data(tokens, 0), 16);
260
261                 dbg("imei = [%s]", res.imei);
262
263                 plugin = tcore_pending_ref_plugin(p);
264                 imei_property = tcore_plugin_ref_property(plugin, "IMEI");
265                 if (imei_property) {
266                         imei_property->sn_index = TAPI_MISC_ME_IMEI;
267                         imei_property->sn_len = strlen(res.imei);
268                         memcpy(imei_property->szNumber, res.imei, imei_property->sn_len);
269                 }
270         } else {
271                 dbg("RESPONSE NOK");
272                 if (resp->lines) {
273                         line = (const char *) resp->lines->data;
274                         tokens = tcore_at_tok_new(line);
275                 }
276
277
278                 if (g_slist_length(tokens) < 1) {
279                         dbg("err cause not specified or string corrupted");
280                         res.result = TCORE_RETURN_3GPP_ERROR;
281                 } else {
282                         response = atoi(g_slist_nth_data(tokens, 0));
283                         /* TODO: CMEE error mapping is required. */
284                         res.result = TCORE_RETURN_3GPP_ERROR;
285                 }
286         }
287
288         ur = tcore_pending_ref_user_request(p);
289         tcore_user_request_send_response(ur, TRESP_MODEM_GET_IMEI, sizeof(struct tresp_modem_get_imei), &res);
290
291 OUT:
292         if (tokens != NULL)
293                 tcore_at_tok_free(tokens);
294
295         return;
296 }
297
298 static void on_response_version(TcorePending *p, int data_len, const void *data, void *user_data)
299 {
300         const TcoreATResponse *resp = data;
301         TcorePlugin *plugin = NULL;
302         struct tresp_modem_get_version res = {0};
303         TelMiscVersionInformation *vi_property = NULL;
304         TelMiscVersionInformation *vi = NULL;
305         UserRequest *ur = NULL;
306         GSList *tokens = NULL;
307         const char *line = NULL;
308         char *swver = NULL;
309         char *hwver = NULL;
310         char *caldate = NULL;
311         char *pcode = NULL;
312         char *id = NULL;
313
314         int response = 0;
315
316         if (resp->success > 0) {
317                 dbg("RESPONSE OK");
318                 if (resp->lines) {
319                         line = (const char *) resp->lines->data;
320                         tokens = tcore_at_tok_new(line);
321                         if (g_slist_length(tokens) != 5) {
322                                 msg("invalid message");
323                                 goto OUT;
324                         }
325                 }
326
327                 swver = g_slist_nth_data(tokens, 0);
328                 hwver = g_slist_nth_data(tokens, 1);
329                 caldate = g_slist_nth_data(tokens, 2);
330                 pcode = g_slist_nth_data(tokens, 3);
331                 id = g_slist_nth_data(tokens, 4);
332
333                 dbg("version: sw=[%s], hw=[%s], rf_cal=[%s], product_code=[%s], model_id=[%s]", swver, hwver, caldate, pcode, id);
334
335                 vi = calloc(sizeof(TelMiscVersionInformation), 1);
336                 if (NULL != swver)
337                         memcpy(vi->szSwVersion, swver, strlen(swver));
338                 if (NULL != hwver)
339                         memcpy(vi->szHwVersion, hwver, strlen(hwver));
340                 if (NULL != caldate)
341                         memcpy(vi->szRfCalDate, caldate, strlen(caldate));
342                 if (NULL != pcode)
343                         memcpy(vi->szProductCode, pcode, strlen(pcode));
344                 if (NULL != id)
345                         memcpy(vi->szModelId, id, strlen(id));
346
347                 memset(&res, 0, sizeof(struct tresp_modem_get_version));
348
349                 if (NULL != swver)
350                         snprintf(res.software, (AT_VER_LEN > strlen(swver) ? strlen(swver) : AT_VER_LEN), "%s", swver);
351                 if (NULL != hwver)
352                         snprintf(res.hardware, (AT_VER_LEN > strlen(hwver) ? strlen(hwver) : AT_VER_LEN), "%s", hwver);
353
354                 plugin = tcore_pending_ref_plugin(p);
355                 vi_property = tcore_plugin_ref_property(plugin, "VERSION");
356                 memcpy(vi_property, vi, sizeof(TelMiscVersionInformation));
357                 free(vi);
358         } else {
359                 dbg("RESPONSE NOK");
360                 if (resp->lines) {
361                         line = (const char *) resp->lines->data;
362                         tokens = tcore_at_tok_new(line);
363                 }
364
365                 memset(&res, 0, sizeof(struct tresp_modem_get_version));
366
367
368                 if (g_slist_length(tokens) < 1) {
369                         dbg("err cause not specified or string corrupted");
370                         res.result = TCORE_RETURN_3GPP_ERROR;
371                 } else {
372                         response = atoi(g_slist_nth_data(tokens, 0));
373                         /* TODO: CMEE error mapping is required. */
374                         res.result = TCORE_RETURN_3GPP_ERROR;
375                 }
376         }
377
378         ur = tcore_pending_ref_user_request(p);
379         tcore_user_request_send_response(ur, TRESP_MODEM_GET_VERSION, sizeof(struct tresp_modem_get_version), &res);
380
381 OUT:
382         if (tokens != NULL)
383                 tcore_at_tok_free(tokens);
384
385         return;
386 }
387
388 static gboolean on_event_bootup_sim_status(CoreObject *o, const void *event_info, void *user_data)
389 {
390         GSList *tok = NULL;
391         GSList *lines = NULL;
392         int value = -1;
393         char *line = NULL;
394
395         lines = (GSList *) event_info;
396         if (1 != g_slist_length(lines)) {
397                 dbg("unsolicited msg but multiple line");
398                 goto OUT;
399         }
400         line = (char *) (lines->data);
401         dbg("on_bootup_event_sim_status notification : %s", line);
402
403         tok = tcore_at_tok_new(line);
404         value = atoi(g_slist_nth_data(tok, 0));
405
406         if (7 == value) {
407                 dbg("SIM ready. request COPS & remove callback");
408                 dbg("power on done set for proactive command receiving mode");
409                 prepare_and_send_pending_request(o, "AT+CFUN=6", NULL, TCORE_AT_NO_RESULT, on_response_enable_proactive_command);
410                 prepare_and_send_pending_request(o, "AT+COPS=0", NULL, TCORE_AT_NO_RESULT, on_response_bootup_subscription);
411                 return FALSE;
412         }
413
414 OUT:
415         if (tok != NULL)
416                 tcore_at_tok_free(tok);
417
418         return TRUE;
419 }
420
421
422
423 gboolean modem_power_on(TcorePlugin *p)
424 {
425         CoreObject *co_modem = NULL;
426         struct treq_modem_set_flightmode flight_mode_set = {0};
427         struct tnoti_modem_power modem_power = {0};
428         TcoreHal *h = NULL;
429         Storage *strg = NULL;
430
431         co_modem = tcore_plugin_ref_core_object(p, CORE_OBJECT_TYPE_MODEM);
432
433         strg = tcore_server_find_storage(tcore_plugin_ref_server(p), "vconf");
434         flight_mode_set.enable = tcore_storage_get_bool(strg, STORAGE_KEY_FLIGHT_MODE_BOOL);
435
436         h = tcore_object_get_hal(co_modem);
437         tcore_hal_set_power_state(h, TRUE);
438
439         /* Set Flight mode as per AP settings */
440         if (flight_mode_set.enable) { /* Radio Off */
441                 prepare_and_send_pending_request(co_modem, "AT+CFUN=4", NULL, TCORE_AT_NO_RESULT, on_response_set_flight_mode);
442                 tcore_modem_set_flight_mode_state(co_modem, TRUE);
443         } else { /* Radio On */
444                 prepare_and_send_pending_request(co_modem, "AT+CFUN=1", NULL, TCORE_AT_NO_RESULT, on_response_set_flight_mode);
445                 tcore_modem_set_flight_mode_state(co_modem, FALSE);
446         }
447
448         /* Get IMEI */
449         prepare_and_send_pending_request(co_modem, "AT+CGSN", NULL, TCORE_AT_NUMERIC, on_response_imei);
450
451         /* Get Version Number  */
452         prepare_and_send_pending_request(co_modem, "AT+CGMR", NULL, TCORE_AT_SINGLELINE, on_response_version);
453
454         tcore_modem_set_powered(co_modem, TRUE);
455
456         modem_power.state = MODEM_STATE_ONLINE;
457
458         tcore_server_send_notification(tcore_plugin_ref_server(p), co_modem, TNOTI_MODEM_POWER,
459                                                                    sizeof(struct tnoti_modem_power), &modem_power);
460
461         return TRUE;
462 }
463
464 static TReturn power_off(CoreObject *o, UserRequest *ur)
465 {
466         TcoreHal *hal = NULL;
467         TcoreATRequest *req = NULL;
468         TcorePending *pending = NULL;
469
470         hal = tcore_object_get_hal(o);
471         pending = tcore_pending_new(o, 0);
472
473         req = tcore_at_request_new("AT+CFUN=0", NULL, TCORE_AT_NO_RESULT);
474
475         dbg("cmd : %s, prefix(if any) :%s, cmd_len : %d", req->cmd, req->prefix, strlen(req->cmd));
476
477         tcore_pending_set_request_data(pending, 0, req);
478         tcore_pending_set_response_callback(pending, on_response_power_off, hal);
479         tcore_pending_link_user_request(pending, ur);
480         tcore_pending_set_send_callback(pending, on_confirmation_modem_message_send, NULL);
481
482         tcore_hal_send_request(hal, pending);
483
484         return TCORE_RETURN_SUCCESS;
485 }
486
487 static TReturn get_imei(CoreObject *o, UserRequest *ur)
488 {
489         TcoreHal *hal;
490         TcoreATRequest *req;
491         TcorePending *pending = NULL;
492
493         hal = tcore_object_get_hal(o);
494         if (FALSE == tcore_hal_get_power_state(hal)) {
495                 dbg("cp not ready/n");
496                 return TCORE_RETURN_ENOSYS;
497         }
498         pending = tcore_pending_new(o, 0);
499
500         req = tcore_at_request_new("AT+CGSN", NULL, TCORE_AT_NUMERIC);
501
502         dbg("cmd : %s, prefix(if any) :%s, cmd_len : %d", req->cmd, req->prefix, strlen(req->cmd));
503
504         tcore_pending_set_request_data(pending, 0, req);
505         tcore_pending_set_response_callback(pending, on_response_imei, hal);
506         tcore_pending_link_user_request(pending, ur);
507         tcore_pending_set_send_callback(pending, on_confirmation_modem_message_send, NULL);
508
509         tcore_hal_send_request(hal, pending);
510
511         return TCORE_RETURN_SUCCESS;
512 }
513
514
515 static TReturn get_version(CoreObject *o, UserRequest *ur)
516 {
517         TcoreHal *hal;
518         TcoreATRequest *req;
519         TcorePending *pending = NULL;
520
521         hal = tcore_object_get_hal(o);
522         if (FALSE == tcore_hal_get_power_state(hal)) {
523                 dbg("cp not ready/n");
524                 return TCORE_RETURN_ENOSYS;
525         }
526         pending = tcore_pending_new(o, 0);
527
528         req = tcore_at_request_new("AT+CGMR", NULL, TCORE_AT_SINGLELINE);
529
530         dbg("cmd : %s, prefix(if any) :%s, cmd_len : %d", req->cmd, req->prefix, strlen(req->cmd));
531
532         tcore_pending_set_request_data(pending, 0, req);
533         tcore_pending_set_response_callback(pending, on_response_version, hal);
534         tcore_pending_link_user_request(pending, ur);
535         tcore_pending_set_send_callback(pending, on_confirmation_modem_message_send, NULL);
536
537         tcore_hal_send_request(hal, pending);
538
539         return TCORE_RETURN_SUCCESS;
540 }
541
542 static TReturn set_flight_mode(CoreObject *o, UserRequest *ur)
543 {
544         TcoreHal *hal = NULL;
545         TcoreATRequest *req = NULL;
546         TcorePending *pending = NULL;
547         const struct treq_modem_set_flightmode *req_data = NULL;
548         char *cmd_str = NULL;
549
550         hal = tcore_object_get_hal(o);
551         if (FALSE == tcore_hal_get_power_state(hal)) {
552                 dbg("cp not ready/n");
553                 return TCORE_RETURN_ENOSYS;
554         }
555         pending = tcore_pending_new(o, 0);
556
557         req_data = tcore_user_request_ref_data(ur, NULL);
558
559         if (req_data->enable) {
560                 dbg("Flight mode on/n");
561                 cmd_str = g_strdup("AT+CFUN=4");
562         } else {
563                 dbg("Flight mode off/n");
564                 cmd_str = g_strdup("AT+CFUN=1");
565         }
566
567         req = tcore_at_request_new((const char *) cmd_str, NULL, TCORE_AT_NO_RESULT);
568
569         dbg("cmd : %s, prefix(if any) :%s, cmd_len : %d", req->cmd, req->prefix, strlen(req->cmd));
570
571         tcore_pending_set_request_data(pending, 0, req);
572         tcore_pending_set_response_callback(pending, on_response_set_flight_mode, hal);
573         tcore_pending_link_user_request(pending, ur);
574         tcore_pending_set_send_callback(pending, on_confirmation_modem_message_send, NULL);
575
576         tcore_hal_send_request(hal, pending);
577
578         return TCORE_RETURN_SUCCESS;
579 }
580
581
582 static struct tcore_modem_operations modem_ops = {
583         .power_on = NULL,
584         .power_off = power_off,
585         .power_reset = NULL,
586         .set_flight_mode = set_flight_mode,
587         .get_imei = get_imei,
588         .get_version = get_version,
589         .get_sn = NULL,
590         .dun_pin_ctrl = NULL,
591 };
592
593 gboolean s_modem_init(TcorePlugin *cp, CoreObject *co_modem)
594 {
595         TelMiscVersionInformation *vi_property;
596         TelMiscSNInformation *imei_property;
597         TelMiscSNInformation *sn_property;
598
599         dbg("Enter");
600
601         tcore_modem_override_ops(co_modem, &modem_ops);
602
603         vi_property = g_try_new0(TelMiscVersionInformation, 1);
604         tcore_plugin_link_property(cp, "VERSION", vi_property);
605
606         imei_property = g_try_new0(TelMiscSNInformation, 1);
607         tcore_plugin_link_property(cp, "IMEI", imei_property);
608
609         sn_property = g_try_new0(TelMiscSNInformation, 1);
610         tcore_plugin_link_property(cp, "SN", sn_property);
611
612         dbg("Registering for +XSIM event");
613         tcore_object_override_callback(co_modem, "+XSIM", on_event_bootup_sim_status, NULL);
614
615         dbg("Exit");
616
617         return TRUE;
618 }
619
620 void s_modem_exit(TcorePlugin *cp, CoreObject *co_modem)
621 {
622         TelMiscVersionInformation *vi_property;
623         TelMiscSNInformation *imei_property;
624         TelMiscSNInformation *sn_property;
625         TcorePlugin *plugin = tcore_object_ref_plugin(co_modem);
626
627         vi_property = tcore_plugin_ref_property(plugin, "VERSION");
628         g_free(vi_property);
629
630         imei_property = tcore_plugin_ref_property(plugin, "IMEI");
631         g_free(imei_property);
632
633         sn_property = tcore_plugin_ref_property(plugin, "SN");
634         g_free(sn_property);
635
636         dbg("Exit");
637 }