Add glibc support(default)

Change-Id: I7675edcf14df8707ecd424a962e4cc464a4c6ae4
diff --git a/mbtk/Make.defines b/mbtk/Make.defines
index 3f25cb4..83117fa 100755
--- a/mbtk/Make.defines
+++ b/mbtk/Make.defines
@@ -128,45 +128,36 @@
 endif

 endif

 

-MBTK_AF_SUPPORT=$(shell cat $(ROOT)/config | grep CONFIG_AF_SUPPORT | cut -d '=' -f 2)

-$(info MBTK_AF_SUPPORT=$(MBTK_AF_SUPPORT))

 ifeq ($(MBTK_AF_SUPPORT), y)

 DEFINE += -DMBTK_AF_SUPPORT

 endif

 

-MBTK_YX_SUPPORT=$(shell cat $(ROOT)/config | grep CONFIG_YX_SUPPORT | cut -d '=' -f 2)

-$(info MBTK_YX_SUPPORT=$(MBTK_YX_SUPPORT))

 ifeq ($(MBTK_YX_SUPPORT), y)

 DEFINE += -DMBTK_YX_SUPPORT

 endif

 

-MBTK_SG_SUPPORT=$(shell cat $(ROOT)/config | grep CONFIG_SG_SUPPORT | cut -d '=' -f 2)

-$(info MBTK_SG_SUPPORT=$(MBTK_SG_SUPPORT))

 ifeq ($(MBTK_SG_SUPPORT), y)

 DEFINE += -DMBTK_SG_SUPPORT

-DEFINE += -DMBTK_ALL_CID_SUPPORT

 endif

 

-MBTK_ALL_CID_SUPPORT=$(shell cat $(ROOT)/config | grep CONFIG_MBTK_ALL_CID_SUPPORT | cut -d '=' -f 2)

-$(info MBTK_ALL_CID_SUPPORT=$(MBTK_ALL_CID_SUPPORT))

 ifeq ($(MBTK_ALL_CID_SUPPORT), y)

 DEFINE += -DMBTK_ALL_CID_SUPPORT

 endif

 

-MBTK_GNSS_MODE=$(shell cat $(ROOT)/config | grep CONFIG_MBTK_GNSS_MODE | cut -d '=' -f 2)

-$(info MBTK_GNSS_MODE=$(MBTK_GNSS_MODE))

 ifeq ($(MBTK_GNSS_MODE), gnss_6228)

 DEFINE += -DMBTK_GNSS_6228

 else

 ifeq ($(MBTK_GNSS_MODE), gnss_5311)

 DEFINE += -DMBTK_GNSS_5311

 else

+ifeq ($(MBTK_GNSS_MODE), gnss_8122)

 DEFINE += -DMBTK_GNSS_8122

+else

+DEFINE += -DMBTK_GNSS_ALL

+endif

 endif

 endif

 

-MBTK_DUMP_SUPPORT=$(shell cat $(ROOT)/config | grep CONFIG_MBTK_DUMP_SUPPORT | cut -d '=' -f 2)

-$(info MBTK_DUMP_SUPPORT=$(MBTK_DUMP_SUPPORT))

 ifeq ($(MBTK_DUMP_SUPPORT), y)

 DEFINE += -DMBTK_DUMP_SUPPORT

 endif

diff --git a/mbtk/Makefile b/mbtk/Makefile
index 3b1956a..58dbee1 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 mbtk_gnssd mbtk_servicesd
+DIRS += aboot-tiny mbtk_adbd mbtk_rild mbtk_logd mbtk_utils mbtk_utils_linux mbtk_sdk_ready mbtk_gnssd mbtk_servicesd
 
 # Build test file.
 DIRS += test
diff --git a/mbtk/aboot-tiny/Makefile b/mbtk/aboot-tiny/Makefile
new file mode 100755
index 0000000..5757116
--- /dev/null
+++ b/mbtk/aboot-tiny/Makefile
@@ -0,0 +1,60 @@
+BUILD_ROOT = $(shell pwd)/..
+include $(BUILD_ROOT)/Make.defines
+
+LOCAL_PATH=$(BUILD_ROOT)/aboot-tiny
+
+INC_DIR += \
+		-I$(LOCAL_PATH) \
+		-I$(LOCAL_PATH)/include \
+		-I$(LOCAL_PATH)/jacana
+
+LIB_DIR += -L$(LOCAL_PATH)/files/prebuild
+
+LIBS += -llog -laboot_tiny -lrt
+
+CFLAGS +=
+
+DEFINE += -DNOT_DROPPED_FLAGS_TEST
+
+# -D_FORTIFY_SOURCE=1
+
+#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 = \
+    jacana/jacana_callback.c      \
+	jacana/jacana_clock_posix.c   \
+	jacana/jacana_main.c\
+	jacana/jacana_firmware.c	  \
+	jacana/jacana_firmware_posix.c	  \
+	jacana/jacana_download.c      \
+	jacana/jacana_pvt.c           \
+	jacana/jacana_serialport_posix.c  \
+	jacana/sparse_file.c
+
+OBJS = $(patsubst %.c, %.o, $(patsubst %.cpp, %.o, $(LOCAL_SRC_FILES)))
+$(info OBJS = $(OBJS))
+
+dtarget := $(OUT_DIR)/bin/aboot-tiny
+
+all: $(dtarget)
+
+$(dtarget): $(OBJS)
+	@echo "  BIN     $@"
+	$(CC) $(CFLAGS) $(LIB_DIR) $(OBJS) -o $@ $(LIBS)
+
+%.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/aboot-tiny/files/config/MSLConfig.demo b/mbtk/aboot-tiny/files/config/MSLConfig.demo
new file mode 100755
index 0000000..51cbe99
--- /dev/null
+++ b/mbtk/aboot-tiny/files/config/MSLConfig.demo
@@ -0,0 +1,7 @@
+Version: 2

+Day: 0

+Seed_Host: loc.asrmicro.com

+Seed_Port: 8088

+USR: xx

+PWD: xx

+

diff --git a/mbtk/aboot-tiny/files/config/MSLConfig.txt b/mbtk/aboot-tiny/files/config/MSLConfig.txt
new file mode 100755
index 0000000..3928b08
--- /dev/null
+++ b/mbtk/aboot-tiny/files/config/MSLConfig.txt
@@ -0,0 +1,8 @@
+Version: 2
+Day: 0
+Seed_Host: agnssloc.asrmicro.com
+Seed_Port: 80
+all_in_one: 1
+USR: MobileTek
+PWD: h3Q9YI2441
+
diff --git a/mbtk/aboot-tiny/files/config/jacana_pvt.bin b/mbtk/aboot-tiny/files/config/jacana_pvt.bin
new file mode 100755
index 0000000..c97c12f
--- /dev/null
+++ b/mbtk/aboot-tiny/files/config/jacana_pvt.bin
Binary files differ
diff --git a/mbtk/aboot-tiny/files/prebuild/libaboot_tiny.a b/mbtk/aboot-tiny/files/prebuild/libaboot_tiny.a
new file mode 100755
index 0000000..0615199
--- /dev/null
+++ b/mbtk/aboot-tiny/files/prebuild/libaboot_tiny.a
Binary files differ
diff --git a/mbtk/aboot-tiny/files/release/jacana_fw.bin b/mbtk/aboot-tiny/files/release/jacana_fw.bin
new file mode 100755
index 0000000..8685cc4
--- /dev/null
+++ b/mbtk/aboot-tiny/files/release/jacana_fw.bin
Binary files differ
diff --git a/mbtk/aboot-tiny/include/aboot-tiny.h b/mbtk/aboot-tiny/include/aboot-tiny.h
new file mode 100755
index 0000000..9e9a72c
--- /dev/null
+++ b/mbtk/aboot-tiny/include/aboot-tiny.h
@@ -0,0 +1,242 @@
+/**
+ * @file
+ * @author  Jinhua Huang <jinhuahuang@asrmicro.com>
+ * @version 1.0
+ *
+ * @section LICENSE
+ * Copyright (C) 2020, ASR microelectronics, All rights reserved.
+ *
+ * @section DESCRIPTION
+ *
+ * The time class represents a moment of time.
+ */
+#ifndef ABOOT_TINY_H
+#define ABOOT_TINY_H
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#ifdef __linux__
+#include <sys/select.h>
+#endif
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+#if defined _WIN32 || defined __CYGWIN__ || defined __MINGW32__
+#if defined(BUILD_DLL)
+#define ABOOT_API __declspec(dllexport)
+#elif defined(USE_DLL)
+#define ABOOT_API __declspec(dllimport)
+#else
+#define ABOOT_API
+#endif
+#else
+#define ABOOT_API __attribute__ ((visibility("default")))
+#endif
+
+/**
+ * Callback events to upper layer
+ */
+typedef enum {
+  ABOOT_TINY_EVENT_START,
+  ABOOT_TINY_EVENT_DOWNLOAD,
+  ABOOT_TINY_EVENT_STOP,
+  ABOOT_TINY_EVENT_PROGRESS,
+  ABOOT_TINY_EVENT_STATUS,
+} aboot_tiny_event_t;
+
+/**
+ * Aboot device running status
+ */
+#define ABOOT_TINY_STATUS_CONNECTING  "CONNECTING"
+#define ABOOT_TINY_STATUS_RUNNING     "RUNNING"
+#define ABOOT_TINY_STATUS_FAILED      "FAILED"
+#define ABOOT_TINY_STATUS_SUCCEEDED   "SUCCEEDED"
+
+/**
+ * Aboot command/response size definition
+ */
+#define ABOOT_COMMAND_SZ        128
+#define ABOOT_RESPONSE_SZ       128
+
+/**
+ * Aboot error code definition
+ */
+typedef enum {
+  ABOOT_TINY_ERROR_SUCCESS,
+  ABOOT_TINY_ERROR_PREAMBLE_TIMEOUT,
+  ABOOT_TINY_ERROR_TRANSPORT_INIT_FAILED,
+  ABOOT_TINY_ERROR_LINK_LOST,
+  ABOOT_TINY_ERROR_NO_RESPONSE,
+  ABOOT_TINY_ERROR_HANDLE_GETVAR,
+  ABOOT_TINY_ERROR_HANDLE_DOWNLOAD,
+  ABOOT_TINY_ERROR_READ_DATA,
+  ABOOT_TINY_ERROR_UNEXPECT_RESPONSE,
+  ABOOT_TINY_ERROR_FORCE_STOP,
+} aboot_tiny_error_t;
+
+/**
+ * Callback message to upper layer
+ */
+typedef struct {
+  aboot_tiny_event_t event;
+  aboot_tiny_error_t error;
+  union {
+    const char *message;
+    int progress;
+    const char *status;
+  } u;
+} aboot_tiny_message_t;
+
+/**
+ * Callback function pointer type
+ *
+ * @param msg callback info
+ *
+ */
+typedef void (*aboot_tiny_callback_t)(const aboot_tiny_message_t *msg);
+
+/**
+ * Function poiner type for customization layer interface
+ */
+typedef struct firmware_handle firmware_handle_t;
+typedef int (*aboot_tiny_uart_send_t)(const uint8_t *data, size_t size);
+typedef int (*aboot_tiny_firmware_read_line_t)(firmware_handle_t *handle,
+                                               char *data);
+typedef int (*aboot_tiny_firmware_read_data_t)(firmware_handle_t *handle,
+                                               uint8_t *data, size_t size);
+typedef unsigned (*aboot_tiny_clock_get_t)(void);
+typedef size_t (*aboot_tiny_uart_rx_callback_t)(const uint8_t *data,
+                                                size_t size);
+typedef void *(*aboot_tiny_mem_alloc_t)(size_t size);
+typedef void (*aboot_tiny_mem_free_t)(void *ptr);
+typedef int (*aboot_tiny_log_printf_t)(const char *format, ...);
+typedef int (*aboot_tiny_usleep_t)(unsigned long usec);
+
+/**
+ * Global function pointer should be assign by porting implementation
+ */
+extern aboot_tiny_uart_send_t aboot_tiny_uart_send;
+extern aboot_tiny_firmware_read_line_t aboot_tiny_firmware_read_line;
+extern aboot_tiny_firmware_read_data_t aboot_tiny_firmware_read_data;
+extern aboot_tiny_clock_get_t aboot_tiny_clock_get;
+extern aboot_tiny_mem_alloc_t aboot_tiny_mem_alloc;
+extern aboot_tiny_mem_free_t aboot_tiny_mem_free;
+extern aboot_tiny_log_printf_t aboot_tiny_log_printf;
+extern aboot_tiny_usleep_t aboot_tiny_usleep;
+
+#ifndef WITH_CONTIKI
+
+/**
+ * System main loop needed for non-protothread environment
+ *
+ * @return 0 on success, otherwise failed
+ */
+int ABOOT_API aboot_tiny_main_loop(void);
+
+#ifdef __linux__
+struct select_callback {
+  int (*set_fd)(fd_set *fdr, fd_set *fdw);
+  void (*handle_fd)(fd_set *fdr, fd_set *fdw);
+};
+int aboot_tiny_select_set_callback(int fd, const struct select_callback *callback);
+#endif
+
+#endif /* WITH_CONTIKI */
+
+/**
+ * Initialize base platform environment
+ *
+ * @return 0 on success, otherwise failed
+ */
+int ABOOT_API aboot_tiny_platform_init(void);
+
+/**
+ * Callback for uart rx data ready.
+ *
+ * @param data uart rx data pointer
+ * @param size uart rx data size
+ *
+ * @return data size processed
+ *
+ */
+size_t ABOOT_API aboot_tiny_uart_rx_callback(const uint8_t *data, size_t size);
+
+/**
+ * Get aboot max download size.
+ *
+ * @return max download size
+ *
+ */
+size_t ABOOT_API aboot_tiny_get_max_download_size(void);
+
+/**
+ * Set aboot link lost check timeout.
+ *
+ * @param second link lost check timeout in seconds
+ *
+ */
+void ABOOT_API aboot_tiny_set_link_lost_timeout(int second);
+
+/**
+ * Set aboot command response check timeout.
+ *
+ * @param second command response check timeout in seconds
+ *
+ */
+void ABOOT_API aboot_tiny_set_cmd_response_timeout(int second);
+
+/**
+ * Initialize aboot tiny engine.
+ *
+ * @param cb callback function pointer
+ *
+ * @return 0 on success, otherwise failed
+ */
+int ABOOT_API aboot_tiny_init(aboot_tiny_callback_t cb);
+
+/**
+ * Start aboot download engine.
+ *
+ * @param progress_fixup downloading extra image progress
+ * @param reboot if reboot after completed
+ *
+ * @return 0 on success, otherwise failed
+ */
+int ABOOT_API aboot_tiny_start(firmware_handle_t *firmware,
+                               size_t progress_fixup, bool reboot);
+
+/**
+ * Begin aboot download process
+ *
+ * @return 0 on success, otherwise failed
+ */
+int ABOOT_API aboot_tiny_download(void);
+
+/**
+ * Stop aboot download engine.
+ *
+ * @return 0 on success, otherwise failed
+ */
+int ABOOT_API aboot_tiny_stop(void);
+
+/**
+ * Cleanup aboot tiny engine.
+ */
+int ABOOT_API aboot_tiny_exit(void);
+
+/**
+ * Check if aboot tiny engine is running
+ *
+ * @return true for running, otherwise false
+ */
+bool aboot_tiny_is_running(void);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif /* ABOOT_TINY_H */
diff --git a/mbtk/aboot-tiny/include/sparse_defs.h b/mbtk/aboot-tiny/include/sparse_defs.h
new file mode 100755
index 0000000..b99cfd5
--- /dev/null
+++ b/mbtk/aboot-tiny/include/sparse_defs.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBSPARSE_SPARSE_DEFS_
+#define _LIBSPARSE_SPARSE_DEFS_
+
+#include <errno.h>
+#include <stdio.h>
+
+#define __le64 u64
+#define __le32 u32
+#define __le16 u16
+
+#define __be64 u64
+#define __be32 u32
+#define __be16 u16
+
+#define __u64 u64
+#define __u32 u32
+#define __u16 u16
+#define __u8 u8
+
+typedef unsigned long long u64;
+typedef signed long long s64;
+typedef unsigned int u32;
+typedef unsigned short int u16;
+typedef unsigned char u8;
+
+#define DIV_ROUND_UP(x, y) (((x) + (y) - 1)/(y))
+#define ALIGN(x, y) ((y) * DIV_ROUND_UP((x), (y)))
+#define ALIGN_DOWN(x, y) ((y) * ((x) / (y)))
+
+#define error(fmt, args...) do { fprintf(stderr, "error: %s: " fmt "\n", __func__, ## args); } while (0)
+#define error_errno(s, args...) error(s ": %s", ##args, strerror(errno))
+
+#endif
diff --git a/mbtk/aboot-tiny/include/sparse_format.h b/mbtk/aboot-tiny/include/sparse_format.h
new file mode 100755
index 0000000..779e038
--- /dev/null
+++ b/mbtk/aboot-tiny/include/sparse_format.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBSPARSE_SPARSE_FORMAT_H_
+#define _LIBSPARSE_SPARSE_FORMAT_H_
+#include "sparse_defs.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct sparse_header {
+  __le32	magic;		/* 0xed26ff3a */
+  __le16	major_version;	/* (0x1) - reject images with higher major versions */
+  __le16	minor_version;	/* (0x0) - allow images with higer minor versions */
+  __le16	file_hdr_sz;	/* 28 bytes for first revision of the file format */
+  __le16	chunk_hdr_sz;	/* 12 bytes for first revision of the file format */
+  __le32	blk_sz;		/* block size in bytes, must be a multiple of 4 (4096) */
+  __le32	total_blks;	/* total blocks in the non-sparse output image */
+  __le32	total_chunks;	/* total chunks in the sparse input image */
+  __le32	image_checksum; /* CRC32 checksum of the original data, counting "don't care" */
+				/* as 0. Standard 802.3 polynomial, use a Public Domain */
+				/* table implementation */
+} sparse_header_t;
+
+#define SPARSE_HEADER_MAGIC	0xed26ff3a
+
+#define CHUNK_TYPE_RAW		0xCAC1
+#define CHUNK_TYPE_FILL		0xCAC2
+#define CHUNK_TYPE_DONT_CARE	0xCAC3
+#define CHUNK_TYPE_CRC32    0xCAC4
+
+typedef struct chunk_header {
+  __le16	chunk_type;	/* 0xCAC1 -> raw; 0xCAC2 -> fill; 0xCAC3 -> don't care */
+  __le16	reserved1;
+  __le32	chunk_sz;	/* in blocks in output image */
+  __le32	total_sz;	/* in bytes of chunk input file including chunk header and data */
+} chunk_header_t;
+
+/* Following a Raw or Fill or CRC32 chunk is data.
+ *  For a Raw chunk, it's the data in chunk_sz * blk_sz.
+ *  For a Fill chunk, it's 4 bytes of the fill data.
+ *  For a CRC32 chunk, it's 4 bytes of CRC32
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/mbtk/aboot-tiny/jacana/jacana_callback.c b/mbtk/aboot-tiny/jacana/jacana_callback.c
new file mode 100755
index 0000000..55a839b
--- /dev/null
+++ b/mbtk/aboot-tiny/jacana/jacana_callback.c
@@ -0,0 +1,74 @@
+#include <string.h>
+#include "aboot-tiny.h"
+#include "jacana_callback.h"
+
+/*---------------------------------------------------------------------------*/
+static aboot_tiny_error_t rc;
+/*---------------------------------------------------------------------------*/
+aboot_tiny_error_t
+jacana_aboot_tiny_get_result(void)
+{
+  return rc;
+}
+/*---------------------------------------------------------------------------*/
+void
+jacana_aboot_tiny_set_result(aboot_tiny_error_t r)
+{
+  rc = r;
+}
+/*---------------------------------------------------------------------------*/
+void
+jacana_aboot_tiny_callback(const aboot_tiny_message_t *msg)
+{
+  switch(msg->event) {
+  case ABOOT_TINY_EVENT_START:
+    if(msg->u.message) {
+      aboot_tiny_log_printf("%s\n", msg->u.message);
+    }
+    if(!msg->error) {
+      if(!aboot_tiny_download()) {
+        break;
+      }
+    } else {
+      rc = msg->error;
+      aboot_tiny_stop();
+    }
+    break;
+
+  case ABOOT_TINY_EVENT_DOWNLOAD:
+    if(msg->u.message) {
+      aboot_tiny_log_printf("%s\n", msg->u.message);
+    }
+    if(msg->error) {
+      rc = msg->error;
+      aboot_tiny_stop();
+    }
+    break;
+
+  case ABOOT_TINY_EVENT_STOP:
+    if(msg->u.message) {
+      aboot_tiny_log_printf("%s\n", msg->u.message);
+    }
+    aboot_tiny_exit();
+    break;
+
+  case ABOOT_TINY_EVENT_PROGRESS:
+    aboot_tiny_log_printf("PROGRESS: %d\n", msg->u.progress);
+    break;
+
+  case ABOOT_TINY_EVENT_STATUS:
+    aboot_tiny_log_printf("STATUS: %s\n", msg->u.status);
+    if(msg->error ||
+       !strcmp(msg->u.status, ABOOT_TINY_STATUS_FAILED) ||
+       !strcmp(msg->u.status, ABOOT_TINY_STATUS_SUCCEEDED)) {
+      rc = msg->error;
+      aboot_tiny_stop();
+    }
+    break;
+
+  default:
+    aboot_tiny_log_printf("Unknown event: %d\n", msg->event);
+    break;
+  }
+}
+/*---------------------------------------------------------------------------*/
diff --git a/mbtk/aboot-tiny/jacana/jacana_callback.h b/mbtk/aboot-tiny/jacana/jacana_callback.h
new file mode 100755
index 0000000..5a53b7a
--- /dev/null
+++ b/mbtk/aboot-tiny/jacana/jacana_callback.h
@@ -0,0 +1,20 @@
+#ifndef JACANA_CALLBACK_H
+#define JACANA_CALLBACK_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "aboot-tiny.h"
+
+aboot_tiny_error_t jacana_aboot_tiny_get_result(void);
+void jacana_aboot_tiny_set_result(aboot_tiny_error_t r);
+void jacana_aboot_tiny_callback(const aboot_tiny_message_t *msg);
+
+/*---------------------------------------------------------------------------*/
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* JACANA_CALLBACK_H */
diff --git a/mbtk/aboot-tiny/jacana/jacana_clock.h b/mbtk/aboot-tiny/jacana/jacana_clock.h
new file mode 100755
index 0000000..e26862b
--- /dev/null
+++ b/mbtk/aboot-tiny/jacana/jacana_clock.h
@@ -0,0 +1,15 @@
+#ifndef JACANA_CLOCK_H
+#define JACANA_CLOCK_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int jacana_clock_init(void);
+unsigned jacana_clock_get(void);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* JACANA_CLOCK_H */
diff --git a/mbtk/aboot-tiny/jacana/jacana_clock_posix.c b/mbtk/aboot-tiny/jacana/jacana_clock_posix.c
new file mode 100755
index 0000000..250efc4
--- /dev/null
+++ b/mbtk/aboot-tiny/jacana/jacana_clock_posix.c
@@ -0,0 +1,22 @@
+#include <time.h>
+#include <sys/time.h>
+
+#include "jacana_clock.h"
+
+/*---------------------------------------------------------------------------*/
+int
+jacana_clock_init(void)
+{
+  return 0;
+}
+/*---------------------------------------------------------------------------*/
+unsigned
+jacana_clock_get(void)
+{
+  struct timespec ts;
+
+  clock_gettime(CLOCK_MONOTONIC, &ts);
+
+  return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
+}
+/*---------------------------------------------------------------------------*/
diff --git a/mbtk/aboot-tiny/jacana/jacana_download.c b/mbtk/aboot-tiny/jacana/jacana_download.c
new file mode 100755
index 0000000..8248c5d
--- /dev/null
+++ b/mbtk/aboot-tiny/jacana/jacana_download.c
@@ -0,0 +1,182 @@
+#include <string.h>
+#include <time.h>
+#include <signal.h>
+#include "aboot-tiny.h"
+#include "jacana_firmware.h"
+#include "jacana_pvt.h"
+#include "jacana_callback.h"
+#include "jacana_serialport.h"
+#include "jacana_clock.h"
+#include "jacana_mem.h"
+#include "jacana_log.h"
+#include "jacana_usleep.h"
+#include "jacana_download.h"
+
+#define   GNSS_MODULE_FW_DOWNLOAD_TIMER_VALUE     5
+static    timer_t  gnss_module_fw_download_timer_id;
+
+/*---------------------------------------------------------------------------*/
+int jacana_aboot_tiny_force_stop(void)
+{
+  jacana_aboot_tiny_set_result(ABOOT_TINY_ERROR_FORCE_STOP);
+  return aboot_tiny_stop();
+}
+/*---------------------------------------------------------------------------*/
+
+/*  Safety  Timer For FW Downloading...*/
+static int gnss_module_fw_download_timer_start(void)
+{
+    int                rc = 0;
+    struct itimerspec  ts;
+
+    memset (&ts, 0x00, sizeof (struct itimerspec));
+    ts.it_value.tv_sec = GNSS_MODULE_FW_DOWNLOAD_TIMER_VALUE;
+    ts.it_value.tv_nsec = 0;
+    ts.it_interval.tv_sec = GNSS_MODULE_FW_DOWNLOAD_TIMER_VALUE;          
+    ts.it_interval.tv_nsec = 0;
+    rc = timer_settime(gnss_module_fw_download_timer_id, 0, &ts, NULL);
+	jacana_log_printf("gnss download timer start.");
+    return rc;
+}
+
+
+static int gnss_module_fw_download_timer_stop(void)
+{
+    int rc = 0;
+    struct   itimerspec  ts;
+
+    memset (&ts, 0x00, sizeof (struct itimerspec));
+    ts.it_value.tv_sec = 0;
+    ts.it_value.tv_nsec = 0;
+    ts.it_interval.tv_sec = 0;          
+    ts.it_interval.tv_nsec = 0;
+    rc = timer_settime(gnss_module_fw_download_timer_id, 0, &ts, NULL);
+	jacana_log_printf("gnss download timer stop.");
+    return rc;
+}
+
+
+static void gps_state_fw_download(__attribute__( (unused)) union sigval sv)
+{
+    if(aboot_tiny_is_running())
+    {
+        jacana_aboot_tiny_force_stop();
+    }
+	
+	jacana_log_printf("gnss download timer timeout.");
+}
+
+static int gnss_module_fw_download_timer_init(void)
+{
+    int rc = 0;
+    struct   sigevent    sigev;
+
+    memset (&sigev, 0, sizeof (struct sigevent));
+    sigev.sigev_value.sival_ptr = &gnss_module_fw_download_timer_id;
+    sigev.sigev_notify = SIGEV_THREAD;
+    sigev.sigev_notify_attributes = NULL;
+    sigev.sigev_notify_function = gps_state_fw_download;
+    rc = timer_create(CLOCK_REALTIME, &sigev, &gnss_module_fw_download_timer_id);
+	jacana_log_printf("gnss download timer init.");
+    return rc;
+}
+
+/*---------------------------------------------------------------------------*/
+int
+jacana_aboot_tiny_download(const char *dev, int baud)
+{
+  /* Should be called at very beginning */
+  aboot_tiny_init(jacana_aboot_tiny_callback);
+
+  /* WDT Timer is created here. */  
+  gnss_module_fw_download_timer_init();
+
+  /* Set initial result as success */
+  jacana_aboot_tiny_set_result(ABOOT_TINY_ERROR_SUCCESS);
+
+  /* Log printf */
+  aboot_tiny_log_printf = jacana_log_printf;
+
+  /* Memory alloc/free */
+  aboot_tiny_mem_alloc = jacana_mem_alloc;
+  aboot_tiny_mem_free = jacana_mem_free;
+  aboot_tiny_usleep = jacana_delay_usleep;
+
+  /* Jacana clock init */
+  jacana_clock_init();
+  aboot_tiny_clock_get = jacana_clock_get;
+
+  /* System platform init */
+  aboot_tiny_platform_init();
+
+  /* Jacana uart init */
+  if(jacana_serialport_init(dev, baud, aboot_tiny_uart_rx_callback) < 0) {
+    aboot_tiny_log_printf("error: open serial port failed\n");
+    return -1;
+  }
+  aboot_tiny_uart_send = jacana_serialport_write;
+
+  /* Get progress fixup size */
+  void *priv = jacana_pvt_raw_open();
+  if(!priv) {
+    aboot_tiny_log_printf("error: open pvt partition failed\n");
+    return -1;
+  }
+  size_t pvt_size = jacana_pvt_raw_get_total_size(priv);
+  size_t progress_fixup = ((pvt_size + SPARSE_BLOCK_SZ - 1)
+                           & ~(SPARSE_BLOCK_SZ - 1)) * 2;
+  jacana_pvt_raw_close(priv);
+
+  /* Jacana firmware interface init */
+  firmware_handle_t firmware;
+  pvt_info_t pvt_info;
+  firmware.pvt_info = &pvt_info;
+  priv = jacana_firmware_raw_open();
+  if(!priv) {
+    aboot_tiny_log_printf("error: open jacana firmware partition failed\n");
+    return -1;
+  }
+  if(jacana_firmware_open(&firmware, priv) < 0) {
+    jacana_firmware_raw_close(priv);
+    aboot_tiny_log_printf("error: open jacana firmware failed\n");
+    return -1;
+  }
+  aboot_tiny_firmware_read_line = jacana_firmware_read_line;
+  aboot_tiny_firmware_read_data = jacana_firmware_read_data;
+
+  gnss_module_fw_download_timer_start();
+  /* Start download engine */
+  if(aboot_tiny_start(&firmware, progress_fixup, true) < 0) {
+    jacana_firmware_raw_close(priv);
+    jacana_firmware_close(&firmware);
+    aboot_tiny_log_printf("error: start aboot engine failed\n");
+    return -1;
+  }
+
+  /* customize timeout parameter in second */
+  aboot_tiny_set_link_lost_timeout(3);
+  aboot_tiny_set_cmd_response_timeout(2);
+
+  /* Enter main event loops */
+  aboot_tiny_main_loop();
+
+  /* Close serialport device */
+  jacana_serialport_exit();
+
+  gnss_module_fw_download_timer_stop();
+  /**
+   * firmware already closed when switch to pvt download,
+   * so we need close pvt here
+   */
+  priv = firmware.priv;
+  jacana_pvt_close(&firmware);
+  jacana_pvt_raw_close(priv);
+
+  int rc = (int)jacana_aboot_tiny_get_result();
+  if(rc) {
+    aboot_tiny_log_printf("Jacana download failed with error code %d.\n", rc);
+  }
+
+  return -rc;
+}
+/*---------------------------------------------------------------------------*/
diff --git a/mbtk/aboot-tiny/jacana/jacana_download.h b/mbtk/aboot-tiny/jacana/jacana_download.h
new file mode 100755
index 0000000..886cb85
--- /dev/null
+++ b/mbtk/aboot-tiny/jacana/jacana_download.h
@@ -0,0 +1,42 @@
+/**
+ * @file
+ * @author  Jinhua Huang <jinhuahuang@asrmicro.com>
+ * @version 1.0
+ *
+ * @section LICENSE
+ * Copyright (C) 2021, ASR microelectronics, All rights reserved.
+ *
+ * @section DESCRIPTION
+ * Aboot tiny downloader for Jacana with firmware and pvt
+ *
+ */
+#ifndef __JACANA_DOWNLOAD_H__
+#define __JACANA_DOWNLOAD_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Aboot tiny downloader for jacana
+ *
+ * @param dev uart device name
+ * @param baud uart baud rate
+ *
+ * @return 0 for success, otherwise failed
+ *
+ */
+int jacana_aboot_tiny_download(const char *dev, int baud);
+
+/**
+ * Aboot tiny force stop for jacana
+ *
+ * @return 0 for success, otherwise failed
+ */
+int jacana_aboot_tiny_force_stop(void);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* __JACANA_DOWNLOAD_H__ */
diff --git a/mbtk/aboot-tiny/jacana/jacana_firmware.c b/mbtk/aboot-tiny/jacana/jacana_firmware.c
new file mode 100755
index 0000000..c65465b
--- /dev/null
+++ b/mbtk/aboot-tiny/jacana/jacana_firmware.c
@@ -0,0 +1,163 @@
+#include <stdio.h>
+#include <inttypes.h>
+#include <string.h>
+
+#include "aboot-tiny.h"
+#include "jacana_firmware.h"
+#include "jacana_pvt.h"
+
+/*---------------------------------------------------------------------------*/
+static int
+firmware_read_next_to_cache(firmware_handle_t *firmware)
+{
+  size_t size = firmware->end_ptr - firmware->read_ptr;
+  memmove(firmware->read_ptr - firmware->read_sz, firmware->read_ptr, size);
+  firmware->read_ptr -= firmware->read_sz;
+  firmware->end_ptr -= firmware->read_sz;
+  size_t remainder = firmware->end - firmware->start;
+  if(remainder > 0) {
+    size_t size = remainder > firmware->read_sz ? firmware->read_sz : remainder;
+    int len = jacana_firmware_raw_read(firmware->priv, firmware->start,
+                                       (uint8_t *)firmware->middle_ptr, size);
+    if(len < 0) {
+      return -1;
+    }
+    firmware->start += len;
+    firmware->end_ptr += len;
+  }
+
+  return 0;
+}
+/*---------------------------------------------------------------------------*/
+int
+jacana_firmware_open(firmware_handle_t *firmware, void *priv)
+{
+  firmware->priv = priv;
+  firmware->start = 0;
+  firmware->end = jacana_firmware_raw_get_total_size(priv);
+
+  firmware->data = aboot_tiny_mem_alloc(MAX_READ_CACHE_SZ * 2);
+  if(!firmware->data) {
+    aboot_tiny_log_printf("Failed: can not alloc data buf\n");
+    return -1;
+  }
+
+  firmware->read_sz = MAX_READ_CACHE_SZ;
+  firmware->middle_ptr = (char *)firmware->data + firmware->read_sz;
+  firmware->end_ptr = firmware->middle_ptr + firmware->read_sz;
+  firmware->read_ptr = firmware->end_ptr;
+  if(firmware_read_next_to_cache(firmware) < 0) {
+    goto failed;
+  }
+
+  char cmd_line[ABOOT_COMMAND_SZ];
+  char magic[9];
+  uint32_t size;
+  int num;
+
+  if(jacana_firmware_read_line(firmware, cmd_line) <= 0) {
+    aboot_tiny_log_printf("Failed: can not read a cmd line\n");
+    goto failed;
+  }
+  num = sscanf(cmd_line, "%08s%" SCNx32, magic, &size);
+  if(num != 2 || size > firmware->end) {
+    aboot_tiny_log_printf("Failed: cmd line format error\n");
+    goto failed;
+  }
+  firmware->end = (size_t)size;
+  if(strcmp(magic, "!JACANA!")) {
+    aboot_tiny_log_printf("Magic error: not a valid firmware\n");
+    goto failed;
+  }
+
+  return 0;
+
+failed:
+  aboot_tiny_mem_free(firmware->data);
+  firmware->data = NULL;
+  return -1;
+}
+/*---------------------------------------------------------------------------*/
+int
+jacana_firmware_read_line(firmware_handle_t *firmware, char *line)
+{
+  while(1) {
+    if(firmware->read_ptr < firmware->middle_ptr) {
+      const char *p = (const char *)strchr(firmware->read_ptr, '\n');
+      if(!p || p >= firmware->end_ptr) {
+        return -1;
+      }
+      int len = p - firmware->read_ptr + 1;
+      memcpy(line, firmware->read_ptr, len);
+      line[len] = '\0';
+      firmware->read_ptr += len;
+      if(firmware->read_ptr == firmware->end_ptr) {
+        /* firmware download finished, prepare for continue with pvt */
+        void *priv = jacana_pvt_raw_open();
+        if(!priv) {
+          return -1;
+        }
+        jacana_firmware_raw_close(firmware->priv);
+        jacana_firmware_close(firmware);
+        if(jacana_pvt_open(firmware, priv) < 0) {
+          return -1;
+        }
+        aboot_tiny_firmware_read_line = jacana_pvt_read_line;
+        aboot_tiny_firmware_read_data = jacana_pvt_read_data;
+      }
+      if(line[0] == '\n') {
+        continue;
+      } else {
+        line[len - 1] = '\0'; /* replace '\n' to '\0' */
+        return len - 1;
+      }
+    } else {
+      /* read_ptr >= middle_ptr */
+      if(firmware_read_next_to_cache(firmware) < 0) {
+        return -1;
+      }
+    }
+  }
+}
+/*---------------------------------------------------------------------------*/
+int
+jacana_firmware_read_data(firmware_handle_t *firmware, uint8_t *data, size_t size)
+{
+  while(1) {
+    if(firmware->read_ptr == firmware->end_ptr) {
+      return 0;
+    }
+
+    if(firmware->read_ptr < firmware->middle_ptr) {
+      size_t remainder = firmware->middle_ptr - firmware->read_ptr;
+      if(remainder < size) {
+        size = remainder;
+      }
+      memcpy(data, firmware->read_ptr, size);
+      firmware->read_ptr += size;
+      return size;
+    } else {
+      /* read_ptr >= middle_ptr */
+      if(firmware_read_next_to_cache(firmware) < 0) {
+        return -1;
+      }
+    }
+  }
+}
+/*---------------------------------------------------------------------------*/
+void
+jacana_firmware_close(firmware_handle_t *firmware)
+{
+  firmware->priv = NULL;
+  firmware->start = 0;
+  firmware->end = 0;
+  if(firmware->data) {
+    aboot_tiny_mem_free(firmware->data);
+    firmware->data = NULL;
+  }
+  firmware->read_sz = 0;
+  firmware->read_ptr = NULL;
+  firmware->middle_ptr = NULL;
+  firmware->end_ptr = NULL;
+}
+/*---------------------------------------------------------------------------*/
diff --git a/mbtk/aboot-tiny/jacana/jacana_firmware.h b/mbtk/aboot-tiny/jacana/jacana_firmware.h
new file mode 100755
index 0000000..0c14dcc
--- /dev/null
+++ b/mbtk/aboot-tiny/jacana/jacana_firmware.h
@@ -0,0 +1,41 @@
+#ifndef JACANA_FIRMWARE_H
+#define JACANA_FIRMWARE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MAX_READ_CACHE_SZ 	4096
+
+/*---------------------------------------------------------------------------*/
+typedef struct firmware_handle {
+  void *priv;
+  size_t start;
+  size_t end;
+  void *data;
+  size_t read_sz;
+  char *read_ptr;
+  char *middle_ptr;
+  char *end_ptr;
+  void *pvt_info;
+} firmware_handle_t;
+/*---------------------------------------------------------------------------*/
+/* portable layer api */
+void *jacana_firmware_raw_open(void);
+size_t jacana_firmware_raw_get_total_size(void *priv);
+int jacana_firmware_raw_read(void *priv, size_t start,
+                             uint8_t *data, size_t size);
+void jacana_firmware_raw_close(void *priv);
+/*---------------------------------------------------------------------------*/
+int jacana_firmware_open(firmware_handle_t *firmware, void *priv);
+int jacana_firmware_read_line(firmware_handle_t *firmware, char *line);
+int jacana_firmware_read_data(firmware_handle_t *firmware,
+                              uint8_t *data, size_t size);
+void jacana_firmware_close(firmware_handle_t *firmware);
+/*---------------------------------------------------------------------------*/
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* JACANA_FIRMWARE_H */
diff --git a/mbtk/aboot-tiny/jacana/jacana_firmware_posix.c b/mbtk/aboot-tiny/jacana/jacana_firmware_posix.c
new file mode 100755
index 0000000..108b199
--- /dev/null
+++ b/mbtk/aboot-tiny/jacana/jacana_firmware_posix.c
@@ -0,0 +1,75 @@
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/stat.h>
+
+#include "aboot-tiny.h"
+#include "jacana_firmware.h"
+#include "jacana_pvt.h"
+
+#ifdef PATH_MAX
+#undef PATH_MAX
+#define PATH_MAX    127
+#else
+#define PATH_MAX    127
+#endif
+
+extern char jacana_firmware_file_name[PATH_MAX];
+extern char jacana_pvt_file_name[PATH_MAX];
+/*---------------------------------------------------------------------------*/
+void *
+jacana_firmware_raw_open(void)
+{
+  FILE *file = fopen(jacana_firmware_file_name, "r");
+  return (void *)file;
+}
+/*---------------------------------------------------------------------------*/
+size_t
+jacana_firmware_raw_get_total_size(void *priv)
+{
+  int fd = fileno((FILE *)priv);
+  struct stat buf;
+  fstat(fd, &buf);
+  off_t size = buf.st_size;
+  return size;
+}
+/*---------------------------------------------------------------------------*/
+int
+jacana_firmware_raw_read(void *priv, size_t start, uint8_t *data, size_t size)
+{
+  FILE *file = (FILE *)priv;
+  fseek(file, start, SEEK_SET);
+  return fread(data, 1, size, file);
+}
+/*---------------------------------------------------------------------------*/
+void
+jacana_firmware_raw_close(void *priv)
+{
+  fclose((FILE *)priv);
+}
+/*---------------------------------------------------------------------------*/
+void *
+jacana_pvt_raw_open(void)
+{
+  FILE *file = fopen(jacana_pvt_file_name, "r");
+  return (void *)file;
+}
+/*---------------------------------------------------------------------------*/
+size_t
+jacana_pvt_raw_get_total_size(void *priv)
+{
+  return jacana_firmware_raw_get_total_size(priv);
+}
+/*---------------------------------------------------------------------------*/
+int
+jacana_pvt_raw_read(void *priv, size_t start, uint8_t *data, size_t size)
+{
+  return jacana_firmware_raw_read(priv, start, data, size);
+}
+/*---------------------------------------------------------------------------*/
+void
+jacana_pvt_raw_close(void *priv)
+{
+  jacana_firmware_raw_close(priv);
+}
+/*---------------------------------------------------------------------------*/
diff --git a/mbtk/aboot-tiny/jacana/jacana_log.h b/mbtk/aboot-tiny/jacana/jacana_log.h
new file mode 100755
index 0000000..6920ea8
--- /dev/null
+++ b/mbtk/aboot-tiny/jacana/jacana_log.h
@@ -0,0 +1,27 @@
+/**
+ * @file
+ * @author  Jinhua Huang <jinhuahuang@asrmicro.com>
+ * @version 1.0
+ *
+ * @section LICENSE
+ * Copyright (C) 2021, ASR microelectronics, All rights reserved.
+ *
+ * @section DESCRIPTION
+ * Aboot tiny downloader for Jacana with firmware and pvt
+ *
+ */
+#ifndef __JACANA_LOG_H__
+#define __JACANA_LOG_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int
+jacana_log_printf(const char *format, ...);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* __JACANA_LOG_H__ */
diff --git a/mbtk/aboot-tiny/jacana/jacana_main.c b/mbtk/aboot-tiny/jacana/jacana_main.c
new file mode 100755
index 0000000..96b4c53
--- /dev/null
+++ b/mbtk/aboot-tiny/jacana/jacana_main.c
@@ -0,0 +1,250 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <time.h>
+#include <fcntl.h>
+#include <include/log.h>
+
+#include "jacana_download.h"
+#include "jacana_mem.h"
+#include "jacana_log.h"
+#include "jacana_usleep.h"
+
+#undef LOG_TAG
+#define LOG_TAG	"ABoot"
+
+#ifdef PATH_MAX
+#undef PATH_MAX
+#define PATH_MAX    127
+#else
+#define PATH_MAX    127
+#endif
+/*---------------------------------------------------------------------------*/
+char jacana_firmware_file_name[PATH_MAX];
+char jacana_pvt_file_name[PATH_MAX];
+#define ABOOT_PM_NAME	  "aboot.pm"
+#define ABOOT_CPU_FREQ  "aboot 1248000"
+#define ABOOT_CPU       "aboot"
+
+char gpioCtrlPath[128] = "/sys/devices/platform/asr-gps/ctrl";
+
+
+static void dubheWrite(char* str)
+{
+    int fd;
+    fd = open((const char*)gpioCtrlPath, O_WRONLY);
+
+    if (fd >= 0)    {
+        write(fd, str, strlen(str));
+        close(fd);
+    }
+}
+
+
+
+static int gpsHalReset()
+{
+	dubheWrite("reset 0");
+    jacana_delay_msleep(100);
+    dubheWrite("reset 1");
+    jacana_delay_msleep(100);
+    return 0;
+}
+
+static int gpsHalFWDone()
+{
+    dubheWrite("on");  
+    return 0;
+}
+
+static int gpsHalFWFail()
+{
+    dubheWrite("off");  
+    return 0;
+}
+/*---------------------------------------------------------------------------*/
+void *
+jacana_mem_alloc(size_t size)
+{
+  void *ptr;  
+  ptr = malloc(size);
+  return ptr;
+}
+/*---------------------------------------------------------------------------*/
+void
+jacana_mem_free(void *ptr)
+{
+  free(ptr);
+}
+/*---------------------------------------------------------------------------*/
+extern int slogFd;
+int
+jacana_log_printf(const char *format, ...)
+{
+  int write_length = 0;
+  va_list args;
+  char buf[1024];
+  va_start(args, format);
+  write_length = vsnprintf(buf, 1024, format, args);
+  va_end(args);
+
+  __android_log_printf(LOG_ID_RADIO, LOG_ERR_LEVEL, "%s", buf);
+
+  return write_length;
+}
+
+/*---------------------------------------------------------------------------*/
+int
+jacana_delay_usleep(unsigned long useconds)
+{
+
+  (void)useconds;
+  jacana_log_printf("jacana_delay_usleep: %d\n", useconds);
+
+  return 0;
+}
+
+
+/*---------------------------------------------------------------------------*/
+int
+jacana_delay_msleep(unsigned long mseconds)
+{
+  struct timespec wait;
+  wait.tv_sec = mseconds / 1000;
+  wait.tv_nsec = (mseconds % 1000) * 1000 * 1000;
+  nanosleep(&wait, NULL);
+
+  return 0;
+}
+
+/*---------------------------------------------------------------------------*/
+static int
+gnss_module_lock( void )
+{
+    FILE *flk;
+    int ret = -1;
+
+    flk = fopen("/sys/power/wake_lock", "w");
+    if(flk) {
+        fprintf(flk, ABOOT_PM_NAME);
+        ret = fclose(flk);
+    }
+
+    flk = fopen("/sys/power/cpu_freq_min_pm_qos", "w");
+    if(flk) {
+        fprintf(flk, ABOOT_CPU_FREQ);
+        ret = fclose(flk);
+    }
+    return ret;
+}
+
+/*---------------------------------------------------------------------------*/
+static int
+gnss_module_unlock( void )
+{
+    FILE *flk;
+    int ret = -1;
+
+    flk = fopen("/sys/power/wake_unlock", "w");
+    if(flk) {
+        fprintf(flk, ABOOT_PM_NAME);
+        ret = fclose(flk);
+    }
+
+    flk = fopen("/sys/power/cpu_freq_min_pm_unqos", "w");
+    if(flk) {
+        fprintf(flk, ABOOT_CPU);
+        ret = fclose(flk);
+    }
+    return ret;
+}
+
+/*---------------------------------------------------------------------------*/
+static void
+usage(const char *prog)
+{
+  fprintf(stderr, "usage:  %s [options]\n", prog);
+  fprintf(stderr, "example: jacana_download -B 115200 -s ttyUSB1 -F firmware.bin -P pvt.bin\n");
+  fprintf(stderr, "Options are:\n");
+  fprintf(stderr, " -B baudrate    9600,19200,38400,57600,115200,921600 (default 115200)\n");
+  fprintf(stderr, " -s siodev      Serial device (e.x. /dev/ttyUSB0)\n");
+  fprintf(stderr, " -F firmware    Firmware file name\n");
+  fprintf(stderr, " -P pvt         PVT file name\n");
+}
+/*---------------------------------------------------------------------------*/
+int
+main(int argc, char *argv[])
+{
+  const char *prog;
+  char serial_dev[32];
+  int baudrate = 115200;
+  int c;
+  int ret = 0;
+//  int i = 0;
+
+  /*LOGCAT showing with ACAT Tool */
+  set_service_log_tag(LOG_TAG);
+
+    prog = argv[0];
+    while((c = getopt(argc, argv, "B:s:F:P:h?")) != -1) {
+    switch(c) {
+    case 'B':
+      baudrate = atoi(optarg);
+      break;
+
+    case 's':
+      if(strncmp("/dev/", optarg, 5) == 0) {
+        strcpy(serial_dev, optarg);
+      } else {
+        sprintf(serial_dev, "/dev/%s", optarg);
+      }
+      break;
+
+    case 'F':
+      strcpy(jacana_firmware_file_name, optarg);
+      break;
+
+    case 'P':
+      strcpy(jacana_pvt_file_name, optarg);
+      break;
+
+    case '?':
+    case 'h':
+    default:
+      usage(prog);
+      exit(1);
+      break;
+    }
+  }
+
+  if(!strlen(serial_dev) || !strlen(jacana_firmware_file_name)
+     || !strlen(jacana_pvt_file_name)) {
+    usage(prog);
+    exit(1);
+  }
+
+//for( i=0; i<3; i++)   {
+    /* Reset Jacana Module..*/
+    gpsHalReset();
+
+    /* Disable PM. */
+    gnss_module_lock();
+    ret = jacana_aboot_tiny_download(serial_dev, baudrate);
+    gnss_module_unlock();
+
+//    if (!ret)
+//      break;
+//  }
+
+  /* Mark Done. */
+  if (!ret)  
+    gpsHalFWDone();
+  else
+    gpsHalFWFail();
+  
+  return ret;
+}
diff --git a/mbtk/aboot-tiny/jacana/jacana_mem.h b/mbtk/aboot-tiny/jacana/jacana_mem.h
new file mode 100755
index 0000000..8383a22
--- /dev/null
+++ b/mbtk/aboot-tiny/jacana/jacana_mem.h
@@ -0,0 +1,29 @@
+/**
+ * @file
+ * @author  Jinhua Huang <jinhuahuang@asrmicro.com>
+ * @version 1.0
+ *
+ * @section LICENSE
+ * Copyright (C) 2021, ASR microelectronics, All rights reserved.
+ *
+ * @section DESCRIPTION
+ * Aboot tiny downloader for Jacana with firmware and pvt
+ *
+ */
+#ifndef __JACANA_MEM_H__
+#define __JACANA_MEM_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+
+void *jacana_mem_alloc(size_t size);
+void jacana_mem_free(void *ptr);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* __JACANA_MEM_H__ */
diff --git a/mbtk/aboot-tiny/jacana/jacana_pvt.c b/mbtk/aboot-tiny/jacana/jacana_pvt.c
new file mode 100755
index 0000000..166128f
--- /dev/null
+++ b/mbtk/aboot-tiny/jacana/jacana_pvt.c
@@ -0,0 +1,246 @@
+#include <string.h>
+
+#include "aboot-tiny.h"
+#include "jacana_pvt.h"
+
+/*---------------------------------------------------------------------------*/
+static const char *jacana_pvt_cmd_resp[] = {
+  "getvar:max-download-size", /* 0 */
+  "OKAY\t%08x",               /* 1 */
+  "download:%08x",            /* 2 */
+  "DATA%08x",                 /* 3 */
+  "OKAY",                     /* 4 */
+  "flash:pvt",                /* 5 */
+  "OKAY"                      /* 6 */
+};
+/*---------------------------------------------------------------------------*/
+static int
+pvt_read_next_to_cache(firmware_handle_t *firmware)
+{
+  size_t size = firmware->end_ptr - firmware->read_ptr;
+  memmove(firmware->read_ptr - firmware->read_sz, firmware->read_ptr, size);
+  firmware->read_ptr -= firmware->read_sz;
+  firmware->end_ptr -= firmware->read_sz;
+  size_t remainder = firmware->end - firmware->start;
+  if(remainder > 0) {
+    size_t size = remainder > firmware->read_sz ? firmware->read_sz : remainder;
+    int len = jacana_pvt_raw_read(firmware->priv, firmware->start,
+                                  (uint8_t *)firmware->middle_ptr, size);
+    if(len < 0) {
+      return -1;
+    }
+    firmware->start += len;
+    firmware->end_ptr += len;
+  }
+
+  return 0;
+}
+/*---------------------------------------------------------------------------*/
+int
+jacana_pvt_open(firmware_handle_t *firmware, void *priv)
+{
+  firmware->priv = priv;
+  firmware->start = 0;
+  firmware->end = jacana_pvt_raw_get_total_size(priv);
+
+  pvt_info_t *pvt_info = (pvt_info_t *)firmware->pvt_info;
+  pvt_info->max_download_size = aboot_tiny_get_max_download_size();
+  if(pvt_info->max_download_size > 8192) {
+   pvt_info->max_download_size = 8192; 
+  }
+  pvt_info->offset = 0;
+  pvt_info->state = 0;
+
+  firmware->data = aboot_tiny_mem_alloc(MAX_READ_CACHE_SZ * 2);
+  if(!firmware->data) {
+    aboot_tiny_log_printf("Failed: can not alloc data buf\n");
+    return -1;
+  }
+
+  firmware->read_sz = MAX_READ_CACHE_SZ;
+  firmware->middle_ptr = (char *)firmware->data + firmware->read_sz;
+  firmware->end_ptr = firmware->middle_ptr + firmware->read_sz;
+  firmware->read_ptr = firmware->end_ptr;
+  if(pvt_read_next_to_cache(firmware) < 0) {
+    aboot_tiny_mem_free(firmware->data);
+    firmware->data = NULL;
+    return -1;
+  }
+
+  sparse_file_t *sparse_file = &pvt_info->sparse_file;
+  size_t remainder = firmware->end - firmware->start;
+  size_t size = (pvt_info->max_download_size
+                 - SPARSE_FILE_HEADER_SIZE
+                 - SPARSE_FILE_FOOTER_SIZE) & ~(SPARSE_BLOCK_SZ - 1);
+  if(remainder < size) {
+    size = remainder;
+  }
+
+  sparse_file_new(sparse_file, pvt_info->offset, size, firmware->end);
+  pvt_info->offset += size;
+
+  return 0;
+}
+/*---------------------------------------------------------------------------*/
+int
+jacana_pvt_read_line(firmware_handle_t *firmware, char *line)
+{
+  pvt_info_t *pvt_info = (pvt_info_t *)firmware->pvt_info;
+  sparse_file_t *sparse_file = &pvt_info->sparse_file;
+
+  switch(pvt_info->state) {
+  case 0:
+  case 4:
+  case 5:
+  case 6:
+    strcpy(line,jacana_pvt_cmd_resp[pvt_info->state]);
+    break;
+
+  case 1:
+    sprintf(line, jacana_pvt_cmd_resp[pvt_info->state],
+            pvt_info->max_download_size);
+    break;
+  case 2:
+  case 3:
+    sprintf(line, jacana_pvt_cmd_resp[pvt_info->state],
+            sparse_file->header_size
+            + sparse_file->data_size
+            + sparse_file->fill_size
+            + sparse_file->footer_size);
+    break;
+
+  case 7: /* pvt command sequence finished */
+    if(firmware->start < firmware->end) {
+      pvt_info->state = 0;
+
+      size_t remainder = firmware->end - firmware->start;
+      size_t size = (pvt_info->max_download_size
+                     - SPARSE_FILE_HEADER_SIZE
+                     - SPARSE_FILE_FOOTER_SIZE) & ~(SPARSE_BLOCK_SZ - 1);
+      if(remainder < size) {
+        size = remainder;
+      }
+      sparse_file_new(sparse_file, pvt_info->offset, size, firmware->end);
+      pvt_info->offset += size;
+      return jacana_pvt_read_line(firmware, line);
+    } else {
+      if(pvt_info->offset < firmware->end) {
+        /* remainder data in cache */
+        pvt_info->state = 0;
+        size_t size = firmware->end - pvt_info->offset;
+        sparse_file_new(sparse_file, pvt_info->offset, size, firmware->end);
+        pvt_info->offset += size;
+        return jacana_pvt_read_line(firmware, line);
+      } else {
+      /* all finished */
+        return 0;
+      }
+    }
+
+  default:
+    return -1;
+  }
+  pvt_info->state++;
+
+  return strlen(line);
+}
+/*---------------------------------------------------------------------------*/
+int
+jacana_pvt_read_data(firmware_handle_t *firmware, uint8_t *data, size_t size)
+{
+  pvt_info_t *pvt_info = (pvt_info_t *)firmware->pvt_info;
+  sparse_file_t *sparse_file = &pvt_info->sparse_file;
+
+  if(sparse_file->header_size) {
+    if(sparse_file->header_size > size) {
+      /* should not happen */
+      return -1;
+    } else {
+      size = sparse_file->header_size;
+      memcpy(data, sparse_file->header, size);
+      sparse_file->header_size = 0;
+      return size;
+    }
+  }
+
+  if(sparse_file->footer_size == 0) {
+    /* should not happen */
+    return -1;
+  }
+
+  if(sparse_file->data_size == 0) {
+    if(sparse_file->fill_size) {
+      if(size > sparse_file->fill_size) {
+        size = sparse_file->fill_size;
+      }
+      memset(data, 0, size);
+      sparse_file->fill_size -= size;
+      return size;
+    } else if(sparse_file->footer_size > size) {
+      /* should not happen */
+      return -1;
+    } else {
+      /* footer_size <= size */
+      size = sparse_file->footer_size;
+      memcpy(data, sparse_file->footer, size);
+      sparse_file->footer_size = 0;
+      return size;
+    }
+  }
+
+  if(sparse_file->data_size < size) {
+    size = sparse_file->data_size;
+  }
+
+  while(1) {
+    if(firmware->read_ptr == firmware->end_ptr) {
+      /* should not happen */
+      return -1;
+    }
+    if(firmware->read_ptr < firmware->middle_ptr) {
+      size_t remainder = firmware->middle_ptr - firmware->read_ptr;
+      if(remainder < size) {
+        size = remainder;
+      }
+      memcpy(data, firmware->read_ptr, size);
+      firmware->read_ptr += size;
+      break;
+    } else {
+      /* read_ptr >= middle_ptr */
+      if(pvt_read_next_to_cache(firmware) < 0) {
+        return -1;
+      }
+    }
+  }
+
+  sparse_file->data_size -= size;
+
+  return size;
+}
+/*---------------------------------------------------------------------------*/
+void
+jacana_pvt_close(firmware_handle_t *firmware)
+{
+  firmware->priv = NULL;
+  firmware->start = 0;
+  firmware->end = 0;
+
+  if(firmware->data) {
+    aboot_tiny_mem_free(firmware->data);
+    firmware->data = NULL;
+  }
+  firmware->read_sz = 0;
+  firmware->read_ptr = NULL;
+  firmware->middle_ptr = NULL;
+  firmware->end_ptr = NULL;
+
+  pvt_info_t *pvt_info = (pvt_info_t *)firmware->pvt_info;
+  pvt_info->max_download_size = 0;
+  pvt_info->state = 0;
+
+  sparse_file_t *sparse_file = &pvt_info->sparse_file;
+  memset(sparse_file->header, 0, SPARSE_FILE_HEADER_SIZE);
+  sparse_file->header_size = 0;
+  sparse_file->data_size = 0;
+}
+/*---------------------------------------------------------------------------*/
diff --git a/mbtk/aboot-tiny/jacana/jacana_pvt.h b/mbtk/aboot-tiny/jacana/jacana_pvt.h
new file mode 100755
index 0000000..2bc8ff4
--- /dev/null
+++ b/mbtk/aboot-tiny/jacana/jacana_pvt.h
@@ -0,0 +1,37 @@
+#ifndef JACANA_PVT_H
+#define JACANA_PVT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "jacana_firmware.h"
+#include "sparse_file.h"
+
+/*---------------------------------------------------------------------------*/
+typedef struct {
+  sparse_file_t sparse_file;
+  size_t max_download_size;
+  size_t offset;
+  int state;
+} pvt_info_t;
+/*---------------------------------------------------------------------------*/
+/* portable layer api */
+void *jacana_pvt_raw_open(void);
+size_t jacana_pvt_raw_get_total_size(void *priv);
+int jacana_pvt_raw_read(void *priv, size_t start,
+                        uint8_t *data, size_t size);
+void jacana_pvt_raw_close(void *priv);
+/*---------------------------------------------------------------------------*/
+int jacana_pvt_open(firmware_handle_t *firmware, void *priv);
+int jacana_pvt_read_line(firmware_handle_t *firmware, char *line);
+int jacana_pvt_read_data(firmware_handle_t *firmware,
+                         uint8_t *data, size_t size);
+void jacana_pvt_close(firmware_handle_t *firmware);
+/*---------------------------------------------------------------------------*/
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* JACANA_PVT_H */
diff --git a/mbtk/aboot-tiny/jacana/jacana_serialport.h b/mbtk/aboot-tiny/jacana/jacana_serialport.h
new file mode 100755
index 0000000..63393cd
--- /dev/null
+++ b/mbtk/aboot-tiny/jacana/jacana_serialport.h
@@ -0,0 +1,25 @@
+#ifndef JACANA_SERIALPORT_H
+#define JACANA_SERIALPORT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stdlib.h>
+#include "aboot-tiny.h"
+
+extern aboot_tiny_uart_rx_callback_t rx_callback;
+
+/*---------------------------------------------------------------------------*/
+int jacana_serialport_init(const char *dev, int baud,
+                           aboot_tiny_uart_rx_callback_t cb);
+void jacana_serialport_exit(void);
+int jacana_serialport_write(const uint8_t *buf, size_t len);
+/*---------------------------------------------------------------------------*/
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* JACANA_SERIALPORT_H */
diff --git a/mbtk/aboot-tiny/jacana/jacana_serialport_posix.c b/mbtk/aboot-tiny/jacana/jacana_serialport_posix.c
new file mode 100755
index 0000000..c5c10bb
--- /dev/null
+++ b/mbtk/aboot-tiny/jacana/jacana_serialport_posix.c
@@ -0,0 +1,526 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <termios.h>
+#include <assert.h>
+#include "aboot-tiny.h"
+#include "jacana_serialport.h"
+#include <time.h>
+#include "jacana_usleep.h"
+
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+#ifndef SERIAL_RX_BUF_SIZE
+#define SERIAL_RX_BUF_SIZE (1024)
+#endif
+
+typedef enum {
+  SERIALPORT_PARITY_NONE  = 1,
+  SERIALPORT_PARITY_MARK  = 2,
+  SERIALPORT_PARITY_EVEN  = 3,
+  SERIALPORT_PARITY_ODD   = 4,
+  SERIALPORT_PARITY_SPACE = 5
+} serialport_parity_t;
+
+typedef enum {
+  SERIALPORT_STOPBITS_ONE         = 1,
+  SERIALPORT_STOPBITS_ONE_FIVE    = 2,
+  SERIALPORT_STOPBITS_TWO         = 3
+} serialport_stop_bits_t;
+
+typedef struct {
+  int baud;
+  int data_bits;
+  bool rtscts;
+  bool xon;
+  bool xoff;
+  bool xany;
+  bool dsrdtr;
+  bool hupcl;
+  serialport_parity_t parity;
+  serialport_stop_bits_t stop_bits;
+} serial_port_connect_opt_t;
+/*---------------------------------------------------------------------------*/
+typedef struct serial_tx_memb {
+  struct serial_tx_memb *next;
+  uint8_t *data;
+  int len;
+  int remain;
+} serial_tx_memb_t;
+/*---------------------------------------------------------------------------*/
+static uint8_t rx_buf[SERIAL_RX_BUF_SIZE];
+static aboot_tiny_uart_rx_callback_t rx_cb;
+static int serial_fd = -1;
+static int fd_error;
+/*---------------------------------------------------------------------------*/
+static serial_tx_memb_t *tx_queue_list;
+static serial_tx_memb_t **tx_queue = &tx_queue_list;
+/*---------------------------------------------------------------------------*/
+static inline void
+queue_init(serial_tx_memb_t **queue)
+{
+  *queue = NULL;
+}
+/*---------------------------------------------------------------------------*/
+static inline bool
+queue_is_empty(serial_tx_memb_t **queue)
+{
+  return *queue == NULL ? true : false;
+}
+/*---------------------------------------------------------------------------*/
+static inline serial_tx_memb_t *
+queue_peek(serial_tx_memb_t **queue)
+{
+  return *queue;
+}
+/*---------------------------------------------------------------------------*/
+static inline serial_tx_memb_t *
+queue_dequeue(serial_tx_memb_t **queue)
+{
+  serial_tx_memb_t *l;
+  l = *queue;
+  if(*queue != NULL) {
+    *queue = ((serial_tx_memb_t *)*queue)->next;
+  }
+
+  return l;
+}
+/*---------------------------------------------------------------------------*/
+static inline void
+queue_enqueue(serial_tx_memb_t **queue, serial_tx_memb_t *element)
+{
+  element->next = NULL;
+
+  if(*queue == NULL) {
+    *queue = element;
+  } else {
+    serial_tx_memb_t *l;
+    for(l = *queue; l->next != NULL; l = l->next) {
+    }
+    l->next = element;
+  }
+}
+/*---------------------------------------------------------------------------*/
+static int
+set_fd(fd_set *rset, fd_set *wset)
+{
+  if(serial_fd > 0 && !fd_error) {
+    FD_SET(serial_fd, rset);
+    if(!queue_is_empty(tx_queue)) {
+      FD_SET(serial_fd, wset);
+    }
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+/*---------------------------------------------------------------------------*/
+static void
+serial_do_tx(void)
+{
+  serial_tx_memb_t *p;
+  int cnt, offset;
+
+  p = queue_peek(tx_queue);
+  if(p) {
+    offset = p->len - p->remain;
+    cnt = write(serial_fd, p->data + offset, p->remain);
+
+#if 0
+    unsigned int ii,j;
+    ii = p->remain;
+    if(ii > 128)
+    {
+        ii = 128;
+    }
+
+    char buff[512];
+    memset(buff, 0, sizeof(buff));
+    for(j=0;j<ii;j++)
+    {
+        sprintf(&buff[j], "%c", *(p->data + offset+j));
+    }
+
+    aboot_tiny_log_printf("Tlen=%d,data=%s", p->remain, buff);
+#endif
+
+    if(cnt > 0) {
+      p->remain -= cnt;
+      if(p->remain == 0) {
+        queue_dequeue(tx_queue);
+        aboot_tiny_mem_free(p->data);
+        aboot_tiny_mem_free(p);
+      }
+    } else if(cnt < 0) {
+      aboot_tiny_log_printf("write error: error code %d\n", errno);
+      fd_error = errno;
+    }
+  }
+}
+/*---------------------------------------------------------------------------*/
+static void
+handle_fd(fd_set *rset, fd_set *wset)
+{
+  int cnt;
+  if(serial_fd >= 0 && !fd_error) {
+    if(FD_ISSET(serial_fd, wset)) {
+      serial_do_tx();
+      if(queue_is_empty(tx_queue)) {
+        FD_CLR(serial_fd, wset);
+      }
+    }
+
+    if(FD_ISSET(serial_fd, rset)) {
+      cnt = read(serial_fd, rx_buf, sizeof(rx_buf));
+#if 0
+      unsigned int ii,j;
+      ii = cnt;
+      if(ii > 64)
+      {
+          ii = 64;
+      }
+      
+      char buf[256];
+      memset(buf, 0, sizeof(buf));
+      for(j=0;j<ii;j++)
+      {
+          sprintf(&buf[j], "%c,", rx_buf[j]);
+      }
+      
+      aboot_tiny_log_printf("Rlen=%d,data=%s", cnt, buf);
+#endif
+      if((cnt > 0) && rx_cb) {
+        rx_cb(rx_buf, cnt);
+      } else if(cnt < 0) {
+        aboot_tiny_log_printf("read error: error code %d\n", errno);
+        fd_error = errno;
+      }
+    }
+  }
+}
+/*---------------------------------------------------------------------------*/
+static const struct select_callback serial_select_callback = {
+  set_fd,
+  handle_fd,
+};
+/*---------------------------------------------------------------------------*/
+static int
+to_baud_const(int baud)
+{
+  switch(baud) {
+  case 0: return B0;
+  case 50: return B50;
+  case 75: return B75;
+  case 110: return B110;
+  case 134: return B134;
+  case 150: return B150;
+  case 200: return B200;
+  case 300: return B300;
+  case 600: return B600;
+  case 1200: return B1200;
+  case 1800: return B1800;
+  case 2400: return B2400;
+  case 4800: return B4800;
+  case 9600: return B9600;
+  case 19200: return B19200;
+  case 38400: return B38400;
+  case 57600: return B57600;
+  case 115200: return B115200;
+  case 230400: return B230400;
+#if defined(__linux__)
+  case 460800: return B460800;
+  case 500000: return B500000;
+  case 576000: return B576000;
+  case 921600: return B921600;
+  case 1000000: return B1000000;
+  case 1152000: return B1152000;
+  case 1500000: return B1500000;
+  case 2000000: return B2000000;
+  case 2500000: return B2500000;
+  case 3000000: return B3000000;
+  case 3500000: return B3500000;
+  case 4000000: return B4000000;
+#endif
+  }
+  return -1;
+}
+/*---------------------------------------------------------------------------*/
+static int
+to_data_bits_const(int data_bits)
+{
+  switch(data_bits) {
+  case 8: default: return CS8;
+  case 7: return CS7;
+  case 6: return CS6;
+  case 5: return CS5;
+  }
+  return -1;
+}
+/*---------------------------------------------------------------------------*/
+static int
+set_baudrate(int fd, serial_port_connect_opt_t *opt)
+{
+  /* lookup the standard baudrates from the table */
+  int baud_rate = to_baud_const(opt->baud);
+
+  /* get port options */
+  struct termios options;
+
+  if(-1 == tcgetattr(fd, &options)) {
+    aboot_tiny_log_printf("Error: %s setting custom baud rate of %d", strerror(errno), opt->baud);
+    return -1;
+  }
+
+  if(-1 == baud_rate) {
+    aboot_tiny_log_printf("Error baud rate of %d is not supported on your platform", opt->baud);
+    return -1;
+  }
+
+  /* If we have a good baud rate set it and lets go */
+  cfsetospeed(&options, baud_rate);
+  cfsetispeed(&options, baud_rate);
+  /* throw away all the buffered data */
+  tcflush(fd, TCIOFLUSH);
+  /* make the changes now */
+  tcsetattr(fd, TCSANOW, &options);
+
+  return 1;
+}
+
+/*---------------------------------------------------------------------------*/
+static int
+set_default_baudrate(int fd)
+{
+  /* Default BaudRate => 115200. Just for AP UART. */
+  int baud_rate = to_baud_const(115200);
+
+  /* get port options */
+  struct termios options;
+
+  if(-1 == tcgetattr(fd, &options))
+    return -1;
+
+
+  if(-1 == baud_rate)
+    return -1;
+
+  /* If we have a good baud rate set it and lets go */
+  cfsetospeed(&options, baud_rate);
+  cfsetispeed(&options, baud_rate);
+  /* throw away all the buffered data */
+  tcflush(fd, TCIOFLUSH);
+  /* make the changes now */
+  tcsetattr(fd, TCSANOW, &options);
+
+  return 1;
+}
+
+
+/*---------------------------------------------------------------------------*/
+static int
+setup(int fd, serial_port_connect_opt_t *opt)
+{
+  int data_bits = to_data_bits_const(opt->data_bits);
+  if(data_bits == -1) {
+    aboot_tiny_log_printf("Invalid data bits %d\n", opt->data_bits);
+    return -1;
+  }
+  if(fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) {
+    aboot_tiny_log_printf("Set FD_CLOEXEC failed\n");
+    return -1;
+  }
+
+  /* Get port configuration for modification */
+  struct termios options;
+  tcgetattr(fd, &options);
+
+  /* IGNPAR: ignore bytes with parity errors */
+  options.c_iflag = IGNPAR;                
+  /* ICRNL: map CR to NL (otherwise a CR input on the other computer will not terminate input) */
+  /* Future potential option */
+  /* options.c_iflag = ICRNL; */
+  /* otherwise make device raw (no other input processing) */
+
+  /* Specify data bits */
+  options.c_cflag &= ~CSIZE;
+  options.c_cflag |= data_bits;
+
+  options.c_cflag &= ~(CRTSCTS);
+
+  if(opt->rtscts) {
+    options.c_cflag |= CRTSCTS;
+  }
+
+  options.c_iflag &= ~(IXON | IXOFF | IXANY);
+
+  if(opt->xon) {
+    options.c_iflag |= IXON;
+  }
+
+  if(opt->xoff) {
+    options.c_iflag |= IXOFF;
+  }
+
+  if(opt->xany) {
+    options.c_iflag |= IXANY;
+  }
+
+  switch(opt->parity) {
+  case SERIALPORT_PARITY_NONE:
+    options.c_cflag &= ~PARENB;
+    break;
+  case SERIALPORT_PARITY_ODD:
+    options.c_cflag |= PARENB;
+    options.c_cflag |= PARODD;
+    break;
+  case SERIALPORT_PARITY_EVEN:
+    options.c_cflag |= PARENB;
+    options.c_cflag &= ~PARODD;
+    break;
+  default:
+    aboot_tiny_log_printf("Invalid parity setting %d", opt->parity);
+    return -1;
+  }
+
+  switch(opt->stop_bits) {
+  case SERIALPORT_STOPBITS_ONE:
+    options.c_cflag &= ~CSTOPB;
+    break;
+  case SERIALPORT_STOPBITS_TWO:
+    options.c_cflag |= CSTOPB;
+    break;
+  default:
+    aboot_tiny_log_printf("Invalid stop bits setting %d", opt->stop_bits);
+    return -1;
+  }
+
+
+  options.c_cflag |= CLOCAL;      /* ignore status lines */
+  options.c_cflag |= CREAD;       /* enable receiver */
+  options.c_oflag = 0;
+  /* ICANON makes partial lines not readable. It should be optional. */
+  /* It works with ICRNL. */
+  options.c_lflag = 0; /* ICANON; */
+  options.c_cc[VMIN] = 0;
+  options.c_cc[VTIME] = 0;
+
+  tcsetattr(fd, TCSANOW, &options);
+
+
+  if(set_baudrate(fd, opt) == -1) {
+    return -1;
+  }
+
+  return 1;
+}
+/*---------------------------------------------------------------------------*/
+static int
+serial_port_open(const char *dev, serial_port_connect_opt_t *opt)
+{
+  assert(serial_fd == -1);   /* previously port has not been closed */
+  serial_fd = open(dev, O_RDWR | O_NOCTTY | O_NONBLOCK | O_CLOEXEC | O_SYNC);
+  if(serial_fd < 0) {
+    return -1;
+  }
+  if(setup(serial_fd, opt) < 0) {
+    return -1;
+  }
+  rx_cb = NULL;
+  fd_error = 0;
+  aboot_tiny_log_printf("serial fd=%d rate=%d", serial_fd, opt->baud);
+  aboot_tiny_select_set_callback(serial_fd, &serial_select_callback);
+  queue_init(tx_queue);
+
+  return 0;
+}
+static void
+serial_port_close(void)
+{
+  serial_tx_memb_t *p;
+
+  if(serial_fd >= 0) {
+    aboot_tiny_select_set_callback(serial_fd, NULL);
+    rx_cb = NULL;
+    /* BaudRate = 115200bps. */
+    set_default_baudrate(serial_fd);
+    close(serial_fd);
+    serial_fd = -1;
+    while(1) {
+      p = queue_dequeue(tx_queue);
+      if(!p) {
+        break;
+      }
+      aboot_tiny_mem_free(p->data);
+      aboot_tiny_mem_free(p);
+    }
+  }
+}
+/*---------------------------------------------------------------------------*/
+int
+jacana_serialport_write(const uint8_t *buf, size_t len)
+{
+  serial_tx_memb_t *p;
+
+  if(serial_fd < 0 || fd_error) {
+    return len;
+  }
+
+  p = aboot_tiny_mem_alloc(sizeof(serial_tx_memb_t));
+  if(!p) {
+    aboot_tiny_log_printf("out of memory\n");
+    return -1;
+  }
+
+  p->data = aboot_tiny_mem_alloc(len);
+  if(!p->data) {
+    aboot_tiny_mem_free((void *)p);
+    aboot_tiny_log_printf("out of memory\n");
+    return -1;
+  }
+  memcpy(p->data, (void *)buf, len);
+  p->len = p->remain = len;
+  queue_enqueue(tx_queue, p);
+
+  return len;
+}
+/*---------------------------------------------------------------------------*/
+int
+jacana_serialport_init(const char *dev, int baud,
+                       aboot_tiny_uart_rx_callback_t cb)
+{
+  serial_port_connect_opt_t connect_op;
+  connect_op.baud = baud;
+  connect_op.data_bits = 8;
+  connect_op.rtscts = false;
+  connect_op.xon = false;
+  connect_op.xoff = false;
+  connect_op.xany = false;
+  connect_op.dsrdtr = false;
+  connect_op.hupcl = false;
+  connect_op.parity = SERIALPORT_PARITY_NONE;
+  connect_op.stop_bits = SERIALPORT_STOPBITS_ONE;
+
+  if(!serial_port_open(dev, &connect_op)) {
+    rx_cb = cb;
+    return 0;
+  } else {
+    aboot_tiny_log_printf("open device \"%s\" failed\n", dev);
+    return -1;
+  }
+}
+/*---------------------------------------------------------------------------*/
+void
+jacana_serialport_exit(void)
+{
+  serial_port_close();
+}
+/*---------------------------------------------------------------------------*/
diff --git a/mbtk/aboot-tiny/jacana/jacana_usleep.h b/mbtk/aboot-tiny/jacana/jacana_usleep.h
new file mode 100755
index 0000000..dc02007
--- /dev/null
+++ b/mbtk/aboot-tiny/jacana/jacana_usleep.h
@@ -0,0 +1,30 @@
+/**
+ * @file
+ * @author  Jinhua Huang <jinhuahuang@asrmicro.com>
+ * @version 1.0
+ *
+ * @section LICENSE
+ * Copyright (C) 2021, ASR microelectronics, All rights reserved.
+ *
+ * @section DESCRIPTION
+ * Aboot tiny downloader for Jacana with firmware and pvt
+ *
+ */
+#ifndef __JACANA_USLEEP_H__
+#define __JACANA_USLEEP_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int
+jacana_delay_usleep(unsigned long useconds);
+
+int
+jacana_delay_msleep(unsigned long useconds);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* __JACANA_USLEEP__H__ */
diff --git a/mbtk/aboot-tiny/jacana/sparse_file.c b/mbtk/aboot-tiny/jacana/sparse_file.c
new file mode 100755
index 0000000..19603d9
--- /dev/null
+++ b/mbtk/aboot-tiny/jacana/sparse_file.c
@@ -0,0 +1,52 @@
+#include "sparse_file.h"
+
+/*---------------------------------------------------------------------------*/
+int
+sparse_file_new(sparse_file_t *sparse_file, size_t offset,
+                size_t size, size_t file_size)
+{
+  sparse_header_t *sparse;
+  chunk_header_t *chunk;
+  unsigned chunk_num = 0;
+
+  /* fill the sparse file structure */
+  sparse = (sparse_header_t *)sparse_file->header;
+  sparse->magic = SPARSE_HEADER_MAGIC;
+  sparse->major_version = 1;
+  sparse->minor_version = 0;
+  sparse->file_hdr_sz = sizeof(sparse_header_t);
+  sparse->chunk_hdr_sz = sizeof(chunk_header_t);
+  sparse->blk_sz = SPARSE_BLOCK_SZ;
+  sparse->total_blks = (file_size + SPARSE_BLOCK_SZ - 1) / SPARSE_BLOCK_SZ;
+  sparse->image_checksum = 0;
+  sparse->total_chunks = 3;
+
+  chunk = (chunk_header_t *)(sparse + 1);
+  chunk->chunk_type = CHUNK_TYPE_DONT_CARE;
+  chunk->reserved1 = 0;
+  chunk->chunk_sz = offset / SPARSE_BLOCK_SZ;
+  chunk->total_sz = sizeof(chunk_header_t);
+  chunk_num += chunk->chunk_sz;
+  chunk++;
+
+  chunk->chunk_type = CHUNK_TYPE_RAW;
+  chunk->reserved1 = 0;
+  chunk->chunk_sz = (size + SPARSE_BLOCK_SZ - 1) / SPARSE_BLOCK_SZ;
+  unsigned fill_size = chunk->chunk_sz * SPARSE_BLOCK_SZ - size;
+  chunk->total_sz = size + fill_size + sizeof(chunk_header_t);
+  chunk_num += chunk->chunk_sz;
+
+  sparse_file->header_size = SPARSE_FILE_HEADER_SIZE;
+  sparse_file->data_size = size;
+  sparse_file->fill_size = fill_size;
+  sparse_file->footer_size = SPARSE_FILE_FOOTER_SIZE;
+
+  chunk = (chunk_header_t *)(sparse_file->footer);
+  chunk->chunk_type = CHUNK_TYPE_DONT_CARE;
+  chunk->reserved1 = 0;
+  chunk->chunk_sz = sparse->total_blks - chunk_num;
+  chunk->total_sz = sizeof(chunk_header_t);
+
+  return 0;
+}
+/*---------------------------------------------------------------------------*/
diff --git a/mbtk/aboot-tiny/jacana/sparse_file.h b/mbtk/aboot-tiny/jacana/sparse_file.h
new file mode 100755
index 0000000..b5951a8
--- /dev/null
+++ b/mbtk/aboot-tiny/jacana/sparse_file.h
@@ -0,0 +1,33 @@
+#ifndef SPARSE_FILE_H
+#define SPARSE_FILE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "sparse_format.h"
+
+#define SPARSE_FILE_HEADER_SIZE \
+  (sizeof(sparse_header_t) + 2 * sizeof(chunk_header_t))
+
+#define SPARSE_FILE_FOOTER_SIZE (sizeof(chunk_header_t))
+
+#define SPARSE_BLOCK_SZ 4096
+
+typedef struct {
+  char header[SPARSE_FILE_HEADER_SIZE];
+  char footer[SPARSE_FILE_FOOTER_SIZE];
+  size_t header_size;
+  size_t data_size;
+  size_t fill_size;
+  size_t footer_size;
+} sparse_file_t;
+
+int sparse_file_new(sparse_file_t *sparse_file, size_t offset,
+                    size_t size, size_t file_size);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SPARSE_FILE_H */
diff --git a/mbtk/libmbtk_lib/src/mbtk_debug.c b/mbtk/libmbtk_lib/src/mbtk_debug.c
index 84e13e0..e9280c9 100755
--- a/mbtk/libmbtk_lib/src/mbtk_debug.c
+++ b/mbtk/libmbtk_lib/src/mbtk_debug.c
@@ -328,6 +328,7 @@
         } while(0)
 
 
+// arm-openwrt-linux-addr2line -e out/bin/mbtk_gnssd 0x12ca8
 void mbtk_debug_open(const char *log_file, bool thread_support)
 {
     struct sigaction sa;
diff --git a/mbtk/mbtk_gnssd/Makefile b/mbtk/mbtk_gnssd/Makefile
index 695d287..0bab3fd 100755
--- a/mbtk/mbtk_gnssd/Makefile
+++ b/mbtk/mbtk_gnssd/Makefile
@@ -34,14 +34,16 @@
 					hd8122_dl/port.c \
 					hd8122_dl/fwup.c
 
-ifeq ($(MBTK_GNSS_MODE), gnss_8122)
+#ifeq ($(MBTK_GNSS_MODE), gnss_8122)
 # Enable mbtk ipc(Disable ubus.)
-LOCAL_SRC_FILES += gnss_ipc.c
-else
+#LOCAL_SRC_FILES += gnss_ipc.c
+#else
+
 # Enable ubus
 LOCAL_SRC_FILES += gnss_ubus.c
 DEFINE += -DMBTK_GNSS_UBUS_ENABLE
-endif
+
+#endif
 
 OBJS = $(patsubst %.c, %.o, $(patsubst %.cpp, %.o, $(LOCAL_SRC_FILES)))
 $(info OBJS = $(OBJS))
diff --git a/mbtk/mbtk_gnssd/gnss_asr5311.c b/mbtk/mbtk_gnssd/gnss_asr5311.c
index 627e949..bca402b 100755
--- a/mbtk/mbtk_gnssd/gnss_asr5311.c
+++ b/mbtk/mbtk_gnssd/gnss_asr5311.c
@@ -116,9 +116,9 @@
 {
     char cmd[256] = {0};
     if(memcmp(dev, "/dev/", 5) == 0) {
-        snprintf(cmd, sizeof(cmd), "nice -n -10 aboot-tiny -B 2000000 -s %s -F /lib/firmware/jacana_fw.bin -P /system/etc/jacana_pvt.bin", dev + 5);
+        snprintf(cmd, sizeof(cmd), "nice -n -10 aboot-tiny -B 2000000 -s %s -F /etc/jacana_fw.bin -P /etc/jacana_pvt.bin", dev + 5);
     } else {
-        snprintf(cmd, sizeof(cmd), "nice -n -10 aboot-tiny -B 2000000 -s %s -F /lib/firmware/jacana_fw.bin -P /system/etc/jacana_pvt.bin", dev);
+        snprintf(cmd, sizeof(cmd), "nice -n -10 aboot-tiny -B 2000000 -s %s -F /etc/jacana_fw.bin -P /etc/jacana_pvt.bin", dev);
     }
     system(cmd);
 
diff --git a/mbtk/mbtk_gnssd/gnss_ipc.c b/mbtk/mbtk_gnssd/gnss_ipc.c
index bd484ba..3e13854 100755
--- a/mbtk/mbtk_gnssd/gnss_ipc.c
+++ b/mbtk/mbtk_gnssd/gnss_ipc.c
@@ -62,7 +62,7 @@
             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);
+                ret = GNSS_ERR_ARG;
             }
         }
 
diff --git a/mbtk/mbtk_gnssd/gnss_main.c b/mbtk/mbtk_gnssd/gnss_main.c
index 772cf21..1966855 100755
--- a/mbtk/mbtk_gnssd/gnss_main.c
+++ b/mbtk/mbtk_gnssd/gnss_main.c
@@ -85,9 +85,12 @@
         goto check_fail;
     }
 
-    if(strcmp(argv[3], "0") && strcmp(argv[3], "1")) {
+#if 0
+    int init_mode = atoi(argv[3]);
+    if(((GNSS_PRINT_PORT_UART1 | GNSS_PRINT_PORT_USB_NMEA | GNSS_PRINT_PORT_USB_AT | GNSS_PRINT_PORT_TTY_AT) & init_mode) != init_mode) {
         goto check_fail;
     }
+#endif
 
     return 0;
 check_fail:
@@ -97,34 +100,73 @@
 
 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 GNSS_ERR_OPEN_DEV;
+    // TTY AT change.
+    if((gnss_info.print_port & GNSS_PRINT_PORT_TTY_AT) != (print_port & GNSS_PRINT_PORT_TTY_AT)) {
+        if(print_port & GNSS_PRINT_PORT_TTY_AT) { // Open
+            if(gnss_pty_open(&gnss_pty_master_fd, &gnss_pty_slave_fd, GNSS_PORT_PTY)) {
+                return GNSS_ERR_OPEN_DEV;
+            }
+            LOGD("Open PTY port success.");
+        } else { // Close
+            if(gnss_pty_slave_fd > 0) {
+                close(gnss_pty_slave_fd);
+                gnss_pty_slave_fd = -1;
+                unlink(GNSS_PORT_PTY);
+            }
+            LOGD("Close PTY port success.");
         }
-        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 GNSS_ERR_OPEN_DEV;
+    // USB AT change.
+    if((gnss_info.print_port & GNSS_PRINT_PORT_USB_AT) != (print_port & GNSS_PRINT_PORT_USB_AT)) {
+        if(print_port & GNSS_PRINT_PORT_USB_AT) { // Open
+            if((gnss_usb_at_port_fd = gnss_port_open(GNSS_PORT_USB_AT, O_RDWR | O_NONBLOCK | O_NOCTTY, 115200, FALSE)) <= 0) {
+                return GNSS_ERR_OPEN_DEV;
+            }
+            LOGD("Open USB AT port success.");
+        } else { // Close
+            if(gnss_usb_at_port_fd > 0) {
+                close(gnss_usb_at_port_fd);
+                gnss_usb_at_port_fd = -1;
+            }
+            LOGD("Close USB AT port success.");
         }
-        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 GNSS_ERR_OPEN_DEV;
+    // USB NMEA change.
+    if((gnss_info.print_port & GNSS_PRINT_PORT_USB_NMEA) != (print_port & GNSS_PRINT_PORT_USB_NMEA)) {
+        if(print_port & GNSS_PRINT_PORT_USB_NMEA) { // Open
+            if((gnss_usb_nmea_port_fd = gnss_port_open(GNSS_PORT_USB_NMEA, O_RDWR | O_NONBLOCK | O_NOCTTY, 115200, FALSE)) <= 0) {
+                return GNSS_ERR_OPEN_DEV;
+            }
+            LOGD("Open USB NMEA port success.");
+        } else { // Close
+            if(gnss_usb_nmea_port_fd > 0) {
+                close(gnss_usb_nmea_port_fd);
+                gnss_usb_nmea_port_fd = -1;
+            }
+            LOGD("Close USB NMEA port success.");
         }
-        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 GNSS_ERR_OPEN_DEV;
+    // Uart AT change.
+    if((gnss_info.print_port & GNSS_PRINT_PORT_UART1) != (print_port & GNSS_PRINT_PORT_UART1)) {
+        if(print_port & GNSS_PRINT_PORT_UART1) { // Open
+            if((gnss_uart_at_port_fd = gnss_port_open(GNSS_PORT_UART_AT, O_RDWR | O_NONBLOCK | O_NOCTTY, 115200, TRUE)) <= 0) {
+                return GNSS_ERR_OPEN_DEV;
+            }
+            LOGD("Open UART AT port success.");
+        } else { // Close
+            if(gnss_uart_at_port_fd > 0) {
+                close(gnss_uart_at_port_fd);
+                gnss_uart_at_port_fd = -1;
+            }
+            LOGD("Close UART AT port success.");
         }
-        LOGD("Open UART AT port success.");
     }
 
+    gnss_info.print_port = print_port;
+
     return GNSS_ERR_OK;
 }
 
@@ -156,6 +198,8 @@
         unlink(GNSS_PORT_PTY);
     }
 
+    gnss_info.print_port = 0;
+
     return 0;
 }
 
@@ -564,7 +608,16 @@
 {
     if(gnss_info.state != GNSS_STATE_CLOSE) {
         LOGW("GNSS not close:%d", gnss_info.state);
-        return GNSS_ERR_OK;
+        if(gnss_info.state == GNSS_STATE_READY) {
+            LOGD("Reset print port : %d -> %d", gnss_info.print_port, print_port);
+            if(gnss_info.print_port != print_port) {
+                return gnss_ports_open(print_port);
+            } else {
+                return GNSS_ERR_OK;
+            }
+        } else {
+            return GNSS_ERR_OK;
+        }
     }
 
     int ret = 0;
@@ -658,11 +711,10 @@
 
     // 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);
+    return gnss_ports_open(print_port);
 
 exit_with_dev_close:
     if(gnss_info.gnss_dev_close()) {
@@ -826,7 +878,7 @@
 }
 
 
-// mbtk_gnssd 6228 /dev/ttyS2 baud 0/1
+// mbtk_gnssd 6228 /dev/ttyS2 baud 0/1 <port_type>
 int main(int argc, char *argv[])
 {
     mbtk_log_init("radio", GNSS_TAG);
@@ -902,10 +954,16 @@
     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;
+        int init_mode = atoi(argv[3]);
+        if(((GNSS_PRINT_PORT_UART1 | GNSS_PRINT_PORT_USB_NMEA | GNSS_PRINT_PORT_USB_AT | GNSS_PRINT_PORT_TTY_AT) & init_mode) != init_mode) {
+            init_mode = 0;
         }
+        if(gnss_init((uint32)init_mode)) {
+            LOGE("gnss_init() fail.");
+            // return -1;
+        }
+    } else {
+        gnss_info.print_port = 0;
     }
 
     // Init ubus and waitting IPC commands.
diff --git a/mbtk/mbtk_gnssd/gnss_ubus.c b/mbtk/mbtk_gnssd/gnss_ubus.c
index 39b4140..d5aba85 100755
--- a/mbtk/mbtk_gnssd/gnss_ubus.c
+++ b/mbtk/mbtk_gnssd/gnss_ubus.c
@@ -104,7 +104,7 @@
         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);
+            ret = GNSS_RESULT_STATE_UNSUPPORT;
         }
 
         if(ret < 0)
@@ -131,7 +131,7 @@
 
     int ret = 0;
     gnss_result_enum gnss_result = GNSS_RESULT_STATE_OPEN_SUCCESS;
-    
+
     ret = gnss_deinit();
     if(ret < 0)
     {