Add mbtk_gnssd

Change-Id: I2e3bc0b7c9cc310c04a4fbcbe809c525b0cfafbe
diff --git a/build.sh b/build.sh
index 8c9f438..4991331 100755
--- a/build.sh
+++ b/build.sh
@@ -127,6 +127,7 @@
 	[ -f out/bin/mtd_info ] && cp -f out/bin/mtd_info $1/bin
 	[ -f out/bin/mbtk_sdk_ready ] && cp -f out/bin/mbtk_sdk_ready $1/bin
 	[ -f out/bin/mbtk_reboot ] && cp -f out/bin/mbtk_reboot $1/bin
+	[ -f out/bin/mbtk_gnssd ] && cp -f out/bin/mbtk_gnssd $1/bin
 }
 
 function file_copy()
diff --git a/mbtk/Makefile b/mbtk/Makefile
index 79902be..d9e355f 100755
--- a/mbtk/Makefile
+++ b/mbtk/Makefile
@@ -9,7 +9,7 @@
 DIRS += libql_lib liblynq_lib
 
 # Build bin file.
-DIRS += mbtk_adbd mbtk_rild mbtk_logd mbtk_utils mbtk_utils_linux mbtk_sdk_ready
+DIRS += mbtk_adbd mbtk_rild mbtk_logd mbtk_utils mbtk_utils_linux mbtk_sdk_ready mbtk_gnssd
 
 # Build test file.
 DIRS += test
diff --git a/mbtk/mbtk_gnssd/Makefile b/mbtk/mbtk_gnssd/Makefile
new file mode 100755
index 0000000..e1735ff
--- /dev/null
+++ b/mbtk/mbtk_gnssd/Makefile
@@ -0,0 +1,47 @@
+BUILD_ROOT = $(shell pwd)/..
+include $(BUILD_ROOT)/Make.defines
+
+LOCAL_PATH=$(BUILD_ROOT)/mbtk_gnssd
+
+INC_DIR += \
+		-I$(LOCAL_PATH) \
+		-I$(BUILD_ROOT)/libmbtk_ril
+
+LIB_DIR +=
+
+LIBS += -lmbtk_lib -lmbtk_net -lmbtk_ril -lrilutil -lprop2uci -lmtel -laudio-apu -lcutils -ltinyalsa -lacm -lubus -lubox
+
+CFLAGS +=
+
+DEFINE +=
+
+#MY_FILES_PATH:=$(LOCAL_PATH)
+#ifeq ($(CONFIG_MBTK_QL_SUPPORT),y)
+#MY_FILES_PATH += $(LOCAL_PATH)/ql
+#endif
+
+#ifeq ($(CONFIG_MBTK_PLATFORM),linux)
+#MY_FILES_PATH += $(LOCAL_PATH)/platform/linux
+#endif
+#LOCAL_SRC_FILES = $(wildcard *.c) $(wildcard *.cpp)
+LOCAL_SRC_FILES = gnss_main.c gnss_nmea.c gnss_6228.c gnss_ubus.c gnss_utils.c
+OBJS = $(patsubst %.c, %.o, $(patsubst %.cpp, %.o, $(LOCAL_SRC_FILES)))
+$(info OBJS = $(OBJS))
+
+dtarget := $(OUT_DIR)/bin/mbtk_gnssd
+
+all: $(dtarget)
+
+$(dtarget): $(OBJS)
+	@echo "  BIN     $@"
+	$(CC) $(CFLAGS) $(LIB_DIR) $(LIBS) $(OBJS) -o $@
+
+%.o:%.c
+	$(CC) $(CFLAGS) $(INC_DIR) $(DEFINE) -c $< -o $@
+
+%.o:%.cpp
+	$(CC) $(CFLAGS) $(INC_DIR) $(DEFINE) -c $< -o $@
+
+clean:
+	rm -f $(OBJS) $(dtarget)
+
diff --git a/mbtk/mbtk_gnssd/gnss_6228.c b/mbtk/mbtk_gnssd/gnss_6228.c
new file mode 100755
index 0000000..2cd86a4
--- /dev/null
+++ b/mbtk/mbtk_gnssd/gnss_6228.c
Binary files differ
diff --git a/mbtk/mbtk_gnssd/gnss_6228.h b/mbtk/mbtk_gnssd/gnss_6228.h
new file mode 100755
index 0000000..3a2a41f
--- /dev/null
+++ b/mbtk/mbtk_gnssd/gnss_6228.h
@@ -0,0 +1,39 @@
+/*
+* gnss_6228.h
+*
+* UC6228CI Header.
+*
+* Author : lb
+* Date   : 2024/5/20 17:53:51
+*/
+#ifndef _GNSS_6228_H
+#define _GNSS_6228_H
+#include "gnss_info.h"
+#include "mbtk_type.h"
+
+typedef struct {
+    char pdtName[32];
+    char Config[32];
+    char hwVer[32];
+    char fwVer[64];
+    char PN[64];
+    char SN[64];
+} gnss_6228_dev_info_t;
+
+int gnss_6228_dev_open();
+
+int gnss_6228_dev_close();
+
+int gnss_6228_open();
+
+int gnss_6228_close();
+
+int gnss_6228_fw_dl();
+
+void gnss_6228_dl_read_cb(const void *data, int data_len);
+
+gnss_err_enum gnss_6228_set(int fd, const char *cmd, void *cmd_rsp, int cmd_rsp_len);
+
+void gnss_6228_set_cb(const void *data, int data_len);
+
+#endif /* _GNSS_6228_H */
diff --git a/mbtk/mbtk_gnssd/gnss_info.h b/mbtk/mbtk_gnssd/gnss_info.h
new file mode 100755
index 0000000..0e7d4fa
--- /dev/null
+++ b/mbtk/mbtk_gnssd/gnss_info.h
@@ -0,0 +1,75 @@
+/*
+* gnss_info.h
+*
+* GNSS informations header.
+*
+* Author : lb
+* Date   : 2024/5/20 15:22:46
+*/
+#ifndef _GNSS_INFO_H
+#define _GNSS_INFO_H
+#include "mbtk_type.h"
+
+#define GNSS_ID_6228 "6228"
+#define GNSS_ID_5311 "5311"
+
+#define GNSS_PRINT_PORT_UART1       1           // 1
+#define GNSS_PRINT_PORT_USB_NMEA    (1<<1)      // 2
+#define GNSS_PRINT_PORT_USB_AT      (1<<2)      // 4
+#define GNSS_PRINT_PORT_TTY_AT      (1<<3)      // 8
+
+typedef enum {
+    GNSS_ERR_OK,
+    GNSS_ERR_UNSUPPORT,
+    GNSS_ERR_TIMEOUT,
+    GNSS_ERR_ARG,
+    GNSS_ERR_CHECKSUM,
+
+    GNSS_ERR_UNKNOWN
+} gnss_err_enum;
+
+typedef int (*gnss_dev_open_func)();
+typedef int (*gnss_dev_close_func)();
+typedef int (*gnss_open_func)(const char *dev);
+typedef int (*gnss_close_func)(int fd);
+typedef int (*gnss_fw_dl_func)(int fd);
+typedef void (*gnss_dl_read_cb_func)(const void *data, int data_len);
+typedef gnss_err_enum (*gnss_set_func)(int fd, const char *cmd, void *cmd_rsp, int cmd_rsp_len);
+typedef void (*gnss_set_cb_func)(const void *data, int data_len);
+
+typedef enum {
+    GNSS_TYPE_6228 = 0,
+    GNSS_TYPE_5311,
+} gnss_id_enum;
+
+typedef enum {
+    GNSS_STATE_CLOSE,       // GNSS is closed.
+    GNSS_STATE_CLOSING,       // GNSS is closing.
+    GNSS_STATE_OPEN,        // GNSS is opened.
+    GNSS_STATE_DOWNLOAD,    // GNSS is downloading.
+    GNSS_STATE_READY,       // GNSS is ready.
+} gnss_state_enum;
+
+typedef struct {
+    gnss_id_enum gnss_id;
+    char dev_name[32];
+    bool auto_open;        // Should auto open gnss?
+    bool auto_dl_fw;       // Should download firmware int the first?
+    int fd;                // GNSS uart fd.
+    int exit_fd[2];        // Use to exit thread.
+    gnss_state_enum state;
+    uint32 print_port;
+    pthread_t read_pid;     // Read NMEA thread.
+
+    // GNSS functions.
+    gnss_dev_open_func gnss_dev_open;
+    gnss_dev_close_func gnss_dev_close;
+    gnss_open_func gnss_open;
+    gnss_close_func gnss_close;
+    gnss_fw_dl_func gnss_fw_dl;
+    gnss_dl_read_cb_func gnss_dl_read_cb;
+    gnss_set_func gnss_set;
+    gnss_set_cb_func gnss_set_cb;
+} gnss_info_t;
+
+#endif /* _GNSS_INFO_H */
diff --git a/mbtk/mbtk_gnssd/gnss_main.c b/mbtk/mbtk_gnssd/gnss_main.c
new file mode 100755
index 0000000..0127a03
--- /dev/null
+++ b/mbtk/mbtk_gnssd/gnss_main.c
@@ -0,0 +1,753 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <pthread.h>
+#include <fcntl.h>
+#include <libubox/ustream.h>
+#include <libubus.h>
+#include <signal.h>
+#include <cutils/properties.h>
+
+#include "mbtk_type.h"
+#include "mbtk_log.h"
+#include "gnss_info.h"
+
+#include "gnss_6228.h"
+
+#define GNSS_DEBUG 1
+
+#define GNSS_TAG "MBTK_GNSS"
+#define GNSS_BUFF_SIZE 2048
+#define MBTK_PROP_GNSS_LOG      "persist.mbtk.gnss_log_enable"
+#define GNSS_PORT_PTY           "/dev/tty_gnss_nmea"
+#define GNSS_PORT_USB_AT        "/dev/ttyGS0"
+#define GNSS_PORT_USB_NMEA      "/dev/ttymodem0"
+#define GNSS_PORT_UART_AT       "/dev/ttyS1"
+
+#ifdef GNSS_DEBUG
+#define GNSS_NMEA_FILE_LOG      "/tmp/mbtk_gnss_nmea.log"
+#define GNSS_NMEA_FILE_LOG_MAX  10485760    // 10MB
+
+#define GNSS_FILE_LOG           "/tmp/mbtk_gnss.log"
+#define GNSS_FILE_LOG_MAX       10485760    // 10MB
+#endif
+
+gnss_info_t gnss_info;
+
+struct ubus_context *gnss_ubus_init(void);
+int gnss_init_config(int fd);
+
+static char gnss_buff[GNSS_BUFF_SIZE*2] = {0};
+static uint32 gnss_buff_len = 0;
+static bool nmea_found = FALSE;
+#ifdef GNSS_DEBUG
+static bool nmea_log_enable = FALSE;
+static int nmea_log_fd = -1;
+static int nmea_log_fd_len = 0;
+#endif
+static int gnss_pty_master_fd = -1;
+static int gnss_pty_slave_fd = -1;
+static int gnss_usb_at_port_fd = -1;
+static int gnss_usb_nmea_port_fd = -1;
+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};
+
+static void help()
+{
+    LOGD("mbtk_gnssd <6228/...> <gnss_dev> <0/1>");
+}
+
+static int arg_check(int argc, char *argv[])
+{
+    if(argc != 4) {
+        goto check_fail;
+    }
+
+    // Only support 6228.
+    if(strcmp(argv[1], GNSS_ID_6228)) {
+        goto check_fail;
+    }
+
+    if(access(argv[2], R_OK | W_OK)) {
+        goto check_fail;
+    }
+
+    if(strcmp(argv[3], "0") && strcmp(argv[3], "1")) {
+        goto check_fail;
+    }
+
+    return 0;
+check_fail:
+    help();
+    return -1;
+}
+
+static int gnss_ports_open(uint32 print_port)
+{
+    if(print_port & GNSS_PRINT_PORT_TTY_AT) {
+        if(gnss_pty_open(&gnss_pty_master_fd, &gnss_pty_slave_fd, GNSS_PORT_PTY)) {
+            return -1;
+        }
+        LOGD("Open PTY port success.");
+    }
+
+    if(print_port & GNSS_PRINT_PORT_USB_AT) {
+        if((gnss_usb_at_port_fd = gnss_port_open(GNSS_PORT_USB_AT, O_RDWR | O_NONBLOCK | O_NOCTTY, 115200, FALSE)) <= 0) {
+            return -1;
+        }
+        LOGD("Open USB AT port success.");
+    }
+
+    if(print_port & GNSS_PRINT_PORT_USB_NMEA) {
+        if((gnss_usb_nmea_port_fd = gnss_port_open(GNSS_PORT_USB_NMEA, O_RDWR | O_NONBLOCK | O_NOCTTY, 115200, FALSE)) <= 0) {
+            return -1;
+        }
+        LOGD("Open USB NMEA port success.");
+    }
+
+    if(print_port & GNSS_PRINT_PORT_UART1) {
+        if((gnss_uart_at_port_fd = gnss_port_open(GNSS_PORT_UART_AT, O_RDWR | O_NONBLOCK | O_NOCTTY, 115200, TRUE)) <= 0) {
+            return -1;
+        }
+        LOGD("Open UART AT port success.");
+    }
+
+    return 0;
+}
+
+static int gnss_ports_close()
+{
+    if(gnss_usb_at_port_fd > 0) {
+        close(gnss_usb_at_port_fd);
+        gnss_usb_at_port_fd = -1;
+    }
+
+    if(gnss_usb_nmea_port_fd > 0) {
+        close(gnss_usb_nmea_port_fd);
+        gnss_usb_nmea_port_fd = -1;
+    }
+
+    if(gnss_uart_at_port_fd > 0) {
+        close(gnss_uart_at_port_fd);
+        gnss_uart_at_port_fd = -1;
+    }
+
+    if(gnss_pty_master_fd > 0) {
+        close(gnss_pty_master_fd);
+        gnss_pty_master_fd = -1;
+    }
+
+    if(gnss_pty_slave_fd > 0) {
+        close(gnss_pty_slave_fd);
+        gnss_pty_slave_fd = -1;
+        unlink(GNSS_PORT_PTY);
+    }
+
+    return 0;
+}
+
+static void nmea_print(const char *nmea, int nmea_len)
+{
+#ifdef GNSS_DEBUG
+    if(nmea_log_enable){
+        if(nmea_log_fd_len > GNSS_NMEA_FILE_LOG_MAX) {
+            close(nmea_log_fd);
+            nmea_log_fd = open(GNSS_NMEA_FILE_LOG, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+            if(nmea_log_fd < 0) {
+                LOGE("Open debug fd fail.");
+            }
+            nmea_log_fd_len = 0;
+        }
+
+        if(nmea_log_fd > 0) {
+            write(nmea_log_fd, nmea, nmea_len);
+            nmea_log_fd_len += nmea_len;
+        }
+    }
+#endif
+
+    if(gnss_usb_at_port_fd > 0) {
+        write(gnss_usb_at_port_fd, nmea, nmea_len);
+    }
+
+    if(gnss_usb_nmea_port_fd > 0) {
+        write(gnss_usb_nmea_port_fd, nmea, nmea_len);
+    }
+
+    if(gnss_uart_at_port_fd > 0) {
+        write(gnss_uart_at_port_fd, nmea, nmea_len);
+    }
+
+    if(gnss_pty_master_fd > 0) {
+        write(gnss_pty_master_fd, nmea, nmea_len);
+    }
+}
+
+static unsigned char nmea_checksum(const char *nmea)
+{
+    const char *p = nmea;
+    unsigned char chs = 0;
+
+    while (*p == '$')   // skip '$'
+        p++;
+    while (*p != '*' && *p != 0)
+        chs ^= *p++;
+
+    return chs;
+}
+
+static bool nmea_check(const char *nmea, int nmea_len)
+{
+    char **ptr = gnss_filter_info;
+    while(*ptr) {
+        if(strstr(nmea, *ptr)) {
+            break;
+        }
+        ptr++;
+    }
+
+    if(*ptr == NULL) {
+        LOGD("Unknown NMEA[%d]:%s", nmea_len, nmea);
+        return FALSE;
+    }
+
+    char *checksum_str = strstr(nmea, "*");
+    checksum_str++; // Jump '*'
+    char checksum_buf[3] = {0};
+    snprintf(checksum_buf, 3, "%02x", nmea_checksum(nmea));
+    if(strncasecmp(checksum_buf, checksum_str, 2)) {
+        LOGD("Checksum error[%d](checksum - %s):%s", nmea_len, checksum_buf, nmea);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static void gnss_nmea_process(const char *data, int data_len)
+{
+    char nmea[GNSS_BUFF_SIZE] = {0};
+    memcpy(nmea, data, data_len);
+
+    if(!nmea_check(nmea, data_len)) {
+        gnss_info.gnss_set_cb(nmea, data_len);
+        return;
+    }
+
+#ifdef GNSS_DEBUG
+    if(nmea_log_enable) {
+        LOGD("NMEA[%d]:%s", data_len, nmea);
+    }
+#endif
+
+    nmea_print(nmea, data_len);
+}
+
+#if 0
+static void gnss_cmd_rsp_process(const char *data, int data_len)
+{
+    char rsp[GNSS_BUFF_SIZE] = {0};
+    memcpy(rsp, data, data_len);
+    LOGD("RSP[%d]:%s", data_len, rsp);
+}
+#endif
+
+static bool nmea_char_check(char ch)
+{
+    if(isalnum(ch) || ch == '$' || ch == '\r' || ch == '\n' || ch == '.'
+        || ch == ',' || ch == '*' || ch == '\0' || ch == '/')
+        return TRUE;
+
+    return FALSE;
+}
+
+static void gnss_data_process(const char *data, int data_len)
+{
+    if(gnss_info.state == GNSS_STATE_OPEN) {
+        LOGD("GNSS_OPEN[%d]:%s", data_len, data);
+    } else if(gnss_info.state == GNSS_STATE_DOWNLOAD) {
+        // LOGD("GNSS_DL[%d]:%s", data_len, data);
+        gnss_info.gnss_dl_read_cb(data, data_len);
+    } else if(gnss_info.state == GNSS_STATE_READY) {
+        int index = 0;
+        while(index < data_len) {
+            if(nmea_found) {
+                if(!nmea_char_check(data[index])) {
+                    gnss_buff_len = 0;
+                    nmea_found = FALSE;
+#if 0
+                    if(isascii(data[index])) {
+                        LOGD("\n\n###%c###%s\n\n", data[index], data);
+                    }
+#endif
+                    continue;
+                }
+
+                if(data[index] != '\0') {
+                    gnss_buff[gnss_buff_len++] = data[index];
+                    if(gnss_buff[gnss_buff_len - 1] == '\n') {
+
+                        if(gnss_buff_len > 6 && gnss_buff[gnss_buff_len - 5] == '*') { // $XXX*YY\r\n
+                            gnss_nmea_process(gnss_buff, gnss_buff_len);
+                        }
+
+                        gnss_buff_len = 0;
+                        nmea_found = FALSE;
+                    }
+                }
+            } else {
+                if(data[index] == '$') {
+                    gnss_buff_len = 0;
+                    nmea_found = TRUE;
+                    gnss_buff[gnss_buff_len++] = data[index];
+                }
+            }
+            index++;
+        }
+    } else {
+        LOGW("Unknown state : %d", gnss_info.state);
+    }
+}
+
+void* gnss_read_pthread(void* arg)
+{
+    LOGD("gnss_read_pthread enter.");
+    char buffer[GNSS_BUFF_SIZE];
+    int len = 0;
+    int ret = 0;
+    fd_set fdr, fdw;
+    int fd_max = 0;
+
+    FD_ZERO(&fdw);
+    FD_ZERO(&fdr);
+    FD_SET(gnss_info.fd, &fdr);
+    fd_max = (gnss_info.fd > fd_max) ? gnss_info.fd : fd_max;
+    FD_SET(gnss_info.exit_fd[0], &fdr);
+    fd_max = (gnss_info.exit_fd[0] > fd_max) ? gnss_info.exit_fd[0] : fd_max;
+    memset(gnss_buff, 0, sizeof(gnss_buff));
+    gnss_buff_len = 0;
+#if GNSS_DEBUG
+    int debug_fd = -1;
+    int debug_fd_len = 0;
+    if(nmea_log_enable) {
+        debug_fd = open(GNSS_FILE_LOG, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+        if(debug_fd < 0) {
+            LOGE("Open debug fd fail.");
+        }
+        nmea_log_fd = open(GNSS_NMEA_FILE_LOG, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+        if(nmea_log_fd < 0) {
+            LOGE("Open nmea fd fail.");
+        }
+        nmea_log_fd_len = 0;
+    }
+#endif
+
+    while(gnss_info.state >= GNSS_STATE_OPEN) {
+        ret = select(fd_max + 1, &fdr, &fdw, 0, NULL);
+        //LOGD("select - %d", ret);
+        if (ret < 0)
+        {
+            if (errno == EINTR)
+            {
+                continue;
+            }
+            LOGE("select error, errno = %d (%s)", errno, strerror(errno));
+            break;
+        }
+        else if (ret == 0)
+        {
+            LOGE("select ret == 0");
+            break;
+        }
+
+        if (FD_ISSET(gnss_info.fd, &fdr))
+        {
+            memset(buffer, 0, GNSS_BUFF_SIZE);
+            len = read(gnss_info.fd, buffer, GNSS_BUFF_SIZE);
+            if(len > 0) {
+                //log_hex("READ", buffer, len);
+
+#if GNSS_DEBUG
+                if(nmea_log_enable){
+                    if(debug_fd_len > GNSS_FILE_LOG_MAX) {
+                        close(debug_fd);
+                        debug_fd = open(GNSS_FILE_LOG, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+                        if(debug_fd < 0) {
+                            LOGE("Open debug fd fail.");
+                        }
+                        debug_fd_len = 0;
+                    }
+
+                    if(debug_fd > 0) {
+                        write(debug_fd, buffer, len);
+                        debug_fd_len += len;
+                    }
+                }
+#endif
+
+                gnss_data_process(buffer, len);
+
+            } else if(len ==0 ){
+                LOGE("Read end : len = 0");
+                break;
+            } else {
+                if(EAGAIN == errno) {
+                    usleep(50000);
+                    continue;
+                } else {
+                    LOGD("Read ret = -1 ,errno = %d", errno);
+                    break;
+                }
+            }
+        }
+        else if (FD_ISSET(gnss_info.exit_fd[0], &fdr))
+        {
+            memset(buffer, 0, GNSS_BUFF_SIZE);
+            len = read(gnss_info.fd, buffer, GNSS_BUFF_SIZE);
+            if(len > 0) {
+                if(strcmp(buffer, "exit") == 0) {
+                    LOGD("Get thread exit message.");
+                    break;
+                }
+            }
+        }
+        else
+        {
+            LOGW("Unknown select event.");
+            continue;
+        }
+    }
+
+#if GNSS_DEBUG
+    if(debug_fd > 0) {
+        close(debug_fd);
+        debug_fd = -1;
+    }
+    if(nmea_log_fd > 0) {
+        close(nmea_log_fd);
+        nmea_log_fd = -1;
+    }
+#endif
+
+    gnss_info.state = GNSS_STATE_CLOSE;
+    LOGD("gnss_read_pthread exit.");
+    return NULL;
+}
+
+#if 0
+int gnss_write(int fd, const void *data, int data_len)
+{
+    int count = 0;
+    int len = 0;
+    while(1)
+    {
+        len = write(fd, data + count, data_len - count);
+        if (len > 0)
+        {
+            count += len;
+        }
+        else
+        {
+            LOGE("write() fail,ret = %d,errno = %d", len, errno);
+            break;
+        }
+
+        if (count == data_len)
+            break;
+    }
+
+    return count;
+}
+#else
+int gnss_write(int fd, const void* buf, unsigned int buf_len)
+{
+    size_t size;
+    size_t size_to_wr;
+    ssize_t size_written;
+    if(GNSS_BUFF_SIZE < buf_len)
+    {
+        return -1;
+    }
+    for(size = 0; size < buf_len;)
+    {
+        size_to_wr = buf_len - size;
+        if( size_to_wr > GNSS_BUFF_SIZE)
+            size_to_wr = GNSS_BUFF_SIZE;
+
+        size_written = write(fd, &buf[size], size_to_wr);
+        if (size_written==-1)
+        {
+            return -1;
+        }
+        size += size_written;
+        if(size_written != size_to_wr)
+        {
+            return size;
+        }
+    }
+    // LOGD("SEND %d / %d", size, buf_len);
+    return size;
+}
+#endif
+
+int gnss_init(uint32 print_port)
+{
+    if(gnss_info.state != GNSS_STATE_CLOSE) {
+        LOGW("GNSS not close:%d", gnss_info.state);
+        return 0;
+    }
+
+    int ret = 0;
+
+    gnss_info.fd = gnss_info.gnss_open(gnss_info.dev_name);
+    if(gnss_info.fd <= 0) {
+        LOGE("gnss_open(%s) fail : %d", gnss_info.dev_name, gnss_info.fd);
+        gnss_info.state = GNSS_STATE_CLOSE;
+        return -1;
+    }
+    if(pipe(gnss_info.exit_fd)) {
+        LOGE("pipe() fail[%d].", errno);
+        return -1;
+    }
+    // GNSS is opened.
+    gnss_info.state = GNSS_STATE_OPEN;
+
+    #if 0
+    // Start gnss read thread.
+    pthread_attr_t thread_attr;
+    pthread_attr_init(&thread_attr);
+    if(pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED))
+    {
+        LOGE("pthread_attr_setdetachstate() fail.");
+        goto main_exit;
+    }
+
+    if(pthread_create(&gnss_info.read_pid, &thread_attr, gnss_read_pthread, NULL))
+#else
+    if(pthread_create(&gnss_info.read_pid, NULL, gnss_read_pthread, NULL))
+#endif
+    {
+        LOGE("pthread_create() fail.");
+        goto exit_with_close;
+    }
+
+    ret = gnss_info.gnss_dev_open();
+    if(ret) {
+        LOGE("gnss_dev_open() fail : %d", ret);
+        goto exit_with_thread_exit;
+    }
+
+    if(gnss_info.auto_dl_fw) {
+        gnss_info.state = GNSS_STATE_DOWNLOAD;
+        ret = gnss_info.gnss_fw_dl(gnss_info.fd);
+        if(ret) {
+            LOGE("gnss_fw_dl() fail : %d", ret);
+            goto exit_with_dev_close;
+        }
+    }
+
+    // GNSS is ready, NMEA can print from uart.
+    gnss_info.state = GNSS_STATE_READY;
+    gnss_info.print_port = print_port;
+
+    LOGD("GNSS open success.");
+
+    return gnss_ports_open(gnss_info.print_port);
+
+exit_with_dev_close:
+    if(gnss_info.gnss_dev_close()) {
+        LOGE("gnss_dev_close() fail.");
+    }
+exit_with_thread_exit:
+    gnss_info.state = GNSS_STATE_CLOSING;
+    // Wait for read thread exit.
+    ret = pthread_join(gnss_info.read_pid, NULL);
+    if(ret){
+        LOGE("pthrad_join fail(%d)",ret);
+    }
+exit_with_close:
+    if(gnss_info.gnss_close(gnss_info.fd)) {
+        LOGE("gnss_close() fail.");
+    }
+    if(gnss_info.exit_fd[0] > 0) {
+        close(gnss_info.exit_fd[0]);
+        gnss_info.exit_fd[0] = -1;
+    }
+    if(gnss_info.exit_fd[1] > 0) {
+        close(gnss_info.exit_fd[1]);
+        gnss_info.exit_fd[1] = -1;
+    }
+    gnss_info.state = GNSS_STATE_CLOSE;
+    return -1;
+}
+
+int gnss_deinit()
+{
+    if(gnss_info.state == GNSS_STATE_CLOSE) {
+        LOGW("GNSS is closed.");
+        return 0;
+    } else if(gnss_info.state == GNSS_STATE_CLOSING) {
+        LOGW("GNSS is closing...");
+        return -1;
+    } else if(gnss_info.state == GNSS_STATE_DOWNLOAD) {
+        LOGW("GNSS is downloading...");
+        return -1;
+    }
+
+    // Wait for read thread exit.
+    if(gnss_info.exit_fd[1] > 0) {
+        write(gnss_info.exit_fd[1], "exit", 4);
+    }
+
+    gnss_info.state = GNSS_STATE_CLOSING;
+    int ret = pthread_join(gnss_info.read_pid, NULL);
+    if(ret){
+        LOGE("pthrad_join fail(%d)",ret);
+        return -1;
+    }
+
+    if(gnss_info.gnss_close(gnss_info.fd)) {
+        LOGE("gnss_close() fail.");
+        return -1;
+    }
+
+    if(gnss_info.gnss_dev_close()) {
+        LOGE("gnss_dev_close() fail.");
+        return -1;
+    }
+
+    if(gnss_ports_close()) {
+        LOGE("gnss_ports_close fail.");
+        return -1;
+    }
+
+    gnss_info.fd = -1;
+    if(gnss_info.exit_fd[0] > 0) {
+        close(gnss_info.exit_fd[0]);
+        gnss_info.exit_fd[0] = -1;
+    }
+    if(gnss_info.exit_fd[1] > 0) {
+        close(gnss_info.exit_fd[1]);
+        gnss_info.exit_fd[1] = -1;
+    }
+    gnss_info.state = GNSS_STATE_CLOSE;
+    LOGD("GNSS close success.");
+    return 0;
+}
+
+int gnss_set(const void* buf, unsigned int buf_len, void *cmd_rsp, int cmd_rsp_len)
+{
+    if(cmd_rsp && buf && buf_len > 0 && cmd_rsp_len > 0) {
+        memset(cmd_rsp, 0, cmd_rsp_len);
+        return gnss_info.gnss_set(gnss_info.fd, buf, cmd_rsp, cmd_rsp_len);
+    } else {
+        return -1;
+    }
+}
+
+static void sig_process(int sig)
+{
+    LOGI("I got signal %d\n", sig);
+    if(gnss_deinit()) {
+        LOGE("gnss_deinit() fail, no exist...");
+        return;
+    }
+
+    switch(sig)
+    {
+        case SIGINT: // Ctrl + C
+        {
+            LOGI("Exit by SIGINT.\n");
+            exit(0);
+        }
+        case SIGQUIT: // Ctrl + \ (ÀàËÆ SIGINT £¬µ«Òª²úÉúcoreÎļþ)
+        {
+            LOGI("Exit by SIGQUIT.\n");
+            exit(0);
+        }
+        case SIGTERM:// ĬÈÏkill   (ͬ SIGKILL £¬µ« SIGKILL ²»¿É²¶»ñ)
+        {
+            LOGI("Exit by SIGTERM.\n");
+            exit(0);
+        }
+        case SIGTSTP:// Ctrl + Z (ͬ SIGSTOP £¬µ« SIGSTOP ²»¿É²¶»ñ)
+        {
+            LOGI("Exit by SIGTSTP.\n");
+            exit(0);
+        }
+        case SIGSEGV: // Èç¿ÕÖ¸Õë
+        {
+            LOGI("Exit by SIGSEGV.\n");
+            exit(0);
+        }
+        default:
+        {
+            LOGI("Unknown sig:%d\n",sig);
+            break;
+        }
+    }
+}
+
+
+// mbtk_gnssd 6228 /dev/ttyS2 baud 0/1
+int main(int argc, char *argv[])
+{
+    mbtk_log_init("radio", GNSS_TAG);
+
+    signal(SIGINT, sig_process);
+    signal(SIGQUIT, sig_process);
+    signal(SIGTERM, sig_process);
+
+    if(arg_check(argc, argv)) {
+        return -1;
+    }
+
+#ifdef GNSS_DEBUG
+    char buff[10];
+    memset(buff, 0, 10);
+    property_get(MBTK_PROP_GNSS_LOG, buff, "");
+    if(strlen(buff) > 0 && atoi(buff) > 0) {
+        nmea_log_enable = TRUE;
+    }
+#endif
+
+    memset(&gnss_info, 0, sizeof(gnss_info_t));
+    memcpy(gnss_info.dev_name, argv[2], strlen(argv[2]));
+    gnss_info.state = GNSS_STATE_CLOSE;
+    if(!strcmp(argv[1], GNSS_ID_6228)) {
+        gnss_info.gnss_id = GNSS_TYPE_6228;
+        gnss_info.auto_open = (bool)atoi(argv[3]);
+        gnss_info.auto_dl_fw = TRUE;
+        gnss_info.gnss_dev_open = gnss_6228_dev_open;
+        gnss_info.gnss_dev_close = gnss_6228_dev_close;
+        gnss_info.gnss_open = gnss_6228_open;
+        gnss_info.gnss_close = gnss_6228_close;
+        gnss_info.gnss_fw_dl = gnss_6228_fw_dl;
+        gnss_info.gnss_dl_read_cb = gnss_6228_dl_read_cb;
+        gnss_info.gnss_set = gnss_6228_set;
+        gnss_info.gnss_set_cb = gnss_6228_set_cb;
+    } else {
+        LOGE("No support : %s", argv[1]);
+        return -1;
+    }
+
+    LOGD("GNSS : %s, Device: %s", argv[1], gnss_info.dev_name);
+    // Auto open gnss.
+    if(gnss_info.auto_open) {
+        if(gnss_init(0)) { // No print to any port.
+            LOGE("gnss_init() fail.");
+            return -1;
+        }
+    }
+
+    // Init ubus and waitting IPC commands.
+    if(gnss_ubus_init()) {
+        LOGD("main() run...");
+        uloop_run();
+    } else {
+        LOGE("gnss_ubus_init() fail.");
+    }
+
+    LOGD("main() exit.");
+    return 0;
+}
diff --git a/mbtk/mbtk_gnssd/gnss_nmea.c b/mbtk/mbtk_gnssd/gnss_nmea.c
new file mode 100755
index 0000000..8b13789
--- /dev/null
+++ b/mbtk/mbtk_gnssd/gnss_nmea.c
@@ -0,0 +1 @@
+
diff --git a/mbtk/mbtk_gnssd/gnss_ubus.c b/mbtk/mbtk_gnssd/gnss_ubus.c
new file mode 100755
index 0000000..30de33a
--- /dev/null
+++ b/mbtk/mbtk_gnssd/gnss_ubus.c
@@ -0,0 +1,358 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <libubox/ustream.h>
+#include <libubus.h>
+
+#include "gnss_info.h"
+#include "mbtk_log.h"
+
+//static struct blob_buf b;
+static struct blob_buf gps_blob;
+
+int gnss_init(uint32 print_port);
+int gnss_deinit();
+int gnss_set(const void* buf, unsigned int buf_len, void *cmd_rsp, int cmd_rsp_len);
+
+const struct blobmsg_policy gnss_init_policy[] ={
+	[0] = {
+		.name = "gnss_init_param",
+		.type = BLOBMSG_TYPE_INT32,
+	},
+};
+
+
+const struct blobmsg_policy get_agps_policy[] ={
+	[0] = {
+		.name = "server_name",
+		.type = BLOBMSG_TYPE_STRING,
+	},
+	[1] = {
+		.name = "alam_flag",
+		.type = BLOBMSG_TYPE_INT32,
+	},
+};
+
+const struct blobmsg_policy gnss_sleep_policy[] ={
+    [0] = {
+        .name = "gnss_sleep_param",
+        .type = BLOBMSG_TYPE_INT32,
+    },
+};
+
+const struct blobmsg_policy gnss_setting_policy[] ={
+    [0] = {
+        .name = "gnss_setting_param",
+        .type = BLOBMSG_TYPE_STRING,
+    },
+};
+
+static int gps_ubus_send_reply(struct ubus_context *ctx, struct ubus_request_data *req, int ret)
+{
+    blob_buf_init(&gps_blob, 0);
+    blobmsg_add_u32(&gps_blob, "event", ret);
+    ubus_send_reply(ctx, req, gps_blob.head);
+    return 0;
+}
+
+static int gps_ubus_string_reply(struct ubus_context *ctx, struct ubus_request_data *req, char *str)
+{
+    blob_buf_init(&gps_blob, 0);
+    blobmsg_add_string(&gps_blob, "gps_state_resp", str);
+    ubus_send_reply(ctx, req, gps_blob.head);
+    return 0;
+}
+
+static int ubus_gnss_init(struct ubus_context *ctx, struct ubus_object *obj,
+              struct ubus_request_data *req, const char *method,
+              struct blob_attr *msg)
+{
+    UNUSED(obj);
+    UNUSED(method);
+    struct blob_attr *tb[1];
+    struct blob_attr *cur;
+    int init_mode = 0, err = 0, ret = 0;
+    int status = 0;
+
+    /*parsing blob to be accessed easily with tb array - parse "1" argument*/
+    err = blobmsg_parse(gnss_init_policy, 1, tb, blob_data(msg), blob_len(msg));
+    if (err < 0)
+    {
+        LOGE("blobmsg_parse fail");
+        return -1;
+    }
+    cur = tb[0];
+    if (!cur) {
+        LOGE("missing parameter");
+        return -2;
+    }
+
+    init_mode = blobmsg_get_u32(cur);
+    LOGD("init_mode=%d", init_mode);
+    if(init_mode == 0) { // Close gnss.
+        ret = gnss_deinit();
+    } else {
+        if(((GNSS_PRINT_PORT_UART1 | GNSS_PRINT_PORT_USB_NMEA | GNSS_PRINT_PORT_USB_AT | GNSS_PRINT_PORT_TTY_AT) & init_mode) == init_mode) {
+            ret = gnss_init(init_mode);
+        } else { // ARG error, no print nmea.
+            ret = gnss_init(0);
+        }
+    }
+
+    LOGD("ubus_gnss_init() ret=%d", ret);
+    gps_ubus_send_reply(ctx, req, ret);
+
+    return 0;
+}
+
+static int ubus_gnss_deinit(struct ubus_context *ctx, struct ubus_object *obj,
+            struct ubus_request_data *req, const char *method,
+            struct blob_attr *msg)
+{
+    UNUSED(ctx);
+    UNUSED(obj);
+    UNUSED(req);
+    UNUSED(method);
+    UNUSED(msg);
+
+    int ret = 0;
+
+    ret = gnss_deinit();
+
+    gps_ubus_send_reply(ctx, req, ret);
+
+    return 0;
+}
+
+static int ubus_gnss_get_agps(struct ubus_context *ctx, struct ubus_object *obj,
+              struct ubus_request_data *req, const char *method,
+              struct blob_attr *msg)
+{
+    UNUSED(obj);
+    UNUSED(method);
+    struct blob_attr *tb[ARRAY_SIZE(get_agps_policy)];
+    struct blob_attr *cur;
+    char *server_name = NULL;
+    int err = 0, alm_flag = 0, ret = 0;
+
+    err = blobmsg_parse(get_agps_policy, ARRAY_SIZE(get_agps_policy), tb, blob_data(msg), blob_len(msg));
+    if (err < 0)
+    {
+        LOGE("blobmsg_parse table fail");
+        return -1;
+    }
+
+    cur = tb[0];
+    if (cur)
+        server_name = blobmsg_get_string(cur);
+    else
+        LOGE("missing parameter1");
+
+    cur = tb[1];
+    if (cur)
+        alm_flag = blobmsg_get_u32(cur);
+    else
+        LOGE("missing parameter2");
+
+    LOGD("server_name=%s, alm_flag=%d", server_name, alm_flag);
+
+    if (server_name)
+    {
+        ret = 3;
+    }
+
+    gps_ubus_send_reply(ctx, req, ret);
+
+    return 0;
+}
+
+static int ubus_gnss_set_agps(struct ubus_context *ctx, struct ubus_object *obj,
+            struct ubus_request_data *req, const char *method,
+            struct blob_attr *msg)
+{
+    UNUSED(ctx);
+    UNUSED(obj);
+    UNUSED(req);
+    UNUSED(method);
+    UNUSED(msg);
+
+    int err = 0, ret = 0;
+
+    ret = 4;
+
+
+    gps_ubus_send_reply(ctx, req, ret);
+
+    return 0;
+}
+
+static int ubus_gnss_sleep(struct ubus_context *ctx, struct ubus_object *obj,
+              struct ubus_request_data *req, const char *method,
+              struct blob_attr *msg)
+{
+    UNUSED(ctx);
+    UNUSED(obj);
+    UNUSED(req);
+    UNUSED(method);
+    struct blob_attr *tb[1];
+    struct blob_attr *cur;
+    int workmode = 0, err = 0;
+    int status = 0;
+
+    /*parsing blob to be accessed easily with tb array - parse "1" argument*/
+    err = blobmsg_parse(gnss_sleep_policy, 1, tb, blob_data(msg), blob_len(msg));
+    if (err < 0)
+    {
+        LOGE("blobmsg_parse fail");
+        return -1;
+    }
+    cur = tb[0];
+    if (!cur) {
+        LOGE("missing parameter");
+        return -2;
+    }
+
+    workmode = blobmsg_get_u32(cur);
+    LOGD("workMode=%d", workmode);
+    /* Goto Sleeping.....*/
+
+    LOGD("ret=%d", status);
+    return 0;
+}
+
+static int ubus_gnss_setting(struct ubus_context *ctx, struct ubus_object *obj,
+            struct ubus_request_data *req, const char *method,
+            struct blob_attr *msg)
+{
+    UNUSED(ctx);
+    UNUSED(obj);
+    UNUSED(req);
+    UNUSED(method);
+    struct blob_attr *tb[1];
+    struct blob_attr *cur;
+    int err = 0, ret = 0;
+    char *gpsCfg = NULL;
+    int status = 0;
+
+    /*parsing blob to be accessed easily with tb array - parse "1" argument*/
+    err = blobmsg_parse(gnss_setting_policy, 1, tb, blob_data(msg), blob_len(msg));
+    if (err < 0)
+    {
+      LOGE("blobmsg_parse fail");
+      return -1;
+    }
+    cur = tb[0];
+    if (!cur) {
+      LOGE("missing parameter");
+      return -2;
+    }
+
+    gpsCfg = blobmsg_get_string(cur);
+    LOGD("gpsCfg=%s", gpsCfg);
+
+    char rsp[1024];
+    ret = gnss_set(gpsCfg, strlen(gpsCfg), rsp, 1024);
+
+    LOGD("ret=%d", ret);
+    gps_ubus_send_reply(ctx, req, ret);
+
+    return 0;
+}
+
+#define ASR_GNSS_STATUS_LEN  128
+static int ubus_gnss_get_state(struct ubus_context *ctx, struct ubus_object *obj,
+            struct ubus_request_data *req, const char *method,
+            struct blob_attr *msg)
+{
+    UNUSED(obj);
+    UNUSED(req);
+    UNUSED(method);
+    UNUSED(msg);
+
+    char    tmpBuf[ASR_GNSS_STATUS_LEN]  = {0};
+    int              i   = 0;
+    int              len = 0;
+    int              ret = 0;
+    int              num = 0;
+
+    //ret = asr_gnss_get_gps_info(&param);
+    if (0 == ret) {
+        len = snprintf(tmpBuf, sizeof(tmpBuf), "%d, %d, %d, %d, %d;", 0, 0, 0, 0, 0);
+        num = 6;
+        for(i=0; i<num; i++)  {
+            len += sprintf(&tmpBuf[len], " %d, %d;", 1, 2);
+
+            if(len > ASR_GNSS_STATUS_LEN)
+                break;
+        }
+
+        LOGD("[%d]tmpBuf=%s", len, tmpBuf);
+        gps_ubus_string_reply(ctx, req, tmpBuf);
+    }
+
+    return 0;
+}
+
+static const struct ubus_method gps_ubus_methods[] = {
+    UBUS_METHOD("gnss_init", ubus_gnss_init, gnss_init_policy),
+    UBUS_METHOD_NOARG("gnss_deinit", ubus_gnss_deinit),
+    UBUS_METHOD("gnss_get_agps", ubus_gnss_get_agps, get_agps_policy),
+    UBUS_METHOD_NOARG("gnss_set_agps", ubus_gnss_set_agps),
+    UBUS_METHOD("gnss_sleep", ubus_gnss_sleep, gnss_sleep_policy),
+    UBUS_METHOD("gnss_setting", ubus_gnss_setting, gnss_setting_policy),
+    UBUS_METHOD_NOARG("gnss_get_state", ubus_gnss_get_state),
+};
+
+static struct ubus_object_type gps_object_type =
+    UBUS_OBJECT_TYPE("mbtk_gnss", gps_ubus_methods);
+
+static struct ubus_object gps_ubus_obj = {
+    .name = "mbtk_gnss",
+    .type = &gps_object_type,
+    .methods = gps_ubus_methods,
+    .n_methods = ARRAY_SIZE(gps_ubus_methods),
+};
+
+int gnss_ubus_exit(struct ubus_context *ctx)
+{
+	if(!ctx) {
+        return -1;
+	}
+
+	ubus_remove_object(ctx, &gps_ubus_obj);
+	ubus_free(ctx);
+	uloop_done();
+
+	LOGD("ubus exit done");
+	return 0;
+}
+
+struct ubus_context *gnss_ubus_init(void)
+{
+	struct ubus_context *ctx;
+
+	uloop_init();
+
+	ctx = ubus_connect(NULL);
+	if (!ctx) {
+		LOGE("Failed to connect to ubus");
+		return NULL;
+	}
+
+	ubus_add_uloop(ctx);
+	if (ubus_add_object(ctx, &gps_ubus_obj)) {
+		LOGE("Failed to add server");
+		ubus_free(ctx);
+		uloop_done();
+		return NULL;
+	}
+
+	LOGD("gps ubus init done!");
+
+	return ctx;
+}
+
diff --git a/mbtk/mbtk_gnssd/gnss_utils.c b/mbtk/mbtk_gnssd/gnss_utils.c
new file mode 100755
index 0000000..e0fa746
--- /dev/null
+++ b/mbtk/mbtk_gnssd/gnss_utils.c
@@ -0,0 +1,318 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "mbtk_type.h"
+#include "mbtk_log.h"
+
+#define DATABITS    CS8
+#define STOPBITS    0
+#define PARITYON    0
+#define PARITY      0
+#define MBTK_SLAVE_DEV_NAME_MAX_LEN       24
+
+int uart_baud_get(int baud)
+{
+    int rate = 0;
+    switch(baud)
+    {
+        case 300:
+            rate = B300;
+            break;
+        case 600:
+            rate = B600;
+            break;
+        case 1200:
+            rate = B1200;
+            break;
+        case 2400:
+            rate = B2400;
+            break;
+        case 4800:
+            rate = B4800;
+            break;
+        case 9600:
+            rate = B9600;
+            break;
+        case 19200:
+            rate = B19200;
+            break;
+        case 38400:
+            rate = B38400;
+            break;
+        case 57600:
+            rate = B57600;
+            break;
+        case 115200:
+            rate = B115200;
+            break;
+        case 230400:
+            rate = B230400;
+            break;
+        case 460800:
+            rate = B460800;
+            break;
+        case 921600:
+            rate = B921600;
+            break;
+        case 1500000:
+            rate = B1500000;
+            break;
+        case 2000000:
+            rate = B2000000;
+            break;
+        case 3000000:
+            rate = B3000000;
+            break;
+        case 4000000:
+            rate = B4000000;
+            break;
+        default:
+            rate = B115200;
+            break;
+    }
+
+    return rate;
+}
+
+int gnss_port_open(const char *dev, int flag, int baud, bool tty)
+{
+
+    int fd = -1;
+    if((fd = open(dev, flag)) < 0)
+    {
+        LOGE("Open %s fail errno = [%d].", dev, errno);
+        return -1;
+    }
+
+    LOGD("Open %s success.", dev);
+    if (tty)
+    {
+        int rate = uart_baud_get(baud);
+        /* set newtio */
+        struct termios newtio;
+        memset(&newtio, 0, sizeof(newtio));
+        //(void)fcntl(fd, F_SETFL, 0);
+        /* no flow control for uart by default */
+        newtio.c_cflag = rate | DATABITS | STOPBITS | PARITYON | PARITY | CLOCAL | CREAD;
+        newtio.c_iflag = IGNPAR;
+        //newtio.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+        newtio.c_oflag = 0;
+        newtio.c_lflag = 0;    /* disable ECHO, ICANON, etc... */
+
+        newtio.c_cc[VERASE]   = 0x8;      /* del */
+        newtio.c_cc[VEOF]     = 4;      /* Ctrl-d */
+        newtio.c_cc[VMIN]     = 1;      /* blocking read until 1 character arrives */
+        newtio.c_cc[VEOL]     = 0xD;      /* '\0' */
+
+        tcflush(fd, TCIOFLUSH);
+        tcsetattr(fd, TCSANOW, &newtio);
+    }
+
+    return fd;
+}
+
+int gnss_port_close(int fd)
+{
+    if(fd > 0)
+    {
+        close(fd);
+    }
+    return 0;
+}
+
+int gnss_set_baudrate(int fd, int baudrate)
+{
+	struct termios options, oldtio;
+
+	if(fcntl(fd, F_SETFL, 0) < 0) {
+		LOGE("fcntl failed!");
+		return -1;
+	}
+
+	if(tcgetattr(fd, &oldtio) != 0) {
+		LOGE("setup serial error!");
+		return -1;
+	}
+
+	/* Get the current options for the port... */
+	tcgetattr(fd, &options);
+
+	/* Set the baud rates to baudrate... */
+	cfsetispeed(&options,baudrate);
+	cfsetospeed(&options,baudrate);
+	tcsetattr(fd, TCSANOW, &options);
+
+	if (0 != tcgetattr(fd, &options))
+	{
+		LOGE("get options error!");
+		return -1;
+	}
+
+	/*
+	 * 8bit Data,no partity,1 stop bit...
+	 */
+	options.c_cflag &= ~PARENB;//无奇偶校验
+	options.c_cflag &= ~CSTOPB;//停止位,1位
+	options.c_cflag &= ~CSIZE; //数据位的位掩码
+	options.c_cflag |= CS8;    //数据位,8位
+
+	cfmakeraw(&options);
+
+	/*
+	 * Set the new options for the port...
+	 */
+	if (tcsetattr(fd, TCSANOW, &options) != 0)
+	{
+		LOGE("setup serial error!");
+		return -1 ;
+	}
+
+	return 0 ;
+}
+
+uint16 get_crc16(const char *ptr, uint16 count)
+{
+	uint16 crc, i;
+
+	crc = 0;
+	while(count--)
+	{
+		crc = crc ^ (int) *ptr++ << 8;
+
+		for(i = 0; i < 8; i++)
+		{
+			if(crc & 0x8000)
+				crc = crc << 1 ^ 0x1021;
+			else
+				crc = crc << 1;
+		}
+	}
+
+	return (crc & 0xFFFF);
+}
+
+int gnss_pty_open(int *master_fd, int *slave_fd, const char *dev)
+{
+    int flags = -1;
+    int ret = -1;
+    if(*master_fd > 0) {
+        LOGD("PTY has inited.");
+        return 0;
+    }
+    char spty_name[MBTK_SLAVE_DEV_NAME_MAX_LEN] = {0};
+    int result = openpty(master_fd, slave_fd, spty_name, NULL, NULL);
+    if (-1 == result) {
+        LOGE("Failed to get a pty.");
+        return -1;
+    }
+
+    LOGD("Get a pty pair, FD -- master[%d] slave[%d]", *master_fd, *slave_fd);
+    LOGD("Slave name is:%s", spty_name);
+
+    if(access(dev, F_OK) == -1)
+    {
+        LOGD("symlink %s -> %s", spty_name, dev);
+        result = symlink(spty_name, dev);
+        if (-1 == result) {
+            LOGE("symlink error.");
+            goto ERROR;
+        }
+    }
+
+    flags = fcntl(*master_fd, F_GETFL);
+    if (flags == -1)
+    {
+        LOGE("fcntl get error.");
+        goto ERROR;
+    }
+    flags |= O_NONBLOCK;
+    flags |= O_NOCTTY;
+    ret = fcntl(*master_fd, F_SETFL, flags);
+    if(ret == -1)
+    {
+        LOGE("fcntl set error.");
+        goto ERROR;
+    }
+
+    if (1) {
+		/* set newtio */
+		struct termios newtio;
+		memset(&newtio, 0, sizeof(newtio));
+		/* no flow control for uart by default */
+		newtio.c_cflag = B115200 | CRTSCTS | DATABITS | STOPBITS | PARITYON | PARITY | CLOCAL | CREAD;
+		newtio.c_iflag = IGNPAR;
+                //newtio.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+		newtio.c_oflag = 0;
+		newtio.c_lflag = 0;    /* disable ECHO, ICANON, etc... */
+
+		newtio.c_cc[VERASE]   = 0x8;      /* del */
+		newtio.c_cc[VEOF]     = 4;      /* Ctrl-d */
+		newtio.c_cc[VMIN]     = 1;      /* blocking read until 1 character arrives */
+		newtio.c_cc[VEOL]     = 0xD;      /* '\0' */
+
+		tcflush(*master_fd, TCIFLUSH);
+		tcsetattr(*master_fd, TCSANOW, &newtio);
+	}
+    return 0;
+
+ERROR:
+    if (0 < *master_fd) {
+        close(*master_fd);
+        *master_fd = -1;
+    }
+
+    if (0 < *slave_fd) {
+        close(*slave_fd);
+        *slave_fd = -1;
+    }
+
+    return -1;
+}
+
+int gnss_nmea_sscanf(const char *str, char *ret,...)
+{
+    const char *ptr = str;
+    char *argv[16];
+    int argc;
+    va_list ap;
+    int i = 0;
+
+    va_start(ap, ret);
+    argc = 0;
+    argv[argc] = ret; // First arg.
+
+    do {
+        i = 0;
+        while(*ptr && *ptr != ',' && *ptr != '*') {
+            argv[argc][i++] = *ptr++;
+        }
+        ptr++; // Jump ',' or '*'
+        argc++;
+    } while((argv[argc] = va_arg(ap, char*)) != 0);
+
+    va_end(ap);
+
+    return argc;
+}
+
+void gnssStartTimer(struct uloop_timeout *timeout, int timeVal)
+{
+    //UNUSED(timeout);
+    LOGD("%s: timeVal=%lu.", __FUNCTION__, timeVal);
+    uloop_timeout_set(timeout, timeVal);
+    return;
+}
+
+void gnssStopTimer(struct uloop_timeout *timeout)
+{
+    //UNUSED(timeout);
+    uloop_timeout_cancel(timeout);
+    return;
+}
+
diff --git a/mbtk/mbtk_gnssd/gnss_utils.h b/mbtk/mbtk_gnssd/gnss_utils.h
new file mode 100755
index 0000000..319315e
--- /dev/null
+++ b/mbtk/mbtk_gnssd/gnss_utils.h
@@ -0,0 +1,34 @@
+/*
+* gnss_utils.h
+*
+* GNSS utils header.
+*
+* Author : lb
+* Date   : 2024/5/28 11:27:34
+*/
+#ifndef _GNSS_UTILS_H
+#define _GNSS_UTILS_H
+#include <libubus.h>
+
+#include "mbtk_type.h"
+
+int uart_baud_get(int baud);
+
+int gnss_port_open(const char *dev, int flag, int baud, bool tty);
+
+int gnss_port_close(int fd);
+
+int gnss_set_baudrate(int fd, int baudrate);
+
+uint16 get_crc16 (const char *ptr, uint16 count);
+
+int gnss_pty_open(int *master_fd, int *slave_fd, const char *dev);
+
+int gnss_nmea_sscanf(const char *str, char *ret,...);
+
+void gnssStartTimer(struct uloop_timeout *timeout, int timeVal);
+
+void gnssStopTimer(struct uloop_timeout *timeout);
+
+
+#endif /* _GNSS_UTILS_H */