Add mbtk gnss callback.

Change-Id: I5c720e8e742648aade43f02eae1fa2274ce361ea
diff --git a/mbtk/include/mbtk/mbtk_gnss.h b/mbtk/include/mbtk/mbtk_gnss.h
new file mode 100755
index 0000000..b585f7d
--- /dev/null
+++ b/mbtk/include/mbtk/mbtk_gnss.h
@@ -0,0 +1,58 @@
+/*
+* mbtk_gnss.h
+*
+* MBTK GNSS API header.
+*
+* Author : lb
+* Date   : 2024/7/11 16:14:33
+*/
+#ifndef __MBTK_GNSS_H
+#define __MBTK_GNSS_H
+#include <pthread.h>
+
+#include "mbtk_type.h"
+
+#define MBTK_GNSS_IND_LOCATION (1)
+#define MBTK_GNSS_IND_NMEA (1 << 1)
+
+#define MBTK_IND_START_FLAG     0xFF
+#define MBTK_IND_END_FLAG       0xEE
+#define MBTK_IND_LOCATION_TAG   "IND_LOC:"
+#define MBTK_IND_NMEA_TAG       "IND_NMEA:"
+
+typedef uint32 mbtk_gnss_handle;
+
+typedef void (*mbtk_gnss_callback_func)(uint32 ind_type, const void* data, uint32 data_len);
+
+typedef enum {
+    GNSS_ERR_OK,
+    GNSS_ERR_CLI_FULL,
+    GNSS_ERR_UNKNOWN_HANDLE,
+    GNSS_ERR_UNSUPPORT,
+    GNSS_ERR_TIMEOUT,
+    GNSS_ERR_ARG,
+    GNSS_ERR_CHECKSUM,
+    GNSS_ERR_SET_BUSY,
+    GNSS_ERR_DL_FW,
+    GNSS_ERR_OPEN_DEV,
+    GNSS_ERR_FORMAT,
+    GNSS_ERR_BUSY,
+
+    GNSS_ERR_UNKNOWN
+} gnss_err_enum;
+
+gnss_err_enum mbtk_gnss_init(mbtk_gnss_callback_func cb);
+
+gnss_err_enum mbtk_gnss_deinit();
+
+gnss_err_enum mbtk_gnss_open(int type, int timeout);
+
+gnss_err_enum mbtk_gnss_close(int timeout);
+
+gnss_err_enum mbtk_gnss_setting(const char *setting_cmd, int timeout);
+
+gnss_err_enum mbtk_gnss_dl(const char *fw_path, int timeout);
+
+gnss_err_enum mbtk_gnss_ind_set(uint32 gnss_ind, int timeout);
+
+#endif /* __MBTK_GNSS_H */
diff --git a/mbtk/liblynq_lib/Makefile b/mbtk/liblynq_lib/Makefile
index d8ef441..c30aeee 100755
--- a/mbtk/liblynq_lib/Makefile
+++ b/mbtk/liblynq_lib/Makefile
@@ -29,7 +29,6 @@
 	src/lynq_alarm.c \
 	src/lynq_data_call.c \
 	src/lynq_fota.c \
-	src/lynq_gnss.c \
 	src/lynq_gpio.c \
 	src/lynq_irq.c \
 	src/lynq_log.c \
diff --git a/mbtk/libmbtk_gnss/Makefile b/mbtk/libmbtk_gnss/Makefile
index df4dd33..dd61f5a 100755
--- a/mbtk/libmbtk_gnss/Makefile
+++ b/mbtk/libmbtk_gnss/Makefile
@@ -11,7 +11,7 @@
 
 CFLAGS += -shared -Wl,-shared,-Bsymbolic
 
-LOCAL_SRC_FILES = mbtk_gnss_5311.c
+LOCAL_SRC_FILES = mbtk_gnss.c
 
 OBJS = $(patsubst %.c, %.o, $(patsubst %.cpp, %.o, $(LOCAL_SRC_FILES)))
 $(info OBJS = $(OBJS))
diff --git a/mbtk/libmbtk_gnss/mbtk_gnss.c b/mbtk/libmbtk_gnss/mbtk_gnss.c
new file mode 100755
index 0000000..e8310b3
--- /dev/null
+++ b/mbtk/libmbtk_gnss/mbtk_gnss.c
@@ -0,0 +1,511 @@
+/*
+*    mbtk_gnss.c
+*
+*    MBTK GNSS API source.
+*
+*/
+/******************************************************************************
+
+                          EDIT HISTORY FOR FILE
+
+  WHEN        WHO       WHAT,WHERE,WHY
+--------    --------    -------------------------------------------------------
+2024/7/11     LiuBin      Initial version
+
+******************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <pthread.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <fcntl.h>
+#include <sys/epoll.h>
+
+#include "mbtk_gnss_inter.h"
+#include "mbtk_log.h"
+#include "mbtk_utils.h"
+
+#define GNSS_BUFF_SIZE 2048
+
+static int gnss_cli_fd = -1;
+static pthread_t read_thread_id;
+static int exit_fd[2] = {-1};
+static mbtk_gnss_callback_func gnss_cb = NULL;
+static bool gnss_busy = FALSE;
+static pthread_cond_t gnss_cond;
+static pthread_mutex_t gnss_mutex;
+static gnss_err_enum gnss_result;
+
+static int sock_read(int fd, uint8 *msg, int data_len)
+{
+    memset(msg, 0, data_len);
+    int len = 0;
+
+    int read_len = 0;
+    while(1)
+    {
+        len = read(fd, msg + read_len, data_len - read_len);
+        if(len > 0)
+        {
+            read_len += len;
+        }
+        else if(len == 0)
+        {
+            LOGE("read() end.");
+            break;
+        }
+        else
+        {
+            if(EAGAIN == errno)
+            {
+                LOGE("Read end, lenght = %d", read_len);
+            }
+            else
+            {
+                LOGE("read() error[%d].", errno);
+            }
+            break;
+        }
+    }
+
+    if(read_len > 0)
+    {
+        // log_hex("DATA_RECV", msg, read_len);
+        return read_len;
+    }
+    else
+    {
+        return -1;
+    }
+}
+
+static int sock_write(int fd, uint8 *msg, int data_len)
+{
+    int len = 0;
+    int write_len = 0;
+    while(write_len < data_len)
+    {
+        len = write(fd, msg + write_len, data_len - write_len);
+        if(len > 0)
+        {
+            write_len += len;
+        }
+        else if(len == 0)
+        {
+            LOGE("write() end.");
+            break;
+        }
+        else
+        {
+            LOGE("write() error[%d].", errno);
+            break;
+        }
+    }
+
+    if(write_len > 0)
+    {
+        // log_hex("DATA_SEND", msg, write_len);
+        return write_len;
+    }
+    else
+    {
+        return -1;
+    }
+}
+
+static void gnss_timer_cb(int signo)
+{
+    if(gnss_busy) {
+        pthread_mutex_lock(&gnss_mutex);
+        pthread_cond_signal(&gnss_cond);
+        pthread_mutex_unlock(&gnss_mutex);
+        gnss_result = GNSS_ERR_TIMEOUT;
+    }
+    return;
+}
+
+static void gnss_rsp_process(const char *data, int data_len)
+{
+    int index = 0;
+    char buff[GNSS_BUFF_SIZE];
+    int buff_len = 0;
+    while(index < data_len) {
+        if(data[index] == MBTK_IND_START_FLAG) {
+            memset(buff, 0, sizeof(buff));
+            buff_len = 0;
+        } else if(data[index] == MBTK_IND_END_FLAG) {
+            buff[buff_len] = '\0';
+            LOGD("RSP[len - %d] : %s", buff_len, buff);
+            if(memcmp(MBTK_IND_LOCATION_TAG, buff, strlen(MBTK_IND_LOCATION_TAG)) == 0) {
+                if(gnss_cb) {
+                    gnss_cb(MBTK_GNSS_IND_LOCATION, buff, buff_len);
+                }
+            } else if(memcmp(MBTK_IND_NMEA_TAG, buff, strlen(MBTK_IND_NMEA_TAG)) == 0) {
+                if(gnss_cb) {
+                    gnss_cb(MBTK_GNSS_IND_NMEA, buff, buff_len);
+                }
+            } else {
+                if(gnss_busy) {
+                    pthread_mutex_lock(&gnss_mutex);
+                    pthread_cond_signal(&gnss_cond);
+                    pthread_mutex_unlock(&gnss_mutex);
+                }
+            }
+        } else {
+            buff[buff_len++] = data[index];
+        }
+        index++;
+    }
+}
+
+static void* gnss_read_run(void* arg)
+{
+    int epoll_fd = epoll_create(5);
+    if(epoll_fd < 0)
+    {
+        LOGE("epoll_create() fail[%d].", errno);
+        return NULL;
+    }
+
+    uint32 event = EPOLLIN | EPOLLET;
+    struct epoll_event ev_cli, ev_exit;
+    ev_cli.data.fd = gnss_cli_fd;
+    ev_cli.events = event; //EPOLLIN | EPOLLERR | EPOLLET;
+    epoll_ctl(epoll_fd,EPOLL_CTL_ADD,gnss_cli_fd,&ev_cli);
+
+    ev_exit.data.fd = exit_fd[0];
+    ev_exit.events = event; //EPOLLIN | EPOLLERR | EPOLLET;
+    epoll_ctl(epoll_fd,EPOLL_CTL_ADD,exit_fd[0],&ev_exit);
+
+    int nready = -1;
+    struct epoll_event epoll_events[EPOLL_LISTEN_MAX];
+    while(1)
+    {
+        nready = epoll_wait(epoll_fd, epoll_events, EPOLL_LISTEN_MAX, -1);
+        if(nready > 0)
+        {
+            int i;
+            for(i = 0; i < nready; i++)
+            {
+                LOGD("fd[%d] event = %x",epoll_events[i].data.fd, epoll_events[i].events);
+                if(epoll_events[i].events & EPOLLHUP)   // Closed by server.
+                {
+
+                }
+                else if(epoll_events[i].events & EPOLLIN)
+                {
+                    if(gnss_cli_fd == epoll_events[i].data.fd)  // Server data arrive.
+                    {
+                        char buff[GNSS_BUFF_SIZE + 1];
+                        int len = sock_read(gnss_cli_fd, buff, GNSS_BUFF_SIZE);
+                        if(len > 0) {
+                            gnss_rsp_process(buff, len);
+                        }
+                    }
+                    else if(exit_fd[0] == epoll_events[i].data.fd) //
+                    {
+                        char buff[100] = {0};
+                        int len = read(exit_fd[0], buff, 100);
+                        if(len > 0) {
+                            LOGI("CMD : %s", buff);
+                            if(strcmp(buff, "EXIT") == 0) {
+                                goto read_thread_exit;
+                            } else {
+                                LOGD("Unkonw cmd : %s", buff);
+                            }
+                        } else {
+                            LOGE("sock_read() fail.");
+                        }
+                    }
+                    else
+                    {
+                        LOGW("Unknown socket : %d", epoll_events[i].data.fd);
+                    }
+                }
+                else
+                {
+                    LOGW("Unknown event : %x", epoll_events[i].events);
+                }
+            }
+        }
+        else
+        {
+            LOGW("epoll_wait() fail[%d].", errno);
+        }
+    }
+
+read_thread_exit:
+    LOGD("info_read thread exit.");
+    return NULL;
+}
+
+gnss_err_enum mbtk_gnss_init(mbtk_gnss_callback_func cb)
+{
+    if(gnss_cli_fd > 0) {
+        LOGW("GNSS client has inited.");
+        return GNSS_ERR_OK;
+    }
+
+    gnss_cli_fd = socket(AF_LOCAL, SOCK_STREAM, 0);
+    if(gnss_cli_fd < 0)
+    {
+        LOGE("socket() fail[%d].", errno);
+        goto error;
+    }
+
+    // Set O_NONBLOCK
+    int flags = fcntl(gnss_cli_fd, F_GETFL, 0);
+    if (flags < 0)
+    {
+        LOGE("Get flags error:%d", errno);
+        goto error;
+    }
+    flags |= O_NONBLOCK;
+    if (fcntl(gnss_cli_fd, F_SETFL, flags) < 0)
+    {
+        LOGE("Set flags error:%d", errno);
+        goto error;
+    }
+
+    struct sockaddr_un cli_addr;
+    memset(&cli_addr, 0, sizeof(cli_addr));
+    cli_addr.sun_family = AF_LOCAL;
+    strcpy(cli_addr.sun_path, SOCK_GNSS_PATH);
+    if(connect(gnss_cli_fd, (struct sockaddr *)&cli_addr, sizeof(cli_addr)))
+    {
+        LOGE("connect() fail[%d].", errno);
+        goto error;
+    }
+
+    if(pipe(exit_fd)) {
+        LOGE("pipe() fail[%d].", errno);
+        goto error;
+    }
+
+    if(pthread_create(&read_thread_id, NULL, gnss_read_run, NULL))
+    {
+        LOGE("pthread_create() fail.");
+        goto error;
+    }
+    pthread_mutex_init(&gnss_mutex, NULL);
+    pthread_cond_init(&gnss_cond, NULL);
+    gnss_cb = cb;
+    return GNSS_ERR_OK;
+
+error:
+    if(gnss_cli_fd > 0) {
+        close(gnss_cli_fd);
+        gnss_cli_fd = -1;
+    }
+    if(exit_fd[0] > 0) {
+        close(exit_fd[0]);
+        exit_fd[0] = -1;
+    }
+    if(exit_fd[1] > 0) {
+        close(exit_fd[1]);
+        exit_fd[1] = -1;
+    }
+
+    return GNSS_ERR_UNKNOWN;
+}
+
+gnss_err_enum mbtk_gnss_deinit()
+{
+    if(gnss_cli_fd < 0) {
+        LOGW("GNSS client not inited.");
+        return GNSS_ERR_UNKNOWN;
+    }
+
+    if(gnss_busy) {
+        LOGE("BUSY");
+        return GNSS_ERR_BUSY;
+    }
+
+    if(exit_fd[1] > 0) {
+        write(exit_fd[1], "EXIT", 4);
+    }
+
+    int ret = pthread_join(read_thread_id, NULL);
+    if(ret){
+        LOGE("pthrad_join fail(%d)",ret);
+        return GNSS_ERR_UNKNOWN;
+    }
+
+    close(gnss_cli_fd);
+    gnss_cli_fd = -1;
+    close(exit_fd[0]);
+    exit_fd[0] = -1;
+    close(exit_fd[1]);
+    exit_fd[1] = -1;
+    gnss_cb = NULL;
+    gnss_busy = FALSE;
+    pthread_mutex_destroy(&gnss_mutex);
+    pthread_cond_destroy(&gnss_cond);
+
+    return GNSS_ERR_OK;
+}
+
+gnss_err_enum mbtk_gnss_open(int type, int timeout)
+{
+    if(gnss_cli_fd < 0) {
+        LOGW("GNSS client not inited.");
+        return GNSS_ERR_UNKNOWN;
+    }
+
+    if(gnss_busy) {
+        LOGE("BUSY");
+        return GNSS_ERR_BUSY;
+    } else {
+        gnss_result = GNSS_ERR_OK;
+        gnss_busy = TRUE;
+        if(timeout > 0) {
+            mbtk_timer_set(gnss_timer_cb, timeout * 1000);
+        }
+
+        // gnss_init:x
+        char cmd[32] = {0};
+        snprintf(cmd, sizeof(cmd), "gnss_init:%d", type);
+        sock_write(gnss_cli_fd, cmd, strlen(cmd));
+
+        pthread_mutex_lock(&gnss_mutex);
+        pthread_cond_wait(&gnss_cond, &gnss_mutex);
+        pthread_mutex_unlock(&gnss_mutex);
+
+        gnss_busy = FALSE;
+
+        return gnss_result;
+    }
+}
+
+gnss_err_enum mbtk_gnss_close(int timeout)
+{
+    if(gnss_cli_fd < 0) {
+        LOGW("GNSS client not inited.");
+        return GNSS_ERR_UNKNOWN;
+    }
+
+    if(gnss_busy) {
+        LOGE("BUSY");
+        return GNSS_ERR_BUSY;
+    } else {
+        gnss_result = GNSS_ERR_OK;
+        gnss_busy = TRUE;
+        if(timeout > 0) {
+            mbtk_timer_set(gnss_timer_cb, timeout * 1000);
+        }
+
+        // gnss_deinit
+        char *cmd = "gnss_deinit";
+        sock_write(gnss_cli_fd, cmd, strlen(cmd));
+
+        pthread_mutex_lock(&gnss_mutex);
+        pthread_cond_wait(&gnss_cond, &gnss_mutex);
+        pthread_mutex_unlock(&gnss_mutex);
+
+        gnss_busy = FALSE;
+
+        return gnss_result;
+    }
+}
+
+
+gnss_err_enum mbtk_gnss_setting(const char *setting_cmd, int timeout)
+{
+    if(gnss_cli_fd < 0) {
+        LOGW("GNSS client not inited.");
+        return GNSS_ERR_UNKNOWN;
+    }
+
+    if(gnss_busy) {
+        LOGE("BUSY");
+        return GNSS_ERR_BUSY;
+    } else {
+        gnss_result = GNSS_ERR_OK;
+        gnss_busy = TRUE;
+        if(timeout > 0) {
+            mbtk_timer_set(gnss_timer_cb, timeout * 1000);
+        }
+
+        // gnss_setting:cmd
+        char cmd[32] = {0};
+        snprintf(cmd, sizeof(cmd), "gnss_setting:%s", setting_cmd);
+        sock_write(gnss_cli_fd, cmd, strlen(cmd));
+
+        pthread_mutex_lock(&gnss_mutex);
+        pthread_cond_wait(&gnss_cond, &gnss_mutex);
+        pthread_mutex_unlock(&gnss_mutex);
+
+        gnss_busy = FALSE;
+
+        return gnss_result;
+    }
+}
+
+gnss_err_enum mbtk_gnss_dl(const char *fw_path, int timeout)
+{
+    if(gnss_cli_fd < 0) {
+        LOGW("GNSS client not inited.");
+        return GNSS_ERR_UNKNOWN;
+    }
+
+    if(gnss_busy) {
+        LOGE("BUSY");
+        return GNSS_ERR_BUSY;
+    } else {
+        gnss_result = GNSS_ERR_OK;
+        gnss_busy = TRUE;
+        if(timeout > 0) {
+            mbtk_timer_set(gnss_timer_cb, timeout * 1000);
+        }
+
+        // gnss_dl:fw_name
+        char cmd[32] = {0};
+        snprintf(cmd, sizeof(cmd), "gnss_dl:%s", fw_path);
+        sock_write(gnss_cli_fd, cmd, strlen(cmd));
+
+        pthread_mutex_lock(&gnss_mutex);
+        pthread_cond_wait(&gnss_cond, &gnss_mutex);
+        pthread_mutex_unlock(&gnss_mutex);
+
+        gnss_busy = FALSE;
+
+        return gnss_result;
+    }
+}
+
+
+gnss_err_enum mbtk_gnss_ind_set(uint32 gnss_ind, int timeout)
+{
+    if(gnss_cli_fd < 0) {
+        LOGW("GNSS client not inited.");
+        return GNSS_ERR_UNKNOWN;
+    }
+
+    if(gnss_busy) {
+        LOGE("BUSY");
+        return GNSS_ERR_BUSY;
+    } else {
+        gnss_result = GNSS_ERR_OK;
+        gnss_busy = TRUE;
+        if(timeout > 0) {
+            mbtk_timer_set(gnss_timer_cb, timeout * 1000);
+        }
+
+        // gnss_ind:ind_type
+        char cmd[32] = {0};
+        snprintf(cmd, sizeof(cmd), "gnss_ind:%d", gnss_ind);
+        sock_write(gnss_cli_fd, cmd, strlen(cmd));
+
+        pthread_mutex_lock(&gnss_mutex);
+        pthread_cond_wait(&gnss_cond, &gnss_mutex);
+        pthread_mutex_unlock(&gnss_mutex);
+
+        gnss_busy = FALSE;
+
+        return gnss_result;
+    }
+}
+
diff --git a/mbtk/libmbtk_gnss/mbtk_gnss_inter.h b/mbtk/libmbtk_gnss/mbtk_gnss_inter.h
new file mode 100755
index 0000000..97bc78f
--- /dev/null
+++ b/mbtk/libmbtk_gnss/mbtk_gnss_inter.h
@@ -0,0 +1,20 @@
+/*
+* mbtk_gnss_inter.h
+*
+* MBTK GNSS internal header.
+*
+* Author : lb
+* Date   : 2024/7/11 16:18:42
+*/
+#ifndef __MBTK_GNSS_INTER_H
+#define __MBTK_GNSS_INTER_H
+#include <pthread.h>
+
+#include "mbtk_gnss.h"
+#include "mbtk_type.h"
+
+#define SOCK_GNSS_PATH "/tmp/mbtk_gnss_sock"
+#define EPOLL_LISTEN_MAX 100
+
+
+#endif /* __MBTK_GNSS_INTER_H */
diff --git a/mbtk/mbtk_gnssd/gnss_info.h b/mbtk/mbtk_gnssd/gnss_info.h
index 1e279a3..744aba5 100755
--- a/mbtk/mbtk_gnssd/gnss_info.h
+++ b/mbtk/mbtk_gnssd/gnss_info.h
@@ -9,6 +9,7 @@
 #ifndef _GNSS_INFO_H
 #define _GNSS_INFO_H
 #include "mbtk_type.h"
+#include "mbtk_gnss.h"
 
 #define GNSS_ID_6228 "6228"
 #define GNSS_ID_5311 "5311"
@@ -48,19 +49,6 @@
 #define GNSS_SET_MSGCFG_EPHABNORMAL (1<<21)
 
 typedef enum {
-    GNSS_ERR_OK,
-    GNSS_ERR_UNSUPPORT,
-    GNSS_ERR_TIMEOUT,
-    GNSS_ERR_ARG,
-    GNSS_ERR_CHECKSUM,
-    GNSS_ERR_SET_BUSY,
-    GNSS_ERR_DL_FW,
-    GNSS_ERR_OPEN_DEV,
-
-    GNSS_ERR_UNKNOWN
-} gnss_err_enum;
-
-typedef enum {
     GNSS_RESET_TYPE_HOT = 1,
     GNSS_RESET_TYPE_WARM,
     GNSS_RESET_TYPE_COLD
@@ -96,6 +84,11 @@
 } gnss_state_enum;
 
 typedef struct {
+    int cli_fd;
+    uint32 ind_flag;
+} gnss_ind_info_t;
+
+typedef struct {
     gnss_id_enum gnss_id;
     char dev_name[32];
     bool auto_open;        // Should auto open gnss?
@@ -126,4 +119,6 @@
 
 int gnss_dl_fw(const char* fw_name, void *rsp, int rsp_len);
 
+int gnss_ind_set(int fd, int ind_type);
+
 #endif /* _GNSS_INFO_H */
diff --git a/mbtk/mbtk_gnssd/gnss_ipc.c b/mbtk/mbtk_gnssd/gnss_ipc.c
index 3e13854..54ba823 100755
--- a/mbtk/mbtk_gnssd/gnss_ipc.c
+++ b/mbtk/mbtk_gnssd/gnss_ipc.c
@@ -67,25 +67,32 @@
         }
 
         char rsp[100] = {0};
-        sprintf(rsp, "gnss_init:%d", ret);
+        sprintf(rsp, "%cgnss_init:%d%c", MBTK_IND_START_FLAG, ret, MBTK_IND_END_FLAG);
         gnss_write(fd, rsp, strlen(rsp));
     } else if(memcmp(msg, "gnss_deinit", 11) == 0) { // gnss_deinit
         int ret = gnss_deinit();
 
         char rsp[100] = {0};
-        sprintf(rsp, "gnss_deinit:%d", ret);
+        sprintf(rsp, "%cgnss_deinit:%d%c", MBTK_IND_START_FLAG, ret, MBTK_IND_END_FLAG);
         gnss_write(fd, rsp, strlen(rsp));
     } else if(memcmp(msg, "gnss_setting", 12) == 0) {// gnss_setting:cmd
         int ret = gnss_set(msg + 13, strlen(msg + 13), NULL, 0);
 
         char rsp[100] = {0};
-        sprintf(rsp, "gnss_setting:%d", ret);
+        sprintf(rsp, "%cgnss_setting:%d%c", MBTK_IND_START_FLAG, ret, MBTK_IND_END_FLAG);
         gnss_write(fd, rsp, strlen(rsp));
     } else if(memcmp(msg, "gnss_dl", 7) == 0) {// gnss_dl:fw_name
         int ret = gnss_dl_fw(msg + 8, NULL, 0);
 
         char rsp[100] = {0};
-        sprintf(rsp, "gnss_dl:%d", ret);
+        sprintf(rsp, "%cgnss_dl:%d%c", MBTK_IND_START_FLAG, ret, MBTK_IND_END_FLAG);
+        gnss_write(fd, rsp, strlen(rsp));
+    } else if(memcmp(msg, "gnss_ind", 8) == 0) {// gnss_ind:ind_type
+        int ind_type = atoi(msg + 9);
+        int ret = gnss_ind_set(fd, ind_type);
+
+        char rsp[100] = {0};
+        sprintf(rsp, "%cgnss_ind:%d%c", MBTK_IND_START_FLAG, ret, MBTK_IND_END_FLAG);
         gnss_write(fd, rsp, strlen(rsp));
     } else {
         LOGW("Unknown gnss msg : %s", msg);
@@ -135,6 +142,9 @@
 
                         close(epoll_events[i].data.fd);
                         sock_cli_fds[index] = -1;
+
+                        // Clear IND flag.
+                        gnss_ind_set(epoll_events[i].data.fd, 0);
                     }
                     else
                     {
diff --git a/mbtk/mbtk_gnssd/gnss_main.c b/mbtk/mbtk_gnssd/gnss_main.c
index 5109126..c4f2d8d 100755
--- a/mbtk/mbtk_gnssd/gnss_main.c
+++ b/mbtk/mbtk_gnssd/gnss_main.c
@@ -30,6 +30,7 @@
 #define GNSS_PORT_USB_AT        "/dev/ttyGS0"
 #define GNSS_PORT_USB_NMEA      "/dev/ttymodem0"
 #define GNSS_PORT_UART_AT       "/dev/ttyS1"
+#define GNSS_CLI_IND_MAX        10
 
 #ifdef GNSS_DEBUG
 #define GNSS_NMEA_FILE_LOG      "/tmp/mbtk_gnss_nmea.log"
@@ -51,6 +52,7 @@
 static char data_buff[GNSS_BUFF_SIZE*4] = {0};
 static uint32 nmea_buff_len = 0;
 static uint32 data_buff_len = 0;
+static gnss_ind_info_t ind_info[GNSS_CLI_IND_MAX];
 
 static bool nmea_found = FALSE;
 #ifdef GNSS_DEBUG
@@ -66,6 +68,8 @@
 static int gnss_uart_at_port_fd = -1;
 static char *gnss_filter_info[] = {"RMC", "VTG", "GGA", "GSA", "GSV", "GLL", "ZDA", "GST", "TXT", "DHV", "DTM", NULL};
 
+int gnss_write(int fd, const void* buf, int buf_len);
+
 static void help()
 {
     LOGD("mbtk_gnssd <6228/8122/5311> <gnss_dev> <0/1>");
@@ -248,6 +252,49 @@
 }
 #endif
 
+static void ind_location_print(const char *data)
+{
+    int index = 0;
+    int buff_len = 0;
+    char buff[GNSS_BUFF_SIZE] = {0};
+    buff[0] = MBTK_IND_START_FLAG;
+    buff_len++;
+    memcpy(buff + 1, MBTK_IND_LOCATION_TAG, strlen(MBTK_IND_LOCATION_TAG));
+    buff_len += strlen(MBTK_IND_LOCATION_TAG);
+    memcpy(buff + strlen(buff), data, strlen(data));
+    buff_len += strlen(data);
+    buff[strlen(buff)] = MBTK_IND_END_FLAG;
+    buff_len++;
+
+    while(index < GNSS_CLI_IND_MAX) {
+        if(ind_info[index].cli_fd > 0 && (ind_info[index].ind_flag & MBTK_GNSS_IND_LOCATION)) {
+            gnss_write(ind_info[index].cli_fd, buff, buff_len);
+        }
+        index++;
+    }
+}
+
+static void ind_nmea_print(const char *data)
+{
+    int index = 0;
+    int buff_len = 0;
+    char buff[GNSS_BUFF_SIZE] = {0};
+    buff[0] = MBTK_IND_START_FLAG;
+    buff_len++;
+    memcpy(buff + 1, MBTK_IND_NMEA_TAG, strlen(MBTK_IND_NMEA_TAG));
+    buff_len += strlen(MBTK_IND_NMEA_TAG);
+    memcpy(buff + strlen(buff), data, strlen(data));
+    buff_len += strlen(data);
+    buff[strlen(buff)] = MBTK_IND_END_FLAG;
+    buff_len++;
+    while(index < GNSS_CLI_IND_MAX) {
+        if(ind_info[index].cli_fd > 0 && (ind_info[index].ind_flag & MBTK_GNSS_IND_NMEA)) {
+            gnss_write(ind_info[index].cli_fd, buff, buff_len);
+        }
+        index++;
+    }
+}
+
 static void nmea_print(const char *nmea, int nmea_len)
 {
     if(gnss_usb_at_port_fd > 0) {
@@ -265,6 +312,8 @@
     if(gnss_pty_master_fd > 0) {
         write(gnss_pty_master_fd, nmea, nmea_len);
     }
+
+    ind_nmea_print(nmea);
 }
 
 static unsigned char nmea_checksum(const char *nmea)
@@ -839,6 +888,47 @@
     }
 }
 
+int gnss_ind_set(int fd, int ind_type)
+{
+    int index = 0;
+    if(ind_type) { // Add IND flag.
+        while(index < GNSS_CLI_IND_MAX) {
+            if(ind_info[index].cli_fd == fd)
+                break;
+            index++;
+        }
+
+        if(index == GNSS_CLI_IND_MAX) { // Add flag
+            index = 0;
+            while(index < GNSS_CLI_IND_MAX) {
+                if(ind_info[index].cli_fd <= 0)
+                    break;
+                index++;
+            }
+            ind_info[index].cli_fd = fd;
+            ind_info[index].ind_flag = (uint32)ind_type;
+        } else { // Change flag
+            ind_info[index].cli_fd = fd;
+            ind_info[index].ind_flag = (uint32)ind_type;
+        }
+    } else { // Clear IND flag.
+        while(index < GNSS_CLI_IND_MAX) {
+            if(ind_info[index].cli_fd == fd)
+                break;
+            index++;
+        }
+
+        if(index == GNSS_CLI_IND_MAX) {
+            return GNSS_ERR_ARG;
+        }
+
+        ind_info[index].cli_fd = 0;
+        ind_info[index].ind_flag = 0;
+    }
+
+    return GNSS_ERR_OK;
+}
+
 static void sig_process(int sig)
 {
     LOGI("I got signal %d\n", sig);
diff --git a/mbtk/test/libmbtk_gnss/Makefile b/mbtk/test/libmbtk_gnss/Makefile
index b815c36..b2cb779 100755
--- a/mbtk/test/libmbtk_gnss/Makefile
+++ b/mbtk/test/libmbtk_gnss/Makefile
@@ -5,9 +5,9 @@
 
 LIB_DIR +=
 
-LIBS += -lmbtk_lib -lmbtk_net -lmbtk_ftp
+LIBS += -lmbtk_lib -lmbtk_gnss
 
-CFLAGS += 
+CFLAGS +=
 
 DEFINE +=
 
diff --git a/mbtk/test/libmbtk_gnss/mbtk_gnss_test.c b/mbtk/test/libmbtk_gnss/mbtk_gnss_test.c
new file mode 100755
index 0000000..7119e11
--- /dev/null
+++ b/mbtk/test/libmbtk_gnss/mbtk_gnss_test.c
Binary files differ
diff --git a/mbtk/test/others/mbtk_gnss_cli.c b/mbtk/test/others/mbtk_gnss_cli.c
index 40d827c..8699a39 100755
--- a/mbtk/test/others/mbtk_gnss_cli.c
+++ b/mbtk/test/others/mbtk_gnss_cli.c
@@ -26,6 +26,7 @@
 
 #include "mbtk_log.h"
 #include "mbtk_type.h"
+#include "mbtk_gnss.h"
 
 #define GNSS_SOCK_PATH "/tmp/mbtk_gnss_sock"
 
@@ -91,35 +92,43 @@
     write(sock_listen_fd, buff, strlen(buff));
 
     int len = 0;
+    char *rsp = NULL;
     while(1) {
         memset(buff, 0, sizeof(buff));
         len = read(sock_listen_fd, buff, sizeof(buff));
         if(len > 0) {
-            printf("RSP : %s\n", buff);
+            rsp = buff;
+            if(rsp[len - 1] == MBTK_IND_END_FLAG) {
+                rsp[len - 1] = '\0';
+            }
+            if(rsp[0] == MBTK_IND_START_FLAG) {
+                rsp++;
+            }
+            printf("RSP : %s\n", rsp);
             if(cmd == GNSS_CMD_INIT) {
-                if(memcmp(buff, "gnss_init", 9) == 0) {
-                    return atoi(buff + 10);
+                if(memcmp(rsp, "gnss_init", 9) == 0) {
+                    return atoi(rsp + 10);
                 } else {
                     printf("gnss_init response error.\n");
                     return -1;
                 }
             } else if(cmd == GNSS_CMD_DEINIT) {
-                if(memcmp(buff, "gnss_deinit", 11) == 0) {
-                    return atoi(buff + 12);
+                if(memcmp(rsp, "gnss_deinit", 11) == 0) {
+                    return atoi(rsp + 12);
                 } else {
                     printf("gnss_deinit response error.\n");
                     return -1;
                 }
             } else if(cmd == GNSS_CMD_SETTING) {
-                if(memcmp(buff, "gnss_setting", 12) == 0) {
-                    return atoi(buff + 13);
+                if(memcmp(rsp, "gnss_setting", 12) == 0) {
+                    return atoi(rsp + 13);
                 } else {
                     printf("gnss_setting response error.\n");
                     return -1;
                 }
             } else if(cmd == GNSS_CMD_DL) {
-                if(memcmp(buff, "gnss_dl", 7) == 0) {
-                    return atoi(buff + 8);
+                if(memcmp(rsp, "gnss_dl", 7) == 0) {
+                    return atoi(rsp + 8);
                 } else {
                     printf("gnss_dl response error.\n");
                     return -1;