Code sync from tizen_2.4
[platform/core/telephony/tel-plugin-vmodem.git] / src / desc-vmodem.c
1 /*
2  * tel-plugin-vmodem
3  *
4  * Copyright (c) 2012 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact: Junhwan An <jh48.an@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 <string.h>
23 #include <pthread.h>
24 #include <unistd.h>
25 #include <stdlib.h>
26 #include <time.h>
27 #include <fcntl.h>
28
29 #include <glib.h>
30
31 #include <tcore.h>
32 #include <plugin.h>
33 #include <server.h>
34 #include <user_request.h>
35 #include <hal.h>
36 #include <core_object.h>
37
38 #include "vdpram.h"
39
40 #define SERVER_INIT_WAIT_TIMEOUT                500
41
42 #define DEVICE_NAME_LEN_MAX                     16
43 #define DEVICE_NAME_PREFIX                      "pdp"
44
45 #define BUF_LEN_MAX                             512
46
47 #define CORE_OBJECT_NAME_MAX                    16
48
49 #define MODEM_PLUGIN_NAME                       "atmodem-plugin.so"
50
51 #define BIT_SIZE(type) (sizeof(type) * 8)
52
53 #define COPY_MASK(type) ((0xffffffff) >> (32 - BIT_SIZE(type)))
54
55 #define MASK(width, offset, data) \
56         (((width) == BIT_SIZE(data)) ? (data) :  \
57          ((((COPY_MASK(data) << (BIT_SIZE(data) - ((width) % BIT_SIZE(data)))) & COPY_MASK(data)) >> (offset)) & (data))) \
58
59
60 #define MASK_AND_SHIFT(width, offset, shift, data)      \
61         ((((signed) (shift)) < 0) ?               \
62          MASK((width), (offset), (data)) << -(shift) :  \
63          MASK((width), (offset), (data)) >> (((signed) (shift)))) \
64
65 struct custom_data {
66         int vdpram_fd;
67         guint watch_id_vdpram;
68 };
69
70 typedef struct {
71         TcoreHal *hal;
72         TcoreModem *modem;
73 } PluginData;
74
75 struct v_modules {
76         unsigned int co_type;
77         char co_name[CORE_OBJECT_NAME_MAX];
78 };
79
80 static char __util_unpackb(const char *src, int pos, int len)
81 {
82         char result = 0;
83         int rshift = 0;
84
85         src += pos / 8;
86         pos %= 8;
87
88         rshift = MAX(8 - (pos + len), 0);
89
90         if (rshift > 0) {
91                 result = MASK_AND_SHIFT(len, pos, rshift, (unsigned char)*src);
92         } else {
93                 result = MASK(8 - pos, pos, (unsigned char)*src);
94                 src++;
95                 len -= 8 - pos;
96
97                 if (len > 0) result = (result << len) | (*src >> (8 - len));   /* if any bits left */
98         }
99
100         return result;
101 }
102
103 static char __util_convert_byte_hexchar(char val)
104 {
105         char hex_char;
106
107         if (val <= 9)
108                 hex_char = (char) (val + '0');
109         else if (val >= 10 && val <= 15)
110                 hex_char = (char) (val - 10 + 'A');
111         else
112                 hex_char = '0';
113
114         return (hex_char);
115 }
116
117 static gboolean __util_byte_to_hex(const char *byte_pdu, char *hex_pdu, int num_bytes)
118 {
119         int i;
120         char nibble;
121         int buf_pos = 0;
122
123         for (i = 0; i < num_bytes * 2; i++) {
124                 nibble = __util_unpackb(byte_pdu, buf_pos, 4);
125                 buf_pos += 4;
126                 hex_pdu[i] = __util_convert_byte_hexchar(nibble);
127         }
128
129         return TRUE;
130 }
131
132 static TcoreModem *__get_modem(TcorePlugin *modem_iface_plugin)
133 {
134         PluginData *user_data;
135
136         if (modem_iface_plugin == NULL)
137                 return NULL;
138
139         user_data = tcore_plugin_ref_user_data(modem_iface_plugin);
140         if (user_data == NULL)
141                         return NULL;
142
143         /* 'modem' corresponding to Modem Interface plug-in */
144         return user_data->modem;
145 }
146
147 static guint __vmodem_reencode_mt_sms(gchar *mt_sms, guint mt_sms_len)
148 {
149 #define VMODEM_CR       0x0D
150 #define VMODEM_LF       0x0A
151 #define VMODEM_COLON    0x3A
152
153         gchar sms_buf[BUF_LEN_MAX] = {0, };
154         guint sca_len, pdu_len, tpdu_len;
155         gushort tpdu_len_ptr = 0;
156         gchar tpdu_len_str[8] = {0};
157         guint i, local_index = 0;
158
159         if (mt_sms_len > (BUF_LEN_MAX - 2))
160                 mt_sms_len = BUF_LEN_MAX - 2;
161
162         for (i = 0; i < mt_sms_len; i++) {
163                 if ((mt_sms[i] == VMODEM_CR)
164                                 && (mt_sms[i+1] == VMODEM_LF)) {
165                         sms_buf[i] = mt_sms[i];
166                         i++;
167                         sms_buf[i] = mt_sms[i];
168                         i++;
169                         break;
170                 } else if (mt_sms[i] == VMODEM_COLON) {
171                         tpdu_len_ptr = i + 1;
172                 }
173
174                 /* Byte copy */
175                 sms_buf[i] = mt_sms[i];
176         }
177         sca_len = mt_sms[i];
178         dbg("SCA length: [%d] TPDU length offset: [%d]", sca_len, tpdu_len_ptr);
179
180         pdu_len = (mt_sms_len-i);
181         tpdu_len = pdu_len - (sca_len+1);
182         dbg("PDU length: [%d] Actual TPDU Length: [%d]", pdu_len, tpdu_len);
183
184         if (pdu_len >= 100 && tpdu_len < 100) {
185                 /*
186                  * Move back complete buffer by a Byte (to fill up the
187                  * void created by hundreds place).
188                  */
189                 if (i > 3) {
190                         sms_buf[i-3] = VMODEM_CR;
191                         sms_buf[i-2] = VMODEM_LF;
192
193                         __util_byte_to_hex(&mt_sms[i], &sms_buf[i - 1], pdu_len);
194                         i += (2*pdu_len - 1);
195                 }
196         } else {
197                 __util_byte_to_hex(&mt_sms[i], &sms_buf[i], pdu_len);
198                 i += 2*pdu_len;
199         }
200
201         /* Update actual TPDU length */
202         snprintf(tpdu_len_str, 8, "%d", tpdu_len);
203         switch (strlen(tpdu_len_str)) {
204         case 4:                 /* 100s place */
205                 dbg("1000s : [%d]", tpdu_len_str[local_index]);
206
207                 sms_buf[tpdu_len_ptr] = tpdu_len_str[local_index];
208                 tpdu_len_ptr++;
209
210                 local_index++;
211         case 3:                 /* 100s place */
212                 dbg("100s : [%d]", tpdu_len_str[local_index]);
213
214                 sms_buf[tpdu_len_ptr] = tpdu_len_str[local_index];
215                 tpdu_len_ptr++;
216
217                 local_index++;
218         case 2:                 /* 10s place */
219                 dbg("10s : [%d]", tpdu_len_str[local_index]);
220
221                 sms_buf[tpdu_len_ptr] = tpdu_len_str[local_index];
222                 tpdu_len_ptr++;
223
224                 local_index++;
225         case 1:                 /* 1s place */
226                 dbg("1s : [%d]", tpdu_len_str[local_index]);
227
228                 sms_buf[tpdu_len_ptr] = tpdu_len_str[local_index];
229                 tpdu_len_ptr++;
230         break;
231         default:
232                 dbg("Unsupported length: [%d]", strlen(tpdu_len_str));
233         break;
234         }
235
236         /*
237          * Greater than  (BUF_LEN_MAX - 2),
238          * restrict the length to ( BUF_LEN_MAX - 2).
239          *
240          * This is to accomadate <CR> & <LF>.
241          */
242         if (i > (BUF_LEN_MAX - 2))
243                 i = BUF_LEN_MAX - 2;
244
245         /* Append <CR> & <LF> */
246         sms_buf[i++] = VMODEM_CR;
247         sms_buf[i++] = VMODEM_LF;
248         dbg("MT SMS: [%s]", sms_buf);
249
250         tcore_util_hex_dump("        ", (int)i, sms_buf);
251
252         /*
253          * Copy back
254          *
255          * 'data_len' is not accessed hence it need not be updated.
256          */
257         g_strlcpy(mt_sms, sms_buf, i+1);
258         dbg("Encoded MT SMS: [%d][%s]", i, mt_sms);
259
260         return i;
261 }
262
263 static guint __register_gio_watch(TcoreHal *h, int fd, void *callback)
264 {
265         GIOChannel *channel = NULL;
266         guint source;
267
268         dbg("Register to Watch list - fd: [%d]", fd);
269
270         if ((fd < 0) || (callback == NULL))
271                 return 0;
272
273         channel = g_io_channel_unix_new(fd);
274         source = g_io_add_watch(channel, G_IO_IN, (GIOFunc) callback, h);
275         g_io_channel_unref(channel);
276         channel = NULL;
277
278         return source;
279 }
280
281 static void __deregister_gio_watch(guint watch_id)
282 {
283         dbg("Deregister Watch ID: [%d]", watch_id);
284
285         /* Remove source */
286         g_source_remove(watch_id);
287 }
288
289 static gboolean __load_modem_plugin(gpointer data)
290 {
291         TcoreHal *hal;
292         TcorePlugin *plugin;
293         struct custom_data *user_data;
294         TcoreModem *modem;
295         unsigned int slot_count = 1;
296
297         dbg("Entry");
298
299         if (data == NULL) {
300                 err("data is NULL");
301                 return FALSE;
302         }
303
304         hal = data;
305         plugin = tcore_hal_ref_plugin(hal);
306         modem = __get_modem(plugin);
307
308         /* Load Modem Plug-in */
309         if (tcore_server_load_modem_plugin(tcore_plugin_ref_server(plugin),
310                         modem, MODEM_PLUGIN_NAME) == TCORE_RETURN_FAILURE) {
311                 err("Load Modem Plug-in - [FAIL]");
312
313                 goto EXIT;
314         } else {
315                 dbg("Load Modem Plug-in - [SUCCESS]");
316         }
317
318         tcore_server_send_notification(tcore_plugin_ref_server(plugin),
319                 NULL, TNOTI_SERVER_ADDED_MODEM_PLUGIN_COMPLETED,
320                 sizeof(slot_count), &slot_count);
321
322         /* To stop the cycle need to return FALSE */
323         return FALSE;
324
325 EXIT:
326         user_data = tcore_hal_ref_user_data(hal);
327         if (user_data == NULL)
328                 return FALSE;
329
330         /* Deregister from Watch list */
331         __deregister_gio_watch(user_data->watch_id_vdpram);
332
333         /* Free HAL */
334         tcore_hal_free(hal);
335
336         /* Close VDPRAM device */
337         vdpram_close(user_data->vdpram_fd);
338
339         /* Free custom data */
340         g_free(user_data);
341
342         return FALSE;
343 }
344
345 static TReturn _modem_power(TcoreHal *hal, gboolean enable)
346 {
347         struct custom_data *user_data;
348
349         user_data = tcore_hal_ref_user_data(hal);
350         if (user_data == NULL) {
351                 err(" User data is NULL");
352                 return TCORE_RETURN_FAILURE;
353         }
354
355         if (enable == TRUE) {                   /* POWER ON */
356                 if (FALSE == vdpram_poweron(user_data->vdpram_fd)) {
357                         err(" Power ON - [FAIL]");
358                         return TCORE_RETURN_FAILURE;
359                 }
360
361                 /* Set Power State - ON */
362                 tcore_hal_set_power_state(hal, TRUE);
363         } else {                                        /* POWER OFF */
364                 if (vdpram_poweroff(user_data->vdpram_fd) == FALSE) {
365                         err(" Power OFF - [FAIL]");
366                         return TCORE_RETURN_FAILURE;
367                 }
368
369                 /* Set Power state - OFF */
370                 tcore_hal_set_power_state(hal, FALSE);
371         }
372
373         return TCORE_RETURN_SUCCESS;
374 }
375
376 static gboolean on_recv_vdpram_message(GIOChannel *channel,
377         GIOCondition condition, gpointer data)
378 {
379         TcoreHal *hal = data;
380         struct custom_data *custom;
381         char buf[BUF_LEN_MAX];
382         int n = 0;
383         TReturn ret;
384
385         custom = tcore_hal_ref_user_data(hal);
386         memset(buf, 0x0, BUF_LEN_MAX);
387
388         /* Read from Device */
389         n = vdpram_tty_read(custom->vdpram_fd, buf, BUF_LEN_MAX);
390         if (n < 0) {
391                 err(" Read error - Data received: [%d]", n);
392                 return TRUE;
393         }
394         dbg(" DPRAM Receive - Data length: [%d]", n);
395
396         msg("\n---------- [RECV] Length of received data: [%d] ----------\n", n);
397
398         /* Emit receive callback */
399         tcore_hal_emit_recv_callback(hal, n, buf);
400
401         /*
402          * This is to ensure that the received MT SMS (+CMT:) is
403          * encoded according to 3GPP standard
404          */
405         if (buf[0] == 0x2B && buf[1] == 0x43 && buf[2] == 0x4D
406                         && buf[3] == 0x54 && buf[4] == 0x3A) {
407                 dbg("Received - [MT SMS]");
408                 n = __vmodem_reencode_mt_sms((gchar *)buf, n);
409         } else if (buf[0] == 0x25) {
410                 dbg("Replaced % --> +");
411                 buf[0] = 0x2B;
412         }
413
414         /* Dispatch received data to response handler */
415         dbg("Invoking tcore_hal_dispatch_response_data()");
416         ret = tcore_hal_dispatch_response_data(hal, 0, n, buf);
417         msg("\n---------- [RECV FINISH] Receive processing: [%d] ----------\n", ret);
418
419         return TRUE;
420 }
421
422 static TReturn hal_power(TcoreHal *hal, gboolean flag)
423 {
424         return _modem_power(hal, flag);
425 }
426
427 static TReturn hal_send(TcoreHal *hal, unsigned int data_len, void *data)
428 {
429         int ret;
430         struct custom_data *user_data;
431
432         if (tcore_hal_get_power_state(hal) == FALSE) {
433                 err(" HAL Power state - OFF");
434                 return TCORE_RETURN_FAILURE;
435         }
436
437         user_data = tcore_hal_ref_user_data(hal);
438         if (user_data == NULL) {
439                 err(" User data is NULL");
440                 return TCORE_RETURN_FAILURE;
441         }
442
443         ret = vdpram_tty_write(user_data->vdpram_fd, data, data_len);
444         if (ret < 0) {
445                 err(" Write failed");
446                 return TCORE_RETURN_FAILURE;
447         } else {
448                 dbg("vdpram_tty_write success ret=%d (fd=%d, len=%d)",
449                         ret, user_data->vdpram_fd, data_len);
450                 return TCORE_RETURN_SUCCESS;
451         }
452 }
453
454 static TReturn hal_setup_netif(CoreObject *co,
455         TcoreHalSetupNetifCallback func, void *user_data,
456         unsigned int cid, gboolean enable)
457 {
458         char ifname[DEVICE_NAME_LEN_MAX];
459         int size = 0;
460         int fd = 0;
461         char buf[32];
462         const char *control = NULL;
463
464         if (cid > 3) {
465                 err(" Context ID: [%d]", cid);
466                 return TCORE_RETURN_EINVAL;
467         }
468
469         if (enable == TRUE) {
470                 dbg(" ACTIVATE - Context ID: [%d]", cid);
471                 control = "/sys/class/net/svnet0/pdp/activate";
472         } else {
473                 dbg(" DEACTIVATE - Context ID: [%d]", cid);
474                 control = "/sys/class/net/svnet0/pdp/deactivate";
475         }
476
477         fd = open(control, O_WRONLY);
478         if (fd < 0) {
479                 err(" Failed to Open interface: [%s]", control);
480
481                 /* Invoke callback function */
482                 if (func)
483                         func(co, -1, NULL, user_data);
484
485                 return TCORE_RETURN_FAILURE;
486         }
487
488         /* Context ID needs to be written to the Device */
489         snprintf(buf, sizeof(buf), "%d", cid);
490         size = write(fd, buf, strlen(buf));
491         dbg(" SIZE [%d]", size);
492
493         /* Close 'fd' */
494         close(fd);
495
496         /* Device name */
497         snprintf(ifname, DEVICE_NAME_LEN_MAX, "%s%d", DEVICE_NAME_PREFIX, (cid - 1));
498         dbg(" Interface Name: [%s]", ifname);
499
500         /* Invoke callback function */
501         if (func)
502                 func(co, 0, ifname, user_data);
503
504         return TCORE_RETURN_SUCCESS;
505 }
506
507 /* HAL Operations */
508 static struct tcore_hal_operations hal_ops = {
509         .power = hal_power,
510         .send = hal_send,
511         .setup_netif = hal_setup_netif,
512 };
513
514 static gboolean on_load()
515 {
516         dbg(" Load!!!");
517
518         return TRUE;
519 }
520
521 static gboolean on_init(TcorePlugin *plugin)
522 {
523         TcoreHal *hal;
524         PluginData *user_data;
525         struct custom_data *data;
526
527         dbg(" Init!!!");
528
529         if (plugin == NULL) {
530                 err(" PLug-in is NULL");
531                 return FALSE;
532         }
533
534         /* User Data for Modem Interface Plug-in */
535         user_data = g_try_new0(PluginData, 1);
536         if (user_data == NULL) {
537                 err(" Failed to allocate memory for Plugin data");
538                 return FALSE;
539         }
540
541         /* Register to Server */
542         user_data->modem = tcore_server_register_modem(tcore_plugin_ref_server(plugin), plugin);
543         if (user_data->modem == NULL) {
544                 err(" Registration Failed");
545                 g_free(user_data);
546                 return FALSE;
547         }
548         dbg(" Registered from Server");
549
550
551         data = g_try_new0(struct custom_data, 1);
552         if (data == NULL) {
553                 err(" Failed to allocate memory for Custom data");
554
555                 /* Unregister from Server */
556                 tcore_server_unregister_modem(tcore_plugin_ref_server(plugin), user_data->modem);
557
558                 /* Free Plugin data */
559                 g_free(user_data);
560
561                 return FALSE;
562         }
563
564         /*
565          * Open DPRAM device
566          */
567         data->vdpram_fd = vdpram_open();
568         if (data->vdpram_fd < 0) {
569                 /* Fre custom data */
570                 g_free(data);
571
572                 /* Unregister from Server */
573                 tcore_server_unregister_modem(tcore_plugin_ref_server(plugin), user_data->modem);
574
575                 /* Free Plugin data */
576                 g_free(user_data);
577
578                 return FALSE;
579         }
580         /*
581          * Create and initialize HAL
582          */
583         hal = tcore_hal_new(plugin, "vmodem", &hal_ops, TCORE_HAL_MODE_AT);
584         if (hal == NULL) {
585                 /* Close VDPRAM device */
586                 vdpram_close(data->vdpram_fd);
587
588                 /* Fre custom data */
589                 g_free(data);
590
591                 /* Unregister from Server */
592                 tcore_server_unregister_modem(tcore_plugin_ref_server(plugin), user_data->modem);
593
594                 /* Fre Plugin data */
595                 g_free(user_data);
596
597                 return FALSE;
598         }
599         user_data->hal = hal;
600
601         /* Link custom data to HAL user data */
602         tcore_hal_link_user_data(hal, data);
603
604         /* Set HAL as Modem Interface Plug-in's User data */
605         tcore_plugin_link_user_data(plugin, user_data);
606
607         /* Register to Watch list */
608         data->watch_id_vdpram = __register_gio_watch(hal,
609                                 data->vdpram_fd, on_recv_vdpram_message);
610         dbg(" fd: [%d] Watch ID: [%d]",
611                                 data->vdpram_fd, data->watch_id_vdpram);
612
613         /* Power ON VDPRAM device */
614         if (_modem_power(hal, TRUE) == TCORE_RETURN_SUCCESS) {
615                 dbg(" Power ON - [SUCCESS]");
616         } else {
617                 err(" Power ON - [FAIL]");
618                 goto EXIT;
619         }
620
621         /* Check CP Power ON */
622         g_timeout_add_full(G_PRIORITY_HIGH, SERVER_INIT_WAIT_TIMEOUT, __load_modem_plugin, hal, 0);
623
624         dbg("[VMMODEM] Exit");
625         return TRUE;
626
627 EXIT:
628         /* Deregister from Watch list */
629         __deregister_gio_watch(data->watch_id_vdpram);
630
631         /* Free HAL */
632         tcore_hal_free(hal);
633
634         /* Close VDPRAM device */
635         vdpram_close(data->vdpram_fd);
636
637         /* Free custom data */
638         g_free(data);
639
640         /* Unregister from Server */
641         tcore_server_unregister_modem(tcore_plugin_ref_server(plugin), user_data->modem);
642
643         /*Free Plugin Data*/
644         g_free(user_data);
645
646         return FALSE;
647 }
648
649 static void on_unload(TcorePlugin *plugin)
650 {
651         TcoreHal *hal;
652         struct custom_data *data;
653         PluginData *user_data;
654
655         dbg(" Unload!!!");
656
657         if (plugin == NULL)
658                 return;
659
660         user_data = tcore_plugin_ref_user_data(plugin);
661         if (user_data == NULL)
662                 return;
663
664         hal = user_data->hal;
665
666         /* Unload Modem Plug-in */
667 #if 0   /* TODO - Open the code below */
668         tcore_server_unload_modem_plugin(tcore_plugin_ref_server(plugin), plugin);
669 #endif
670         data = tcore_hal_ref_user_data(hal);
671         if (data == NULL)
672                 return;
673
674         /* Deregister from Watch list */
675         __deregister_gio_watch(data->watch_id_vdpram);
676         dbg(" Deregistered Watch ID");
677
678         /* Free HAL */
679         tcore_hal_free(hal);
680         dbg(" Freed HAL");
681
682         /* Close VDPRAM device */
683         vdpram_close(data->vdpram_fd);
684         dbg(" Closed VDPRAM device");
685
686         /* Free custom data */
687         g_free(data);
688
689         tcore_server_unregister_modem(tcore_plugin_ref_server(plugin), user_data->modem);
690         dbg(" Unregistered from Server");
691
692         dbg(" Unloaded MODEM");
693         g_free(user_data);
694 }
695
696 /* VMODEM Descriptor Structure */
697 EXPORT_API struct tcore_plugin_define_desc plugin_define_desc = {
698         .name = "VMODEM",
699         .priority = TCORE_PLUGIN_PRIORITY_HIGH,
700         .version = 1,
701         .load = on_load,
702         .init = on_init,
703         .unload = on_unload
704 };