4 * Copyright (c) 2012 Samsung Electronics Co., Ltd. All rights reserved.
6 * Contact: Ja-young Gu <jygu@samsung.com>
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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.
31 #include "user_request.h"
33 #include "core_object.h"
35 #define NUM_ELEMS(x) (sizeof(x)/sizeof(x[0]))
42 #define MAX_AT_RESPONSE 255 // for testing
43 //#define MAX_AT_RESPONSE 8191
45 typedef gboolean (*rfs_hook_cb) (const gchar *data);
47 struct tcore_at_type {
50 enum tcore_at_command_type cmd_type;
52 GHashTable *unsolicited_table;
54 struct tcore_at_request *req;
55 struct tcore_at_response *resp;
57 unsigned int buf_size;
63 struct _notification *pdu_noti;
70 struct _notification_callback {
71 TcoreATNotificationCallback callback;
75 struct _notification {
81 * returns 1 if line is a final response indicating success
84 static const char *list_final_responses_success[] = {
90 * returns 1 if line is a final response indicating error
93 static const char *list_final_responses_error[] = {
101 static int _check_final_response(const char *line)
105 for (i = 0; i < NUM_ELEMS(list_final_responses_success); i++) {
106 if (g_str_has_prefix(line, list_final_responses_success[i])) {
111 for (i = 0; i < NUM_ELEMS(list_final_responses_error); i++) {
112 if (g_str_has_prefix(line, list_final_responses_error[i])) {
121 static char *_find_next_EOL(char *cur)
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]);
133 * Line should either end with -
136 * - Carriage Return '\r' and Line Feed '\n'.
138 while (*cur != '\0' && !((*cur == CR) && (*(cur + 1) == LF)))
141 return *cur == '\0' ? NULL : cur;
144 static struct tcore_at_response* _response_new()
146 struct tcore_at_response *resp;
148 resp = calloc(1, sizeof(struct tcore_at_response));
155 static void _response_free(struct tcore_at_response *resp)
161 g_slist_free_full(resp->lines, g_free);
164 if (resp->final_response)
165 free(resp->final_response);
171 static void _response_add(struct tcore_at_response *resp,
177 dbg("current lines = %d", g_slist_length(resp->lines));
179 resp->lines = g_slist_append(resp->lines, strdup(line));
182 static void _emit_pending_response(TcoreAT *at)
189 p = tcore_queue_pop(tcore_hal_ref_queue(at->hal));
194 tcore_pending_emit_response_callback(p, sizeof(TcoreATResponse), at->resp);
195 tcore_user_request_unref(tcore_pending_ref_user_request(p));
197 tcore_at_request_free(at->req);
199 tcore_pending_free(p);
201 _response_free(at->resp);
205 static void _emit_unsolicited_message(TcoreAT *at, const char *line)
207 struct _notification *noti = NULL;
208 struct _notification_callback *item = NULL;
211 gpointer key = NULL, value;
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);
222 while (g_hash_table_iter_next(&iter, &key, &value)) {
223 if (!g_str_has_prefix(line, key))
233 if (noti->type_pdu == TRUE) {
234 at->pdu_status = TRUE;
236 at->pdu_lines = g_slist_append(NULL, g_strdup(line));
241 if (at->data_mode == MODE_BIN) {
242 at->pdu_lines = g_slist_append(at->pdu_lines, (gpointer)line);
243 data = at->pdu_lines;
245 data = g_slist_append(NULL, g_strdup(line));
250 at->pdu_status = FALSE;
253 if (at->data_mode == MODE_BIN) {
255 at->pdu_lines = g_slist_append(at->pdu_lines, (gpointer)line);
256 dbg("at->pdu_lines: 0x%x", at->pdu_lines);
258 at->pdu_lines = g_slist_append(at->pdu_lines, g_strdup(line));
261 data = at->pdu_lines;
272 ret = item->callback(at, data, item->user_data);
275 noti->callbacks = g_slist_remove(noti->callbacks, item);
282 dbg(" Free the list");
283 if (at->data_mode != MODE_BIN) {
284 g_slist_free_full(data, g_free);
286 at->pdu_lines = NULL;
288 if (g_slist_length(noti->callbacks) == 0) {
289 g_hash_table_remove(at->unsolicited_table, key);
294 static void _free_noti_list(void *data)
296 struct _notification *noti = data;
301 g_slist_free_full(noti->callbacks, g_free);
305 static void _msgat(const char *prefix, const char *str)
308 char buf[8192] = {0,};
316 if (strlen(str) > 4096) {
317 msg("%s[%s]", prefix, str);
322 for (i = 0; i < strlen(str); i++) {
323 if (str[i] == '\r') {
324 strncpy(pos, "<CR>", 4);
327 else if (str[i] == '\n') {
328 strncpy(pos, "<LF>", 4);
338 msg("%s[%s]", prefix, buf);
342 TcoreAT *tcore_at_new(TcoreHal *hal)
346 at = calloc(1, sizeof(struct tcore_at_type));
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 );
362 void tcore_at_free(TcoreAT *at)
370 if (at->unsolicited_table)
371 g_hash_table_destroy(at->unsolicited_table);
376 TReturn tcore_at_remove_notification_full(TcoreAT *at, const char *prefix,
377 TcoreATNotificationCallback callback, void *user_data)
379 struct _notification *noti;
380 struct _notification_callback *item;
384 return TCORE_RETURN_EINVAL;
387 /* remove all callbacks for prefix */
388 g_hash_table_remove(at->unsolicited_table, prefix);
389 return TCORE_RETURN_SUCCESS;
392 noti = g_hash_table_lookup(at->unsolicited_table, prefix);
394 return TCORE_RETURN_SUCCESS;
397 for(; p; p = p->next) {
402 if (callback == item->callback) {
404 noti->callbacks = g_slist_remove(noti->callbacks, item);
408 if (user_data == item->user_data) {
409 noti->callbacks = g_slist_remove(noti->callbacks, item);
415 return TCORE_RETURN_SUCCESS;
419 TReturn tcore_at_remove_notification(TcoreAT *at, const char *prefix,
420 TcoreATNotificationCallback callback)
422 return tcore_at_remove_notification_full(at, prefix, callback, NULL);
425 TReturn tcore_at_add_notification(TcoreAT *at, const char *prefix,
426 gboolean pdu, TcoreATNotificationCallback callback,
429 struct _notification *noti;
430 struct _notification_callback *item;
432 if (!at || !prefix || !callback)
433 return TCORE_RETURN_EINVAL;
435 noti = g_hash_table_lookup(at->unsolicited_table, prefix);
437 noti = calloc(1, sizeof(struct _notification));
439 return TCORE_RETURN_ENOMEM;
441 noti->type_pdu = pdu;
442 noti->callbacks = NULL;
444 g_hash_table_insert(at->unsolicited_table, g_strdup(prefix), noti);
447 if (noti->type_pdu != pdu)
448 return TCORE_RETURN_EINVAL;
450 item = calloc(1, sizeof(struct _notification_callback));
452 return TCORE_RETURN_ENOMEM;
454 item->callback = callback;
455 item->user_data = user_data;
457 noti->callbacks = g_slist_append(noti->callbacks, item);
459 return TCORE_RETURN_SUCCESS;
462 TReturn tcore_at_set_request(TcoreAT *at, TcoreATRequest *req, gboolean send)
470 return TCORE_RETURN_EINVAL;
475 return TCORE_RETURN_EINVAL;
481 return TCORE_RETURN_SUCCESS;
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);
490 end = strchr(at->req->cmd, CR);
494 return tcore_hal_send_data(at->hal, strlen(req->cmd), req->cmd);
497 at->req->next_send_pos = end + 1;
498 dbg("backup data = [%c] next data = [%s]", next, at->req->next_send_pos);
501 ret = tcore_hal_send_data(at->hal, strlen(req->cmd), req->cmd);
506 return TCORE_RETURN_EINVAL;
510 TcoreATRequest *tcore_at_get_request(TcoreAT *at)
519 TcoreATResponse *tcore_at_get_response(TcoreAT *at)
527 TReturn tcore_at_buf_write(TcoreAT *at, unsigned int data_len, const char *data)
529 unsigned int read_pos;
530 unsigned int write_pos;
534 return TCORE_RETURN_EINVAL;
537 read_pos = at->buf_read_pos - at->buf;
538 write_pos = at->buf_write_pos - at->buf;
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));
553 write_pos = at->buf_write_pos - at->buf;
554 if (write_pos + data_len >= at->buf_size) {
556 at->buf_size = at->buf_size << 1;
557 if (at->buf_size > write_pos + data_len)
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));
565 dbg("resize buffer to %d", at->buf_size);
568 memcpy(at->buf_write_pos, data, data_len);
570 at->buf_write_pos += data_len;
571 return TCORE_RETURN_SUCCESS;
574 TcoreATRequest* tcore_at_request_new(const char *cmd, const char *prefix, enum tcore_at_command_type type)
584 req = calloc(1, sizeof(struct tcore_at_request));
588 if (!strchr(cmd, CR))
589 req->cmd = g_strdup_printf("%s%c", cmd, CR);
591 req->cmd = g_strdup_printf("%s%c", cmd, 26);
594 req->prefix = strdup(prefix);
601 void tcore_at_request_free(TcoreATRequest *req)
615 /* To get the length value from little-endian bytes */
616 static int __sum_4_bytes(const char *posn)
619 sum = sum | (*(posn+3)) << 24;
620 sum = sum | (*(posn+2)) << 16;
621 sum = sum | (*(posn+1)) << 8;
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)
630 #define NVM_PAYLOAD_LENGTH_0 52
631 #define NVM_PAYLOAD_LENGTH_1 68
633 int m_length_0 = ZERO , m_length_1 = ZERO;
634 static int data_len_final = ZERO, actual_buffer_size = ZERO;
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;
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);
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;
657 gboolean tcore_at_process(TcoreAT *at, unsigned int data_len, const char *data)
666 tcore_at_buf_write(at, data_len, data);
668 pos = at->buf_read_pos;
669 dbg("On entry at->buf_read_pos: 0x%x", at->buf_read_pos);
673 while (*pos == CR || *pos == LF)
676 next_pos = _find_next_EOL(pos);
678 dbg("Data could be in Binary mode !!");
680 if (TRUE == at->rfs_hook(pos)){
681 at->data_mode = MODE_BIN;
682 tcore_at_process_binary_data(at, pos, data_len);
684 dbg("Not Binary data");
686 dbg("Rfs hook is not set !!");
693 //dbg("complete line found.");
694 dbg("line = [%s]", pos);
698 _emit_unsolicited_message(at, pos);
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;
712 at->resp = _response_new();
717 ret = _check_final_response(pos);
720 at->resp->success = TRUE;
722 at->resp->success = FALSE;
724 at->resp->final_response = strdup(pos);
726 _emit_pending_response(at);
727 at->buf_read_pos = next_pos + 1;
731 switch (at->req->type) {
732 case TCORE_AT_NO_RESULT:
733 _emit_unsolicited_message(at, pos);
736 case TCORE_AT_NUMERIC:
737 if (at->resp->lines == NULL && isdigit(pos[0])) {
738 _response_add(at->resp, pos);
741 _emit_unsolicited_message(at, pos);
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);
753 _emit_unsolicited_message(at, pos);
757 _response_add(at->resp, pos);
761 _emit_unsolicited_message(at, pos);
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);
771 _emit_unsolicited_message(at, pos);
775 _response_add(at->resp, pos);
780 if (at->req->prefix) {
781 if (g_str_has_prefix(pos, at->req->prefix)) {
782 _response_add(at->resp, pos);
785 if (at->resp->lines != NULL) {
786 _response_add(at->resp, pos);
789 _emit_unsolicited_message(at, pos);
794 _response_add(at->resp, pos);
800 _emit_unsolicited_message(at, pos);
808 at->buf_read_pos = pos;
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)
822 req = tcore_at_request_new(cmd, prefix, type);
826 p = tcore_pending_new(co, 0);
828 tcore_at_request_free(req);
832 tcore_pending_set_request_data(p, 0, req);
833 tcore_pending_set_response_callback(p, func, user_data);
841 #define TYPE_STR_FIN 3
843 #define TYPE_PAREN_FIN 5
845 GSList *tcore_at_tok_new(const char *line)
850 char *mark_end = NULL;
851 int type = TYPE_NONE;
852 GSList *tokens = NULL;
857 if (strlen(line) == 0)
860 if (line[0] == '(') {
861 /* list token container */
863 if (line[strlen(line)-1] == ')')
864 mark_end = (char *)line + strlen(line) - 1;
867 /* normal at message */
868 pos = strchr(line, ':');
870 tokens = g_slist_append(tokens, strdup(line));
877 /* skip whitespace */
878 while (*pos != '\0' && isspace(*pos)) {
890 else if (*pos == ',') {
891 tokens = g_slist_append(tokens, strdup(""));
893 else if (*pos == ' ') {
896 else if (*pos == '(') {
908 buf = calloc(1, (pos - begin) + 2);
911 memcpy(buf, begin, pos - begin + 1);
912 tokens = g_slist_append(tokens, buf);
918 type = TYPE_PAREN_FIN;
919 buf = calloc(1, (pos - begin) + 2);
922 memcpy(buf, begin, pos - begin + 1);
923 tokens = g_slist_append(tokens, buf);
928 if (*pos == ',' || *pos == '\0') {
930 buf = calloc(1, (pos - begin) + 1);
933 memcpy(buf, begin, pos - begin);
934 tokens = g_slist_append(tokens, buf);
945 err("invalid string type");
949 if (*pos == '\0' || pos == mark_end)
955 if (type == TYPE_RAW) {
956 buf = calloc(1, (pos - begin) + 1);
959 memcpy(buf, begin, pos - begin);
960 tokens = g_slist_append(tokens, buf);
966 void tcore_at_tok_free(GSList *tokens)
971 g_slist_free_full(tokens, g_free);
974 char *tcore_at_tok_extract(const char *src)
983 return g_strdup(src);
985 last = (char *)src + strlen(src) - 1;
990 dest = g_strdup(src + 1);
991 dest[strlen(dest) - 1] = '\0';
997 dest = g_strdup(src + 1);
998 dest[strlen(dest) - 1] = '\0';
1003 return g_strdup(src);
1010 char *tcore_at_tok_nth(GSList *tokens, unsigned int token_index)
1015 if (token_index > g_slist_length(tokens))
1018 return (char *)g_slist_nth_data(tokens, token_index);
1021 gboolean tcore_at_add_hook(TcoreHal *hal, void *hook_func)
1024 at = tcore_hal_get_at(hal);
1027 dbg("Setting the rfs hook callback function");
1028 at->rfs_hook = (rfs_hook_cb) hook_func;
1031 dbg("AT is NULL !!!");
1035 void tcore_free_pending_timeout_at_request(TcoreAT *at)
1040 tcore_at_request_free(at->req);
1044 TReturn tcore_prepare_and_send_at_request(CoreObject *co,
1046 const char *at_cmd_prefix,
1047 enum tcore_at_command_type at_cmd_type,
1049 TcorePendingResponseCallback resp_cb,
1051 TcorePendingSendCallback send_cb,
1053 unsigned int timeout,
1054 TcorePendingTimeoutCallback timeout_cb,
1055 void *timeout_cb_data)
1057 TcorePending *pending = NULL;
1058 TcoreATRequest *req = NULL;
1059 TcoreHal *hal = NULL;
1060 TReturn ret = TCORE_RETURN_FAILURE;
1062 hal = tcore_object_get_hal(co);
1067 dbg("hal: [0x%x]", hal);
1069 /* Create Pending Request */
1070 pending = tcore_pending_new(co, 0);
1072 dbg("Pending is NULL");
1076 /* Create AT-Command Request */
1077 req = tcore_at_request_new(at_cmd, at_cmd_prefix, at_cmd_type);
1079 dbg("Request is NULL");
1080 tcore_pending_free(pending);
1083 dbg("AT Command: [%s], Prefix(if any): [%s], AT-Command length: [%d]",
1084 req->cmd, req->prefix, strlen(req->cmd));
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);
1093 ret = tcore_hal_send_request(hal, pending);
1094 dbg("ret: [0x%x]", ret);