tizen 2.3.1 release
[framework/telephony/libtcore.git] / src / at.c
1 /*
2  * libtcore
3  *
4  * Copyright (c) 2012 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact: Ja-young Gu <jygu@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 <stdlib.h>
24 #include <ctype.h>
25
26 #include <glib.h>
27
28 #include "tcore.h"
29 #include "hal.h"
30 #include "queue.h"
31 #include "user_request.h"
32 #include "at.h"
33 #include "core_object.h"
34
35 #define NUM_ELEMS(x) (sizeof(x)/sizeof(x[0]))
36 #define MODE_HEX        0
37 #define MODE_BIN        1
38
39 #define CR '\r'
40 #define LF '\n'
41
42 #define MAX_AT_RESPONSE    255  // for testing
43 //#define MAX_AT_RESPONSE    8191
44
45 typedef gboolean (*rfs_hook_cb) (const gchar *data);
46
47 struct tcore_at_type {
48         TcoreHal *hal;
49
50         enum tcore_at_command_type cmd_type;
51
52         GHashTable *unsolicited_table;
53
54         struct tcore_at_request *req;
55         struct tcore_at_response *resp;
56
57         unsigned int buf_size;
58         char *buf;
59         char *buf_read_pos;
60         char *buf_write_pos;
61
62         gboolean pdu_status;
63         struct _notification *pdu_noti;
64         GSList *pdu_lines;
65
66         rfs_hook_cb rfs_hook;
67         gboolean data_mode;
68 };
69
70 struct _notification_callback {
71         TcoreATNotificationCallback callback;
72         void *user_data;
73 };
74
75 struct _notification {
76         gboolean type_pdu;
77         GSList *callbacks;
78 };
79
80 /**
81  * returns 1 if line is a final response indicating success
82  * See 27.007 annex B
83  */
84 static const char *list_final_responses_success[] = {
85     "OK",
86         "CONNECT",
87 };
88
89 /**
90  * returns 1 if line is a final response indicating error
91  * See 27.007 annex B
92  */
93 static const char *list_final_responses_error[] = {
94     "ERROR",
95     "+CMS ERROR:",
96     "+CME ERROR:",
97     "NO ANSWER",
98     "NO DIALTONE",
99 };
100
101 static int _check_final_response(const char *line)
102 {
103         unsigned int i;
104
105         for (i = 0; i < NUM_ELEMS(list_final_responses_success); i++) {
106                 if (g_str_has_prefix(line, list_final_responses_success[i])) {
107                         return 1;
108                 }
109         }
110
111         for (i = 0; i < NUM_ELEMS(list_final_responses_error); i++) {
112                 if (g_str_has_prefix(line, list_final_responses_error[i])) {
113                         return 2;
114                 }
115         }
116
117         return 0;
118 }
119
120
121 static char *_find_next_EOL(char *cur)
122 {
123         if ((cur[0] == '>' && cur[1] == ' ')
124                         && (cur[2] == '\0' || cur[2] == CR)) {
125                 /* SMS prompt character...not \r terminated */
126                 dbg("SMS prompt character: [%c]", cur[0]);
127                 return cur + 2;
128         }
129
130         /*
131          * Find next newline
132          *
133          * Line should either end with -
134          *  - '\0' termination
135          * or
136          *  - Carriage Return '\r' and Line Feed  '\n'.
137          */
138         while (*cur != '\0' && !((*cur == CR) && (*(cur + 1) == LF)))
139                 cur++;
140
141         return *cur == '\0' ? NULL : cur;
142 }
143
144 static struct tcore_at_response* _response_new()
145 {
146         struct tcore_at_response *resp;
147
148         resp = calloc(1, sizeof(struct tcore_at_response));
149         if (!resp)
150                 return NULL;
151
152         return resp;
153 }
154
155 static void _response_free(struct tcore_at_response *resp)
156 {
157         if (!resp)
158                 return;
159
160         if (resp->lines) {
161                 g_slist_free_full(resp->lines, g_free);
162         }
163
164         if (resp->final_response)
165                 free(resp->final_response);
166
167         free(resp);
168 }
169
170
171 static void _response_add(struct tcore_at_response *resp,
172                 const char *line)
173 {
174         if (!resp || !line)
175                 return;
176
177         dbg("current lines = %d", g_slist_length(resp->lines));
178
179         resp->lines = g_slist_append(resp->lines, strdup(line));
180 }
181
182 static void _emit_pending_response(TcoreAT *at)
183 {
184         TcorePending *p;
185
186         if (!at)
187                 return;
188
189         p = tcore_queue_pop(tcore_hal_ref_queue(at->hal));
190         if (!p) {
191                 dbg("no pending");
192         }
193
194         tcore_pending_emit_response_callback(p, sizeof(TcoreATResponse), at->resp);
195         tcore_user_request_unref(tcore_pending_ref_user_request(p));
196
197         tcore_at_request_free(at->req);
198         at->req = NULL;
199         tcore_pending_free(p);
200
201         _response_free(at->resp);
202         at->resp = NULL;
203 }
204
205 static void _emit_unsolicited_message(TcoreAT *at, const char *line)
206 {
207         struct _notification *noti = NULL;
208         struct _notification_callback *item = NULL;
209         GSList *p;
210         GHashTableIter iter;
211         gpointer key = NULL, value;
212         gboolean ret;
213         GSList *data = NULL;
214
215         if (!at || !line)
216                 return;
217
218         dbg("at->pdu_status  %d line 0x%x at->data_mode %d", at->pdu_status, line, at->data_mode);
219         if (at->pdu_status == FALSE) {
220                 g_hash_table_iter_init(&iter, at->unsolicited_table);
221
222                 while (g_hash_table_iter_next(&iter, &key, &value)) {
223                         if (!g_str_has_prefix(line, key))
224                                 continue;
225
226                         noti = value;
227                         break;
228                 }
229
230                 if (!noti)
231                         return;
232
233                 if (noti->type_pdu == TRUE) {
234                         at->pdu_status = TRUE;
235                         at->pdu_noti = noti;
236                         at->pdu_lines = g_slist_append(NULL, g_strdup(line));
237                         dbg("PDU mode");
238                         return;
239                 }
240
241                 if (at->data_mode == MODE_BIN) {
242                         at->pdu_lines = g_slist_append(at->pdu_lines, (gpointer)line);
243                         data = at->pdu_lines;
244                 } else {
245                         data = g_slist_append(NULL, g_strdup(line));
246                 }
247         }
248         else {
249                 noti = at->pdu_noti;
250                 at->pdu_status = FALSE;
251                 at->pdu_noti = NULL;
252
253                 if (at->data_mode == MODE_BIN) {
254                         dbg("Binary mode");
255                         at->pdu_lines = g_slist_append(at->pdu_lines, (gpointer)line);
256                         dbg("at->pdu_lines: 0x%x", at->pdu_lines);
257                 } else {
258                         at->pdu_lines = g_slist_append(at->pdu_lines, g_strdup(line));
259                 }
260
261                 data = at->pdu_lines;
262         }
263
264         p = noti->callbacks;
265         while (p) {
266                 item = p->data;
267                 if (!item) {
268                         p = p->next;
269                         continue;
270                 }
271
272                 ret = item->callback(at, data, item->user_data);
273                 if (ret == FALSE) {
274                         p = p->next;
275                         noti->callbacks = g_slist_remove(noti->callbacks, item);
276                         continue;
277                 }
278
279                 p = p->next;
280         }
281
282         dbg(" Free the list");
283         if (at->data_mode != MODE_BIN) {
284                 g_slist_free_full(data, g_free);
285         }
286         at->pdu_lines = NULL;
287
288         if (g_slist_length(noti->callbacks) == 0) {
289                 g_hash_table_remove(at->unsolicited_table, key);
290         }
291         dbg("exit");
292 }
293
294 static void _free_noti_list(void *data)
295 {
296         struct _notification *noti = data;
297
298         if (!data)
299                 return;
300
301         g_slist_free_full(noti->callbacks, g_free);
302 }
303
304 #if 0
305 static void _msgat(const char *prefix, const char *str)
306 {
307         unsigned int i;
308         char buf[8192] = {0,};
309         char *pos;
310
311         if (!str) {
312                 msg("str is NULL");
313                 return;
314         }
315
316         if (strlen(str) > 4096) {
317                 msg("%s[%s]", prefix, str);
318                 return;
319         }
320
321         pos = buf;
322         for (i = 0; i < strlen(str); i++) {
323                 if (str[i] == '\r') {
324                         strncpy(pos, "<CR>", 4);
325                         pos += 4;
326                 }
327                 else if (str[i] == '\n') {
328                         strncpy(pos, "<LF>", 4);
329                         pos += 4;
330                 }
331                 else {
332                         *pos = str[i];
333                         pos++;
334                 }
335
336         }
337
338         msg("%s[%s]", prefix, buf);
339 }
340 #endif
341
342 TcoreAT *tcore_at_new(TcoreHal *hal)
343 {
344         TcoreAT *at;
345
346         at = calloc(1, sizeof(struct tcore_at_type));
347         if (!at)
348                 return NULL;
349
350         at->hal = hal;
351         at->buf_size = MAX_AT_RESPONSE;
352         at->buf = calloc(1, at->buf_size + 1);
353         at->buf_read_pos = at->buf;
354         at->buf_write_pos = at->buf;
355         at->data_mode = MODE_HEX;
356         at->unsolicited_table = g_hash_table_new_full(g_str_hash, g_str_equal,
357                         g_free, _free_noti_list );
358
359         return at;
360 }
361
362 void tcore_at_free(TcoreAT *at)
363 {
364         if (!at)
365                 return;
366
367         if (at->buf)
368                 free(at->buf);
369
370         if (at->unsolicited_table)
371                 g_hash_table_destroy(at->unsolicited_table);
372
373         free(at);
374 }
375
376 TReturn tcore_at_remove_notification_full(TcoreAT *at, const char *prefix,
377                 TcoreATNotificationCallback callback, void *user_data)
378 {
379         struct _notification *noti;
380         struct _notification_callback *item;
381         GSList *p;
382
383         if (!at || !prefix)
384                 return TCORE_RETURN_EINVAL;
385
386         if (!callback) {
387                 /* remove all callbacks for prefix */
388                 g_hash_table_remove(at->unsolicited_table, prefix);
389                 return TCORE_RETURN_SUCCESS;
390         }
391
392         noti = g_hash_table_lookup(at->unsolicited_table, prefix);
393         if (!noti)
394                 return TCORE_RETURN_SUCCESS;
395
396         p = noti->callbacks;
397         for(; p; p = p->next) {
398                 item = p->data;
399                 if (!item)
400                         continue;
401
402                 if (callback == item->callback) {
403                         if (!user_data) {
404                                 noti->callbacks = g_slist_remove(noti->callbacks, item);
405                                 continue;
406                         }
407
408                         if (user_data == item->user_data) {
409                                 noti->callbacks = g_slist_remove(noti->callbacks, item);
410                                 continue;
411                         }
412                 }
413         }
414
415         return TCORE_RETURN_SUCCESS;
416 }
417
418
419 TReturn tcore_at_remove_notification(TcoreAT *at, const char *prefix,
420                 TcoreATNotificationCallback callback)
421 {
422         return tcore_at_remove_notification_full(at, prefix, callback, NULL);
423 }
424
425 TReturn tcore_at_add_notification(TcoreAT *at, const char *prefix,
426                 gboolean pdu, TcoreATNotificationCallback callback,
427                 void *user_data)
428 {
429         struct _notification *noti;
430         struct _notification_callback *item;
431
432         if (!at || !prefix || !callback)
433                 return TCORE_RETURN_EINVAL;
434
435         noti = g_hash_table_lookup(at->unsolicited_table, prefix);
436         if (!noti) {
437                 noti = calloc(1, sizeof(struct _notification));
438                 if (!noti)
439                         return TCORE_RETURN_ENOMEM;
440
441                 noti->type_pdu = pdu;
442                 noti->callbacks = NULL;
443
444                 g_hash_table_insert(at->unsolicited_table, g_strdup(prefix), noti);
445         }
446
447         if (noti->type_pdu != pdu)
448                 return TCORE_RETURN_EINVAL;
449
450         item = calloc(1, sizeof(struct _notification_callback));
451         if (!item)
452                 return TCORE_RETURN_ENOMEM;
453
454         item->callback = callback;
455         item->user_data = user_data;
456
457         noti->callbacks = g_slist_append(noti->callbacks, item);
458
459         return TCORE_RETURN_SUCCESS;
460 }
461
462 TReturn tcore_at_set_request(TcoreAT *at, TcoreATRequest *req, gboolean send)
463 {
464         TReturn ret;
465         char *end;
466         char next;
467
468         if (!at || !req) {
469                 dbg("Invalid data");
470                 return TCORE_RETURN_EINVAL;
471         }
472
473         if (!req->cmd) {
474                 dbg("Invalid cmd");
475                 return TCORE_RETURN_EINVAL;
476         }
477
478         at->req = req;
479
480         if (send == FALSE)
481                 return TCORE_RETURN_SUCCESS;
482
483 #ifdef EMULATOR
484         if (strstr(at->req->cmd, "CMGS")) {
485                 dbg("In case of emulator, do not check CR for CMGS");
486                 return tcore_hal_send_data(at->hal, strlen(req->cmd), req->cmd);
487         }
488 #endif
489
490         end = strchr(at->req->cmd, CR);
491         if(end) {
492                 next = *(end + 1);
493                 if (next == '\0') {
494                         return tcore_hal_send_data(at->hal, strlen(req->cmd), req->cmd);
495                 }
496
497                 at->req->next_send_pos = end + 1;
498                 dbg("backup data = [%c] next data = [%s]", next, at->req->next_send_pos);
499
500                 *(end+1) = '\0';
501                 ret = tcore_hal_send_data(at->hal, strlen(req->cmd), req->cmd);
502                 *(end+1) = next;
503
504                 return ret;
505         } else {
506                 return TCORE_RETURN_EINVAL;
507         }
508 }
509
510 TcoreATRequest *tcore_at_get_request(TcoreAT *at)
511 {
512         if (!at)
513                 return NULL;
514
515         return at->req;
516 }
517
518
519 TcoreATResponse *tcore_at_get_response(TcoreAT *at)
520 {
521         if (!at)
522                 return NULL;
523
524         return at->resp;
525 }
526
527 TReturn tcore_at_buf_write(TcoreAT *at, unsigned int data_len, const char *data)
528 {
529         unsigned int read_pos;
530         unsigned int write_pos;
531
532         if (!at) {
533                 err("at is NULL");
534                 return TCORE_RETURN_EINVAL;
535         }
536
537         read_pos = at->buf_read_pos - at->buf;
538         write_pos = at->buf_write_pos - at->buf;
539
540         if (write_pos + data_len >= at->buf_size) {
541                 /* shift left (trim completed data) */
542                 dbg("shift left buffer (request data_len = %d)", data_len);
543                 dbg("before read_pos=buf+%d, write_pos=buf+%d", read_pos, write_pos);
544                 memmove(at->buf, at->buf_read_pos, at->buf_size - read_pos);
545                 at->buf_read_pos = at->buf;
546                 at->buf_write_pos = at->buf + write_pos - read_pos;
547                 dbg("after  read_pos=buf+%d, write_pos=buf+%d",
548                                 at->buf_read_pos - at->buf,
549                                 at->buf_write_pos - at->buf);
550                 memset(at->buf_write_pos, 0, at->buf_size - (at->buf_write_pos - at->buf));
551         }
552
553         write_pos = at->buf_write_pos - at->buf;
554         if (write_pos + data_len >= at->buf_size) {
555                 while (1) {
556                         at->buf_size = at->buf_size << 1;
557                         if (at->buf_size > write_pos + data_len)
558                                 break;
559                 }
560                 at->buf = realloc(at->buf, at->buf_size);
561                 at->buf_read_pos = at->buf;
562                 at->buf_write_pos = at->buf + write_pos;
563                 memset(at->buf_write_pos, 0, at->buf_size - (at->buf_write_pos - at->buf));
564
565                 dbg("resize buffer to %d", at->buf_size);
566         }
567
568         memcpy(at->buf_write_pos, data, data_len);
569
570         at->buf_write_pos += data_len;
571         return TCORE_RETURN_SUCCESS;
572 }
573
574 TcoreATRequest* tcore_at_request_new(const char *cmd, const char *prefix, enum tcore_at_command_type type)
575 {
576         TcoreATRequest *req;
577
578         if (!cmd)
579                 return NULL;
580
581         if (strlen(cmd) < 1)
582                 return NULL;
583
584         req = calloc(1, sizeof(struct tcore_at_request));
585         if (!req)
586                 return NULL;
587
588         if (!strchr(cmd, CR))
589                 req->cmd = g_strdup_printf("%s%c", cmd, CR);
590         else
591                 req->cmd = g_strdup_printf("%s%c", cmd, 26);
592
593         if (prefix)
594                 req->prefix = strdup(prefix);
595
596         req->type = type;
597
598         return req;
599 }
600
601 void tcore_at_request_free(TcoreATRequest *req)
602 {
603         if (!req)
604                 return;
605
606         if (req->cmd)
607                 free(req->cmd);
608
609         if (req->prefix)
610                 free(req->prefix);
611
612         free(req);
613 }
614
615 /* To get the length value from little-endian bytes */
616 static int __sum_4_bytes(const char *posn)
617 {
618         int sum = 0;
619         sum = sum | (*(posn+3)) << 24;
620         sum = sum | (*(posn+2)) << 16;
621         sum = sum | (*(posn+1)) << 8;
622         sum = sum | *posn ;
623         return sum;
624 }
625
626 /* Function to process binary data received as part of XDRV Indication */
627 void tcore_at_process_binary_data(TcoreAT *at, char *position, int data_len)
628 {
629
630         #define NVM_PAYLOAD_LENGTH_0                    52
631         #define NVM_PAYLOAD_LENGTH_1                    68
632
633         int m_length_0 = ZERO , m_length_1 = ZERO;
634         static int data_len_final = ZERO, actual_buffer_size = ZERO;
635         dbg("Entered");
636
637         m_length_0 = __sum_4_bytes(&position[NVM_PAYLOAD_LENGTH_0]);
638         m_length_1 = __sum_4_bytes(&position[NVM_PAYLOAD_LENGTH_1]);
639         data_len_final = data_len_final + data_len;
640
641         dbg("m_length_0 = %d , m_length_1 = %d, data_len_final = %d actual_buffer_size: %d", m_length_0, m_length_1, data_len_final, actual_buffer_size);
642         if (actual_buffer_size == ZERO) {
643                 actual_buffer_size = data_len + m_length_0 + m_length_1;
644                 dbg("Actual buffer size is %d", actual_buffer_size);
645         }
646
647         if (data_len_final == actual_buffer_size) {
648                 _emit_unsolicited_message(at, position);
649                 at->data_mode = MODE_HEX;
650                 at->buf_read_pos = at->buf_read_pos + (actual_buffer_size + 1);
651                 data_len_final = ZERO;
652                 actual_buffer_size = ZERO;
653         }
654         dbg("Exit");
655 }
656
657 gboolean tcore_at_process(TcoreAT *at, unsigned int data_len, const char *data)
658 {
659         char *pos;
660         char *next_pos;
661         int ret;
662
663         if (!at || !data)
664                 return FALSE;
665
666         tcore_at_buf_write(at, data_len, data);
667
668         pos = at->buf_read_pos;
669         dbg("On entry at->buf_read_pos: 0x%x", at->buf_read_pos);
670
671         while (1) {
672
673                 while (*pos == CR || *pos == LF)
674                         pos++;
675
676                 next_pos = _find_next_EOL(pos);
677                 if (!next_pos) {
678                         dbg("Data could be in Binary mode !!");
679                         if (at->rfs_hook) {
680                                 if (TRUE == at->rfs_hook(pos)){
681                                         at->data_mode = MODE_BIN;
682                                         tcore_at_process_binary_data(at, pos, data_len);
683                                 }
684                                 dbg("Not Binary data");
685                         }else
686                                 dbg("Rfs hook is not set !!");
687                         break;
688                 }
689
690                 if (pos != next_pos)
691                         *next_pos = '\0';
692
693                 //dbg("complete line found.");
694                 dbg("line = [%s]", pos);
695
696                 // check request
697                 if (!at->req) {
698                         _emit_unsolicited_message(at, pos);
699                 }
700                 else {
701                         if (g_strcmp0(pos, "> ") == 0) {
702                                 if (at->req->next_send_pos) {
703                                         dbg("send next: [%s]", at->req->next_send_pos);
704                                         tcore_hal_send_data(at->hal, strlen(at->req->next_send_pos), at->req->next_send_pos);
705                                         pos += 3;       /* Including NULL character */
706                                         at->buf_read_pos = pos;
707                                         break;
708                                 }
709                         }
710
711                         if (!at->resp) {
712                                 at->resp = _response_new();
713                                 if (!at->resp)
714                                         return FALSE;
715                         }
716
717                         ret = _check_final_response(pos);
718                         if (ret) {
719                                 if (ret == 1)
720                                         at->resp->success = TRUE;
721                                 else
722                                         at->resp->success = FALSE;
723
724                                 at->resp->final_response = strdup(pos);
725
726                                 _emit_pending_response(at);
727                                 at->buf_read_pos = next_pos + 1;
728                                 return TRUE;
729                         }
730                         else {
731                                 switch (at->req->type) {
732                                         case TCORE_AT_NO_RESULT:
733                                                 _emit_unsolicited_message(at, pos);
734                                                 break;
735
736                                         case TCORE_AT_NUMERIC:
737                                                 if (at->resp->lines == NULL && isdigit(pos[0])) {
738                                                         _response_add(at->resp, pos);
739                                                 }
740                                                 else {
741                                                         _emit_unsolicited_message(at, pos);
742                                                 }
743
744                                                 break;
745
746                                         case TCORE_AT_SINGLELINE:
747                                                 if (at->resp->lines == NULL) {
748                                                         if (at->req->prefix) {
749                                                                 if (g_str_has_prefix(pos, at->req->prefix)) {
750                                                                         _response_add(at->resp, pos);
751                                                                 }
752                                                                 else {
753                                                                         _emit_unsolicited_message(at, pos);
754                                                                 }
755                                                         }
756                                                         else {
757                                                                 _response_add(at->resp, pos);
758                                                         }
759                                                 }
760                                                 else {
761                                                         _emit_unsolicited_message(at, pos);
762                                                 }
763                                                 break;
764
765                                         case TCORE_AT_MULTILINE:
766                                                 if (at->req->prefix) {
767                                                         if (g_str_has_prefix(pos, at->req->prefix)) {
768                                                                 _response_add(at->resp, pos);
769                                                         }
770                                                         else {
771                                                                 _emit_unsolicited_message(at, pos);
772                                                         }
773                                                 }
774                                                 else {
775                                                         _response_add(at->resp, pos);
776                                                 }
777                                                 break;
778
779                                         case TCORE_AT_PDU:
780                                                 if (at->req->prefix) {
781                                                         if (g_str_has_prefix(pos, at->req->prefix)) {
782                                                                 _response_add(at->resp, pos);
783                                                         }
784                                                         else {
785                                                                 if (at->resp->lines != NULL) {
786                                                                         _response_add(at->resp, pos);
787                                                                 }
788                                                                 else {
789                                                                         _emit_unsolicited_message(at, pos);
790                                                                 }
791                                                         }
792                                                 }
793                                                 else {
794                                                         _response_add(at->resp, pos);
795                                                 }
796                                                 break;
797
798                                         default:
799                                                 dbg("unknown");
800                                                 _emit_unsolicited_message(at, pos);
801                                                 break;
802                                 }
803                         }
804                 }
805
806                 //
807                 pos = next_pos + 1;
808                 at->buf_read_pos = pos;
809         }
810
811         return FALSE;
812 }
813
814 TcorePending *tcore_at_pending_new(CoreObject *co, const char *cmd, const char *prefix, enum tcore_at_command_type type, TcorePendingResponseCallback func, void *user_data)
815 {
816         TcorePending *p;
817         TcoreATRequest *req;
818
819         if (!cmd)
820                 return NULL;
821
822         req = tcore_at_request_new(cmd, prefix, type);
823         if (!req)
824                 return NULL;
825
826         p = tcore_pending_new(co, 0);
827         if (!p) {
828                 tcore_at_request_free(req);
829                 return NULL;
830         }
831
832         tcore_pending_set_request_data(p, 0, req);
833         tcore_pending_set_response_callback(p, func, user_data);
834
835         return p;
836 }
837
838 #define TYPE_NONE               0
839 #define TYPE_RAW                1
840 #define TYPE_STR                2
841 #define TYPE_STR_FIN    3
842 #define TYPE_PAREN              4
843 #define TYPE_PAREN_FIN  5
844
845 GSList *tcore_at_tok_new(const char *line)
846 {
847         char *begin;
848         char *pos;
849         char *buf = NULL;
850         char *mark_end = NULL;
851         int type = TYPE_NONE;
852         GSList *tokens = NULL;
853
854         if (!line)
855                 return NULL;
856
857         if (strlen(line) == 0)
858                 return NULL;
859
860         if (line[0] == '(') {
861                 /* list token container */
862                 pos = (char *)line;
863                 if (line[strlen(line)-1] == ')')
864                         mark_end = (char *)line + strlen(line) - 1;
865         }
866         else {
867                 /* normal at message */
868                 pos = strchr(line, ':');
869                 if (!pos) {
870                         tokens = g_slist_append(tokens, strdup(line));
871                         return tokens;
872                 }
873         }
874
875         pos++;
876
877         /* skip whitespace */
878         while (*pos != '\0' && isspace(*pos)) {
879                 pos++;
880         }
881
882         begin = pos;
883
884         do {
885                 switch (type) {
886                 case TYPE_NONE:
887                         if (*pos == '"') {
888                                 type = TYPE_STR;
889                         }
890                         else if (*pos == ',') {
891                                 tokens = g_slist_append(tokens, strdup(""));
892                         }
893                         else if (*pos == ' ') {
894                                 // skip
895                         }
896                         else if (*pos == '(') {
897                                 type = TYPE_PAREN;
898                         }
899                         else {
900                                 type = TYPE_RAW;
901                         }
902                         begin = pos;
903                         break;
904
905                 case TYPE_STR:
906                         if (*pos == '"') {
907                                 type = TYPE_STR_FIN;
908                                 buf = calloc(1, (pos - begin) + 2);
909                                 if (!buf)
910                                         return NULL;
911                                 memcpy(buf, begin, pos - begin + 1);
912                                 tokens = g_slist_append(tokens, buf);
913                         }
914                         break;
915
916                 case TYPE_PAREN:
917                         if (*pos == ')') {
918                                 type = TYPE_PAREN_FIN;
919                                 buf = calloc(1, (pos - begin) + 2);
920                                 if (!buf)
921                                         return NULL;
922                                 memcpy(buf, begin, pos - begin + 1);
923                                 tokens = g_slist_append(tokens, buf);
924                         }
925                         break;
926
927                 case TYPE_RAW:
928                         if (*pos == ',' || *pos == '\0') {
929                                 type = TYPE_NONE;
930                                 buf = calloc(1, (pos - begin) + 1);
931                                 if (!buf)
932                                         return NULL;
933                                 memcpy(buf, begin, pos - begin);
934                                 tokens = g_slist_append(tokens, buf);
935                         }
936                         break;
937
938                 case TYPE_STR_FIN:
939                 case TYPE_PAREN_FIN:
940                         if (*pos == ',') {
941                                 type = TYPE_NONE;
942                         }
943                         break;
944                 default:
945                         err("invalid string type");
946                         break;
947                 }
948
949                 if (*pos == '\0' || pos == mark_end)
950                         break;
951
952                 pos++;
953         } while (1);
954
955         if (type == TYPE_RAW) {
956                 buf = calloc(1, (pos - begin) + 1);
957                 if (!buf)
958                         return NULL;
959                 memcpy(buf, begin, pos - begin);
960                 tokens = g_slist_append(tokens, buf);
961         }
962
963         return tokens;
964 }
965
966 void tcore_at_tok_free(GSList *tokens)
967 {
968         if (!tokens)
969                 return;
970
971         g_slist_free_full(tokens, g_free);
972 }
973
974 char *tcore_at_tok_extract(const char *src)
975 {
976         char *dest = NULL;
977         char *last = NULL;
978
979         if (!src)
980                 return NULL;
981
982         if (strlen(src) < 2)
983                 return g_strdup(src);
984
985         last = (char *)src + strlen(src) - 1;
986
987         switch (*src) {
988                 case '(':
989                         if (*last == ')') {
990                                 dest = g_strdup(src + 1);
991                                 dest[strlen(dest) - 1] = '\0';
992                         }
993                         break;
994
995                 case '"':
996                         if (*last == '"') {
997                                 dest = g_strdup(src + 1);
998                                 dest[strlen(dest) - 1] = '\0';
999                         }
1000                         break;
1001
1002                 default:
1003                         return g_strdup(src);
1004                         break;
1005         }
1006
1007         return dest;
1008 }
1009
1010 char *tcore_at_tok_nth(GSList *tokens, unsigned int token_index)
1011 {
1012         if (!tokens)
1013                 return NULL;
1014
1015         if (token_index > g_slist_length(tokens))
1016                 return NULL;
1017
1018         return (char *)g_slist_nth_data(tokens, token_index);
1019 }
1020
1021 gboolean tcore_at_add_hook(TcoreHal *hal, void *hook_func)
1022 {
1023         TcoreAT *at;
1024         at = tcore_hal_get_at(hal);
1025
1026         if (at != NULL) {
1027                 dbg("Setting the rfs hook callback function");
1028                 at->rfs_hook = (rfs_hook_cb) hook_func;
1029                 return TRUE;
1030         }
1031         dbg("AT is NULL !!!");
1032         return FALSE;
1033 }
1034
1035 void tcore_free_pending_timeout_at_request(TcoreAT *at)
1036 {
1037         if (!at)
1038                 return;
1039
1040         tcore_at_request_free(at->req);
1041         at->req = NULL;
1042 }
1043
1044 TReturn tcore_prepare_and_send_at_request(CoreObject *co,
1045                                                                                                 const char *at_cmd,
1046                                                                                                 const char *at_cmd_prefix,
1047                                                                                                 enum tcore_at_command_type at_cmd_type,
1048                                                                                                 UserRequest *ur,
1049                                                                                                 TcorePendingResponseCallback resp_cb,
1050                                                                                                 void *resp_cb_data,
1051                                                                                                 TcorePendingSendCallback send_cb,
1052                                                                                                 void *send_cb_data,
1053                                                                                                 unsigned int timeout,
1054                                                                                                 TcorePendingTimeoutCallback timeout_cb,
1055                                                                                                 void *timeout_cb_data)
1056 {
1057         TcorePending *pending = NULL;
1058         TcoreATRequest *req = NULL;
1059         TcoreHal *hal = NULL;
1060         TReturn ret = TCORE_RETURN_FAILURE;
1061
1062         hal = tcore_object_get_hal(co);
1063         if (!hal) {
1064                 dbg("HAL is NULL");
1065                 return ret;
1066         }
1067         dbg("hal: [0x%x]", hal);
1068
1069         /* Create Pending Request */
1070         pending = tcore_pending_new(co, 0);
1071         if (!pending) {
1072                 dbg("Pending is NULL");
1073                 return ret;
1074         }
1075
1076         /* Create AT-Command Request */
1077         req = tcore_at_request_new(at_cmd, at_cmd_prefix, at_cmd_type);
1078         if (req == NULL) {
1079                 dbg("Request is NULL");
1080                 tcore_pending_free(pending);
1081                 return ret;
1082         }
1083         dbg("AT Command: [%s], Prefix(if any): [%s], AT-Command length: [%d]",
1084                                                                 req->cmd, req->prefix, strlen(req->cmd));
1085
1086         tcore_pending_set_request_data(pending, 0, req);
1087         tcore_pending_set_response_callback(pending, resp_cb, resp_cb_data);
1088         tcore_pending_set_send_callback(pending, send_cb, send_cb_data);
1089         tcore_pending_set_timeout(pending, timeout);
1090         tcore_pending_set_timeout_callback(pending, timeout_cb, timeout_cb_data);
1091         tcore_pending_link_user_request(pending, ur);
1092
1093         ret = tcore_hal_send_request(hal, pending);
1094         dbg("ret: [0x%x]", ret);
1095         return ret;
1096 }
1097