35ef200ad1988077a94eb3e136aafc7d1c20c398
[platform/core/telephony/tel-plugin-vmodem.git] / src / desc-vmodem.c
1 /*
2  * tel-plugin-vmodem
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 #include <pthread.h>
23 #include <unistd.h>
24 #include <time.h>
25 #include <fcntl.h>
26
27 #include <glib.h>
28
29 #include <tcore.h>
30 #include <server.h>
31 #include <plugin.h>
32 #include <hal.h>
33
34 #include "config.h"
35 #include "vdpram.h"
36 #include "vdpram_dump.h"
37
38 #define VMODEM_HAL_NAME         "vmodem"
39
40 #define DEVICE_NAME_LEN_MAX             16
41 #define DEVICE_NAME_PREFIX              "pdp"
42
43 #define BUF_LEN_MAX                     512
44
45 #define AT_CP_POWER_ON_TIMEOUT  500
46
47 static guint __vmodem_reencode_mt_sms(gchar *mt_sms, guint mt_sms_len)
48 {
49 #define VMODEM_CR       0x0D
50 #define VMODEM_LF       0x0A
51 #define VMODEM_COLON    0x3A
52
53         gchar sms_buf[BUF_LEN_MAX] = {0, };
54         guint sca_len, pdu_len, tpdu_len;
55         gushort tpdu_len_ptr;
56         gchar data;
57         guint i;
58
59         for (i = 0; i < mt_sms_len; i++) {
60                 if ((mt_sms[i] == VMODEM_CR)
61                                 && (mt_sms[i+1] == VMODEM_LF)) {
62                         sms_buf[i] = mt_sms[i];
63                         i++;
64                         sms_buf[i] = mt_sms[i];
65                         i++;
66                         break;
67                 }
68                 else if (mt_sms[i] == VMODEM_COLON)
69                         tpdu_len_ptr = i+1;
70
71                 /* Byte copy */
72                 sms_buf[i] = mt_sms[i];
73         }
74         sca_len = mt_sms[i];
75         dbg("SCA length: [%d] TPDU length offset: [%d]", sca_len, tpdu_len_ptr);
76
77         pdu_len = (mt_sms_len-i);
78         tpdu_len = pdu_len - (sca_len+1);
79         dbg("PDU length: [%d] Actual TPDU Length: [%d]", pdu_len, tpdu_len);
80
81         tcore_util_byte_to_hex(&mt_sms[i], &sms_buf[i], pdu_len);
82         dbg("MT SMS: [%s]", sms_buf);
83
84         /* Append <CR> & <LF> */
85         i += 2*pdu_len;
86         sms_buf[i++] = VMODEM_CR;
87         sms_buf[i++] = VMODEM_LF;
88
89         /* Update actual TPDU length */
90         data = (tpdu_len/10) + '0';
91         sms_buf[tpdu_len_ptr] = data;
92         tpdu_len_ptr++;
93
94         data = (tpdu_len%10) + '0';
95         sms_buf[tpdu_len_ptr] = data;
96
97         tcore_util_hex_dump("        ", (gint)i, sms_buf);
98
99         /*
100          * Copy back
101          *
102          * 'data_len' is not accessed hence it need not be updated.
103          */
104         g_strlcpy(mt_sms, sms_buf, i+1);
105         dbg("Encoded MT SMS: [%d][%s]", i, mt_sms);
106
107         return i;
108 }
109
110 static guint __register_gio_watch(TcoreHal *h, int fd, void *callback)
111 {
112         GIOChannel *channel = NULL;
113         guint source;
114
115         dbg("Register to Watch list - fd: [%d]", fd);
116
117         if ((fd < 0) || (callback == NULL))
118                 return 0;
119
120         channel = g_io_channel_unix_new(fd);
121         source = g_io_add_watch(channel, G_IO_IN, (GIOFunc) callback, h);
122         g_io_channel_unref(channel);
123         channel = NULL;
124
125         return source;
126 }
127
128 static void __deregister_gio_watch(guint watch_id)
129 {
130         dbg("Deregister Watch ID: [%d]", watch_id);
131
132         /* Remove source */
133         g_source_remove(watch_id);
134 }
135
136 static TcoreHookReturn __on_hal_send(TcoreHal *hal,
137                 guint data_len, void *data, void *user_data)
138 {
139         /* Dumping Send (Write) data */
140         vdpram_hex_dump(TRUE, data_len, data);
141
142         return TCORE_HOOK_RETURN_CONTINUE;
143 }
144
145 static void __on_hal_recv(TcoreHal *hal,
146         guint data_len, const void *data, void *user_data)
147 {
148         /* Dumping Receive (Read) data */
149         vdpram_hex_dump(FALSE, data_len, (void *)data);
150 }
151
152 static gboolean __modem_power(TcoreHal *hal, gboolean enable)
153 {
154         CustomData *user_data;
155
156         user_data = tcore_hal_ref_user_data(hal);
157         if (user_data == NULL) {
158                 err("User data is NULL");
159                 return FALSE;
160         }
161
162         if (enable == TRUE) {           /* POWER ON */
163                 if (FALSE == vdpram_poweron(user_data->vdpram_fd)) {
164                         err("Power ON - [FAIL]");
165                         return FALSE;
166                 }
167
168                 /* Set Power State - ON */
169                 tcore_hal_set_power_state(hal, TRUE);
170         } else {                                /* POWER OFF */
171                 if (vdpram_poweroff(user_data->vdpram_fd) == FALSE) {
172                         err("Power OFF - [FAIL]");
173                         return FALSE;
174                 }
175
176                 /* Set Power state - OFF */
177                 tcore_hal_set_power_state(hal, FALSE);
178         }
179
180         return TRUE;
181 }
182
183 static gboolean __on_recv_vdpram_message(GIOChannel *channel,
184         GIOCondition condition, gpointer data)
185 {
186         TcoreHal *hal = data;
187         CustomData *custom;
188         char buf[BUF_LEN_MAX] = {0, };
189         int n = 0;
190         TelReturn ret;
191
192         custom = tcore_hal_ref_user_data(hal);
193         memset(buf, 0x0, BUF_LEN_MAX);
194
195         /* Read from Device */
196         n = vdpram_tty_read(custom->vdpram_fd, buf, BUF_LEN_MAX);
197         if (n < 0) {
198                 err("Read error - Data received: [%d]", n);
199                 return TRUE;
200         }
201         dbg("DPRAM Receive - Data length: [%d]", n);
202
203         /* Emit receive callback */
204
205
206         msg("\n---------- [RECV] Length of received data: [%d] ----------\n", n);
207
208         /* Emit response callback */
209         tcore_hal_emit_recv_callback(hal, n, buf);
210
211         /*
212          * This is to ensure that the received MT SMS (+CMT:) is
213          * encoded according to 3GPP standard
214          */
215         if (buf[0] == 0x2B && buf[1] == 0x43 && buf[2] == 0x4D
216                         && buf[3] == 0x54 && buf[4] == 0x3A) {
217                 dbg("Received - [MT SMS]");
218                 n = __vmodem_reencode_mt_sms((gchar *)buf, n);
219         }
220
221         /* Dispatch received data to response handler */
222         ret = tcore_hal_dispatch_response_data(hal, 0, n, buf);
223         msg("\n---------- [RECV FINISH] Receive processing: [%d] ----------\n", ret);
224
225         return TRUE;
226 }
227
228 static gboolean __power_on(gpointer data)
229 {
230         CustomData *user_data;
231         TcoreHal *hal = (TcoreHal*)data;
232
233         dbg("Entry");
234
235         user_data = tcore_hal_ref_user_data(hal);
236         tcore_check_return_value_assert(user_data != NULL, TRUE);
237
238         /*
239          * Open DPRAM device: Create and Open interface to CP
240          */
241         user_data->vdpram_fd = vdpram_open();
242         if (user_data->vdpram_fd < 1) {
243                 TcorePlugin *plugin = tcore_hal_ref_plugin(hal);
244                 Server *server = tcore_plugin_ref_server(plugin);
245
246                 err("Failed to Create/Open CP interface");
247
248                 /* Notify server a modem error occured */
249                 tcore_server_send_server_notification(server,
250                         TCORE_SERVER_NOTIFICATION_MODEM_ERR, 0, NULL);
251
252                 goto EXIT;
253         }
254         dbg("Created AP-CP interface");
255
256         /* Register to Watch llist */
257         user_data->vdpram_watch_id = __register_gio_watch(hal,
258                                 user_data->vdpram_fd, __on_recv_vdpram_message);
259         dbg("fd: [%d] Watch ID: [%d]", user_data->vdpram_fd, user_data->vdpram_watch_id);
260
261         /* Power ON VDPRAM device */
262         if (__modem_power(hal, TRUE)) {
263                 dbg("Power ON - [SUCCESS]");
264         } else {
265                 err("Power ON - [FAIL]");
266                 goto EXIT;
267         }
268
269         /* CP is ONLINE, send AT+CPAS */
270         vmodem_config_check_cp_power(hal);
271
272         /* To stop the cycle need to return FALSE */
273         return FALSE;
274
275 EXIT:
276         /* TODO: Handle Deregister */
277
278         /* To stop the cycle need to return FALSE */
279         return FALSE;
280 }
281
282 /* HAL Operations */
283 static TelReturn _hal_power(TcoreHal *hal, gboolean flag)
284 {
285         return __modem_power(hal, flag);
286 }
287
288 static TelReturn _hal_send(TcoreHal *hal,
289         guint data_len, void *data)
290 {
291         CustomData *user_data;
292         gint ret;
293
294         if (tcore_hal_get_power_state(hal) == FALSE) {
295                 err("HAL Power state - OFF");
296                 return TEL_RETURN_FAILURE;
297         }
298
299         user_data = tcore_hal_ref_user_data(hal);
300         if (user_data == NULL) {
301                 err("User data is NULL");
302                 return TEL_RETURN_FAILURE;
303         }
304
305         ret = vdpram_tty_write(user_data->vdpram_fd, data, data_len);
306         if(ret < 0) {
307                 err("Write failed");
308                 return TEL_RETURN_FAILURE;
309         }
310         dbg("vdpram_tty_write success ret=%d (fd=%d, len=%d)",
311                 ret, user_data->vdpram_fd, data_len);
312
313         return TEL_RETURN_SUCCESS;
314 }
315
316 static TelReturn _hal_setup_netif(CoreObject *co,
317         TcoreHalSetupNetifCallback func, void *user_data,
318         guint cid, gboolean enable)
319 {
320         char ifname[DEVICE_NAME_LEN_MAX];
321         int size = 0;
322         int fd = 0;
323         char buf[32];
324         char *control = NULL;
325
326         if (cid > 3) {
327                 err("Context ID: [%d]", cid);
328                 return TEL_RETURN_INVALID_PARAMETER;
329         }
330
331         if (enable == TRUE) {
332                 dbg("ACTIVATE - Context ID: [%d]", cid);
333                 control = "/sys/class/net/svnet0/pdp/activate";
334         } else {
335                 dbg("DEACTIVATE - Context ID: [%d]", cid);
336                 control = "/sys/class/net/svnet0/pdp/deactivate";
337         }
338
339         fd = open(control, O_WRONLY);
340         if (fd < 0) {
341                 err("Failed to Open interface: [%s]", control);
342
343                 /* Invoke callback function */
344                 if (func)
345                         func(co, -1, NULL, user_data);
346
347                 return TEL_RETURN_FAILURE;
348         }
349
350         /* Context ID needs to be written to the Device */
351         snprintf(buf, sizeof(buf), "%d", cid);
352         size = write(fd, buf, strlen(buf));
353
354         /* Close 'fd' */
355         close(fd);
356
357         /* Device name */
358         snprintf(ifname, DEVICE_NAME_LEN_MAX, "%s%d", DEVICE_NAME_PREFIX, (cid - 1));
359         dbg("Interface Name: [%s]", ifname);
360
361         /* Invoke callback function */
362         if (func)
363                 func(co, 0, ifname, user_data);
364
365         return TEL_RETURN_SUCCESS;
366 }
367
368 /* HAL Operations */
369 static TcoreHalOperations hal_ops = {
370         .power = _hal_power,
371         .send = _hal_send,
372         .setup_netif = _hal_setup_netif,
373 };
374
375 static gboolean on_load()
376 {
377         dbg("Load!!!");
378
379         return TRUE;
380 }
381
382 static gboolean on_init(TcorePlugin *plugin)
383 {
384         TcoreHal *hal;
385         CustomData *data;
386         dbg("Init!!!");
387
388         tcore_check_return_value_assert(plugin != NULL, FALSE);
389
390         /* Custom data for Modem Interface Plug-in */
391         data = tcore_malloc0(sizeof(CustomData));
392         dbg("Created custom data memory");
393
394         /* Create Physical HAL */
395         hal = tcore_hal_new(plugin, VMODEM_HAL_NAME,
396                         &hal_ops, TCORE_HAL_MODE_AT);
397         if (hal == NULL) {
398                 err("Failed to Create Physical HAL");
399                 tcore_free(data);
400                 return FALSE;
401         }
402         dbg("HAL [0x%x] created", hal);
403
404         /* Set HAL as Modem Interface Plug-in's User data */
405         tcore_plugin_link_user_data(plugin, hal);
406
407         /* Link Custom data to HAL's 'user_data' */
408         tcore_hal_link_user_data(hal, data);
409
410         /* Add callbacks for Send/Receive Hooks */
411         tcore_hal_add_send_hook(hal, __on_hal_send, NULL);
412         tcore_hal_add_recv_callback(hal, __on_hal_recv, NULL);
413         dbg("Added Send hook and Receive callback");
414
415         /* Set HAL state to Power OFF (FALSE) */
416         (void)tcore_hal_set_power_state(hal, FALSE);
417         dbg("HAL Power State: Power OFF");
418
419         /* Resgister to Server */
420         if (tcore_server_register_modem(tcore_plugin_ref_server(plugin),
421                 plugin) == FALSE) {
422                 err("Registration Failed");
423
424                 tcore_hal_free(hal);
425                 tcore_free(data);
426                 return FALSE;
427         }
428         dbg("Registered from Server");
429
430         /* Check CP Power ON */
431         g_timeout_add_full(G_PRIORITY_HIGH,
432                 AT_CP_POWER_ON_TIMEOUT, __power_on, hal, NULL);
433
434         return TRUE;
435 }
436
437 static void on_unload(TcorePlugin *plugin)
438 {
439         TcoreHal *hal;
440         CustomData *user_data;
441         dbg("Unload!!!");
442
443         tcore_check_return_assert(plugin != NULL);
444
445         /* Unload Modem Plug-in */
446         tcore_server_unload_modem_plugin(tcore_plugin_ref_server(plugin), plugin);
447
448         /* Unregister Modem Interface Plug-in from Server */
449         tcore_server_unregister_modem(tcore_plugin_ref_server(plugin), plugin);
450         dbg("Unregistered from Server");
451
452         /* HAL cleanup */
453         hal = tcore_plugin_ref_user_data(plugin);
454         if (hal == NULL) {
455                 err("HAL is NULL");
456                 return;
457         }
458
459         user_data = tcore_hal_ref_user_data(hal);
460         if (user_data == NULL)
461                 return;
462
463         /* Deregister from Watch list */
464         __deregister_gio_watch(user_data->vdpram_watch_id);
465         dbg("Deregistered Watch ID");
466
467         /* Close VDPRAM device */
468         (void)vdpram_close(user_data->vdpram_fd);
469         dbg("Closed VDPRAM device");
470
471         /* Free custom data */
472         g_free(user_data);
473
474         /* Free HAL */
475         tcore_hal_free(hal);
476         dbg("Freed HAL");
477
478         dbg("Unloaded MODEM Interface Plug-in");
479 }
480
481 /* VMODEM (Modem Interface Plug-in) descriptor */
482 EXPORT_API struct tcore_plugin_define_desc plugin_define_desc = {
483         .name = "vmodem",
484         .priority = TCORE_PLUGIN_PRIORITY_HIGH,
485         .version = 1,
486         .load = on_load,
487         .init = on_init,
488         .unload = on_unload
489 };