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