Add toolchain and mbtk source

Change-Id: Ie12546301367ea59240bf23d5e184ad7e36e40b3
diff --git a/mbtk/mbtk_lib/src/ds_ASBuffer.cpp b/mbtk/mbtk_lib/src/ds_ASBuffer.cpp
new file mode 100755
index 0000000..dc06fc1
--- /dev/null
+++ b/mbtk/mbtk_lib/src/ds_ASBuffer.cpp
@@ -0,0 +1,297 @@
+/*==============================================================================

+

+                            ds_ASBuffer.cpp

+

+GENERAL DESCRIPTION

+  A buffer class with utility functions for parsing raw bytes.

+

+EXTERNALIZED FUNCTIONS

+

+INITIALIZATION AND SEQUENCING REQUIREMENTS

+  None

+

+  Copyright (c) 2014 by Qualcomm Technologies Incorporated. All Rights Reserved.

+==============================================================================*/

+

+/*==============================================================================

+                           EDIT HISTORY FOR MODULE

+

+This section contains comments describing changes made to the module.

+Notice that changes are listed in reverse chronological order.

+

+when        who    what, where, why

+--------    ---    ----------------------------------------------------------

+06/03/15    ml     Remove memory allocation on default constructor

+05/20/15    ml     Use memory from modem heap

+04/21/14    ml     Created file/Initial version.

+==============================================================================*/

+#include "ds_ASBuffer.h"

+

+//#include "ds_appsrv_mem.h"

+#include <string.h>

+#include <mbtk_type.h>

+#include <stdlib.h>

+

+#include <cctype> // isspace

+

+

+// temp

+#include "ds_ASString.h"

+

+#define DEFAULT_BUFFER_SIZE 256

+

+// static const uint8 EMPTY_STRING[] = "";

+

+void memscpy(void* dest, size_t destLen, const void* src, size_t srcLen) {

+    if (srcLen <= destLen)

+        memcpy(dest, src, srcLen);

+}

+

+

+ASBuffer::ASBuffer()

+: buffer(NULL), buffer_size(0), buffer_capacity(DEFAULT_BUFFER_SIZE), error_status(false)

+{ }

+

+

+

+ASBuffer::ASBuffer(uint32 capacity)

+: buffer(NULL), buffer_size(0), buffer_capacity(capacity), error_status(false)

+{ }

+

+

+

+ASBuffer::ASBuffer(ASBuffer &buf)

+: buffer(NULL), buffer_size(0), buffer_capacity(buf.buffer_capacity), error_status(false)

+{

+  init();

+  append(buf);

+}

+

+

+

+ASBuffer::ASBuffer(const ASBuffer &buf)

+: buffer(NULL), buffer_size(0), buffer_capacity(buf.buffer_capacity), error_status(false)

+{

+  init();

+  append(buf);

+}

+

+

+

+ASBuffer::ASBuffer(const uint8* buf, uint32 buf_size)

+: buffer(NULL), buffer_size(0), buffer_capacity(buf_size), error_status(false)

+{

+  init();

+  append(buf, buf_size);

+}

+

+

+

+ASBuffer::~ASBuffer()

+{

+  if(NULL != buffer)

+  {

+    free(buffer);

+	buffer = NULL;

+  }

+}

+

+

+

+ASBuffer& ASBuffer::operator=(const ASBuffer &rhs)

+{

+  clear();

+  append(rhs);

+  return *this;

+}

+

+

+

+

+void ASBuffer::init()

+{

+  if(0 == buffer_capacity)

+  {

+    error_status = true;

+  }

+  else if(NULL == buffer)

+  {

+    buffer = (uint8*)malloc(sizeof(uint8) * (buffer_capacity+1));

+    error_status = (NULL == buffer);

+  }

+}

+

+

+

+// temp name

+#define DO_NOT_DOUBLE_IF_LARGER 2147483647

+bool ASBuffer::increment_buffer_capacity(const uint32 req_size)

+{

+  uint32 new_capacity = buffer_capacity;

+  uint8* tmp          = NULL;

+

+  while(req_size > new_capacity)

+  {

+    // will cause overflow if doubled

+    if(DO_NOT_DOUBLE_IF_LARGER < new_capacity)

+      return false;

+

+    new_capacity *= 2;

+  }

+

+  tmp = (uint8*)malloc(sizeof(uint8) * new_capacity+1);

+  if(NULL == tmp)

+    return false;

+

+  memset(tmp, 0, new_capacity+1);

+  memscpy(tmp, new_capacity, buffer, buffer_size);

+  free(buffer);

+  buffer = NULL;

+

+  buffer          = tmp;

+  buffer_capacity = new_capacity;

+

+  return true;

+}

+

+

+

+uint8* ASBuffer::non_const_content()

+{

+  return buffer;

+}

+

+

+const uint8* ASBuffer::content() const

+{

+  return buffer;

+}

+

+

+const char* ASBuffer::c_str() const

+{

+  if(NULL == buffer)

+    return "";

+  else

+    return (const char*)buffer;

+}

+

+

+

+uint32 ASBuffer::size() const

+{

+  return buffer_size;

+}

+

+

+

+bool ASBuffer::empty() const

+{

+  return (0 == buffer_size);

+}

+

+

+

+bool ASBuffer::error() const

+{

+  return error_status;

+}

+

+

+

+void ASBuffer::clear()

+{

+  if(NULL == buffer)

+  {

+    init();

+  }

+  else

+  {

+    memset(buffer, 0, buffer_capacity+1);

+  }

+  buffer_size = 0;

+}

+

+

+

+bool ASBuffer::append(const uint8* append_buffer, const uint32 append_size)

+{

+  if(NULL == append_buffer || 0 == append_size)

+    return true;

+

+  if(NULL == buffer)

+    init();

+

+  if(error_status) // do nothing if in error state

+    return false;

+

+  uint32 new_size = buffer_size + append_size;

+  if(new_size > buffer_capacity)

+  {

+    if(!increment_buffer_capacity(new_size))

+    {

+      error_status = true;

+      return false;

+    }

+  }

+

+  memscpy(

+          buffer + buffer_size,

+          buffer_capacity - buffer_size,

+          append_buffer,

+          append_size

+          );

+  buffer_size = new_size;

+

+  return true;

+}

+

+

+

+bool ASBuffer::append(const char* append_buffer)

+{

+  if(NULL == append_buffer)

+    return false;

+  else

+    return append((uint8*)append_buffer, strlen(append_buffer));

+}

+

+

+bool ASBuffer::append(const char append_buffer)

+{

+  char buffer[1];

+  buffer[0] = append_buffer;

+

+  return append((uint8*)buffer, 1);

+}

+

+

+bool ASBuffer::append(const char* append_buffer, const uint32 size)

+{

+  if(NULL == append_buffer)

+    return false;

+  else

+    return append((uint8*)append_buffer, size);

+}

+

+

+

+bool ASBuffer::append(const ASBuffer& append_buffer)

+{

+  if(append_buffer.empty())

+    return false;

+  else

+    return append(append_buffer.content(), append_buffer.size());

+}

+

+

+

+bool ASBuffer::append(const ASString& append_buffer)

+{

+  if(append_buffer.empty())

+    return false;

+  else

+    return append((uint8*)append_buffer.c_str(), append_buffer.size());

+}

+

+

diff --git a/mbtk/mbtk_lib/src/ds_ASString.cpp b/mbtk/mbtk_lib/src/ds_ASString.cpp
new file mode 100755
index 0000000..eaa1b82
--- /dev/null
+++ b/mbtk/mbtk_lib/src/ds_ASString.cpp
@@ -0,0 +1,396 @@
+/*==============================================================================

+

+                            ds_ASString.cpp

+

+GENERAL DESCRIPTION

+  A string class with utility functions for parsing AS.

+

+EXTERNALIZED FUNCTIONS

+

+INITIALIZATION AND SEQUENCING REQUIREMENTS

+  None

+

+  Copyright (c) 2014 by Qualcomm Technologies Incorporated. All Rights Reserved.

+==============================================================================*/

+

+/*==============================================================================

+                           EDIT HISTORY FOR MODULE

+

+This section contains comments describing changes made to the module.

+Notice that changes are listed in reverse chronological order.

+

+when        who    what, where, why

+--------    ---    ----------------------------------------------------------

+05/20/15    ml     Use memory from modem heap

+04/21/14    ml     Created file/Initial version.

+==============================================================================*/

+#include "ds_ASString.h"

+

+//#include "ds_appsrv_mem.h"

+//#include "data_msg.h"

+#include "mbtk_type.h"

+#include <string.h>

+#include <stdlib.h>

+#include <cctype> // isspace, tolower

+

+// #define DS_ASSTRING_MAX_LEN 4294967295 // uint32 max

+#define DS_ASSTRING_MAX_LEN 400000

+

+static const char AS_ESCAPE_MARK = '&';

+static const char EMPTY_STRING[] = "";

+

+static const char* ESCAPE_CHARS[] = {"&lt;", "&gt;", "&amp;", "&apos;", "&quot;"};

+static const char UNESCAPE_CHARS[] = {'<', '>', '&', '\'', '"'};

+static const uint32 NUM_ESCAPE_CHARS = 5;

+

+

+static bool has_string_at_start(const char* buf, const char* cmp);

+

+static void memscpy(void* dest, size_t destLen, const void* src, size_t srcLen) {

+    if (srcLen <= destLen)

+        memcpy(dest, src, srcLen);

+}

+

+

+ASString::ASString()

+: str(NULL) { }

+

+

+ASString::ASString(const char* src)

+: str(NULL)

+{

+  if(NULL == src)

+  {

+    str = NULL;

+    return;

+  }

+  copy_string(src, strlen(src));

+}

+

+

+ASString::ASString(const char* src, uint32 len)

+: str(NULL)

+{

+  copy_string(src, len);

+}

+

+

+// copy constructor

+ASString::ASString(ASString& src)

+: str(NULL)

+{

+  copy_string(src.c_str(), src.size());

+}

+

+

+ASString::ASString(const ASString& src)

+: str(NULL)

+{

+  copy_string(src.c_str(), src.size());

+}

+

+

+ASString::~ASString()

+{

+  if(NULL != str)

+  {

+	free(str);

+  }

+}

+

+// Assumes this->str with no memory allocated

+void ASString::copy_string(const char* src, uint32 len)

+{

+  if(NULL == src || 0 == len)

+  {

+    str = NULL;

+    return;

+  }

+  if(len > DS_ASSTRING_MAX_LEN)

+    return;

+

+  str = (char*)malloc(sizeof(char) * (len + 1));

+  if(NULL != str)

+  {

+    memscpy(str, len, src, len);

+  }

+}

+

+

+ASString& ASString::operator=(const ASString& rhs)

+{

+  if(&rhs != this)

+  {

+    if(NULL != str)

+    {

+		free(str);

+		str = NULL;

+	}

+

+    if(NULL == rhs.str)

+      str = NULL;

+    else

+      copy_string(rhs.str, rhs.size());

+  }

+  return *this;

+}

+

+

+ASString& ASString::operator=(const char* rhs)

+{

+  if(NULL != str)

+  {

+	free(str);

+	str = NULL;

+  }

+

+  if(NULL == rhs)

+    str = NULL;

+  else

+    copy_string(rhs, strlen(rhs));

+

+  return *this;

+}

+

+

+

+char ASString::operator[](const int index) const

+{

+  return str[index];

+}

+

+

+char& ASString::operator[](int index)

+{

+  return str[index];

+}

+

+

+const char* ASString::c_str() const

+{

+  if(NULL == str)

+    return EMPTY_STRING;

+  return str;

+}

+

+

+uint32 ASString::size() const

+{

+  return length();

+}

+

+

+uint32 ASString::length() const

+{

+  if(NULL == str)

+    return 0;

+  return (uint32)strlen(str);

+}

+

+

+bool ASString::empty() const

+{

+  return (0 == length());

+}

+

+void ASString::remove_trailing_spaces()

+{

+  uint32 end = length();

+

+  if(0 == end)

+    return;

+

+  while(0 != end)

+  {

+    if(!isspace(str[--end]))

+    {

+      end++;

+      break;

+    }

+  }

+

+  str[end] = '\0';

+}

+

+

+

+

+bool ASString::resolve_xml_escapes()

+{

+  bool   replaced = false;

+  uint32 counter  = 0;

+  uint32 len      = length();

+  char*  buf      = str;

+  char*  new_str  = NULL;

+

+  if(0 == len)

+    return true;

+

+  new_str = (char*)malloc(sizeof(char) * (len + 1) );

+  if(NULL == new_str)

+    return true;

+

+  while('\0' != *buf && counter < len)

+  {

+    if(AS_ESCAPE_MARK == *buf)

+    {

+      replaced = false;

+      for(uint32 i=0; i<NUM_ESCAPE_CHARS; i++)

+      {

+        if(has_string_at_start(buf, ESCAPE_CHARS[i]))

+        {

+          new_str[counter++] = UNESCAPE_CHARS[i];

+          buf += strlen(ESCAPE_CHARS[i]);

+          replaced = true;

+          break;

+        }

+      }

+

+      if(!replaced)

+      {

+		free(new_str);

+        return false;

+      }

+    }

+    else

+    {

+      new_str[counter++] = *buf;

+      buf++;

+    }

+  }

+

+  free(str);

+  str = new_str;

+

+  return true;

+}

+

+

+

+void ASString::to_lower()

+{

+  uint32 len = length();

+  for(uint32 i = 0; i < len; ++i)

+  {

+    str[i] = tolower(str[i]);

+  }

+}

+

+

+

+bool ASString::limit_cmp(const char* cstr, const uint32 len) const

+{

+  return (0 == strncmp(str, cstr, len));

+}

+

+

+

+void ASString::append(const char* append_str)

+{

+  if(NULL == append_str)

+    return;

+

+  append(append_str, strlen(append_str));

+}

+

+

+

+void ASString::append(const char* append_str, const uint32 len)

+{

+  uint32 prev_size, new_size;

+

+  if(NULL == append_str)

+    return;

+

+  if(strlen(append_str) < len || 0 == len)

+    return;

+

+  prev_size = length();

+  new_size  = prev_size + len + 1;

+

+  if(new_size < prev_size || new_size < len || DS_ASSTRING_MAX_LEN < new_size) // overflow?

+  {

+    printf("append - append size too long");

+    return;

+  }

+

+  char* tmp = (char*)malloc(sizeof(char) * new_size);

+  if(NULL == tmp)

+  {

+    printf("append - Failed to allocate memory for result string");

+    return;

+  }

+

+  if(NULL != str)

+  {

+    // copy the current string to the new string.

+    memscpy(tmp, new_size, str, prev_size);

+	free(str);

+    str = NULL;

+  }

+  // add the new string

+  memscpy(tmp+prev_size, new_size - prev_size, append_str, len);

+

+  str = tmp;

+}

+

+

+void ASString::append(const ASString& append_str)

+{

+  if(append_str.empty())

+    return;

+  append(append_str.c_str(), append_str.length());

+}

+

+

+

+

+

+bool operator== (const ASString& lhs, const ASString& rhs)

+{

+  return (0 == strcasecmp(lhs.c_str(), rhs.c_str()));

+}

+

+bool operator!= (const ASString& lhs, const ASString& rhs)

+{

+  return (0 != strcasecmp(lhs.c_str(), rhs.c_str()));

+}

+

+

+bool operator== (const ASString& lhs, const char* rhs)

+{

+  return (0 == strcasecmp(lhs.c_str(), rhs));

+}

+

+bool operator!= (const ASString& lhs, const char* rhs)

+{

+  return (0 != strcasecmp(lhs.c_str(), rhs));

+}

+

+

+/*

+res <  0 if lhs <  rhs

+res == 0 if lhs == rhs

+res >  0 if lhs <  rhs

+*/

+bool operator< (const ASString& lhs, const ASString& rhs)

+{

+  return (0 > strcasecmp(lhs.c_str(), rhs.c_str()));

+}

+

+bool operator> (const ASString& lhs, const ASString& rhs)

+{

+  return (0 < strcasecmp(lhs.c_str(), rhs.c_str()));

+}

+

+

+

+

+

+

+

+static bool has_string_at_start(const char* buf, const char* cmp)

+{

+  return (0 == strncasecmp(buf, cmp, strlen(cmp)));

+}

+

diff --git a/mbtk/mbtk_lib/src/g711_pcm_convert.c b/mbtk/mbtk_lib/src/g711_pcm_convert.c
new file mode 100755
index 0000000..0927253
--- /dev/null
+++ b/mbtk/mbtk_lib/src/g711_pcm_convert.c
@@ -0,0 +1,351 @@
+#include "g711_pcm_convert.h"
+
+
+#define SIGN_BIT    (0x80)      /* Sign bit for a A-law byte. */
+#define QUANT_MASK  (0xf)       /* Quantization field mask. */
+#define NSEGS       (8)     /* Number of A-law segments. */
+#define SEG_SHIFT   (4)     /* Left shift for segment number. */
+#define SEG_MASK    (0x70)      /* Segment field mask. */
+#define BIAS        (0x84)      /* Bias for linear code. */
+
+
+static short seg_end[8] = {0xFF, 0x1FF, 0x3FF, 0x7FF,
+                           0xFFF, 0x1FFF, 0x3FFF, 0x7FFF
+                          };
+
+/* copy from CCITT G.711 specifications */
+unsigned char _u2a[128] = {         /* u- to A-law conversions */
+    1,  1,  2,  2,  3,  3,  4,  4,
+    5,  5,  6,  6,  7,  7,  8,  8,
+    9,  10, 11, 12, 13, 14, 15, 16,
+    17, 18, 19, 20, 21, 22, 23, 24,
+    25, 27, 29, 31, 33, 34, 35, 36,
+    37, 38, 39, 40, 41, 42, 43, 44,
+    46, 48, 49, 50, 51, 52, 53, 54,
+    55, 56, 57, 58, 59, 60, 61, 62,
+    64, 65, 66, 67, 68, 69, 70, 71,
+    72, 73, 74, 75, 76, 77, 78, 79,
+    81, 82, 83, 84, 85, 86, 87, 88,
+    89, 90, 91, 92, 93, 94, 95, 96,
+    97, 98, 99, 100,    101,    102,    103,    104,
+    105,    106,    107,    108,    109,    110,    111,    112,
+    113,    114,    115,    116,    117,    118,    119,    120,
+    121,    122,    123,    124,    125,    126,    127,    128
+};
+
+unsigned char _a2u[128] = {         /* A- to u-law conversions */
+    1,  3,  5,  7,  9,  11, 13, 15,
+    16, 17, 18, 19, 20, 21, 22, 23,
+    24, 25, 26, 27, 28, 29, 30, 31,
+    32, 32, 33, 33, 34, 34, 35, 35,
+    36, 37, 38, 39, 40, 41, 42, 43,
+    44, 45, 46, 47, 48, 48, 49, 49,
+    50, 51, 52, 53, 54, 55, 56, 57,
+    58, 59, 60, 61, 62, 63, 64, 64,
+    65, 66, 67, 68, 69, 70, 71, 72,
+    73, 74, 75, 76, 77, 78, 79, 79,
+    80, 81, 82, 83, 84, 85, 86, 87,
+    88, 89, 90, 91, 92, 93, 94, 95,
+    96, 97, 98, 99, 100,    101,    102,    103,
+    104,    105,    106,    107,    108,    109,    110,    111,
+    112,    113,    114,    115,    116,    117,    118,    119,
+    120,    121,    122,    123,    124,    125,    126,    127
+};
+
+
+static short search(short val, short* table, short size)
+{
+    short i;
+    for (i = 0; i < size; i++) {
+        if (val <= *table++) {
+            return (i);
+        }
+    }
+    return (size);
+}
+
+/**
+ * @brief Convert a 16-bit linear PCM value to 8-bit A-law
+ *
+ * linear2alaw() accepts an 16-bit integer and encodes it as A-law data.
+ *
+ *   Linear Input Code   Compressed Code
+ *  ------------------------    ---------------
+ *  0000000wxyza            000wxyz
+ *  0000001wxyza            001wxyz
+ *  000001wxyzab            010wxyz
+ *  00001wxyzabc            011wxyz
+ *  0001wxyzabcd            100wxyz
+ *  001wxyzabcde            101wxyz
+ *  01wxyzabcdef            110wxyz
+ *  1wxyzabcdefg            111wxyz
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+unsigned char linear2alaw(short pcm_val)    /* 2's complement (16-bit range) */
+{
+    short     mask;
+    short     seg;
+    unsigned char   aval;
+
+    if (pcm_val >= 0) {
+        mask = 0xD5;        /* sign (7th) bit = 1 */
+    } else {
+        mask = 0x55;        /* sign bit = 0 */
+        pcm_val = -pcm_val - 8;
+    }
+
+    /* Convert the scaled magnitude to segment number. */
+    seg = search(pcm_val, seg_end, 8);
+
+    /* Combine the sign, segment, and quantization bits. */
+
+    if (seg >= 8) {      /* out of range, return maximum value. */
+        return (0x7F ^ mask);
+    } else {
+        aval = seg << SEG_SHIFT;
+        if (seg < 2) {
+            aval |= (pcm_val >> 4) & QUANT_MASK;
+        } else {
+            aval |= (pcm_val >> (seg + 3)) & QUANT_MASK;
+        }
+        return (aval ^ mask);
+    }
+}
+
+/**
+ * @brief Convert an A-law value to 16-bit linear PCM
+ *
+ */
+short alaw2linear(unsigned char a_val)
+{
+    short     t;
+    short     seg;
+
+    a_val ^= 0x55;
+    t = (a_val & QUANT_MASK) << 4;
+    seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT;
+
+    switch (seg) {
+        case 0:
+            t += 8;
+            break;
+        case 1:
+            t += 0x108;
+            break;
+        default:
+            t += 0x108;
+            t <<= seg - 1;
+    }
+
+    return ((a_val & SIGN_BIT) ? t : -t);
+}
+
+/**
+ * @brief Convert a linear PCM value to u-law
+ *
+ * In order to simplify the encoding process, the original linear magnitude
+ * is biased by adding 33 which shifts the encoding range from (0 - 8158) to
+ * (33 - 8191). The result can be seen in the following encoding table:
+ *
+ *  Biased Linear Input Code    Compressed Code
+ *  ------------------------    ---------------
+ *  00000001wxyza           000wxyz
+ *  0000001wxyzab           001wxyz
+ *  000001wxyzabc           010wxyz
+ *  00001wxyzabcd           011wxyz
+ *  0001wxyzabcde           100wxyz
+ *  001wxyzabcdef           101wxyz
+ *  01wxyzabcdefg           110wxyz
+ *  1wxyzabcdefgh           111wxyz
+ *
+ * Each biased linear code has a leading 1 which identifies the segment
+ * number. The value of the segment number is equal to 7 minus the number
+ * of leading 0's. The quantization interval is directly available as the
+ * four bits wxyz.  * The trailing bits (a - h) are ignored.
+ *
+ * Ordinarily the complement of the resulting code word is used for
+ * transmission, and so the code word is complemented before it is returned.
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+unsigned char linear2ulaw(int pcm_val)    /* 2's complement (16-bit range) */
+{
+    short     mask;
+    short     seg;
+    unsigned char   uval;
+
+    /* Get the sign and the magnitude of the value. */
+    if (pcm_val < 0) {
+        pcm_val = BIAS - pcm_val;
+        mask = 0x7F;
+    } else {
+        pcm_val += BIAS;
+        mask = 0xFF;
+    }
+
+    /* Convert the scaled magnitude to segment number. */
+    seg = search(pcm_val, seg_end, 8);
+
+    /*
+     * Combine the sign, segment, quantization bits;
+     * and complement the code word.
+     */
+    if (seg >= 8) {      /* out of range, return maximum value. */
+        return (0x7F ^ mask);
+    } else {
+        uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF);
+        return (uval ^ mask);
+    }
+}
+
+/**
+ * @brief Convert a u-law value to 16-bit linear PCM
+ *
+ * First, a biased linear code is derived from the code word. An unbiased
+ * output can then be obtained by subtracting 33 from the biased code.
+ *
+ * Note that this function expects to be passed the complement of the
+ * original code word. This is in keeping with ISDN conventions.
+ */
+int  ulaw2linear(unsigned char u_val)
+{
+    short     t;
+
+    /* Complement to obtain normal u-law value. */
+    u_val = ~u_val;
+
+    /*
+     * Extract and bias the quantization bits. Then
+     * shift up by the segment number and subtract out the bias.
+     */
+    t = ((u_val & QUANT_MASK) << 3) + BIAS;
+    t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;
+
+    return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
+}
+
+/**
+ * @brief A-law to u-law conversion
+ *
+ * @param aval A-law value
+ * @return unsigned char u-law value
+ */
+unsigned char alaw2ulaw(unsigned char aval)
+{
+    aval &= 0xff;
+    return ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) :
+            (0x7F ^ _a2u[aval ^ 0x55]));
+}
+
+/**
+ * @brief u-law to A-law conversion
+ *
+ * @param uval u-law value
+ * @return unsigned char A-law value
+ */
+unsigned char ulaw2alaw(unsigned char uval)
+{
+    uval &= 0xff;
+    return ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) :
+            (0x55 ^ (_u2a[0x7F ^ uval] - 1)));
+}
+
+/**
+ * @brief pcm data encode to g711 data
+ *
+ *  user should be responsible for pCodecbit memmory
+ *
+ * @param pCodecBits store g711 encoded data
+ * @param pBuffer pcm raw data
+ * @param BufferSize pcm data len
+ * @param type g711 data type
+ * @return int encode data length
+ */
+int G711EnCode(char* pCodecBits, char* pBuffer, int BufferSize, enum g711type type)
+{
+    int i;
+    unsigned char* codecbits = (unsigned char*)pCodecBits;
+    short* buffer = (short*)pBuffer;
+
+    if (pCodecBits == 0 || pBuffer == 0 || BufferSize <= 0) {
+        return -1;
+    }
+
+    if (type == G711ALAW) {
+        for (i = 0; i < BufferSize / 2; i++)  {
+            codecbits[i] = linear2alaw(buffer[i]);
+        }
+    } else {
+        for (i = 0; i < BufferSize / 2; i++)  {
+            codecbits[i] = linear2ulaw(buffer[i]);
+        }
+    }
+
+    return BufferSize / 2;
+}
+
+/**
+ * @brief g711 data decode to pcm data
+ *
+ * user should be responsible for pRawData memmory
+ *
+ * @param pRawData store uncoded pcm data
+ * @param pBuffer g711 encoded data
+ * @param BufferSize g711 data len
+ * @param type g711 data type
+ * @return int pcm data len
+ */
+int G711Decode(char* pRawData, char* pBuffer, int BufferSize, enum g711type type)
+{
+    int i;
+    short* out_data = (short*)pRawData;
+    unsigned char* buffer = (unsigned char*)pBuffer;
+
+    if (pRawData == 0 || pBuffer == 0 || BufferSize <= 0) {
+        return -1;
+    }
+
+    if (type == G711ALAW) {
+        for (i = 0; i < BufferSize; i++) {
+            out_data[i] = alaw2linear(buffer[i]);
+        }
+    } else {
+        for (i = 0; i < BufferSize; i++) {
+            out_data[i] = ulaw2linear(buffer[i]);
+        }
+    }
+
+    return BufferSize * 2;
+}
+
+/**
+ * @brief g711 u-law data and a-law data convert
+ *
+ * @param alawdata g711 a-law data
+ * @param ulawdata g711 u-lwa data
+ * @param datasize input data length
+ * @param type target g711 data type
+ * @return int sucess:1; failed:0
+ */
+int G711TypeChange(unsigned char* alawdata, unsigned char* ulawdata, int datasize, enum g711type type)
+{
+    int i;
+
+    if (alawdata == 0 || ulawdata == 0 || datasize <= 0) {
+        return 0;
+    }
+
+    if (type == G711ALAW) {
+        for (i = 0; i < datasize; i++) {
+            alawdata[i] = ulaw2alaw(ulawdata[i]);
+        }
+    } else {
+        for (i = 0; i < datasize; i++) {
+            ulawdata[i] = alaw2ulaw(alawdata[i]);
+        }
+    }
+    return 1;
+}
diff --git a/mbtk/mbtk_lib/src/mbtk_adc.c b/mbtk/mbtk_lib/src/mbtk_adc.c
new file mode 100755
index 0000000..222d828
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_adc.c
@@ -0,0 +1,60 @@
+/**
+ *   \file mbtk_adc.c
+ *   \brief A Documented file.
+ *
+ *  Detailed description
+ *   \Author:  js.wang <js.wang@mobiletek.cn>
+ *   \Version: 1.0.0
+ *   \Date: 2022-04-22
+ */
+#include <fcntl.h>
+#include <stdint.h>
+#include <limits.h>
+#include <termios.h>
+#include <stdarg.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <sys/types.h>
+#include "mbtk_log.h"
+#include "mbtk_type.h"
+#include "mbtk_adc.h"
+
+#define ADC_DEVICE "/sys/devices/soc.0/d4000000.apb/pxa2xx-i2c.2/i2c-2/2-0030/pm802-bat/adc"
+
+int mbtk_adc_get(mbtk_adc_enum channle)
+{
+    int ret = 0;
+    int fd = 0;
+    char adc_buf[24] = {0};
+    char *adc_value = NULL;
+    char adc = (channle == MBTK_ADC0 ? '0' : '1');
+
+    fd = open(ADC_DEVICE, O_RDWR|O_CREAT|O_TRUNC, 0644);
+    if(fd < 0) {
+        LOGE("[%s]  file open error\n", __FUNCTION__);
+        return -2;
+    }
+    ret = write(fd, &adc, 1);
+    if (ret < 0) {
+        LOGE("%s: error writing to file!\n", __FUNCTION__);
+        close(fd);
+        return -2;
+    }
+    ret = read(fd, adc_buf, 24);
+    if (ret < 0) {
+        LOGE("%s: error writing to file!\n", __FUNCTION__);
+        close(fd);
+        return -2;
+    }else{
+        LOGI("%s %d adc:%s\n", __FUNCTION__, __LINE__, adc_buf);
+        adc_value = strstr(adc_buf, "channel");
+    }
+    close(fd);
+    if(adc_value)
+        LOGI("%s adc: %s\n", __FUNCTION__, adc_value);
+    else
+        return -2;
+
+    return atoi(&adc_value[9]);
+}
diff --git a/mbtk/mbtk_lib/src/mbtk_at.c b/mbtk/mbtk_lib/src/mbtk_at.c
new file mode 100755
index 0000000..8721a52
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_at.c
@@ -0,0 +1,207 @@
+#include <termios.h>
+#include <pthread.h>
+#include <sys/un.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <netinet/in.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include "mbtk_log.h"
+
+#define MBTK_AT_SOCK "/tmp/atcmd_at"
+#define TEMP_FAILURE_RETRY(exp) ({         \
+    typeof (exp) _rc;                      \
+    do {                                   \
+        _rc = (exp);                       \
+    } while (_rc == -1 && errno == EINTR); \
+    _rc; })
+
+static char *at_rsp_complete_tag[] = {
+        "OK",
+        "ERROR",
+        "CONNECT",
+		"+CMS ERROR:",
+		"+CME ERROR:",
+		"NO ANSWER",
+		"NO DIALTONE",
+		NULL};
+static int at_fd = -1;
+
+static int openSocket(const char* sockname)
+{
+	int sock = socket(AF_UNIX, SOCK_STREAM, 0);
+	if (sock < 0) {
+		LOGE("Error create socket: %s\n", strerror(errno));
+		return -1;
+	}
+	struct sockaddr_un addr;
+	memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	strncpy(addr.sun_path, sockname, sizeof(addr.sun_path));
+	while (TEMP_FAILURE_RETRY(connect(sock,(const struct sockaddr*)&addr, sizeof(addr))) != 0) {
+		LOGE("Error connect to socket %s: %s, try again", sockname, strerror(errno));
+		sleep(1);
+	}
+
+#if 0
+	int sk_flags = fcntl(sock, F_GETFL, 0);
+	fcntl(sock, F_SETFL, sk_flags | O_NONBLOCK);
+#endif
+
+	return sock;
+}
+
+
+static int at_complete(char *rsp)
+{
+#if 0
+    char *ptr = at_rsp_complete_tag;
+    while(ptr) {
+        LOGD("ptr = %s", ptr);
+        if(strstr(rsp, ptr)) {
+            LOGD("%s , %s", rsp, ptr);
+            return 1;
+        }
+        ptr++;
+    }
+#else
+    int i = 0;
+    while(at_rsp_complete_tag[i]) {
+        LOGD("ptr = %s", at_rsp_complete_tag[i]);
+        if(strstr(rsp, at_rsp_complete_tag[i])) {
+            LOGD("%s , %s", rsp, at_rsp_complete_tag[i]);
+            return 1;
+        }
+        i++;
+    }
+
+#endif
+    return 0;
+}
+
+static int at_rsp_read(char* rsp, int rsp_len)
+{
+    int len = 0;
+    int index = 0;
+    memset(rsp, 0x0, rsp_len);
+    while(1) {
+        if((len = read(at_fd, rsp + index, rsp_len - index)) > 0) {
+            if(at_complete(rsp)) {
+                LOGD("AT<%s", rsp);
+                return 0;
+            } else {
+                index += len;
+
+                if(index >= rsp_len) {
+                    LOGE("Buffer too small.");
+                    return -1;
+                }
+            }
+        } else {
+            printf("Read error:%d\n",errno);
+            return -1;
+        }
+    }
+}
+
+
+/*===========================================================================
+FUNCTION mbtk_at_init
+
+DESCRIPTION:
+  Initial MBTK AT.
+
+PARAMETERS:
+  None.
+
+RETURN VALUE:
+  int : Return 0 if success,other for failure.
+
+===========================================================================*/
+int mbtk_at_init()
+{
+    if(at_fd > 0) {
+        LOGW("MBTK AT has inited.");
+        return 0;
+    }
+
+    at_fd = openSocket(MBTK_AT_SOCK);
+    return at_fd > 0 ? 0 : -1;
+}
+
+
+/*===========================================================================
+FUNCTION mbtk_at_deinit
+
+DESCRIPTION:
+  Deinitial MBTK AT.
+
+PARAMETERS:
+  None.
+
+RETURN VALUE:
+  int : Return 0 if success,other for failure.
+
+===========================================================================*/
+int mbtk_at_deinit()
+{
+    if(at_fd < 0) {
+        LOGW("MBTK AT not inited.");
+        return 0;
+    }
+
+    close(at_fd);
+    at_fd = -1;
+    return 0;
+}
+
+/*===========================================================================
+FUNCTION mbtk_at_send
+
+DESCRIPTION:
+  Send AT command.
+
+PARAMETERS:
+  cmd [IN]: AT command.
+  rsp [OUT]: AT command response.
+  rsp_len[IN] : AT command response buffer size.
+
+RETURN VALUE:
+  int : Return 0 if success,other for failure.
+
+===========================================================================*/
+int mbtk_at_send(char* cmd, char* rsp, int rsp_len)
+{
+    if(cmd == NULL || strlen(cmd) == 0 || rsp == NULL || rsp_len <= 0) {
+        return -1;
+    }
+    char at_cmd[2048] = {0};
+    memcpy(at_cmd, cmd, strlen(cmd));
+    char *ptr = at_cmd + strlen(at_cmd) - 1;
+    while(ptr >= at_cmd && (*ptr == '\r' || *ptr == '\n'))
+    {
+        *ptr-- = '\0';
+    }
+    if(!strncasecmp(at_cmd, "at", 2))
+    {
+        LOGD("AT>%s", at_cmd);
+        *(++ptr) = '\r';
+        *(++ptr) = '\n';
+        if(write(at_fd, at_cmd, strlen(at_cmd)) != strlen(at_cmd)) {
+            LOGE("Write error:%d",errno);
+            return -1;
+        }
+
+        return at_rsp_read(rsp, rsp_len);
+    } else {
+        LOGE("AT command error:%s",at_cmd);
+        return -1;
+    }
+}
+
diff --git a/mbtk/mbtk_lib/src/mbtk_audio.c b/mbtk/mbtk_lib/src/mbtk_audio.c
new file mode 100755
index 0000000..5d4af20
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_audio.c
@@ -0,0 +1,861 @@
+/**
+ *   \file mbtk_audio.c
+ *   \brief A Documented file.
+ *
+ *  Detailed description
+ *   \Author:  js.wang <js.wang@mobiletek.cn>
+ *   \Version: 1.0.0
+ *   \Date: 2022-03-31
+ */
+#include <fcntl.h>
+#include <stdint.h>
+#include <limits.h>
+#include <termios.h>
+#include <stdarg.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <sys/types.h>
+#include "mbtk_log.h"
+#include "mbtk_type.h"
+#include <libubox/blobmsg_json.h>
+#include "libubus.h"
+#include "mbtk_audio.h"
+
+/******************************************************************************\
+ *  Macros / Defines
+\******************************************************************************/
+
+#ifndef UNUSEDPARAM
+#define UNUSEDPARAM(a)		(void)(a)
+#endif
+
+#define AUDIO_UBUS_REQUEST_NAME         "audio_if"
+#define mbtk_dtmf_UBUS_NAME	            "proslic_uBus"      //Used by wake lock
+#define DTMFCODEID                      "dtmfcode"
+#define DTMFCODE_NOTIFICATION_NAME      "audioif.dtmfcode"
+#define AUDIO_UBUS_VOLUME_SET           "volume_set"
+#define AUDIO_UBUS_VOLUME_GET           "volume_status"
+#define AUDIO_UBUS_MODE_SET             "audio_mode_set"
+#define AUDIO_UBUS_DSP_SET          	"config_dspgain"
+#define AUDIO_UBUS_LOOPBACK_EN          "loopback_enable"
+#define AUDIO_UBUS_LOOPBACK_DIS         "loopback_disable"
+
+// #define DEBUG 1
+
+#ifdef DEBUG
+	#define mbtk_audio_log(...)                    mbtk_audio_log(__VA_ARGS__)
+#else
+	#define mbtk_audio_log(...)
+#endif
+
+typedef enum {
+    PARAM_INT_POLICY_0,
+    PARAM_INT_POLICY_1,
+    PARAM_INT_POLICY_2,
+    PARAM_INT_POLICY_3,
+    PARAM_INT_POLICY_MAX,
+} PARAM_INT_POLICY;
+const struct blobmsg_policy int_policy[] ={
+	[PARAM_INT_POLICY_0] = {
+		.name = "param0",
+		.type = BLOBMSG_TYPE_INT32,
+	},
+};
+
+const struct blobmsg_policy dtmf_code_policy[] = {
+	[0] = {
+		.name = DTMFCODEID,
+		.type = BLOBMSG_TYPE_INT32,
+	},
+};
+
+static void mbtk_dtmf_add_subscriber_AudioIf(struct uloop_timeout *timeout);
+static struct uloop_timeout mbtk_dtmf_add_subscribe_timeout_AudioIf =
+{
+    .cb = mbtk_dtmf_add_subscriber_AudioIf,
+};
+
+static struct blob_buf audio_cm_b;
+pthread_mutex_t mbtk_dtmf_mutex;
+int  mbtk_dtmf_PMConstraintWorks;
+//For UBUS listening to RILD & audio_if
+struct mbtk_audio_ubus_db_t
+{
+    pthread_t  dtmf_pthread;
+    mbtk_dtmf_cb dtmf_cb;
+    mbtk_volume_cb audio_volume_get_cb;
+    int work_state;
+    struct ubus_context     *ctx;
+
+    /* Audio_If */
+    struct ubus_subscriber 	audioif_event;
+    uint32_t	 		    audioif_subscriber_id;
+    uint32_t	 		    audioif_request_id;
+};
+
+static struct mbtk_audio_ubus_db_t  *mbtk_audio_ubus_db = NULL;
+
+
+static int record_fd = 0;
+
+#define AUDIO_FILE_DIR	"/data"
+#define MBTK_AUD_DEMO_WAV "/data/demo.wav"
+
+int create_audio_dir(const char *dirname)
+{
+    DIR *p_dir;
+    int res = -1;
+
+    if(NULL == (p_dir = opendir((const char *)dirname)))
+    {
+        if(mkdir(dirname, 0777) == 0)
+        {
+            res = 0;
+        }
+        else
+        {
+            fprintf(stderr, "create audio dir error \n");
+            res = -1;
+        }
+    }
+    else
+    {
+        closedir(p_dir);
+        res = 0;
+    }
+    return res;
+}
+int mbtk_at_play(const char *args)
+{
+    int fd = 0;
+    int play_hdl = 0;
+    char databuf[1024];
+    int size;
+
+    LOGI("%s %d", __FUNCTION__, __LINE__);
+    // play_hdl = Ql_AudPlayer_Open(NULL, NULL);
+    play_hdl = mbtk_audio_open(0, 1, 8000, NULL);
+    if(0 == play_hdl)
+        printf("Ql_AudPlayer_Open fail\n");
+
+    fd = open(MBTK_AUD_DEMO_WAV, O_RDWR);
+    if (fd <= 0)
+    {
+        printf("file open error\n");
+        goto err;
+    }
+    memset(databuf, 0, sizeof(databuf));
+    while(0 < (size = read(fd, databuf, sizeof(databuf))))
+    {
+        if(-1 == mbtk_audio_play_stream(play_hdl, databuf, size))
+            break;
+    }
+    mbtk_audio_log("aplay Stream end \n");
+
+err:
+    if(fd > 0)
+        close(fd);
+    mbtk_audio_close(play_hdl);
+
+    return 0;
+}
+
+void record_cb_func(int cb_result, char* databuf, unsigned int len)
+{
+    int rc;
+
+    if(NULL != databuf && len > 0 && record_fd > 0)
+    {
+        //for debug:save into file
+        rc = write(record_fd, databuf, len);
+        if (rc < 0) {
+            printf("%s: error writing to file!\n", __FUNCTION__);
+        } else if (rc < len) {
+            printf("%s: wrote less the buffer size!\n", __FUNCTION__);
+        }
+    }
+}
+
+int mbtk_at_rec(const char *args)
+{
+    LOGI("%s %d", __FUNCTION__, __LINE__);
+    //hdl = Ql_AudRecorder_Open(NULL, record_cb_func);
+    void *record_hdl = mbtk_audio_open(1, 1, 8000, NULL);
+    if (record_hdl == NULL)
+    {
+        printf("AudRecorder open error\n");
+        return -1;
+    }
+
+    create_audio_dir(AUDIO_FILE_DIR);
+    record_fd = open(MBTK_AUD_DEMO_WAV, O_RDWR|O_CREAT|O_TRUNC, 0644);
+    if (record_fd <= 0)
+    {
+        printf("file open error\n");
+        goto err;
+    }
+
+    if(-1 == mbtk_audio_record(record_hdl, record_cb_func, NULL))
+    {
+        printf("file write error\n");
+        close(record_fd);
+        record_fd = 0;
+    }
+    sleep(10);
+err:
+    mbtk_audio_close(record_hdl);
+    record_hdl = NULL;
+    if(record_fd > 0)
+    {
+        close(record_fd);
+        record_fd = 0;
+    }
+
+    return 0;
+}
+
+static void mbtk_ubus_complete_cb(struct ubus_request *req, int rc)
+{
+    if(NULL == mbtk_audio_ubus_db)
+    {
+        printf("mbtk_dtmf_ubus not init!\n");
+        return;
+    }
+    if (req)
+    {
+        free(req);
+        req = NULL;
+    }
+    mbtk_audio_log("%s do nothing, rc=%d\n", __FUNCTION__, rc);
+    LOGI("%s do nothing, rc=%d\n", __FUNCTION__, rc);
+    if(mbtk_audio_ubus_db->work_state)
+        mbtk_audio_ubus_db->work_state--;
+}
+/**
+ * @brief      mbtk_audio_mode_set
+ *
+ * @details    detailed description
+ *
+ * @param      param
+ *             "param0": UINT32
+ *             typedef enum {
+ *                 AUDIO_MODE_INVALID = -2,
+ *                 AUDIO_MODE_CURRENT = -1,
+ *                 AUDIO_MODE_NORMAL = 0,
+ *                 AUDIO_MODE_RINGTONE = 1,
+ *                 AUDIO_MODE_IN_CALL = 2,
+ *                 AUDIO_MODE_IN_COMMUNICATION=3,
+ *                 AUDIO_MODE_IN_VT_CALL= 4,
+ *                 AUDIO_MODE_CNT,
+ *                 AUDIO_MODE_MAX = AUDIO_MODE_CNT-1,
+ *             } audio_mode_
+ * @return     return type
+ */
+static void mbtk_audio_mode_set(int mode)
+{
+    int rc = 0;
+    struct ubus_request *req = NULL;
+
+    if(NULL == mbtk_audio_ubus_db)
+    {
+        printf("mbtk_dtmf_ubus not init!\n");
+        return;
+    }
+    req = (struct ubus_request *)malloc(sizeof(struct ubus_request));
+    if (req == NULL)
+    {
+        printf("leave %s: lack of memory\n", __FUNCTION__);
+        return;
+    }
+    memset(req, 0, sizeof(struct ubus_request));
+    blob_buf_init(&audio_cm_b, 0);
+    blobmsg_add_u32(&audio_cm_b, "param0", mode);
+    if ((rc = ubus_invoke_async(mbtk_audio_ubus_db->ctx,
+                                mbtk_audio_ubus_db->audioif_request_id,
+                                AUDIO_UBUS_MODE_SET,
+                                audio_cm_b.head, req)) != UBUS_STATUS_OK)
+    {
+        free(req);
+        printf("%s, ubus_invoke_async %s failed %s\n", __FUNCTION__, AUDIO_UBUS_MODE_SET, ubus_strerror(rc));
+    }
+    else
+    {
+        printf("%s: ubus_invoke_async success\n", __FUNCTION__);
+        mbtk_audio_ubus_db->work_state++;
+        req->complete_cb = mbtk_ubus_complete_cb;
+        ubus_complete_request_async(mbtk_audio_ubus_db->ctx, req);
+    }
+}
+int mbtk_audio_dsp_set(int type, int gain)
+{
+    LOGE("1type:%d, gain:%d\n", type, gain);
+    int rc = 0;
+    struct ubus_request *req = NULL;
+
+    if(NULL == mbtk_audio_ubus_db)
+    {
+        LOGE("mbtk_dtmf_ubus not init!\n");
+        return -1;
+    }
+    req = (struct ubus_request *)malloc(sizeof(struct ubus_request));
+    if (req == NULL)
+    {
+        LOGE("leave %s: lack of memory\n", __FUNCTION__);
+        return -1;
+    }
+
+    memset(req, 0, sizeof(struct ubus_request));
+    blob_buf_init(&audio_cm_b, 0);
+    blobmsg_add_u32(&audio_cm_b, "type", type);
+	blobmsg_add_u32(&audio_cm_b, "gain", gain);
+    if ((rc = ubus_invoke_async(mbtk_audio_ubus_db->ctx,
+                                mbtk_audio_ubus_db->audioif_request_id,
+                                AUDIO_UBUS_DSP_SET,
+                                audio_cm_b.head, req)) != UBUS_STATUS_OK)
+    {
+        free(req);
+        LOGE("%s, ubus_invoke_async %s failed %s\n", __FUNCTION__, AUDIO_UBUS_DSP_SET, ubus_strerror(rc));
+        return -1;
+    }
+    else
+    {
+        LOGE("%s: ubus_invoke_async success\n", __FUNCTION__);
+        mbtk_audio_ubus_db->work_state++;
+        req->complete_cb = mbtk_ubus_complete_cb;
+        ubus_complete_request_async(mbtk_audio_ubus_db->ctx, req);
+    }
+    return 0;
+}
+/**
+ * @brief      mbtk_audio_loopback_start
+ *
+ * @details    detailed description
+ *
+ * @param      param
+ *             device: UINT32
+ *             0: earpiece
+ *             1: speaker
+ *             2: headset
+ * @return     return type
+ */
+static void mbtk_audio_loopback_start(int device)
+{
+    int rc = 0;
+    struct ubus_request *req = NULL;
+
+    if(NULL == mbtk_audio_ubus_db)
+    {
+        LOGI("mbtk_dtmf_ubus not init!\n");
+        return;
+    }
+    req = (struct ubus_request *)malloc(sizeof(struct ubus_request));
+    if (req == NULL)
+    {
+        LOGI("leave %s: lack of memory\n", __FUNCTION__);
+        return;
+    }
+    memset(req, 0, sizeof(struct ubus_request));
+    blob_buf_init(&audio_cm_b, 0);
+    blobmsg_add_u32(&audio_cm_b, "param0", device);
+    if ((rc = ubus_invoke_async(mbtk_audio_ubus_db->ctx,
+                                mbtk_audio_ubus_db->audioif_request_id,
+                                AUDIO_UBUS_LOOPBACK_EN,
+                                audio_cm_b.head, req)) != UBUS_STATUS_OK)
+    {
+        free(req);
+        LOGI("%s, ubus_invoke_async volume get failed %s\n", __FUNCTION__,  ubus_strerror(rc));
+    }
+    else
+    {
+        LOGI("%s: ubus_invoke_async success\n", __FUNCTION__);
+        req->complete_cb = mbtk_ubus_complete_cb;
+        ubus_complete_request_async(mbtk_audio_ubus_db->ctx, req);
+    }
+}
+
+static void mbtk_audio_loopback_stop(void)
+{
+    int rc = 0;
+
+    if(NULL == mbtk_audio_ubus_db)
+    {
+        printf("mbtk_dtmf_ubus not init!\n");
+        return;
+    }
+    blob_buf_init(&audio_cm_b, 0);
+
+    if ((rc = ubus_invoke(mbtk_audio_ubus_db->ctx,
+                          mbtk_audio_ubus_db->audioif_request_id,
+                          AUDIO_UBUS_LOOPBACK_DIS,
+                          audio_cm_b.head, NULL, 0, 0)) != UBUS_STATUS_OK)
+    {
+        LOGI("%s, ubus_invoke volume get failed %s\n", __FUNCTION__,  ubus_strerror(rc));
+    }
+    else
+    {
+        LOGI("%s: ubus_invoke success\n", __FUNCTION__);
+    }
+}
+
+int mbtk_at_loopback(int type)
+{
+    static mbtk_audio_client_handle_type audio_handle = 0;
+    int ret = 0;
+
+    LOGI("%s %d:%d", __FUNCTION__, __LINE__, type);
+    if(0 == type) // Stop
+    {
+        mbtk_audio_loopback_stop();
+        if(audio_handle)
+        {
+            mbtk_audio_ubus_client_deinit(audio_handle);
+            audio_handle = 0;
+        }
+    }
+    else // Start
+    {
+        if(NULL == mbtk_audio_ubus_db)
+            ret = mbtk_audio_ubus_client_init(&audio_handle, NULL);
+        if(ret)
+        {
+            return -1;
+        }
+        mbtk_audio_mode_set(0);
+        mbtk_audio_loopback_start(2);
+    }
+
+    return 0;
+}
+
+/*****************************************************  \
+ *   Retry mechanism to add subscriber for Audio_if
+\*****************************************************/
+static void mbtk_dtmf_add_subscriber_AudioIf(struct uloop_timeout *timeout)
+{
+    /* add subscriber for Audio_If */
+    if (ubus_lookup_id(mbtk_audio_ubus_db->ctx, DTMFCODE_NOTIFICATION_NAME, &mbtk_audio_ubus_db->audioif_subscriber_id))
+    {
+        printf("%s, Failed to look up %s\n", __FUNCTION__, DTMFCODE_NOTIFICATION_NAME);
+        uloop_timeout_set(timeout, 2000);
+        return;
+    }
+
+    ubus_subscribe(mbtk_audio_ubus_db->ctx, &mbtk_audio_ubus_db->audioif_event, mbtk_audio_ubus_db->audioif_subscriber_id);
+    mbtk_audio_log("Audio_If subscribe %s object ok\n", DTMFCODE_NOTIFICATION_NAME);
+    return;
+}
+/*******************************************************************************\
+*   Function:      mbtk_dtmf_uBus_pmConstraint
+*   Description:   Lock/unlock system power management.
+*                   0: unlock
+*                   1: lock
+*   Returns:
+\*******************************************************************************/
+static void mbtk_dtmf_uBus_PMConstraint(int set)
+{
+    FILE *flk;
+    if (mbtk_dtmf_PMConstraintWorks != 0)
+        return; //wake_lock unavailable or not exist. Do nothing
+
+#ifdef PROSLIC_ENABLEFREERUN
+    mbtk_audio_log("%s(%d): %s\n", __FUNCTION__, set, set ? "no sleep" : "sleep after 7s");
+#else
+    mbtk_audio_log("%s(%d): free-run is disabled, no sleep\n", __FUNCTION__, set);
+#endif
+
+    /* Do Not check fopen success. It must be ok, if failed
+    ** (for any reason) better to crash for logging and recovery
+    */
+    flk = fopen("/sys/power/wake_lock", "w");
+    if(set)
+    {
+        fprintf(flk, mbtk_dtmf_UBUS_NAME);
+    }
+    else
+    {
+#ifdef PROSLIC_ENABLEFREERUN
+        /* Clear constraint but with TimeOut enough
+        ** Stay awake for (mbtk_dtmf_DIAL_TIMER + 1) seconds, wait for ringing timer expire
+        */
+        fprintf(flk, mbtk_dtmf_UBUS_NAME " 7000000000"/*nsec*/);
+#endif
+    }
+
+    fclose(flk);
+}
+
+/*******************************************************************************\
+*   Function:       mbtk_dtmf_uBusInd_AudioIf_remove
+*   Description:    Handle upon Audio_if remove indication.
+*   Returns:
+\*******************************************************************************/
+static void mbtk_dtmf_uBusInd_AudioIf_remove(struct ubus_context *ctx, struct ubus_subscriber *s,
+                              uint32_t id)
+{
+    UNUSEDPARAM(ctx);
+    UNUSEDPARAM(s);
+    UNUSEDPARAM(id);
+
+    mbtk_audio_log("%s\n", __FUNCTION__);
+    uloop_timeout_set(&mbtk_dtmf_add_subscribe_timeout_AudioIf, 2000);
+}
+
+
+/*******************************************************************************\
+*   Function:       mbtk_dtmf_uBusInd_AudioIf
+*   Description:    Handle upon Audio_If incoming indication.
+*   Returns:        0 on success
+\*******************************************************************************/
+static int mbtk_dtmf_uBusInd_AudioIf(struct ubus_context *ctx, struct ubus_object *obj,
+                               struct ubus_request_data *req, const char *method,
+                               struct blob_attr *msg)
+{
+    UNUSEDPARAM(ctx);
+    UNUSEDPARAM(obj);
+    UNUSEDPARAM(req);
+    UNUSEDPARAM(method);
+
+    char DTMFCode = 0;
+    struct blob_attr *tb[1];
+    int rc = 0;
+
+    //Lock from mbtk_dtmf interrupt handler (mbtk_dtmf_MainLoop)
+    pthread_mutex_lock(&mbtk_dtmf_mutex);
+    mbtk_audio_log("==================================%s : Begin==================================\n", __FUNCTION__);
+    mbtk_dtmf_uBus_PMConstraint(1);
+
+    /*parsing blob to be accessed easily with tb array - parse "1" argument*/
+    rc = blobmsg_parse(dtmf_code_policy, 1, tb, blob_data(msg), blob_len(msg));
+
+    if (rc < 0) {
+    	printf("%s: parsing fail, rc = 0x%x\n", __FUNCTION__, rc);
+    }
+    else {
+        DTMFCode = (char)blobmsg_get_u32(tb[0]);
+        if(mbtk_audio_ubus_db->dtmf_cb)
+            mbtk_audio_ubus_db->dtmf_cb(DTMFCode);
+        mbtk_audio_log("%s got DTMF Code: Char[%c]=ASCII[0x%x]\n", __FUNCTION__, DTMFCode, DTMFCode);
+    }
+
+    mbtk_dtmf_uBus_PMConstraint(0);
+    mbtk_audio_log("==================================%s : End==================================", __FUNCTION__);
+    pthread_mutex_unlock(&mbtk_dtmf_mutex);
+
+    return rc;
+}
+
+/*******************************************************************************\
+*   Function:       mbtk_dtmf_uBusRegisterNotifications
+*   Description:    Register UBUS notifications of RIL and Audio_If.
+*   Returns:        0 on success
+\*******************************************************************************/
+static int mbtk_dtmf_uBusRegisterNotifications(void)
+{
+    int ret = 0;
+
+    /* register UBUS notifications of Audio_If */
+    ret = ubus_register_subscriber(mbtk_audio_ubus_db->ctx, &mbtk_audio_ubus_db->audioif_event);
+    if (ret)
+    {
+        printf("%s Failed to add audio_if watch handler: %s\n", __FUNCTION__, ubus_strerror(ret));
+        return -1;
+    }
+
+    /* specify callback to handle notifications */
+    mbtk_audio_ubus_db->audioif_event.remove_cb = mbtk_dtmf_uBusInd_AudioIf_remove;
+    mbtk_audio_ubus_db->audioif_event.cb = mbtk_dtmf_uBusInd_AudioIf;
+
+    /* add subscribe */
+    uloop_timeout_set(&mbtk_dtmf_add_subscribe_timeout_AudioIf, 0);
+
+    mbtk_audio_log("%s: Make use of DTMF detection in modem.\n", __FUNCTION__);
+
+    return 0;
+}
+
+void mbtk_switch_dtmf_detection_cb(struct ubus_request *req, int rc)
+{
+    if (req)
+    {
+        free(req);
+        req = NULL;
+    }
+    mbtk_audio_log("%s do nothing, rc=%d\n", __FUNCTION__, rc);
+}
+/**
+ * @brief      mbtk_switch_dtmf_detection
+ *
+ * @details    detailed description
+ *
+ * @param      param
+ *            “param0": UINT32 onoff
+ *              0: disbale Tx DTMF detection
+ *              1: enable Tx DTMF detection
+ *              2: disbale Rx DTMF detection
+ *              3: enable Rx DTMF detection
+ * @return     return type
+ */
+void mbtk_switch_dtmf_detection(unsigned short on_off, unsigned short param1,
+                                unsigned short param2, unsigned short param3)
+{
+#if PCM_CTRL_OVER_VCM
+    vcm_DTMFDetection(on_off, param1, param2, param3);
+#else
+    int rc = 0;
+    struct ubus_request *req = NULL;
+
+    if(NULL == mbtk_audio_ubus_db)
+    {
+        printf("mbtk_dtmf_ubus not init!\n");
+        return;
+    }
+    req = (struct ubus_request *)malloc(sizeof(struct ubus_request));
+    if (req == NULL)
+    {
+        printf("leave %s: lack of memory\n", __FUNCTION__);
+        return;
+    }
+    memset(req, 0, sizeof(struct ubus_request));
+    blob_buf_init(&audio_cm_b, 0);
+    blobmsg_add_u32(&audio_cm_b, "param0", on_off);
+    blobmsg_add_u32(&audio_cm_b, "param1", param1);
+    blobmsg_add_u32(&audio_cm_b, "param2", param2);
+    blobmsg_add_u32(&audio_cm_b, "param3", param3);
+
+    if ((rc = ubus_invoke_async(mbtk_audio_ubus_db->ctx, mbtk_audio_ubus_db->audioif_request_id, "dtmf_detection", audio_cm_b.head, req)) != UBUS_STATUS_OK)
+    {
+        free(req);
+        printf("%s, ubus_invoke dtmf_detection failed %s\n", __FUNCTION__,  ubus_strerror(rc));
+    }
+    else
+    {
+        mbtk_audio_log("%s: ubus_invoke_async success\n", __FUNCTION__);
+        req->complete_cb = mbtk_switch_dtmf_detection_cb;
+        ubus_complete_request_async(mbtk_audio_ubus_db->ctx, req);
+    }
+#endif
+}
+/*******************************************************************************\
+*   Function:       mbtk_dtmf_uBusInit
+*   Description:    Init UBUS context and register UBUS notifications of RIL.
+*   Returns:        0 on success
+\*******************************************************************************/
+int mbtk_dtmf_uBusInit(void)
+{
+    int rc = 0;
+
+    uloop_init();
+
+    mbtk_audio_ubus_db->ctx = ubus_connect(NULL);
+    if (mbtk_audio_ubus_db->ctx == NULL)
+    {
+        printf(" %s Failed to connect to ubus\n", __FUNCTION__);
+        return -1;
+    }
+
+    ubus_add_uloop(mbtk_audio_ubus_db->ctx);
+
+    /* lookup fail if audio_if is not ready */
+    while(1)
+    {
+        rc = ubus_lookup_id(mbtk_audio_ubus_db->ctx, AUDIO_UBUS_REQUEST_NAME, &mbtk_audio_ubus_db->audioif_request_id);
+        if (0 != rc)
+        {
+            printf("%s, Failed to look up(%s), rc=%d\n", __FUNCTION__, AUDIO_UBUS_REQUEST_NAME, rc);
+            usleep(600000); //600ms
+        }
+        else
+            break;
+    }
+    mbtk_audio_log("%s, ubus_lookup_id(%s) OK\n", __FUNCTION__, AUDIO_UBUS_REQUEST_NAME);
+
+    /* subscribe for RIL & audio_if notifications */
+    if (mbtk_dtmf_uBusRegisterNotifications() != 0)
+        return -1;
+
+    mbtk_audio_log("%s success!\n", __FUNCTION__);
+    return 0;
+}
+
+
+/*******************************************************************************\
+*   Function:       mbtk_dtmf_uBusRun
+*   Description:    Start running of UBUS.
+*   Returns:
+\*******************************************************************************/
+static void mbtk_audio_ubus_thread(void* hdl)
+{
+    pthread_detach(pthread_self());
+    uloop_run();
+    mbtk_audio_log("%s exit!\n", __FUNCTION__);
+    pthread_exit(NULL);
+}
+
+int mbtk_audio_ubus_client_init(mbtk_audio_client_handle_type *ph_audio, mbtk_dtmf_cb cb)
+{
+    int id;
+
+    // Set call handle.
+    if(ph_audio == NULL || mbtk_audio_ubus_db != NULL)
+    {
+        printf("ARG error or has inited.");
+        return -1;
+    }
+    mbtk_audio_ubus_db = malloc(sizeof(struct mbtk_audio_ubus_db_t));
+    if(NULL == mbtk_audio_ubus_db)
+    {
+        printf("malloc memory error\n");
+    }
+    memset(mbtk_audio_ubus_db, 0, sizeof(struct mbtk_audio_ubus_db_t));
+
+	mbtk_dtmf_PMConstraintWorks = access("/sys/power/wake_lock", R_OK | W_OK);
+    mbtk_dtmf_uBusInit();
+    mbtk_audio_ubus_db->dtmf_cb = cb;
+
+    mbtk_switch_dtmf_detection(3, 50, 4, 3);
+    pthread_create(&mbtk_audio_ubus_db->dtmf_pthread, NULL, (void *)mbtk_audio_ubus_thread, (void *)mbtk_audio_ubus_db);
+
+    *ph_audio = mbtk_audio_ubus_db;
+    LOGI("%s %d:%x", __FUNCTION__, __LINE__, mbtk_audio_ubus_db);
+
+    return 0;
+}
+
+int mbtk_audio_ubus_client_deinit(mbtk_audio_client_handle_type h_audio)
+{
+    int ret;
+    struct mbtk_audio_ubus_db_t *audio_ubus = (struct mbtk_audio_ubus_db_t *)h_audio;
+
+    mbtk_audio_log("%s \n", __FUNCTION__);
+    if(h_audio == NULL || mbtk_audio_ubus_db == NULL)
+    {
+        printf("ARG error or not inited.");
+        return -1;
+    }
+    LOGI("%s %d:%x", __FUNCTION__, __LINE__, audio_ubus);
+    ret = pthread_cancel(audio_ubus->dtmf_pthread);
+    mbtk_audio_log("kill pthread : %d \n", ret);
+    pthread_join(audio_ubus->dtmf_pthread, NULL);
+    do{
+        ret = pthread_kill(audio_ubus->dtmf_pthread, 0);
+        mbtk_audio_log("kill pthread: %d \n", ret);
+        if(ret == ESRCH)
+            mbtk_audio_log("The specified thread does not exist or has terminated\n");
+        else if(ret == EINVAL)
+            mbtk_audio_log("Useless signal\n");
+        else
+            mbtk_audio_log("The thread exists\n");
+        usleep(100000);
+    }while(0 == ret);
+    LOGI("%s %d", __FUNCTION__, __LINE__);
+
+    mbtk_switch_dtmf_detection(2, 0, 0, 0);
+    /*unregister uloop*/
+    /*thread will get here only if uloop stopped*/
+    ubus_free(mbtk_audio_ubus_db->ctx);
+    uloop_done();
+    LOGI("%s %d", __FUNCTION__, __LINE__);
+    free(h_audio);
+    mbtk_audio_ubus_db = NULL;
+
+    return 0;
+}
+
+/**********************************************************************\
+*   Function:      APP_Audio_VolumeSet
+*   Description:   This function is called to send command into audio_if.
+*   unsigned int volume(0~100)
+*   Returns:        0 on success
+\**********************************************************************/
+void mbtk_audio_ubus_volume_set(unsigned int volume)
+{
+    int rc = 0;
+    struct ubus_request *req = NULL;
+
+    if(NULL == mbtk_audio_ubus_db)
+    {
+        printf("mbtk_dtmf_ubus not init!\n");
+        return;
+    }
+    req = (struct ubus_request *)malloc(sizeof(struct ubus_request));
+    if (req == NULL)
+    {
+        printf("leave %s: lack of memory\n", __FUNCTION__);
+        return;
+    }
+    memset(req, 0, sizeof(struct ubus_request));
+    blob_buf_init(&audio_cm_b, 0);
+    blobmsg_add_u32(&audio_cm_b, "param0", volume);
+
+    if ((rc = ubus_invoke_async(mbtk_audio_ubus_db->ctx, mbtk_audio_ubus_db->audioif_request_id, AUDIO_UBUS_VOLUME_SET, audio_cm_b.head, req)) != UBUS_STATUS_OK)
+    {
+        free(req);
+        printf("%s, ubus_invoke_async volume set failed: %s\n", __FUNCTION__,  ubus_strerror(rc));
+    }
+    else
+    {
+        mbtk_audio_log("%s: ubus_invoke_async success\n", __FUNCTION__);
+        req->complete_cb = mbtk_ubus_complete_cb;
+        ubus_complete_request_async(mbtk_audio_ubus_db->ctx, req);
+    }
+}
+
+static void audio_volume_get_data_cb (struct ubus_request *req, int type, struct blob_attr *msg)
+{
+    UNUSEDPARAM(req);
+    UNUSEDPARAM(type);
+    struct blob_attr *tb[1];
+    unsigned int volume = 888;
+    int rc = 0;
+
+    rc = blobmsg_parse(int_policy, 1, tb, blob_data(msg), blob_len(msg));
+    if (rc < 0)
+    {
+    	printf("[%s] parsing blobmsg failed\n", __FUNCTION__);
+    	return;
+    }
+
+    if(tb[0])
+        volume = blobmsg_get_u32(tb[0]);
+    if (mbtk_audio_ubus_db->audio_volume_get_cb)
+        mbtk_audio_ubus_db->audio_volume_get_cb(volume);
+    mbtk_audio_log("%s; volume:%d\n", __FUNCTION__, volume);
+}
+
+void mbtk_audio_ubus_volume_get(mbtk_volume_cb cb)
+{
+    int rc = 0;
+    struct ubus_request *req = NULL;
+
+    if(NULL == mbtk_audio_ubus_db ||  NULL == cb)
+    {
+        printf("mbtk_dtmf_ubus not init or callback invalid!\n");
+        return;
+    }
+    mbtk_audio_ubus_db->audio_volume_get_cb = cb;
+    req = (struct ubus_request *)malloc(sizeof(struct ubus_request));
+    if (req == NULL)
+    {
+        printf("leave %s: lack of memory\n", __FUNCTION__);
+        return;
+    }
+    memset(req, 0, sizeof(struct ubus_request));
+    blob_buf_init(&audio_cm_b, 0);
+
+    if ((rc = ubus_invoke_async(mbtk_audio_ubus_db->ctx,
+                                mbtk_audio_ubus_db->audioif_request_id,
+                                AUDIO_UBUS_VOLUME_GET,
+                                audio_cm_b.head, req)) != UBUS_STATUS_OK)
+    {
+        free(req);
+        printf("%s, ubus_invoke_async volume get failed %s\n", __FUNCTION__,  ubus_strerror(rc));
+    }
+    else
+    {
+        mbtk_audio_log("%s: ubus_invoke_async success\n", __FUNCTION__);
+        req->data_cb = audio_volume_get_data_cb;
+        req->complete_cb = mbtk_ubus_complete_cb;
+        ubus_complete_request_async(mbtk_audio_ubus_db->ctx, req);
+    }
+}
diff --git a/mbtk/mbtk_lib/src/mbtk_audio_alsa.c b/mbtk/mbtk_lib/src/mbtk_audio_alsa.c
new file mode 100755
index 0000000..405ceda
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_audio_alsa.c
@@ -0,0 +1,826 @@
+/**
+ *   \file mbtk_audio_alsa.c
+ *   \brief A Documented file.
+ *
+ *  Detailed description
+ *   \Author:  js.wang <js.wang@mobiletek.cn>
+ *   \Version: 1.0.0
+ *   \Date: 2020-09-21
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+
+#include "audio_if_types.h"
+#include "audio_if_ubus.h"
+#include "audio_if_parameter.h"
+#include "audio_if.h"
+#include "audio_if_api.h"
+#include <unistd.h>
+#include <sys/stat.h>
+#include "audio_if_audio_hw_mrvl.h"
+#include "audio_hw_mrvl.h"
+#include <cutils/str_parms.h>
+#include "vcm.h"
+#include "voice_control.h"
+#include "mbtk_audio.h"
+
+
+// #define DEBUG 1
+
+#ifdef DEBUG
+	#define mbtk_audio_log(...)                    printf(__VA_ARGS__)
+#else
+	#define mbtk_audio_log(...)
+#endif
+
+typedef int (*_play_callback)(int hdl, int result);
+
+#define FAILURE -1
+#define ID_RIFF 0x46464952
+#define ID_WAVE 0x45564157
+#define ID_FMT  0x20746d66
+#define ID_DATA 0x61746164
+#define FORMAT_PCM 1
+
+
+
+struct mopen_audio_t
+{
+    int device;
+    audio_hw_device_t *audio_ahw_dev_ubus;
+    struct audio_stream_in *stream_in;
+    struct audio_stream_out *stream_out;
+    int pcm_packet_size;     //320:NB, 640:WB
+    int pipe_data;
+    int fd[2];
+    pthread_t pid;
+    mbtk_audio_state_enum state;
+    pthread_mutex_t _cond_mutex;
+    pthread_mutex_t _stream_mutex;
+    void *usrData;
+};
+struct record_cb_s
+{
+    mbtk_audio_record_cb_func _cb;
+    void *cb_data;
+};
+
+static struct mopen_audio_t *internal_hdl = NULL;
+
+
+int mbtk_wav_pcm16Le_check(int fd)
+{
+    printf("MBTK_wav_pcm16Le_check()----------strart5, fd:%d\n", fd);
+    struct wav_header hdr;
+
+    if (fd <= 0)
+        return -1;
+
+    if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr))
+    {
+        printf("\n%s: cannot read header\n", __FUNCTION__);
+        return -1;
+    }
+
+        printf("hdr.riff_id:%X, hdr.riff_fmt:%X, hdr.fmt_id:%X", hdr.riff_id, hdr.riff_fmt, hdr.fmt_id);
+
+    if ((hdr.riff_id != ID_RIFF)
+            || (hdr.riff_fmt != ID_WAVE)
+            || (hdr.fmt_id != ID_FMT))
+    {
+        printf("\n%s: is not a riff/wave file\n", __FUNCTION__);
+        return -1;
+    }
+
+    if ((hdr.audio_format != FORMAT_PCM) || (hdr.fmt_sz != 16)) {
+        printf("\n%s: is not pcm format\n", __FUNCTION__);
+        return -1;
+    }
+
+    if (hdr.bits_per_sample != 16) {
+        printf("\n%s: is not 16bit per sample\n", __FUNCTION__);
+        return -1;
+    }
+
+    printf("audio_format: %d,num_channels: %d,sample_rate: %d,byte_rate: %d,bits_per_sample:  %d data_sz: %d\n",
+           hdr.audio_format, hdr.num_channels, hdr.sample_rate, hdr.byte_rate, hdr.bits_per_sample, hdr.data_sz);
+
+    return hdr.data_sz;
+}
+
+
+static void simulateOffhook(struct mopen_audio_t *aud_hdl, unsigned int onoff)
+{
+    unsigned int pcm_on;
+    unsigned int DTMFDetectiononoff;
+    unsigned int dialToneToOthersTones;
+    unsigned int dialTonesToOthersDialTones;
+    unsigned int dialVadDuration;
+
+    pcm_on = onoff;
+    //send the command of "AUDIOSTUB_PCMCTL"
+    aud_hdl->audio_ahw_dev_ubus->switch_pcm(aud_hdl->audio_ahw_dev_ubus, pcm_on);
+
+    DTMFDetectiononoff = onoff;
+    dialToneToOthersTones = 50;
+    dialTonesToOthersDialTones = 4;
+    dialVadDuration = 3;
+    //send the command of "AUDIOSTUB_DTMFDETECTIONCTL"
+    //vcm_DTMFDetection(1, 50, 4, 3);
+    vcm_DTMFDetection(DTMFDetectiononoff, dialToneToOthersTones, dialTonesToOthersDialTones, dialVadDuration);
+
+    return;
+}
+static int config_parameters(int in_out, int NBWB)
+{
+    unsigned int direction = 0xFF, type, srcdst, priority, dest;
+    char kvpair[128];
+    struct str_parms *param = NULL;
+    int data[5];
+    const char *key = NULL;
+    bool update_vcm = false;
+
+    direction = in_out;/* 0-play, 1-record */
+    type = NBWB; /* 0:PCM_NB_BUF_SIZE, 1:PCM_WB_BUF_SIZE */
+    srcdst = 1;/* 0-None, 1-Near end, 2-Far end, 3-Both ends */
+    priority = 1;/* 0-Do not combine(override), 1-Combine */
+    dest = 1;/* 0-Near codec, 1-Near Vocoder */
+
+    memset(kvpair, 0x00, sizeof(kvpair));
+    sprintf(kvpair, "%s=%d;%s=%d;%s=%d;%s=%d;%s=%d", VCM_CONFIG_DIRECTION, direction,
+            VCM_CONFIG_TYPE, type, VCM_CONFIG_SRC_DST, srcdst,
+            VCM_CONFIG_PRIORITY, priority, VCM_CONFIG_DEST, dest);
+
+    mbtk_audio_log("%s: config information kvpair is %s.\n", __FUNCTION__, kvpair);
+
+    //extract the parameter and config from string
+    param = str_parms_create_str(kvpair);
+    if (!param) {
+        printf("%s: param create str is null!", __FUNCTION__);
+        return -1;
+    }
+
+    //set vcm configurations
+    key = VCM_CONFIG_DIRECTION;
+    if (str_parms_get_int(param, key, &data[0]) == 0) {
+        update_vcm = true;
+        str_parms_del(param, key);
+    }
+    key = VCM_CONFIG_TYPE;
+    if (str_parms_get_int(param, key, &data[1]) == 0) {
+        update_vcm = true;
+        str_parms_del(param, key);
+    }
+    key = VCM_CONFIG_SRC_DST;
+    if (str_parms_get_int(param, key, &data[2]) == 0) {
+        update_vcm = true;
+        str_parms_del(param, key);
+    }
+    key = VCM_CONFIG_PRIORITY;
+    if (str_parms_get_int(param, key, &data[3]) == 0) {
+        update_vcm = true;
+        str_parms_del(param, key);
+    }
+    key = VCM_CONFIG_DEST;
+    if (str_parms_get_int(param, key, &data[4]) == 0) {
+        update_vcm = true;
+        str_parms_del(param, key);
+    }
+
+    //printf("Direction is %d, Type is %d, Src_Dst is %d, Priority is %d, Dest is %d. \n",data[0], data[1], data[2], data[3], data[4]);
+
+    if (update_vcm) {
+        configure_vcm(data);   /*TODO check if all inputs got all values successfully*/
+    }
+
+    return 0;
+}
+
+void mbtk_audio_set_status(void* hdl, mbtk_audio_state_enum _status)
+{
+    struct mopen_audio_t *pcxt = (struct mopen_audio_t *)hdl;
+    if (NULL == hdl || NULL == internal_hdl)
+        return 0;
+    pthread_mutex_lock(&pcxt->_cond_mutex);
+    pcxt->state = _status;
+    pthread_mutex_unlock(&pcxt->_cond_mutex);
+}
+
+static void* mbtk_record_pthread(void* hdl)
+{
+    struct mopen_audio_t *pcxt = (struct mopen_audio_t *)hdl;
+    struct record_cb_s *_usrData = (struct record_cb_s *)pcxt->usrData;
+    unsigned bufsize = 0;
+    char *data = NULL;
+    int len;
+
+    if (NULL == hdl)
+        return NULL;
+
+    bufsize = pcxt->pcm_packet_size;
+    data = calloc(1, bufsize);
+    if (!data) {
+        fprintf(stderr, "\n%s:could not allocate %d bytes\n", __FUNCTION__, bufsize);
+        return NULL;
+    }
+
+    mbtk_audio_set_status(hdl, AUDIO_RUNNING);
+
+    while (pcxt->state != AUDIO_STOP)
+    {
+        len = pcxt->stream_in->read(pcxt->stream_in, data, bufsize);
+        if (len < 0) {
+            printf("%s: error reading!\n", __FUNCTION__);
+            break;
+        }
+        if ((bufsize > 0) && (NULL != _usrData->_cb))
+        {
+            _usrData->_cb(2, data, bufsize);
+        }
+    }
+    pcxt->pid = 0;
+    if(pcxt->usrData)
+    {
+        free(pcxt->usrData);
+        pcxt->usrData = NULL;
+    }
+    free(data);
+    mbtk_audio_log("pcm pthread end-\n");
+    return NULL;
+}
+
+static void* mbtk_play_pthread(void* hdl)
+{
+    struct mopen_audio_t *pcxt = (struct mopen_audio_t *)hdl;
+    _play_callback audio_play_cb = (_play_callback)pcxt->usrData;
+    unsigned bufsize = 0;
+    char *data = NULL;
+    int first_set = 0;
+    int ret = 0;
+    int rc;
+
+    pthread_detach(pthread_self());
+    if (NULL == hdl)
+        return NULL;
+
+    bufsize = pcxt->pcm_packet_size;
+    data = calloc(1, bufsize);
+    if (!data) {
+        fprintf(stderr, "\n%s:could not allocate %d bytes\n",__FUNCTION__,bufsize);
+        pthread_exit(NULL);
+    }
+
+    while (pcxt->state != AUDIO_STOP && 0 != pcxt->pipe_data)
+    {
+        ret = read(pcxt->fd[0], data, bufsize);
+        // printf("%s:read : %d bytes\n", __FUNCTION__, ret);
+        if(ret == 0) {
+            mbtk_audio_log("%s %d [%d]", __FUNCTION__, __LINE__, ret);
+            continue;
+        }
+        if(ret < 0) {
+            mbtk_audio_log("%s %d [%d]", __FUNCTION__, __LINE__, ret);
+            break;
+        }
+        if ((0 == first_set || AUDIO_RESUME == pcxt->state))
+        {
+            first_set = 1;
+            mbtk_audio_set_status(hdl, AUDIO_RUNNING);
+        }
+
+        pthread_mutex_lock(&pcxt->_stream_mutex);
+        pcxt->pipe_data -= ret;
+        pthread_mutex_unlock(&pcxt->_stream_mutex);
+        if(pcxt->pipe_data < 0)
+            pcxt->pipe_data = 0;
+        if(ret < pcxt->pcm_packet_size)
+            printf("pcm %d - %d\n", pcxt->pipe_data, ret);
+
+        rc = pcxt->stream_out->write(pcxt->stream_out, data, ret);
+        if (rc < 0) {
+            printf("%s: error writing (child).\n", __FUNCTION__);
+            break;
+        } else if (rc < (signed int)pcxt->pcm_packet_size) {
+            printf("%s: wrote less than buffer size, rc=%d.\n", __FUNCTION__, rc);
+            break;
+        }
+    }
+
+    if (pcxt->fd[0] != 0 && pcxt->fd[1] != 0)
+    {
+        close(pcxt->fd[0]);
+        close(pcxt->fd[1]);
+        pcxt->fd[0] = 0;
+        pcxt->fd[1] = 0;
+        /* printf("close pcxt->fd!\n"); */
+    }
+    if (audio_play_cb)
+        audio_play_cb(pcxt, 5);
+    pcxt->pid = 0;
+//    mbtk_audio_set_status(hdl, AUDIO_STOP);
+    mbtk_audio_set_status(hdl, AUDIO_OPEN);
+    free(data);
+    mbtk_audio_log("\n%s:exit!\n", __FUNCTION__);
+    pthread_exit(NULL);
+
+    return NULL;
+}
+
+static int audio_init_play_pthread(void* hdl)
+{
+    struct mopen_audio_t *pcxt = (struct mopen_audio_t *)hdl;
+    _play_callback audio_play_cb = (_play_callback)pcxt->usrData;
+    int ret;
+
+    if (pcxt->fd[0] == 0 && pcxt->fd[1] == 0) {
+        if(pipe(pcxt->fd) == 0) {
+
+            pcxt->pipe_data = 0;
+            fcntl(pcxt->fd[0], F_SETFL, O_NONBLOCK);
+            fcntl(pcxt->fd[1], F_SETFL, O_NONBLOCK);
+        } else {
+            fprintf(stderr, "\n%d: create pipe error\n", __LINE__);
+            return -1;
+        }
+    }
+    if (pcxt->pid == 0) {
+        if (audio_play_cb)
+            audio_play_cb(pcxt, 0);
+        mbtk_audio_set_status(pcxt, AUDIO_RUNNING);
+        ret = pthread_create(&pcxt->pid, NULL, mbtk_play_pthread, hdl);
+        if (ret != 0)
+        {
+            fprintf(stderr, "\n%s: Failed create pthread\n",__FUNCTION__);
+            return ret;
+        }
+    }
+
+    return 0;
+}
+
+int  mbtk_audio_play_wait_end(void* hdl)
+{
+    struct mopen_audio_t *pcxt = (struct mopen_audio_t *)hdl;
+
+    if (NULL == hdl)
+    {
+        fprintf(stderr, "\n%s: intput invalid params\n",__FUNCTION__);
+        return -1;
+    }
+
+    if(pcxt->pid != 0)
+    {
+        do{
+            usleep(10000);
+        }
+        while(pcxt->state != AUDIO_STOP);
+        pcxt->pid = 0;
+    }
+
+    return 0;
+}
+
+static int audio_open_pcm(void *dev_hdl, int num_channels, int rate, int in_out)
+{
+    struct mopen_audio_t *pcxt = (struct mopen_audio_t *)dev_hdl;
+    int nb_wb;
+
+    //Support 8k/16k & mono wave file
+    if (((rate != 8000) && (rate != 16000))
+            || (num_channels != 1) ) {
+        printf("%s: error wave file:rate = %d, num_channels = %d!! \n",
+                __FUNCTION__,rate, num_channels);
+        return -1;
+    }
+
+    printf("%s: success open, rate = %d, num_channels = %d.\n",
+            __FUNCTION__, rate, num_channels);
+
+    if ((8000 == rate) && (1 == num_channels)) {
+        nb_wb = 0;//NB
+        pcxt->pcm_packet_size = PCM_NB_BUF_SIZE;
+    } else if ((16000 == rate) && (1 == num_channels)) {
+        nb_wb = 1;//WB
+        pcxt->pcm_packet_size = PCM_WB_BUF_SIZE;
+    }
+
+    //config playback parameters.
+    config_parameters(in_out, nb_wb);
+    pcxt->device = in_out;
+    return 0;
+}
+
+mbtk_audio_handle mbtk_audio_open(mbtk_audio_dev_enum dev, int flag, int rate, void *usrData)
+{
+    int value = 1;
+    struct mopen_audio_t *aud_hdl = NULL;
+    int ret;
+
+    // if (dev <= AUDIO_NULL || dev >= AUDIO_MAX)
+    // {
+    //     printf("invaild PCM dev\n");
+    //     return NULL;
+    // }
+    if(internal_hdl)
+    {
+        printf("Audio device inited\n");
+        return internal_hdl;
+    }
+    aud_hdl = (struct mopen_audio_t *) calloc(1, sizeof(struct mopen_audio_t));
+    if (!aud_hdl)
+    {
+        fprintf(stderr, "\n%s:Failed to allocate audio hdl!, errno=%d\n", __FUNCTION__, errno);
+        return NULL;
+    }
+    memset(aud_hdl, 0, sizeof(struct mopen_audio_t));
+
+    //init global variables
+    aud_hdl->audio_ahw_dev_ubus = audio_hal_install();
+    if (aud_hdl->audio_ahw_dev_ubus == NULL) {
+        printf("%s: audio_hal_install failed!\n", __FUNCTION__);
+        goto error;
+    }
+
+    if(1 == dev)
+    {
+        //send the command of "AUDIOSTUB_PCMCTL" and "AUDIOSTUB_DTMFDETECTIONCTL" to simulate telephone offhook.
+            // ASR Question #80497  Using it causes the recording to crash
+    //    simulateOffhook(aud_hdl, 1);
+        //config playback parameters.
+        config_parameters(0, 0);
+    }
+    ret = audio_open_pcm(aud_hdl, flag, rate, dev);
+    if (ret)
+    {
+        printf("\n%s: pcm open error, errno=%d\n", __FUNCTION__, errno);
+        goto error;
+    }
+    if(1 == dev)
+    {
+        //open the audiostub_ctl, prepare for record and playback
+        VCMInit();
+        //open record stream
+        ret = aud_hdl->audio_ahw_dev_ubus->open_input_stream(aud_hdl->audio_ahw_dev_ubus, 0,
+                aud_hdl->audio_ahw_dev_ubus->get_supported_devices(aud_hdl->audio_ahw_dev_ubus),
+                NULL, &aud_hdl->stream_in, 0, 0, AUDIO_SOURCE_VOICE_CALL);
+    }
+    else
+    {
+        ret = aud_hdl->audio_ahw_dev_ubus->open_output_stream(aud_hdl->audio_ahw_dev_ubus, 0,
+                aud_hdl->audio_ahw_dev_ubus->get_supported_devices(aud_hdl->audio_ahw_dev_ubus),
+                AUDIO_OUTPUT_FLAG_DIRECT, NULL, &aud_hdl->stream_out, 0);
+    }
+
+    if (ret < 0) {
+        printf("%s: error opening output device. rc = %d\n", __FUNCTION__, ret);
+        goto error;
+    }
+    pthread_mutex_init(&aud_hdl->_cond_mutex, NULL);
+    pthread_mutex_init(&aud_hdl->_stream_mutex, NULL);
+    aud_hdl->usrData = usrData;
+    aud_hdl->state = AUDIO_OPEN;
+    /* printf("Mbtk_Audio_Open aud_hdl[%x][%x][%x]\n", aud_hdl, aud_hdl->_mixer_ctl, aud_hdl->_pcm); */
+    internal_hdl = aud_hdl;
+
+    return (void *)aud_hdl;
+
+error:
+    value = 0;
+    free(aud_hdl);
+    return NULL;
+}
+
+
+int mbtk_audio_play_stream_old(void *dev_hdl, const void *pData, int len)
+{
+    struct mopen_audio_t *pcxt = (struct mopen_audio_t *)dev_hdl;
+
+    if (NULL == pcxt || NULL == internal_hdl)
+        return -1;
+
+    if (len > 1024) {
+        printf("audio play stream max size ( < 1024): %d\n", len);
+        return -2;
+    }
+
+    if (pcxt->state == AUDIO_STOP) {
+        return 0;
+	}
+    audio_init_play_pthread(pcxt);
+
+    pthread_mutex_lock(&pcxt->_stream_mutex);
+    pcxt->pipe_data += len;
+    pthread_mutex_unlock(&pcxt->_stream_mutex);
+    while(pcxt->state != AUDIO_STOP && (pcxt->pipe_data > 40960 || pcxt->fd[1] < 0 || pcxt->fd[1] == 0)) {
+        // printf("%s:wait %d %d\n", __FUNCTION__, pcxt->pipe_data, pcxt->fd[1]);
+        usleep(50000);
+    }
+
+    if(pcxt->fd[1] > 0) {
+        if (len != write(pcxt->fd[1], pData, len)) {
+            fprintf(stderr, "\n%s: pcm_write failed [%d %d]\n", __FUNCTION__, pcxt->fd[1], errno);
+            return -errno;
+        }
+    }
+
+    return 0;
+}
+
+int mbtk_audio_play_stream(void *dev_hdl, const void *pData, int len)
+{
+//    printf("%s,--len:%d\n", __func__, len);
+    unsigned bufsize = 0;
+    char *data = NULL;
+    int first_set = 0;
+    int res = 0;
+    int ret = 0;
+    int read_size = 0;
+    char *p = (char *)pData;
+
+    struct mopen_audio_t *pcxt = (struct mopen_audio_t *)dev_hdl;
+
+    if (NULL == dev_hdl || NULL == internal_hdl || pData == NULL)
+        return -1;
+
+    if(AUDIO_RUNNING == pcxt->state)
+        return -2;
+
+    bufsize = pcxt->pcm_packet_size;
+    data = calloc(1, bufsize);
+    if (!data) {
+        fprintf(stderr, "\n%s:could not allocate %d bytes\n",__FUNCTION__,bufsize);
+        pthread_exit(NULL);
+    }
+
+    mbtk_audio_set_status(dev_hdl, AUDIO_RUNNING);
+//    if(bufsize > len )
+//    {
+//        bufsize = len;
+//    }
+    
+    while (pcxt->state != AUDIO_STOP)
+    {
+
+        memcpy(data,p+read_size, bufsize );
+        read_size += bufsize;
+
+        if(read_size > len)
+        {
+         //    printf(">[%d]\n", read_size); 
+            break;
+        }
+
+        while(AUDIO_PAUSE == pcxt->state)
+        {
+            usleep(80000);
+        }
+
+        if ((0 == first_set || AUDIO_RESUME == pcxt->state))
+        {
+            first_set = 1;
+            mbtk_audio_set_status(dev_hdl, AUDIO_RUNNING);
+        }
+
+        ret = pcxt->stream_out->write(pcxt->stream_out, data, bufsize);
+        if (ret < 0) {
+            printf("%s: error writing (child).\n", __FUNCTION__);
+            break;
+        } else if (ret < (signed int)pcxt->pcm_packet_size) {
+            printf("%s: wrote less than buffer size, rc=%d.\n", __FUNCTION__, ret);
+            break;
+        }
+
+        if(read_size == len)
+        {
+         //    printf("=[%d]", read_size); 
+            break;
+        }
+
+    }
+
+    mbtk_audio_set_status(dev_hdl, AUDIO_OPEN);
+    free(data);
+    return 0;
+
+}
+
+
+int mbtk_audio_play_file(void *dev_hdl, int file_fd, int offset)
+{
+    unsigned bufsize = 0;
+    char *data = NULL;
+    int first_set = 0;
+    int file_data_sz = 0;
+    int res = 0;
+    int ret;
+
+    struct mopen_audio_t *pcxt = (struct mopen_audio_t *)dev_hdl;
+
+    if (NULL == dev_hdl || NULL == internal_hdl)
+        return -1;
+
+    if(AUDIO_RUNNING == pcxt->state)
+        return -2;
+
+    file_data_sz = mbtk_wav_pcm16Le_check(file_fd);
+
+    bufsize = pcxt->pcm_packet_size;
+    data = calloc(1, bufsize);
+    if (!data) {
+        fprintf(stderr, "\n%s:could not allocate %d bytes\n",__FUNCTION__,bufsize);
+        pthread_exit(NULL);
+    }
+    if(offset)
+    {
+        lseek(file_fd, offset, SEEK_SET);
+    }
+
+    mbtk_audio_set_status(dev_hdl, AUDIO_RUNNING);
+    int all_size = 0;
+    
+    while (pcxt->state != AUDIO_STOP)
+    {
+        res = read(file_fd, data, bufsize);
+      //   printf("%s:read : %d bytes\n", __FUNCTION__, res);
+        if(res == 0 || res < 0)
+        {
+         //    printf("[%d]", res); 
+            break;
+        }
+
+        all_size += res;
+        if (file_data_sz < all_size || file_data_sz == all_size) {
+            printf("aplay size :%d - %d\n", file_data_sz, all_size);
+            break;
+        }
+
+        while(AUDIO_PAUSE == pcxt->state)
+        {
+            usleep(80000);
+        }
+
+        if ((0 == first_set || AUDIO_RESUME == pcxt->state))
+        {
+            first_set = 1;
+            mbtk_audio_set_status(dev_hdl, AUDIO_RUNNING);
+        }
+
+        ret = pcxt->stream_out->write(pcxt->stream_out, data, res);
+        if (ret < 0) {
+            printf("%s: error writing (child).\n", __FUNCTION__);
+            break;
+        } else if (ret < (signed int)pcxt->pcm_packet_size) {
+            printf("%s: wrote less than buffer size, rc=%d.\n", __FUNCTION__, ret);
+            break;
+        }
+    }
+
+    mbtk_audio_set_status(dev_hdl, AUDIO_OPEN);
+    free(data);
+
+    return 0;
+}
+
+int mbtk_audio_record(void *dev_hdl, mbtk_audio_record_cb_func cb_func, void *cb_data)
+{
+    struct mopen_audio_t *pcxt = (struct mopen_audio_t *)dev_hdl;
+    int res = -1;
+    struct record_cb_s *_usrData = NULL;
+
+    if (NULL == pcxt || NULL == internal_hdl || NULL == cb_func)
+        return -1;
+
+    if(AUDIO_RUNNING == pcxt->state)
+        return -2;
+
+    _usrData = malloc(sizeof(struct record_cb_s));
+    _usrData->_cb = cb_func;
+    _usrData->cb_data = cb_data;
+    pcxt->usrData = (void *)_usrData;
+    res = pthread_create(&pcxt->pid, NULL, mbtk_record_pthread, dev_hdl);
+    if (res != 0)
+    {
+        if(pcxt->usrData)
+        {
+            free(pcxt->usrData);
+            pcxt->usrData = NULL;
+        }
+        fprintf(stderr, "\n%s: Failed to create pthread\n", __FUNCTION__);
+    }
+    // 防止重复录音
+    usleep(500);
+
+    return res;
+}
+
+int mbtk_audio_close(void *dev_hdl)
+{
+    printf("mbtk_audio_close()\n");
+    int value = 0;
+    struct mopen_audio_t *_hdl = (struct mopen_audio_t *)dev_hdl;
+
+    if (NULL == _hdl || NULL == internal_hdl )
+    {
+        printf("mbtk_audio_close() fail dev_hdl is NULL\n");
+        return -1;
+    }
+
+    mbtk_audio_set_status(_hdl, AUDIO_STOP);
+    if(0 == _hdl->device) {
+        while (_hdl->pid != 0) {
+            usleep(10000);
+        }
+
+        vcm_playback_drain(0);//wait for drain the AP audiostub queue.
+        usleep(80000);//delay 80ms until DSP play out its buffered data.
+        _hdl->stream_out->common.standby(&_hdl->stream_out->common);
+        _hdl->audio_ahw_dev_ubus->close_output_stream(_hdl->audio_ahw_dev_ubus, _hdl->stream_out);
+    } else {
+
+        while (_hdl->pid != 0) {
+            sleep(1);
+        }
+        _hdl->stream_in->common.standby(&_hdl->stream_in->common);
+        _hdl->audio_ahw_dev_ubus->close_input_stream(_hdl->audio_ahw_dev_ubus, _hdl->stream_in);
+        VCMDeinit();//close the fd of audiostub_ctl when exit the thread.
+    }
+
+    audio_hal_uninstall();
+    pthread_mutex_destroy(&_hdl->_cond_mutex);
+    pthread_mutex_destroy(&_hdl->_stream_mutex);
+    free(_hdl);
+    _hdl = NULL;
+    internal_hdl = NULL;
+
+    return 0;
+}
+
+int mbtk_audio_pause(void* dev_hdl)
+{
+    // struct pcm *_pcm = ((struct mopen_audio_t *)dev_hdl)->_pcm;
+
+    if (NULL == dev_hdl || NULL == internal_hdl)
+        return -1;
+
+    if (((struct mopen_audio_t *)dev_hdl)->state != AUDIO_RUNNING)
+    {
+        return -1;
+    }
+
+    // if (ioctl(_pcm->fd, SNDRV_PCM_IOCTL_PAUSE, 1))
+    // {
+    //     printf("\n%s: cannot pause channel: errno =%d\n",__FUNCTION__,-errno);
+    //     return -errno;
+    // }
+    mbtk_audio_set_status(dev_hdl, AUDIO_PAUSE);
+    return 0;
+}
+
+int mbtk_audio_resume(void* dev_hdl)
+{
+    if (NULL == dev_hdl || NULL == internal_hdl)
+        return -1;
+
+    if (((struct mopen_audio_t *)dev_hdl)->state != AUDIO_PAUSE)
+    {
+        return -1;
+    }
+
+    // if (ioctl(_pcm->fd, SNDRV_PCM_IOCTL_PAUSE, 0))
+    // {
+    //     printf("\n%s: cannot Resume channel: errno =%d\n", __FUNCTION__, -errno);
+    //     return -errno;
+    // }
+    mbtk_audio_set_status(dev_hdl, AUDIO_RESUME);
+    return 0;
+}
+
+int mbtk_audio_stop(void* dev_hdl)
+{
+    struct mopen_audio_t *_hdl = (struct mopen_audio_t *)dev_hdl;
+
+    if (NULL == dev_hdl || NULL == internal_hdl)
+        return -1;
+
+    // if (ioctl(_hdl->_pcm->fd, SNDRV_PCM_IOCTL_DROP))
+    // {
+    //     printf("\n%s: cannot Resume channel: errno =%d\n",__FUNCTION__,-errno);
+    //     return -errno;
+    // }
+
+    mbtk_audio_set_status(dev_hdl, AUDIO_STOP);
+    _hdl->pid = 0;
+
+    return 0;
+}
+
+mbtk_audio_state_enum mbtk_audio_state_get(void *hdl)
+{
+    return ((struct mopen_audio_t *)hdl)->state;
+}
diff --git a/mbtk/mbtk_lib/src/mbtk_basic_at_wrapper.c b/mbtk/mbtk_lib/src/mbtk_basic_at_wrapper.c
new file mode 100755
index 0000000..43fb606
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_basic_at_wrapper.c
@@ -0,0 +1,11 @@
+#include "mbtk_adc.h"
+
+int mbtk_at_adc(const char *args)
+{
+    int value = (int)args;
+
+    if(0 != value && 1 != value){
+        return -1;
+    }
+    return mbtk_adc_get((mbtk_adc_enum)value);
+}
diff --git a/mbtk/mbtk_lib/src/mbtk_bs_position.c b/mbtk/mbtk_lib/src/mbtk_bs_position.c
new file mode 100755
index 0000000..0119ae5
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_bs_position.c
@@ -0,0 +1,317 @@
+/**
+ *   \file mbtk_bs_position.c
+ *   \brief A Documented file.
+ *
+ *  Detailed description
+ *   \Author:  Sniper <js.wang@mobiletek.cn>
+ *   \Version: 1.0.0
+ *   \Date: 2022-03-17
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <termios.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+#include "mbtk_type.h"
+#include <telephony/ril.h>
+#include <telephony/ril_ext.h>
+#include "rilutil.h"
+#include "mbtk_log.h"
+
+#define RIL_UBUS_REQ		"ril_request"
+#define RIL_UBUS_ID		"ril"
+#define IMSI_SIZE 15
+
+// #define DEBUG 1
+
+#ifdef DEBUG
+	#define bs_log(...)                    printf(__VA_ARGS__)
+#else
+	#define bs_log(...)
+#endif
+
+struct bs_ril_cellinfo {
+	int type;
+	int mcc;
+	int mnc;
+	int lac;
+	int cid;
+	int reg_state;
+};
+
+struct mbtk_bs_ubus_t
+{
+    struct ubus_context     *ctx;
+
+    /* RIL */
+    struct ubus_subscriber 	ril_ind_event;
+    uint32_t	 		    ril_subscriber_id;
+    uint32_t	 		    ril_request_id;
+    pthread_t  call_status_pthread;
+    struct bs_ril_cellinfo cellinfo;
+};
+struct mbtk_bs_ubus_t *mbtk_bs_ubus = NULL;
+static struct blob_buf b;
+
+static inline int radio_tech_2_act(int radio_tech)
+{
+	switch(radio_tech)
+	{
+		case RADIO_TECH_GPRS: return 0; /* ACT could have been either 1 or 0 !! */
+		case RADIO_TECH_UMTS: return 2;
+		case RADIO_TECH_EDGE: return 3;
+		case RADIO_TECH_HSDPA: return 4;
+		case RADIO_TECH_HSUPA: return 5;
+		case RADIO_TECH_HSPA: return 6;
+		case RADIO_TECH_LTE: return 7;
+		case RADIO_TECH_LTEP: return 7; /* temporary set it to 7, in future we may need to set to a number for 4G+ */
+		case RADIO_TECH_HSPAP: return 8;
+		case RADIO_TECH_UNKNOWN:
+		default: break;
+	}
+
+	return 0;
+}
+static void cellid_cb(void *response)
+{
+	rilutilstrings *resp = NULL;
+
+    resp = (rilutilstrings *) response;
+    bs_log("response num : %d\n", resp->num);
+
+    sscanf(resp->str[0], "%d", &mbtk_bs_ubus->cellinfo.reg_state);
+    sscanf(resp->str[1], "%x", &mbtk_bs_ubus->cellinfo.lac);
+    sscanf(resp->str[2], "%x", &mbtk_bs_ubus->cellinfo.cid);
+    sscanf(resp->str[3], "%d", &mbtk_bs_ubus->cellinfo.type); /* converted ACT value */
+    bs_log("cellinfo (%p): reg_state before=%d",
+        &mbtk_bs_ubus->cellinfo,
+        mbtk_bs_ubus->cellinfo.reg_state);
+    bs_log("lac:%x, cid:%x\n",
+        mbtk_bs_ubus->cellinfo.lac,
+           mbtk_bs_ubus->cellinfo.cid);
+    bs_log("reg_state:%d, lac:%d, cid:%d, type:%d\n",
+        mbtk_bs_ubus->cellinfo.reg_state, mbtk_bs_ubus->cellinfo.lac,
+           mbtk_bs_ubus->cellinfo.cid, mbtk_bs_ubus->cellinfo.type);
+    mbtk_bs_ubus->cellinfo.type = radio_tech_2_act(mbtk_bs_ubus->cellinfo.type);
+    // bs_log("mcc:%d, mnc:%d, type:%d\n",
+    //     mbtk_bs_ubus->cellinfo.mcc, mbtk_bs_ubus->cellinfo.mnc, mbtk_bs_ubus->cellinfo.type);
+    bs_log("cellinfo (%p): reg_state=%d, lac=%d, cid=%d, type=%d, mcc=%d, mnc=%d\n",
+    	 &mbtk_bs_ubus->cellinfo,
+    	 mbtk_bs_ubus->cellinfo.reg_state, mbtk_bs_ubus->cellinfo.lac, mbtk_bs_ubus->cellinfo.cid,
+    	 mbtk_bs_ubus->cellinfo.type, mbtk_bs_ubus->cellinfo.mcc, mbtk_bs_ubus->cellinfo.mnc);
+}
+
+
+static void setid_cb(char *resp)
+{
+	char mcc[4] = { 0 }, mnc[4] = { 0};
+    char *imsi = NULL;
+
+	imsi = malloc(IMSI_SIZE + 1);
+	if (!imsi) {
+		printf("Memory allocation failed\n");
+		return;
+	}
+    memset(imsi, 0, IMSI_SIZE + 1);
+	/* update imsi and cellinfo mcc & mnc */
+	strncpy(imsi, resp, IMSI_SIZE);
+    bs_log("imsi: %s\n", imsi);
+	memcpy(mcc, imsi, 3);
+	memcpy(mnc, (char *)imsi + 3, 2);
+	mbtk_bs_ubus->cellinfo.mcc = atoi(mcc);
+	mbtk_bs_ubus->cellinfo.mnc = atoi(mnc);
+
+    bs_log("reg_stat, mcc:%d, mnc:%d, \n",
+            mbtk_bs_ubus->cellinfo.mcc, mbtk_bs_ubus->cellinfo.mnc);
+    free(imsi);
+}
+
+static void bs_complete_cb(struct ubus_request *req, int ret)
+{
+	bs_log("ubus_request = %08X\n", req);
+	free(req);
+}
+
+static void bs_requset_cb(struct ubus_request *req, int type, struct blob_attr *msg)
+{
+    unsigned int requestid = 0;
+    unsigned int rilerrno;
+    void *response = NULL;
+    int responselen = 0;
+    int ret = 0;
+
+    ret = rilutil_parseResponse(msg, &requestid, &rilerrno, &response, &responselen);
+    if(ret)
+    {
+        fprintf(stderr, "parse blob error\n");
+        goto done;
+    }
+
+    if(rilerrno)
+    {
+        fprintf(stderr, "unsolicited id %d, error code %d\n", requestid, rilerrno);
+        goto done;
+    }
+    bs_log("requestid : %d\n", requestid);
+    if(requestid == RIL_REQUEST_GET_IMSI)
+    {
+        setid_cb((char *)response);
+    }
+    else if(requestid == RIL_REQUEST_DATA_REGISTRATION_STATE)
+    {
+        cellid_cb(response);
+    }
+done:
+    if(response)
+        rilutil_freeResponseData(requestid, response, responselen);
+
+    return;
+}
+
+
+int bs_get_cell_info(struct mbtk_bs_ubus_t *bs)
+{
+	int ret_val;
+	struct ubus_request *req = NULL;
+
+	if(!bs) {
+		printf("ril module not running\n");
+		return 0;
+	}
+	blob_buf_init(&b, 0);
+
+	rilutil_makeRequestBlob(&b, RIL_REQUEST_DATA_REGISTRATION_STATE, NULL, 0);
+
+	req = (struct ubus_request *)malloc(sizeof(struct ubus_request));
+
+	if ((ret_val =
+	     ubus_invoke_async(bs->ctx, bs->ril_request_id, RIL_UBUS_REQ, b.head, req)) != UBUS_STATUS_OK) {
+		printf("mubus_invoke_async failed\n");
+		free(req);
+		return -1;
+	} else {
+        req->data_cb = bs_requset_cb;
+        req->complete_cb = bs_complete_cb;
+		ubus_complete_request_async(bs->ctx, req);
+	}
+		return 0;
+}
+
+int bs_get_setid_info(struct mbtk_bs_ubus_t *bs)
+{
+	int ret_val;
+	struct ubus_request *req = NULL;
+
+	if(!bs) {
+		printf("ril module not running\n");
+		return 0;
+	}
+	blob_buf_init(&b, 0);
+
+	rilutil_makeRequestBlob(&b, RIL_REQUEST_GET_IMSI, NULL, 0);
+
+	req = (struct ubus_request *)malloc(sizeof(struct ubus_request));
+
+	if ((ret_val =
+	     ubus_invoke_async(bs->ctx, bs->ril_request_id, RIL_UBUS_REQ, b.head, req)) != UBUS_STATUS_OK) {
+		printf("mubus_invoke_async failed\n");
+		free(req);
+		return -1;
+	} else {
+        req->data_cb = bs_requset_cb;
+        req->complete_cb = bs_complete_cb;
+		ubus_complete_request_async(bs->ctx, req);
+	}
+		return 0;
+}
+
+static void bs_register_ril(void* hdl)
+{
+    pthread_detach(pthread_self());
+    uloop_run();
+    bs_log("%s uloop_run!\n", __FUNCTION__);
+    pthread_exit(NULL);
+}
+
+struct mbtk_bs_ubus_t *bs_ril_init(struct mbtk_bs_ubus_t *bs)
+{
+	int ret;
+
+	mbtk_bs_ubus = malloc(sizeof(struct mbtk_bs_ubus_t));
+	if (!mbtk_bs_ubus) {
+		printf("memory allocation failed\n");
+		return NULL;
+	}
+
+	memset(mbtk_bs_ubus, 0, sizeof(*mbtk_bs_ubus));
+    uloop_init();
+    mbtk_bs_ubus->ctx = ubus_connect(NULL);
+    if(!mbtk_bs_ubus->ctx)
+    {
+        LOGE("Failed to connect to ubus");
+        goto out_error;
+    }
+
+    ubus_add_uloop(mbtk_bs_ubus->ctx);
+    if (ubus_lookup_id(mbtk_bs_ubus->ctx, RIL_UBUS_ID, &mbtk_bs_ubus->ril_request_id)) {
+    	fprintf(stderr, "%s, Failed to look up test object\n", __FUNCTION__);
+        goto out_error;
+    }
+
+    pthread_create(&mbtk_bs_ubus->call_status_pthread, NULL, (void *)bs_register_ril, NULL);
+
+	return mbtk_bs_ubus;
+
+out_error:
+	free(mbtk_bs_ubus);
+	return NULL;
+}
+
+int bs_ril_exit(struct mbtk_bs_ubus_t *bs)
+{
+    int ret;
+
+	if(!bs) {
+		printf("ril module not running\n");
+		return 0;
+	}
+
+    ret = pthread_cancel(bs->call_status_pthread);
+    pthread_join(bs->call_status_pthread, NULL);
+    do{
+        ret = pthread_kill(bs->call_status_pthread, 0);
+        bs_log("kill pthread: %d \n", ret);
+        if(ret == ESRCH)
+            bs_log("The specified thread does not exist or has terminated\n");
+        else if(ret == EINVAL)
+            printf("Useless signal\n");
+        else
+            printf("The thread exists\n");
+        usleep(100000);
+    }while(0 == ret);
+
+	free(bs);
+	return 0;
+}
+int mbtk_bs_position(void)
+{
+    struct mbtk_bs_ubus_t *bs_hdl;
+    bs_hdl = bs_ril_init(NULL);
+    bs_get_setid_info(bs_hdl);
+    bs_get_cell_info(bs_hdl);
+
+    sleep(1);
+    bs_ril_exit(bs_hdl);
+
+    return 0;
+}
diff --git a/mbtk/mbtk_lib/src/mbtk_coap.c b/mbtk/mbtk_lib/src/mbtk_coap.c
new file mode 100755
index 0000000..186255e
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_coap.c
@@ -0,0 +1,1381 @@
+/*************************************************************

+Description:

+    C file for L620 coap.

+Author:

+    LuoJian

+Date:

+    2019/3/25 9:23:29

+*************************************************************/

+#include <stdlib.h>

+#include <string.h>

+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <linux/in.h>
+#include <unistd.h>

+

+#include "mbtk_coap.h"

+#include "mbtk_type.h"

+#include "mbtk_log.h"

+#include "mbtk_sock2.h"

+

+

+/*************************************************************

+    Variables:public

+*************************************************************/

+

+

+#define MBTK_ECOAP_TIMEOUT (15 * 1000)

+#define MBTK_ECOAP_WAIT_CLOSE_TIMEOUT (5 * 60 * 1000)

+#define MBTK_ECOAP_STACK_SIZE                (12 * 1024)        // task stack size

+

+#define ECOAPVER 1

+#define EOCAPTYPE 2

+#define ECOAPCODE 3

+#define ECOAPMSGID 4

+#define ECOAPTOKEN 5

+

+

+/*param--------------------*/

+// Point the first item.

+static mbtk_ecoap_package_s *mbtk_ecoap_head = NULL;

+static mbtk_ecoap_package_ver mbth_ecoap_package_ver={1,0,1,1};

+

+

+struct sockaddr_in serveraddr = {0};

+int socket_id;

+int addrlen;

+

+static mbtk_init_info *coap_init_info = NULL;

+static int coap_handle = -1;

+static bool coap_sock_inited = FALSE;

+static int coap_fd = -1;

+

+

+

+// Point the last item.

+static mbtk_ecoap_package_s *mbtk_ecoap_tail = NULL;

+static mbtk_ecoap_net_s mbtk_ecoap_net;

+static mbtk_ecoap_state_e mbtk_ecoap_state = MBTK_ECOAP_STATE_NON;

+

+//static uint16 mbtk_ecoap_curr_coap_id = 0;

+static char mbtk_ecoap_host[MBTK_ECOAP_NEW_MAX+1] = {'\0'};

+

+static int mbtk_ecoap_port;

+static int mbtk_ecoap_is_dtls;

+static mbtk_ecoap_opt_2 mbtk_ecoap_option_2;

+static mbtk_ecoap_opt_3 mbtk_ecoap_option_3;

+static int mbtk_ecoap_recv = -1;

+static int mbtk_ecoap_cur_time =0;

+

+/*func------------------------*/

+void mbtk_ecoap_option_add(mbtk_ecoap_option_s *option);

+int mbtk_ecoap_list_del(uint16 message_id,int optDel);

+

+

+#define HEXCHAR(x) ((x >= '0' && x <= '9') || (x >= 'a' && x <= 'f') || (x >= 'A' && x <= 'F'))

+

+

+#if 1

+

+#define toupper(a)  ((((a) >= 'a') && ((a) <= 'z')) ? ((a) - 'a' + 'A') : (a))

+

+void HexToStr(byte *pbDest, byte *pbSrc, int nLen)

+{

+	char   ddl,ddh;

+	int i;

+

+	for (i=0; i<nLen; i++)

+	{

+		ddh = 48 + pbSrc[i] / 16;

+		ddl = 48 + pbSrc[i] % 16;

+		if (ddh > 57)

+		{

+			ddh = ddh + 7;

+		}

+		if (ddl > 57)

+		{

+			ddl = ddl + 7;

+		}

+		pbDest[i*2] = ddh;

+		pbDest[i*2+1] = ddl;

+	}

+	pbDest[nLen*2] = '\0';

+}

+

+uint8_t StrToHex(byte *pbDest, byte *pbSrc, int nLen)

+{

+	char h1,h2;

+	byte s1,s2;

+	uint8_t i;

+

+	for (i=0; i<nLen; i++)

+	{

+		h1 = pbSrc[2*i];

+		h2 = pbSrc[2*i+1];

+		s1 = toupper(h1) - 0x30;

+		if (s1 > 9)

+		{

+			s1 -= 7;

+		}

+		s2 = toupper(h2) - 0x30;

+		if (s2 > 9)

+		{

+			s2 -= 7;

+		}

+		pbDest[i] = s1*16 + s2;

+		//DS_MBTK_MSG1_HIGH("StrToHex:%.2X",pbDest[i]);

+	}

+	return i;

+}

+

+void coap_log(int is_ascii,const char* data,int len)

+{

+    if(len <= 0) {

+        return;

+    }

+

+    if(is_ascii) { // Is ASCII

+        int i = 0;

+        int count = len / 100 + 1;

+

+        // Has '\0'

+        if(len != strlen(data)) {

+            return;

+        }

+

+        while(i < count) {

+            printf("%s\n",data + i * 100);

+            i++;

+        }

+    } else { // Is hex

+        int index = 0;

+        int i = 0;

+        char buf_temp[100];

+        while(index % 16 == 0 && index < len) {

+            memset(buf_temp,0,100);

+            for(i = 0;i < 16 && index + i < len;i++) {

+                snprintf(buf_temp + i * 3,100 - i,"%.2x ",((char*)data)[index + i]);

+            }

+            index += 16;

+            printf("%s\n", buf_temp);

+        }

+    }

+}

+

+

+

+static void

+mbtk_ecoap_show_error

+(

+    int rsp_errno

+)

+{

+    char temp[50] = {0};

+    memset(temp, 0, 50);

+    snprintf(temp,50,"\r\n+MCOAPERROR:%d\r\n",rsp_errno);

+    printf("%s\n",temp);

+}

+

+int ecoap_recv_buffer(char * dest,const char* data,int len)

+{

+    if( len <= 0) {

+        return -1;

+    }

+    int buffer_len = 0;

+    char Payload[MBTK_ECOAP_DATA_MAX*2+1] ={'\0'};

+	int pud_lenth = mbtk_coap_get_pdu_Length();

+	int type = mbtk_ecoap_get_message_type();

+	char code[12];

+    memset(code,0x00,sizeof(code));

+	mbtk_ecoap_get_code(code);

+	uint16_t mesageId = coapGetRecvMessageID();

+	memset(Payload,0x00,MBTK_ECOAP_DATA_MAX*2+1);

+	snprintf(dest,MBTK_ECOAP_DATA_MAX,

+			"+COAPNMI:%d\r\nMessage Type:%d\r\nCode:\"%s\"\r\nMessage ID:%d\r\nPayload:",

+			pud_lenth,

+			type,

+			code,

+			mesageId);	

+

+

+    int i = 0;

+	for(i = 0; i < len; i++)

+	{

+		if(data[i] == 0xFF && i> 3)   //BUG 46397

+		{

+			LOGE("data[i] == 0xFF\n");

+			memcpy(Payload, data+i+1, len - i-1);

+			Payload[len-i -1]='\0';

+			break;

+		}

+	}

+

+    LOGE("len:%d, i :%d\n",len,i);

+	if(Payload[len-i -1] == '\0' )

+	{

+		LOGE("==\0-------------------\n");		

+	//	coap_log(-1,(char*)Payload,len - i-1);

+	}else{

+		LOGE("!=\0--------------------\n");

+		Payload[0]='\0';

+	}

+

+    buffer_len = strlen(dest) + strlen(Payload); 

+    strcat(dest,Payload);

+	LOGE("2---------buffer_len:%d,coap_recv_buffer_dest:%s,Payload:%s\n",buffer_len,dest,Payload);

+    return buffer_len;

+}

+

+int mbtk_ecoap_get_message_type(void)

+{

+    switch(coapGetRecvType())

+    {

+        case COAP_CONFIRMABLE:

+            return 0;

+        break;

+

+        case COAP_NON_CONFIRMABLE:

+            return 1;

+        break;

+

+        case COAP_ACKNOWLEDGEMENT:

+            return 2;

+        break;

+

+        case COAP_RESET:

+            return 3;

+        break;

+    }

+	return 0;

+}

+

+void mbtk_ecoap_get_code(char *ptr)

+{

+	switch(coapGetRecvCode()) {

+		case COAP_EMPTY:

+			memcpy(ptr,"0.00",4);

+		break;

+		case COAP_GET:

+			memcpy(ptr,"0.01",4);

+		break;

+		case COAP_POST:

+			memcpy(ptr,"0.02",4);

+		break;

+		case COAP_PUT:

+			memcpy(ptr,"0.03",4);

+		break;

+		case COAP_DELETE:

+			memcpy(ptr,"0.04",4);

+		break;

+		case COAP_CREATED:

+			memcpy(ptr,"2.01",4);

+		break;

+		case COAP_DELETED:

+			memcpy(ptr,"2.02",4);

+		break;

+		case COAP_VALID:

+			memcpy(ptr,"2.03",4);

+		break;

+		case COAP_CHANGED:

+			memcpy(ptr,"2.04",4);

+		break;

+		case COAP_CONTENT:

+			memcpy(ptr,"2.05",4);

+		break;

+		case COAP_CONTINUE:

+			memcpy(ptr,"2.31",4);

+		break;

+		case COAP_BAD_REQUEST:

+			memcpy(ptr,"4.00",4);

+		break;

+		case COAP_UNAUTHORIZED:

+			memcpy(ptr,"4.01",4);

+		break;

+		case COAP_BAD_OPTION:

+			memcpy(ptr,"4.02",4);

+		break;

+		case COAP_FORBIDDEN:

+			memcpy(ptr,"4.03",4);

+		break;

+		case COAP_NOT_FOUND:

+			memcpy(ptr,"4.04",4);

+		break;

+		case COAP_METHOD_NOT_ALLOWED:

+			memcpy(ptr,"4.05",4);

+		break;

+		case COAP_NOT_ACCEPTABLE:

+			memcpy(ptr,"4.06",4);

+		break;

+		case COAP_PRECONDITION_FAILED:

+			memcpy(ptr,"4.12",4);

+		break;

+		case COAP_REQUEST_ENTITY_TOO_LARGE:

+			memcpy(ptr,"4.13",4);

+		break;

+		case COAP_UNSUPPORTED_CONTENT_FORMAT:

+			memcpy(ptr,"4.15",4);

+		break;

+		case COAP_INTERNAL_SERVER_ERROR:

+			memcpy(ptr,"5.00 ",4);

+		break;

+		case COAP_NOT_IMPLEMENTED:

+			memcpy(ptr,"5.01",4);

+		break;

+		case COAP_BAD_GATEWAY:

+			memcpy(ptr,"5.02",4);

+		break;

+		case COAP_SERVICE_UNAVAILABLE:

+			memcpy(ptr,"5.03",4);

+		break;

+		case COAP_GATEWAY_TIMEOUT:

+			memcpy(ptr,"5.04",4);

+		break;

+		case COAP_PROXYING_NOT_SUPPORTED:

+			memcpy(ptr,"5.05",4);

+		break;

+		default:

+			sprintf(ptr, "UndefCod%u",(unsigned)(coapGetRecvCode()));			

+	}

+}

+

+

+static int

+mbtk_ecoap_is_open()

+{

+    if(mbtk_ecoap_state >= MBTK_ECOAP_STATE_OPENED)

+        return 0;

+

+    return -1;

+}

+

+

+static void mbtk_ecoap_reset()

+{

+    memset(&mbtk_ecoap_net,0x0,sizeof(mbtk_ecoap_net_s));

+    mbtk_ecoap_net.message_id = 1;   //change mbtk_ecoap_net.message_id = 0;

+    memset(&mbtk_ecoap_option_2, 0x0, sizeof(mbtk_ecoap_opt_2));

+    memset(&mbth_ecoap_package_ver, 0x0,sizeof(mbtk_ecoap_package_ver));

+    mbth_ecoap_package_ver.version = 1;

+    mbth_ecoap_package_ver.type = 0;

+    mbth_ecoap_package_ver.code = 1;

+    mbth_ecoap_package_ver.message_id = 1;

+    mbtk_ecoap_recv = -1;

+

+    mbtk_ecoap_package_s *coap = mbtk_ecoap_head;

+	mbtk_ecoap_package_s *coap_next = NULL;

+	mbtk_ecoap_option_s * option_ptr = NULL;

+	mbtk_ecoap_option_s * option_ptr_next = NULL;

+	

+    while(coap != NULL)

+    {

+		coap_next = coap->next;

+		if(coap->payload != NULL)

+			free(coap->payload);

+

+		if(coap->options != NULL)

+		{

+			option_ptr = coap->options;

+			while(option_ptr != NULL)

+			{

+				option_ptr_next = option_ptr->next;

+				free(option_ptr);

+				option_ptr = NULL;

+				option_ptr = option_ptr_next;

+			}

+			

+		}

+		

+		free(coap);

+		coap = NULL;

+		coap = coap_next;

+

+    }

+

+    mbtk_ecoap_head = mbtk_ecoap_tail = NULL;

+}

+

+

+static void mbtk_ecoap_close()

+{

+

+}

+

+

+

+

+

+mbtk_ecoap_package_s *mbtk_ecoap_get_by_msg_id(uint16 message_id)

+{

+    if(mbtk_ecoap_head == NULL)

+    {

+    }

+    else

+    {

+        mbtk_ecoap_package_s *coap_ptr = mbtk_ecoap_head;

+        while(coap_ptr != NULL)

+        {

+            if(coap_ptr->message_id == message_id)

+            {

+                return coap_ptr;;

+            }

+            coap_ptr = coap_ptr->next;

+        }

+    }

+    return NULL;

+}

+

+static void mbtk_ecoap_list_item_init(unsigned int  sig,mbtk_ecoap_package_s *coap,uint16 message_id)

+{

+    switch(sig)

+    {

+        case ECOAPVER:

+            coap->version = message_id;

+            break;

+        case EOCAPTYPE:

+            coap->type = message_id;

+            break;

+        case ECOAPCODE:

+            coap->code = message_id;

+            break;

+        case ECOAPMSGID:

+            coap->message_id = message_id;

+            break;

+        case ECOAPTOKEN:

+            coap->token_len = message_id;

+            break;

+        default:

+            break;

+    }

+    coap->next = NULL;  //change delete this code

+}

+

+

+mbtk_ecoap_package_s* mbtk_ecoap_list_add(uint16 message_id)

+{

+    LOGE("mbtk_ecoap_list_add-----memssage_id:%d\n",message_id);

+    if(mbtk_ecoap_head == NULL)

+    {

+        mbtk_ecoap_head = (mbtk_ecoap_package_s*)malloc(sizeof(mbtk_ecoap_package_s));

+        memset(mbtk_ecoap_head, 0,sizeof(mbtk_ecoap_package_s));

+        mbtk_ecoap_list_item_init(ECOAPMSGID,mbtk_ecoap_head,message_id);

+        mbtk_ecoap_tail = mbtk_ecoap_head;

+        return mbtk_ecoap_head;

+    }

+    else

+    {

+        mbtk_ecoap_package_s *coap_ptr = mbtk_ecoap_head;

+        while(coap_ptr != NULL)

+        {

+            if(coap_ptr->message_id == message_id)

+            {

+                printf("Coap %d exist.\n",message_id);

+                return NULL;

+            }

+            coap_ptr = coap_ptr->next;

+        }

+        mbtk_ecoap_tail->next = (mbtk_ecoap_package_s*)malloc(sizeof(mbtk_ecoap_package_s));

+        mbtk_ecoap_list_item_init(ECOAPMSGID,mbtk_ecoap_tail->next,message_id);

+        mbtk_ecoap_tail = mbtk_ecoap_tail->next;

+        return mbtk_ecoap_tail;

+    }

+}

+

+static void mbtk_ecoap_free(mbtk_ecoap_package_s *coap)

+{

+    if(coap != NULL)

+    {

+        if(coap->payload != NULL)

+            free(coap->payload);

+

+        if(coap->options != NULL)

+        {

+            mbtk_ecoap_option_s *option_ptr = coap->options;

+            while(option_ptr != NULL)

+            {

+                free(option_ptr);

+                option_ptr = option_ptr->next;

+            }

+        }

+

+        free(coap);

+        coap = NULL;

+    }

+}

+

+int mbtk_ecoap_list_del(uint16 message_id,int optDel)

+{

+    LOGE("mbtk_ecoap_list_del:message_id:%d\n",message_id);

+    if(mbtk_ecoap_head == NULL)

+    {

+    	printf("mbtk_ecoap_head == NULL\n");

+	return -1;

+    }

+

+    mbtk_ecoap_recv = -1;

+    if(optDel)

+    {

+        memset(&mbtk_ecoap_option_2, 0x0, sizeof(mbtk_ecoap_opt_2));

+        memset(&mbtk_ecoap_option_3, 0x0, sizeof(mbtk_ecoap_opt_3));

+        memset(&mbth_ecoap_package_ver,0x0,sizeof(mbth_ecoap_package_ver));

+        mbth_ecoap_package_ver.version = 1;

+        mbth_ecoap_package_ver.type = 0;

+        mbth_ecoap_package_ver.code = 1;

+        mbth_ecoap_package_ver.message_id = 1;

+    }

+    mbtk_ecoap_package_s *coap = mbtk_ecoap_head;

+    if(mbtk_ecoap_head->message_id == message_id) // Is first item.

+    {

+        mbtk_ecoap_head = mbtk_ecoap_head->next;

+

+        mbtk_ecoap_free(coap);

+        return 0;

+    }

+    else     // Not first item.

+    {

+        while(coap->next != NULL)  

+        {

+            if(message_id == coap->next->message_id)   // delete

+            {

+                mbtk_ecoap_package_s *coap_tmp = coap->next;

+                if(mbtk_ecoap_tail == coap_tmp)

+                {

+                    mbtk_ecoap_tail = coap;

+                }

+                coap->next = coap_tmp->next;

+                mbtk_ecoap_free(coap_tmp);

+                return 0;;

+            }

+            coap = coap->next;

+        }

+        return -1;

+    }

+}

+

+void mbtk_ecoap_option_add(mbtk_ecoap_option_s *option)

+{

+    if(option != NULL)

+    {

+        switch(option->type)

+        {

+            case COAP_OPTION_IF_MATCH:

+            case COAP_OPTION_URI_HOST:

+            case COAP_OPTION_ETAG:

+            case COAP_OPTION_IF_NONE_MATCH:

+            case COAP_OPTION_LOCATION_PATH:

+            case COAP_OPTION_LOCATION_QUERY:

+            case COAP_OPTION_PROXY_URI:

+            case COAP_OPTION_PROXY_SCHEME:

+            case COAP_OPTION_URI_PORT:

+            case COAP_OPTION_ACCEPT:

+            case COAP_OPTION_SIZE2:

+            case COAP_OPTION_SIZE1:

+            {

+                break;

+            }

+            case COAP_OPTION_BLOCK2:

+            case COAP_OPTION_BLOCK1:

+            {

+                LOGE("option->opt.opt_block.number = %d\n",option->opt.opt_block.number);

+                if(option->opt.opt_block.number <= 0x0F)

+                {

+                    uint8_t temp[1];

+                    temp[0] = (uint8_t)(((option->opt.opt_block.number << 4) & 0xF0)

+                                        | ((option->opt.opt_block.more_flag << 3) & 0x08)

+                                        | (option->opt.opt_block.size & 0x07));

+                    coapAddOption(option->type,1,temp);

+                }

+                else if(option->opt.opt_block.number <= 0x0FFF)

+                {

+                    uint8_t temp[2];

+                    temp[0] = (uint8_t)(option->opt.opt_block.number >> 4);

+                    temp[1] = (uint8_t)(((option->opt.opt_block.number << 4) & 0xF0)

+                                        | ((option->opt.opt_block.more_flag << 3) & 0x08)

+                                        | (option->opt.opt_block.size & 0x07));

+                    coapAddOption(option->type,2,temp);

+

+                }

+                else

+                {

+                    uint8_t temp[3];

+                    temp[0] = (uint8_t)(option->opt.opt_block.number >> 12);

+                    temp[1] = (uint8_t)(option->opt.opt_block.number >> 4);

+                    temp[2] = (uint8_t)(((option->opt.opt_block.number << 4) & 0xF0)

+                                        | ((option->opt.opt_block.more_flag << 3) & 0x08)

+                                        | (option->opt.opt_block.size & 0x07));

+                    coapAddOption(option->type,3,temp);

+

+                }

+                break;

+            }

+            case COAP_OPTION_MAX_AGE:

+            {

+                uint8_t temp[1];

+                temp[0] = (uint8_t)option->opt.opt_int;

+                coapAddOption(option->type,1,temp);

+                break;

+            }

+            case COAP_OPTION_OBSERVE:

+            {

+                uint8_t temp[1];

+                temp[0] = (uint8_t)option->opt.opt_int;

+                coapAddOption(option->type,1,temp);

+                break;

+            }

+            case COAP_OPTION_URI_QUERY:

+            {

+                coapAddURIQuery((char*)option->opt.opt_str);

+                break;

+            }

+            case COAP_OPTION_URI_PATH:

+            {

+                coapSetURI((char*)option->opt.opt_str);

+                break;

+            }

+            case COAP_OPTION_CONTENT_FORMAT:

+            {

+                coapSetContentFormat(option->opt.opt_content_format);

+                break;

+            }

+            default:

+                printf("No such type:%s\n",option->type);

+                break;

+        }

+    }

+}

+

+static void mbtk_ecoap_set_option(mbtk_ecoap_package_s *coap,

+                                 mbtk_coap_option_type type,void *str,

+                                 int int0,int int1,int int2)

+{

+    if(coap != NULL)

+    {

+/*        if(coap->options == NULL)

+        {

+            coap->options = (mbtk_ecoap_option_s*)malloc(sizeof(mbtk_ecoap_option_s));

+            memset(coap->options,0x0,sizeof(mbtk_ecoap_option_s));

+            coap->options->type = type;

+            coap->options->next = NULL;

+        }

+*/

+        mbtk_ecoap_option_s *option = coap->options;

+        while(option != NULL)

+        {

+            LOGE("1option != NULL\n");

+            if(option->type == type)

+            {

+                break;

+            }

+            option = option->next;

+        }

+

+        LOGE("----------------------\n");

+        if(option == NULL) // Now option

+        {

+            LOGE("option == NULL\n");

+            option = (mbtk_ecoap_option_s*)malloc(sizeof(mbtk_ecoap_option_s));

+            memset(option,0x0,sizeof(mbtk_ecoap_option_s));

+//            option->type = type;

+

+            option->next = coap->options;

+            coap->options = option;

+            option->type = type;

+        }

+        else // Change option

+        {

+            // Do noting.

+        }

+

+        switch(type)

+        {

+            case COAP_OPTION_IF_MATCH:

+            case COAP_OPTION_URI_HOST:

+            case COAP_OPTION_ETAG:

+            case COAP_OPTION_IF_NONE_MATCH:

+            case COAP_OPTION_LOCATION_PATH:

+            case COAP_OPTION_URI_QUERY:

+            case COAP_OPTION_LOCATION_QUERY:

+            case COAP_OPTION_PROXY_URI:

+            case COAP_OPTION_PROXY_SCHEME:

+            case COAP_OPTION_URI_PATH:

+            {

+                memset(option->opt.opt_str,0x0,128);

+                memcpy(option->opt.opt_str,str,strlen((char*)str));

+                break;

+            }

+            case COAP_OPTION_OBSERVE:

+            case COAP_OPTION_MAX_AGE:

+            case COAP_OPTION_URI_PORT:

+            case COAP_OPTION_SIZE2:

+            case COAP_OPTION_SIZE1:

+            {

+                option->opt.opt_int = (int)int0;

+                break;

+            }

+            case COAP_OPTION_BLOCK2:

+            case COAP_OPTION_BLOCK1:

+            {

+                option->opt.opt_block.size = (mbtk_ecoap_option_block_e)int0;

+                option->opt.opt_block.number = (int)int1;

+                option->opt.opt_block.more_flag = (uint8)int2;

+                break;

+            }

+            case COAP_OPTION_CONTENT_FORMAT:

+			{

+                option->opt.opt_content_format = (mbtk_content_format_type)int0;

+                break;

+            }

+            case COAP_OPTION_ACCEPT:

+            {

+                option->opt.opt_content_format = (mbtk_content_format_type)int0;

+                break;

+            }

+            default:

+                printf("No such type:%s\n",type);

+                break;

+        }

+    }

+}

+

+

+static void mbtk_ecoap_send_ack(mbtk_coap_code_type code,

+                               uint16_t message_id,uint8 token_len,uint8 *token)

+{

+    coapReset();

+    coapSetVersion(1);

+    coapSetType(COAP_ACKNOWLEDGEMENT);

+    coapSetMessageID(message_id);

+

+    if(code == COAP_POST || code == COAP_PUT)

+        coapSetCode(COAP_CHANGED);

+    else

+        coapSetCode(COAP_EMPTY);

+

+    if(token_len > 0)

+    {

+        coapSetToken(token, token_len);

+    }

+

+    uint8_t* pduPointer = coapGetPDUPointer();

+    int pduLen = coapGetPDULength();

+	int err;

+	int res = mbtk_sock_write(coap_handle, coap_fd, pduPointer, pduLen, 300, &err);

+    if(res != pduLen)

+    {

+        printf("Send ACK fail.[res:%d,pduLen:%d],err:%d\n",res,pduLen,err);

+    }

+    else

+    {

+        printf("Send ACK to server success.\n");

+    }

+}

+

+

+

+static int mbtk_ecoap_handle_sock_init(char *ip_addr, int port, bool is_support_ssl, bool ingnore_cert)

+{

+    mbtk_sock_info *sock_info = NULL;

+    int rc = 0;

+    int errno;

+	

+    sock_info = (mbtk_sock_info *)malloc(sizeof(mbtk_sock_info));

+    if(sock_info == NULL)

+    {

+        rc = -1;

+        return rc;

+    }

+    memcpy(sock_info->address, ip_addr, strlen(ip_addr));

+    sock_info->port = port;

+    sock_info->is_support_ssl = is_support_ssl;

+    sock_info->ingnore_cert = ingnore_cert;

+	sock_info->type = MBTK_SOCK_UDP;

+	

+    printf("host %s\nport %d\nis_support_ssl %d\ningnore_cert %d\n",sock_info->address,sock_info->port,sock_info->is_support_ssl,sock_info->ingnore_cert);

+

+	if(!coap_sock_inited)

+	{

+		coap_handle = mbtk_sock_init(coap_init_info);

+	    if (coap_handle < 0 )

+	    {

+	        rc = -1;

+	        return rc;

+	    }

+		coap_sock_inited = TRUE;

+	}

+

+

+    coap_fd  = mbtk_sock_open(coap_handle, sock_info, 3000, &errno);

+    if(coap_fd < 0)

+    {

+    	printf("creat socket fail, errno:%d\n",errno);

+        rc = -1;

+        return rc;

+    }

+    mbtk_ecoap_state = MBTK_ECOAP_STATE_READY;

+	return rc;

+}

+

+

+

+int mbtk_ecoap_ecoap_send(mbtk_ecoap_package_s* coap)

+{

+    if(coap != NULL)

+    {

+        coapReset();

+        coapSetVersion(coap->version);

+        coapSetType(coap->type);

+        coapSetCode(coap->code);

+        coapSetMessageID(coap->message_id);

+

+        if(coap->token_len > 0)

+        {

+            coapSetToken(coap->token, coap->token_len);

+        }

+

+        mbtk_ecoap_option_s *option = coap->options;

+        while(option != NULL)

+        {

+            mbtk_ecoap_option_add(option);

+            option = option->next;

+        }

+

+        if(coap->payload_len > 0)

+        {

+            coapSetPayload((uint8_t*)coap->payload, coap->payload_len);

+        }

+

+        uint8_t *pduPointer = coapGetPDUPointer();

+        int pduLen = coapGetPDULength();

+        uint8_t pduPointer2[MBTK_ECOAP_DATA_MAX*2];

+		int err;

+        memset ( pduPointer2,0x00,MBTK_ECOAP_DATA_MAX*2 );

+        if((mbtk_ecoap_option_2.optlen + pduLen) > MBTK_ECOAP_DATA_MAX*2)

+        {

+            printf("This length is too long\n");

+            return -1;

+        }

+

+        LOGE("pduLen:%d, optlen:%d\n",pduLen ,mbtk_ecoap_option_2.optlen);

+   //     printf("pduPointer:\n");

+   //     coap_log(-1,(char*)pduPointer,pduLen);

+        int j=0,k=0;

+        LOGE("pduPointer-----------end\n");

+//        int optflag = 0;

+        pduPointer2[0]= *pduPointer++;

+        pduPointer2[1]= *pduPointer++;

+        pduPointer2[2]= *pduPointer++;

+        pduPointer2[3]= *pduPointer++;

+        for(j = 0; j < mbtk_ecoap_option_2.optlen; j++)

+        {

+            pduPointer2[4+j] = mbtk_ecoap_option_2.optVal[j];

+			LOGE("j:%d, %X",j, mbtk_ecoap_option_2.optVal[j]);

+            k++;

+        }

+        int pduLen2 = 4;

+        while(pduLen > pduLen2 )

+        {

+        	LOGE("\r\npduLen > pduLen2 \r\n");

+            LOGE("pduLen:%d,pduPointer:%X,k:%d\n",pduLen2,*pduPointer,k);

+            pduPointer2[pduLen2+k] = *pduPointer++;

+            pduLen2++;

+        }

+

+        pduLen += mbtk_ecoap_option_2.optlen;

+

+        printf("sendpdu:--------pduLen:%d\n",pduLen);

+		coap_log(FALSE,(char*)pduPointer2,pduLen);

+		int send_len = 0;

+

+		if((send_len = mbtk_sock_write(coap_handle, coap_fd, pduPointer2, pduLen, 300, &err)) != pduLen)

+//        if((send_len = sendto(socket_id, pduPointer2, pduLen, 0, (struct sockaddr*)&serveraddr, addrlen)) != pduLen)

+        {

+            printf("coap_udp_sendto complate.[%d/%d]\n",send_len,pduLen);

+

+            coap->send_count++;

+			return -1;

+        }

+

+        if(coap->type != COAP_CONFIRMABLE)

+        {

+            mbtk_ecoap_list_del(coap->message_id,0);

+        }

+

+    }

+	return 0;

+}

+

+void mbtk_ecoap_recv_buffer(char * dest)

+{

+	int pud_lenth = mbtk_coap_get_pdu_Length();

+	int type = mbtk_ecoap_get_message_type();

+	char code[12];

+    memset(code,0x00,sizeof(code));

+	mbtk_ecoap_get_code(code);

+	uint16_t mesageId = coapGetRecvMessageID();

+	memset(dest,0x00,129);

+	snprintf(dest,128,

+			"+COAPNMI:%d\r\nMessage Type:%d\r\nCode:\"%s\"\r\nMessage ID:%d\r\nPayload:",

+			pud_lenth,

+			type,

+			code,

+			mesageId);	

+

+	return 0;

+}

+

+

+static void mbtk_ecoap_recv_resolve(const void *data,uint16 data_len)

+{

+    if(coapCreateRecv((uint8_t*)data, data_len) <= 0)

+    {

+        printf("coapCreateRecv fail.\n");

+    }

+    else

+    {

+        LOGE("coapCreateRecv() success.\n");

+

+        uint8_t version = coapGetRecvVersion();

+        mbtk_coap_type type = coapGetRecvType();

+        mbtk_coap_code_type code = coapGetRecvCode();

+        uint16_t message_id = coapGetRecvMessageID();

+        int pud_lenth = mbtk_coap_get_pdu_Length();

+        LOGE("version:%d;type:%d\n",version,type);

+        LOGE("code:%d;message_id:%d,pud_lenth:%d\n",code,message_id,pud_lenth);

+

+        uint8 token_len = coapGetRecvTokenLength();

+        char token[9] = {0};

+        memcpy(token,coapGetRecvTokenPointer(),token_len);

+        LOGE("token[%d]:%s\n",token_len,token);

+

+        uint16 playload_len = coapGetRecvPayloadLength();

+        uint8 *playload = coapGetRecvPayloadPointer();

+//        printf("----------------payload-----------------\n");

+//        coap_log(0,(char*)playload,playload_len);

+//        printf("--------------payload end----------------\n");

+

+

+		char buff_data[MBTK_ECOAP_DATA_MAX*2+1];

+		memset ( buff_data,0x00,MBTK_ECOAP_DATA_MAX*2+1 );

+        int buffer_len = ecoap_recv_buffer(buff_data, data, data_len);

+		printf("recv buff_len:%d\n",buffer_len);

+        printf("buf:\n%s\r\n",buff_data);

+		

+        LOGE("type:%X\n",type);

+        if(type == COAP_CONFIRMABLE) // Should ACK

+        {

+            mbtk_ecoap_send_ack(code,message_id,token_len,(uint8*)token);

+        }

+        else if(type == COAP_NON_CONFIRMABLE)

+        {

+            // Do nothing.

+        }

+        else if(type == COAP_ACKNOWLEDGEMENT)

+        {

+            mbtk_ecoap_package_s * coap_send = mbtk_ecoap_get_by_msg_id(message_id);

+            if(coap_send != NULL)

+            {

+                int number;

+                uint8_t more_flag;

+                uint8_t size;

+                LOGE("Get message(%d) ack.\n",message_id);

+                if(coapGetRecvOptionBlock2(&number, &more_flag, &size))

+                {

+                    LOGE("Block2 (size:%d; more_flag:%d; number:%d)\n",size,more_flag,number);

+                    if(more_flag) // Has data no read.

+                    {

+                        coap_send->message_id = ++mbtk_ecoap_net.message_id ;

+                        mbtk_ecoap_set_option(coap_send, COAP_OPTION_BLOCK2, NULL,

+                                             size,number + 1,0);

+

+                        mbtk_ecoap_ecoap_send(coap_send);

+                    }

+                    else

+                    {

+                        mbtk_ecoap_list_del(message_id,0);

+                    }

+                }

+                else

+                {

+                    printf("coapGetRecvOptionBlock2() fail.\n");

+                    mbtk_ecoap_list_del(message_id,0);

+                }

+            }

+            else

+            {

+                printf("Coap not match.\n");

+            }

+        }

+        else     // COAP_RESET

+        {

+            printf("Coap type error.\n");

+        }

+    }

+	coapDeleteRecv();

+

+}

+

+

+static int ecoap_sig_recv_handle(void)

+{

+    LOGE("coap_sig_recv_handle----------start\n");

+

+    uint8_t buffer[MBTK_ECOAP_DATA_MAX*2+1];

+    memset ( buffer,0x00,MBTK_ECOAP_DATA_MAX*2+1 );

+    int ret=-1;

+	int err = -1;

+	while(TRUE)

+	{

+		ret = mbtk_sock_read(coap_handle, coap_fd, buffer, MBTK_ECOAP_DATA_MAX*2, 1000*10, &err);

+		if(ret <= 0)

+		{

+            printf("read fail, err:%d",err);

+			break;

+		}

+        printf("recv_from ret:%d\n",ret);

+        coap_log(FALSE,(char*)buffer,ret);

+        mbtk_ecoap_recv_resolve(buffer,ret);

+	}

+

+    return ret;

+}

+

+static int ecoap_sig_send_complete_handle(void )

+{

+    int ret=-1;

+    mbtk_ecoap_package_s *coap = mbtk_ecoap_get_by_msg_id(mbtk_ecoap_net.message_id);

+    if(coap == NULL)

+    {

+        mbtk_ecoap_show_error(MBTK_ECOAP_ERR_MSG_ID);

+        return -1;

+    }

+

+    ret = mbtk_ecoap_ecoap_send(coap);

+

+    return ret;

+

+}

+

+static int mbtk_ecoap_open(int open);

+

+

+

+#endif

+

+

+/*************************************************************

+    Public Function Definitions

+*************************************************************/

+

+/*************************************************************

+	ip_addr: url

+	port

+

+*************************************************************/

+int mbtk_coap_ecoapnew_exec_cmd

+(

+    char *ip_addr,

+    int port,

+    bool is_support_ssl, 

+    bool ingnore_cert

+)

+{

+    printf("mbtk_coap_ecoapnew_exec_cmd()------start1\n");

+	int ret = 0;

+	if( !ip_addr)

+	{

+	    mbtk_ecoap_show_error(MBTK_ECOAP_ERR_NO_READY);

+	    return -1;

+	}

+

+	ret = mbtk_ecoap_handle_sock_init(ip_addr, port, is_support_ssl, ingnore_cert);

+	

+    return ret;

+}

+

+int mbtk_coap_ecoaprxmod_exec_cmd

+(

+	int mode

+)

+{

+    int result = 0;

+	int rx_mod = mode;

+	

+    return result;

+}

+

+int mbtk_coap_ecoappr_exec_cmd

+(

+	int format

+)

+{

+    int result = 0;

+	uint16 ecoappr = format;

+    return result;

+}

+

+int mbtk_coap_ecoaprxget_exec_cmd

+(

+    int len

+)

+{

+    int result = 0;

+	int get_len = len;

+	

+    return result;

+

+}

+

+int mbtk_coap_ecoapver_exec_cmd

+(

+    int version

+)

+{

+    int result = 0;

+	mbth_ecoap_package_ver.version = version;

+    return result;

+

+}

+

+int mbtk_coap_ecoaptype_exec_cmd

+(

+    mbtk_coap_type type

+)

+{

+    int result = 0;

+	mbth_ecoap_package_ver.type = type;

+	

+    return result;

+

+}

+

+

+int mbtk_coap_ecoapcode_exec_cmd

+(

+    mbtk_coap_code_type code

+)

+{

+    int  result = 0;

+	mbth_ecoap_package_ver.code = code;

+        LOGE("ver:%d,type:%d,code:%d,msg:%d\n",

+            mbth_ecoap_package_ver.version,

+            mbth_ecoap_package_ver.type,

+            mbth_ecoap_package_ver.code,

+            mbth_ecoap_package_ver.message_id);

+    return result;

+

+}

+

+int mbtk_coap_ecoaptoken_exec_cmd

+(

+    char *token_buf, int len

+)

+{

+    int result = 0;

+	uint16 token_length = len;

+	char * token = token_buf;	

+	if(strlen(token) != token_length)

+	{

+		return -1;

+	}

+	

+	mbth_ecoap_package_ver.token_len = token_length;

+	memcpy(mbth_ecoap_package_ver.token, token, strlen(token));

+

+	return result;

+}

+

+int mbtk_coap_ecoapmsgid_exec_cmd

+(

+    int msg_id

+)

+{

+    int result = 0;

+	 mbth_ecoap_package_ver.message_id = msg_id;        

+

+	return result;

+}

+

+int mbtk_coap_ecoapopt_exec_cmd

+(

+    char *value_buf, int buf_len

+)

+{

+    int result = 0;

+	int opt = buf_len;

+    char* value = value_buf;

+

+    if(value == NULL)

+    {

+        return -1;

+    }

+    if(opt != strlen(value))

+    {

+        return -1;

+    }

+

+    memset(&mbtk_ecoap_option_2, 0x0, sizeof(mbtk_ecoap_opt_2));

+    memset(&mbtk_ecoap_option_3, 0x0, sizeof(mbtk_ecoap_opt_3));

+

+    mbtk_ecoap_option_3.optlen = opt;

+    mbtk_ecoap_option_2.optlen = opt / 2;

+

+    if(mbtk_ecoap_option_2.optlen > 0)

+    {

+        memcpy(mbtk_ecoap_option_3.optVal, value, opt);

+        mbtk_ecoap_option_3.optVal[opt] = '\0';

+        StrToHex((byte*)mbtk_ecoap_option_2.optVal, (byte*)value, strlen(value));

+//		printf("\r\nopt2---------option_len,option_data");

+//		coap_log(FALSE,(char*)mbtk_ecoap_option_2.optVal,mbtk_ecoap_option_2.optlen);

+    }

+    return result;

+

+}

+

+int mbtk_coap_ecoapsend_exec_cmd

+(

+    int message_id, int Data_len, char *data

+)

+{

+    int data_len = Data_len;

+    char * send_data = NULL;

+	int err = -1;

+    if(data_len != 0 )

+    {

+        send_data = data;

+    }

+    int coap_id = message_id;

+

+	LOGE("send---------coap_id:%d,data_len:%d\n",coap_id,data_len);

+

+     if(mbtk_ecoap_state <= MBTK_ECOAP_STATE_CLOSING )

+    {

+        mbtk_ecoap_show_error(MBTK_ECOAP_ERR_NO_OPEN);

+        return -1;

+    }

+

+    if(coap_id != mbth_ecoap_package_ver.message_id)

+    {

+        mbtk_ecoap_show_error(MBTK_ECOAP_ERR_MSG_ID);

+        return -1;

+    }

+

+    if(coap_id != mbtk_ecoap_net.message_id)

+    {

+        printf("send_cmd del message_id:%d\n",mbtk_ecoap_net.message_id);

+        mbtk_ecoap_list_del(mbtk_ecoap_net.message_id,-1);

+    }

+

+    if(send_data)

+    {

+        if(data_len != strlen(send_data) || data_len > 1024)

+        {

+            mbtk_ecoap_show_error(MBTK_ECOAP_ERR_PAYLOAD_LENGTH);

+            return -1;

+        }

+    }

+

+    mbtk_ecoap_package_s *coap = mbtk_ecoap_get_by_msg_id(coap_id);

+    if( !coap)

+    {

+        mbtk_ecoap_net.message_id = mbth_ecoap_package_ver.message_id;

+        coap = mbtk_ecoap_list_add(mbtk_ecoap_net.message_id);

+        if(coap != NULL)

+        {

+            mbtk_ecoap_list_item_init(ECOAPVER,coap,mbth_ecoap_package_ver.version);

+            mbtk_ecoap_list_item_init(EOCAPTYPE,coap,mbth_ecoap_package_ver.type);

+            mbtk_ecoap_list_item_init(ECOAPCODE,coap,mbth_ecoap_package_ver.code);

+            if(mbth_ecoap_package_ver.token_len > 0)

+            {

+                coap->token_len = mbth_ecoap_package_ver.token_len;

+                memcpy(coap->token, mbth_ecoap_package_ver.token, strlen((char *)mbth_ecoap_package_ver.token));

+            }

+            

+            if(data_len > 0)

+            {

+                coap->payload_len = data_len;

+                if(coap->payload != NULL)

+                {

+                    free(coap->payload);

+                    coap->payload = NULL;

+                }

+                coap->payload = (uint8*)malloc(data_len + 1);

+                memset(coap->payload,0x00,data_len + 1);

+                memcpy(coap->payload,send_data,data_len);

+            }

+

+        }

+        else{

+            printf("coap new error\n");

+        }

+

+    }

+    else

+    {

+		printf("coap is NULL\n");

+    }

+

+    mbtk_ecoap_recv = 0;

+	if(mbtk_ecoap_state < MBTK_ECOAP_STATE_READY)

+	{

+	    printf("State[%d] error,not ready...\n",mbtk_ecoap_state);

+	    return -1;

+	}

+

+	if( !ecoap_sig_send_complete_handle() )

+	{

+		ecoap_sig_recv_handle();

+	}

+

+	if(mbtk_sock_close(coap_handle, coap_fd,1000, &err)) {

+        return -1;

+    }

+

+    coap_fd = -1;

+    return 0;

+

+}

+

+int mbtk_coap_ecoapdel_exec_cmd( int del_id )

+{

+

+    int result = 0;

+	LOGE("del---------del_id:%d\n",del_id);

+	if(del_id != 1)

+	{

+		return -1;

+	}

+	

+	 if(mbtk_ecoap_state <= MBTK_ECOAP_STATE_CLOSING )

+	{

+		mbtk_ecoap_show_error(MBTK_ECOAP_ERR_NO_OPEN);

+		return -1;

+	}

+			

+	LOGE("del---------del_id:%d\n",del_id);

+

+    return result;

+

+}

+

+int mbtk_coap_ecoapclose_exec_cmd( void )

+{

+    int result = 0;

+	if(!coap_sock_inited) {

+        LOGE("HTTP not inited.");

+        return -1;

+    }

+

+    int err = mbtk_sock_deinit(coap_handle);

+    if(err != MBTK_SOCK_SUCCESS) {

+        LOGE("mbtk_sock_deinit() fail.");

+        return -1;

+    }

+

+    coap_handle = -1;

+    coap_sock_inited = FALSE;

+    return 0;

+

+    return result;

+}

+

+

diff --git a/mbtk/mbtk_lib/src/mbtk_coap_api.cpp b/mbtk/mbtk_lib/src/mbtk_coap_api.cpp
new file mode 100755
index 0000000..0a6dc19
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_coap_api.cpp
@@ -0,0 +1,906 @@
+/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*

+

+

+===========================================================================*/

+

+

+/*===========================================================================

+

+                     INCLUDE FILES FOR MODULE

+

+===========================================================================*/

+

+#include <string.h>

+#include <stdio.h>

+#include <stdlib.h>

+#include "mbtk_coap_pdu.h"

+#include "mbtk_coap_api.h"

+

+/*===========================================================================

+

+                        LOCAL DATA STRUCTURES

+

+===========================================================================*/

+//extern "C"

+//{

+

+static CoapPDU * g_CoapPDU = NULL;

+static CoapPDU * g_CoapRecvPointer = NULL;

+

+static void createCoapPDU()

+{

+    if(g_CoapPDU == NULL)

+        g_CoapPDU = new CoapPDU();

+}

+

+void coapReleaseCoapPUD()

+{

+    if(g_CoapPDU)

+    {

+        delete g_CoapPDU;

+        g_CoapPDU = NULL;

+    }

+}

+

+int coapReset()

+{

+    createCoapPDU();

+    return g_CoapPDU->reset();

+}

+

+int coapValidate()

+{

+    createCoapPDU();

+    return g_CoapPDU->validate();

+}

+

+

+uint8_t* coapGetPDUPointer()

+{

+    return g_CoapPDU->getPDUPointer();

+}

+

+void coapSetPDULength(int len)

+{

+    createCoapPDU();

+    g_CoapPDU->setPDULength(len);

+}

+

+int coapSetURI(char *uri)

+{

+    createCoapPDU();

+    return g_CoapPDU->setURI(uri);

+}

+

+int coapAddURIQuery(char *query)

+{

+    createCoapPDU();

+    return g_CoapPDU->addURIQuery(query);

+}

+

+int coapGetURI(char *dst, int dstlen, int *outLen)

+{

+    createCoapPDU();

+    return  g_CoapPDU->getURI(dst, dstlen, outLen);

+}

+

+int coapSetVersion(uint8_t version)

+{

+    createCoapPDU();

+    return g_CoapPDU->setVersion(version);

+}

+

+uint8_t coapGetVersion()

+{

+    createCoapPDU();

+    return g_CoapPDU->getVersion();

+}

+

+void coapSetType(mbtk_coap_type mt)

+{

+    createCoapPDU();

+    g_CoapPDU->setType(mt);

+}

+

+mbtk_coap_type coapGetType()

+{

+    createCoapPDU();

+    return (mbtk_coap_type)g_CoapPDU->getType();

+}

+

+int GetTokenLength(uint8_t tokenLength)

+{

+    createCoapPDU();

+    return g_CoapPDU->setTokenLength(tokenLength);

+}

+

+int coapGetTokenLength()

+{

+    createCoapPDU();

+    return g_CoapPDU->getTokenLength();

+}

+

+uint8_t* coapGetTokenPointer()

+{

+    createCoapPDU();

+    return g_CoapPDU->getTokenPointer();

+}

+

+int coapSetToken(uint8_t *token, uint8_t tokenLength)

+{

+    createCoapPDU();

+    return g_CoapPDU->setToken(token, tokenLength) ;

+}

+

+void coapSetCode(mbtk_coap_code_type code)

+{

+    createCoapPDU();

+    g_CoapPDU->setCode(code);

+}

+

+mbtk_coap_code_type coapGetCode()

+{

+    createCoapPDU();

+    return (mbtk_coap_code_type)g_CoapPDU->getCode();

+}

+

+mbtk_coap_code_type coapHttpStatusToCode(int httpStatus)

+{

+    createCoapPDU();

+    return (mbtk_coap_code_type)g_CoapPDU->httpStatusToCode(httpStatus);

+}

+

+int coapSetMessageID(uint16_t messageID)

+{

+    createCoapPDU();

+    return g_CoapPDU->setMessageID(messageID);

+}

+

+uint16_t coapGetMessageID()

+{

+    createCoapPDU();

+    return g_CoapPDU->getMessageID();

+}

+

+/// Returns the length of the PDU.

+int coapGetPDULength()

+{

+    createCoapPDU();

+    return g_CoapPDU->getPDULength();

+}

+

+/// Return the number of options that the PDU has.

+int coapGetNumOptions()

+{

+    createCoapPDU();

+    return  g_CoapPDU->getNumOptions();

+}

+

+//CoapOption* getOptions() {

+//  return g_CoapPDU->getOptions();

+//}

+

+

+int coapAddOption(uint16_t insertedOptionNumber, uint16_t optionValueLength, uint8_t *optionValue)

+{

+    createCoapPDU();

+    return  g_CoapPDU->addOption(insertedOptionNumber, optionValueLength, optionValue);

+}

+

+uint8_t* coapMallocPayload(int len)

+{

+    createCoapPDU();

+    return g_CoapPDU->mallocPayload(len);

+}

+

+int coapSetPayload(uint8_t *payload, int len)

+{

+    createCoapPDU();

+    return g_CoapPDU->setPayload(payload, len);

+}

+

+/// Returns a pointer to the payload buffer.

+uint8_t* coapGetPayloadPointer()

+{

+    createCoapPDU();

+    return g_CoapPDU->getPayloadPointer();

+}

+

+/// Gets the length of the payload buffer.

+int coapGetPayloadLength()

+{

+    createCoapPDU();

+    return g_CoapPDU->getPayloadLength();

+}

+

+/// Returns a pointer to a buffer which is a copy of the payload buffer (dynamically allocated).

+uint8_t* coapGetPayloadCopy()

+{

+    createCoapPDU();

+    return g_CoapPDU->getPayloadCopy();

+}

+

+int coapSetContentFormat(mbtk_content_format_type format)

+{

+    createCoapPDU();

+    return g_CoapPDU->setContentFormat(format);

+}

+

+int coapHasOption(uint16_t optionNumber )

+{

+    createCoapPDU();

+    return g_CoapPDU->hasOption(optionNumber);

+}

+void coapDeleteRecv()

+{

+    if(g_CoapRecvPointer != NULL)

+    {

+        delete g_CoapRecvPointer;

+        g_CoapRecvPointer = NULL;

+    }

+

+}

+int coapCreateRecv(uint8_t *pdu, int pduLength)

+{

+    coapDeleteRecv();

+    g_CoapRecvPointer = new CoapPDU(pdu, pduLength, pduLength);

+    if(g_CoapRecvPointer != NULL)

+    {

+        if(g_CoapRecvPointer->validate())

+            return g_CoapRecvPointer->getPDULength();

+        else

+        {

+            coapDeleteRecv();

+            return 0;

+        }

+    }

+    else

+        return 0;

+

+}

+

+void coapPrintHuman(char * outBuffer)

+{

+    char type[20];

+    switch(g_CoapRecvPointer->getType())

+    {

+        case COAP_CONFIRMABLE:

+            strcpy(type, "Confirmable");

+            break;

+

+        case COAP_NON_CONFIRMABLE:

+            strcpy(type, "Non-Confirmable");

+            break;

+

+        case COAP_ACKNOWLEDGEMENT:

+            strcpy(type, "Acknowledgement");

+            break;

+

+        case COAP_RESET:

+            strcpy(type, "Reset");

+            break;

+    }

+    char code[30];

+    switch(g_CoapRecvPointer->getCode())

+    {

+        case COAP_EMPTY:

+            strcpy(code, "0.00 Empty");

+            break;

+        case COAP_GET:

+            strcpy(code, "0.01 GET");

+            break;

+        case COAP_POST:

+            strcpy(code, "0.02 POST");

+            break;

+        case COAP_PUT:

+            strcpy(code, "0.03 PUT");

+            break;

+        case COAP_DELETE:

+            strcpy(code, "0.04 DELETE");

+            break;

+        case COAP_CREATED:

+            strcpy(code, "2.01 Created");

+            break;

+        case COAP_DELETED:

+            strcpy(code, "2.02 Deleted");

+            break;

+        case COAP_VALID:

+            strcpy(code, "2.03 Valid");

+            break;

+        case COAP_CHANGED:

+            strcpy(code, "2.04 Changed");

+            break;

+        case COAP_CONTENT:

+            strcpy(code, "2.05 Content");

+            break;

+        case COAP_BAD_REQUEST:

+            strcpy(code, "4.00 Bad Request");

+            break;

+        case COAP_UNAUTHORIZED:

+            strcpy(code, "4.01 Unauthorized");

+            break;

+        case COAP_BAD_OPTION:

+            strcpy(code, "4.02 Bad Option");

+            break;

+        case COAP_FORBIDDEN:

+            strcpy(code, "4.03 Forbidden");

+            break;

+        case COAP_NOT_FOUND:

+            strcpy(code, "4.04 Not Found");

+            break;

+        case COAP_METHOD_NOT_ALLOWED:

+            strcpy(code, "4.05 Method Not Allowed");

+            break;

+        case COAP_NOT_ACCEPTABLE:

+            strcpy(type, "4.06 Not Acceptable");

+            break;

+        case COAP_PRECONDITION_FAILED:

+            strcpy(code, "4.12 Precondition Failed");

+            break;

+        case COAP_REQUEST_ENTITY_TOO_LARGE:

+            strcpy(code, "4.13 Request Entity Too Large");

+            break;

+        case COAP_UNSUPPORTED_CONTENT_FORMAT:

+            strcpy(code, "4.15 Unsupported Content-Format");

+            break;

+        case COAP_INTERNAL_SERVER_ERROR:

+            strcpy(code, "5.00 Internal Server Error");

+            break;

+        case COAP_NOT_IMPLEMENTED:

+            strcpy(code, "5.01 Not Implemented");

+            break;

+        case COAP_BAD_GATEWAY:

+            strcpy(code, "5.02 Bad Gateway");

+            break;

+        case COAP_SERVICE_UNAVAILABLE:

+            strcpy(code, "5.03 Service Unavailable");

+            break;

+        case COAP_GATEWAY_TIMEOUT:

+            strcpy(code, "5.04 Gateway Timeout");

+            break;

+        case COAP_PROXYING_NOT_SUPPORTED:

+            strcpy(code, "5.05 Proxying Not Supported");

+            break;

+        default:

+        {

+            sprintf(code, "Undefined Code %u", g_CoapRecvPointer->getCode());

+        }

+    }

+

+    sprintf(outBuffer,"PDU is %d bytes long\r\n"

+            "CoAP Version: %d\r\n"

+            "Message Type: %s\r\n"

+            "Code: %s\r\n"

+            "Message ID: %u\r\n",

+            g_CoapRecvPointer->getPDULength(),

+            g_CoapRecvPointer->getVersion(),

+            type,

+            code,

+            g_CoapRecvPointer->getMessageID());

+

+    // print token value

+    int tokenLength = g_CoapRecvPointer->getTokenLength();

+    uint8_t *tokenPointer = g_CoapRecvPointer->getPDUPointer()+COAP_HDR_SIZE;

+    if(tokenLength==0)

+    {

+        sprintf(outBuffer,"%sNo token\r\n",outBuffer);

+    }

+    else

+    {

+        sprintf(outBuffer,"%sToken of %d bytes.\r\n"

+                "Value: %s\r\n",

+                outBuffer,tokenLength, (char*)tokenPointer);

+        for(int j=0; j<tokenLength; j++)

+        {

+            sprintf(outBuffer,"%s%.2x",outBuffer, tokenPointer[j]);

+        }

+    }

+    // print options

+    CoapPDU::CoapOption* options = g_CoapRecvPointer->getOptions();

+    if(options==NULL)

+    {

+        sprintf(outBuffer,"\r\n%sNO options\r\n", outBuffer);

+    }

+

+    for(int i=0; i<g_CoapRecvPointer->getNumOptions(); i++)

+    {

+        char optionNumberBuffer[20];

+        switch(options[i].optionNumber)

+        {

+            case COAP_OPTION_IF_MATCH:

+                strcpy(optionNumberBuffer, "IF_MATCH");

+                break;

+            case COAP_OPTION_URI_HOST:

+                strcpy(optionNumberBuffer, "URI_HOST");

+                break;

+            case COAP_OPTION_ETAG:

+                strcpy(optionNumberBuffer, "ETAG");

+                break;

+            case COAP_OPTION_IF_NONE_MATCH:

+                strcpy(optionNumberBuffer, "IF_NONE_MATCH");

+                break;

+            case COAP_OPTION_OBSERVE:

+                strcpy(optionNumberBuffer, "OBSERVE");

+                break;

+            case COAP_OPTION_URI_PORT:

+                strcpy(optionNumberBuffer, "URI_PORT");

+                break;

+            case COAP_OPTION_LOCATION_PATH:

+                strcpy(optionNumberBuffer, "LOCATION_PATH");

+                break;

+            case COAP_OPTION_URI_PATH:

+                strcpy(optionNumberBuffer, "URI_PATH");

+                break;

+            case COAP_OPTION_CONTENT_FORMAT:

+                strcpy(optionNumberBuffer, "CONTENT_FORMAT");

+                break;

+            case COAP_OPTION_MAX_AGE:

+                strcpy(optionNumberBuffer, "MAX_AGE");

+                break;

+            case COAP_OPTION_URI_QUERY:

+                strcpy(optionNumberBuffer, "URI_QUERY");

+                break;

+            case COAP_OPTION_ACCEPT:

+                strcpy(optionNumberBuffer, "ACCEPT");

+                break;

+            case COAP_OPTION_LOCATION_QUERY:

+                strcpy(optionNumberBuffer, "LOCATION_QUERY");

+                break;

+            case COAP_OPTION_PROXY_URI:

+                strcpy(optionNumberBuffer, "PROXY_URI");

+                break;

+            case COAP_OPTION_PROXY_SCHEME:

+                strcpy(optionNumberBuffer, "PROXY_SCHEME");

+                break;

+            case COAP_OPTION_BLOCK1:

+                strcpy(optionNumberBuffer, "BLOCK1");

+                break;

+            case COAP_OPTION_BLOCK2:

+                strcpy(optionNumberBuffer, "BLOCK2");

+                break;

+            case COAP_OPTION_SIZE1:

+                strcpy(optionNumberBuffer, "SIZE1");

+                break;

+            case COAP_OPTION_SIZE2:

+                strcpy(optionNumberBuffer, "SIZE2");

+                break;

+            default:

+                sprintf(optionNumberBuffer, "Unknown option %u",(unsigned)options[i].optionNumber);

+                break;

+        }

+

+        /*char optionValue[options[i].optionValueLength + 1];

+        for(int j=0; j<options[i].optionValueLength; j++) {

+            char c = options[i].optionValuePointer[j];

+            if((c>='!'&&c<='~')||c==' ') {

+                sprintf(optionValue, "%s%c",optionValue,c);

+            } else {

+                sprintf(optionValue,"%s\\%.2d",optionValue,c);

+            }

+        }

+        sprintf(optionValue,"%s\"\r\n",optionValue);*/

+        sprintf(outBuffer,"\r\n%sOPTION (%d/%d)\r\n"

+                "Option number (delta): %hu (%hu)\r\n"

+                "Name: %s\r\n"

+                "Value length: %u\r\n"

+                "Value: \"%s",

+                outBuffer,

+                i + 1,g_CoapRecvPointer->getNumOptions(),

+                options[i].optionNumber,options[i].optionDelta,

+                optionNumberBuffer,

+                options[i].optionValueLength,

+                (char *)options[i].optionValuePointer);

+

+    }

+    if(options)

+        free(options);

+    // print payload

+    if(g_CoapRecvPointer->getPayloadLength() == 0)

+    {

+        sprintf(outBuffer,"%sNo payload\r\n", outBuffer);

+    }

+    else

+    {

+        sprintf(outBuffer, "%sPayload of %d bytes\r\n"

+                "Value: \"%s\"\r\n", outBuffer,

+                g_CoapRecvPointer->getPayloadLength(),

+                (char *)g_CoapRecvPointer->getPayloadPointer());

+        /*sprintf(outBuffer,"%sPayload of %d bytes\r\n", outBuffer,g_CoapRecvPointer->getPayloadLength());

+        sprintf(outBuffer, "%sValue: \"", outBuffer);

+        uint8_t* _payloadPointer = g_CoapRecvPointer->getPayloadPointer();

+        for(int j=0; j<g_CoapRecvPointer->getPayloadLength(); j++) {

+            char c = _payloadPointer[j];

+            if((c>='!'&&c<='~')||c==' ') {

+                sprintf(outBuffer, "%s%c", outBuffer,c);

+            } else {

+                sprintf(outBuffer, "%s\\%.2x",outBuffer,c);

+            }

+        }

+        sprintf(outBuffer, "%s\"\r\n", outBuffer);*/

+    }

+

+}

+

+void coapGetOptionValueById(uint16_t optionNumber, uint16_t * optionValueLength, uint8_t * optionValuePointer)

+{

+    if(g_CoapRecvPointer)

+        g_CoapRecvPointer->getOptionValueById(optionNumber, optionValueLength, optionValuePointer);

+}

+

+uint16_t coapGetRecvMessageID()

+{

+    return g_CoapRecvPointer->getMessageID();

+}

+

+mbtk_coap_type coapGetRecvType()

+{

+    return (mbtk_coap_type)g_CoapRecvPointer->getType();

+}

+

+mbtk_coap_code_type coapGetRecvCode()

+{

+    return (mbtk_coap_code_type)g_CoapRecvPointer->getCode();

+}

+

+int mbtk_coap_get_pdu_Length()

+{

+    return g_CoapRecvPointer->getPDULength();

+}

+

+

+int coapGetRecvTokenLength()

+{

+    return g_CoapRecvPointer->getTokenLength();

+}

+

+uint8_t* coapGetRecvTokenPointer()

+{

+    return g_CoapRecvPointer->getTokenPointer();

+}

+

+/// Returns a pointer to the payload buffer.

+uint8_t* coapGetRecvPayloadPointer()

+{

+    return g_CoapRecvPointer->getPayloadPointer();

+}

+

+/// Gets the length of the payload buffer.

+int coapGetRecvPayloadLength()

+{

+    return g_CoapRecvPointer->getPayloadLength();

+}

+

+uint8_t coapGetRecvVersion()

+{

+    return g_CoapRecvPointer->getVersion();

+}

+

+// Return If-Match length,or 0 if fail.

+uint16_t coapGetRecvOptionIfMatch(uint16_t *optionValueLength, uint8_t *optionValuePointer)

+{

+    *optionValueLength = 0;

+    coapGetOptionValueById(COAP_OPTION_IF_MATCH,optionValueLength,optionValuePointer);

+    return *optionValueLength;

+}

+

+// Return Uri-Host length,or 0 if fail.

+uint16_t coapGetRecvOptionUriHost(uint16_t *optionValueLength, uint8_t *optionValuePointer)

+{

+    *optionValueLength = 0;

+    coapGetOptionValueById(COAP_OPTION_URI_HOST,optionValueLength,optionValuePointer);

+    return *optionValueLength;

+}

+

+// Return ETag length,or 0 if fail.

+uint16_t coapGetRecvOptionETag(uint16_t *optionValueLength, uint8_t *optionValuePointer)

+{

+    *optionValueLength = 0;

+    coapGetOptionValueById(COAP_OPTION_ETAG,optionValueLength,optionValuePointer);

+    return *optionValueLength;

+}

+

+// Return If-None-Match length,or 0 if fail.

+uint16_t coapGetRecvOptionIfNoneMatch(uint16_t *optionValueLength, uint8_t *optionValuePointer)

+{

+    *optionValueLength = 0;

+    coapGetOptionValueById(COAP_OPTION_IF_NONE_MATCH,optionValueLength,optionValuePointer);

+    return *optionValueLength;

+}

+

+// Return Location-Path length,or 0 if fail.

+uint16_t coapGetRecvOptionLocationPath(uint16_t *optionValueLength, uint8_t *optionValuePointer)

+{

+    *optionValueLength = 0;

+    coapGetOptionValueById(COAP_OPTION_LOCATION_PATH,optionValueLength,optionValuePointer);

+    return *optionValueLength;

+}

+

+// Return Location-Query length,or 0 if fail.

+uint16_t coapGetRecvOptionLocationQuery(uint16_t *optionValueLength, uint8_t *optionValuePointer)

+{

+    *optionValueLength = 0;

+    coapGetOptionValueById(COAP_OPTION_LOCATION_QUERY,optionValueLength,optionValuePointer);

+    return *optionValueLength;

+}

+

+// Return Proxy-Uri length,or 0 if fail.

+uint16_t coapGetRecvOptionProxyUri(uint16_t *optionValueLength, uint8_t *optionValuePointer)

+{

+    *optionValueLength = 0;

+    coapGetOptionValueById(COAP_OPTION_PROXY_URI,optionValueLength,optionValuePointer);

+    return *optionValueLength;

+}

+

+// Return Proxy-Scheme length,or 0 if fail.

+uint16_t coapGetRecvOptionProxyScheme(uint16_t *optionValueLength, uint8_t *optionValuePointer)

+{

+    *optionValueLength = 0;

+    coapGetOptionValueById(COAP_OPTION_PROXY_SCHEME,optionValueLength,optionValuePointer);

+    return *optionValueLength;

+}

+

+// Return Uri-Path length,or 0 if fail.

+uint16_t coapGetRecvOptionUriPath(uint16_t *optionValueLength, uint8_t *optionValuePointer)

+{

+    *optionValueLength = 0;

+    coapGetOptionValueById(COAP_OPTION_URI_PATH,optionValueLength,optionValuePointer);

+    return *optionValueLength;

+}

+

+// Return Uri-Query length,or 0 if fail.

+uint16_t coapGetRecvOptionUriQuery(uint16_t *optionValueLength, uint8_t *optionValuePointer)

+{

+    *optionValueLength = 0;

+    coapGetOptionValueById(COAP_OPTION_URI_QUERY,optionValueLength,optionValuePointer);

+    return *optionValueLength;

+}

+

+// Return 1 if get Observe success,or 0 if fail.

+uint16_t coapGetRecvOptionObserve(uint16_t *observe)

+{

+    uint8_t buff[10];

+    uint16_t buff_len = 0;

+    memset(buff,0x0,10);

+    coapGetOptionValueById(COAP_OPTION_OBSERVE,&buff_len,buff);

+    if(buff_len > 0)

+    {

+        *observe = (uint16_t)atoi((char*)buff);

+        return 1;

+    }

+    else

+    {

+        return 0;

+    }

+}

+

+// Return 1 if get Max-Age success,or 0 if fail.

+uint16_t coapGetRecvOptionMaxAge(uint16_t *max_age)

+{

+    uint8_t buff[10];

+    uint16_t buff_len = 0;

+    memset(buff,0x0,10);

+    coapGetOptionValueById(COAP_OPTION_MAX_AGE,&buff_len,buff);

+    if(buff_len > 0)

+    {

+        *max_age = (uint16_t)atoi((char*)buff);

+        return 1;

+    }

+    else

+    {

+        return 0;

+    }

+}

+

+// Return 1 if get Uri-Port success,or 0 if fail.

+uint16_t coapGetRecvOptionUriPort(uint16_t *uri_port)

+{

+    uint8_t buff[10];

+    uint16_t buff_len = 0;

+    memset(buff,0x0,10);

+    coapGetOptionValueById(COAP_OPTION_URI_PORT,&buff_len,buff);

+    if(buff_len > 0)

+    {

+        *uri_port = (uint16_t)atoi((char*)buff);

+        return 1;

+    }

+    else

+    {

+        return 0;

+    }

+}

+

+// Return 1 if get Size2 success,or 0 if fail.

+uint16_t coapGetRecvOptionSize2(uint16_t *size2)

+{

+    uint8_t buff[10];

+    uint16_t buff_len = 0;

+    memset(buff,0x0,10);

+    coapGetOptionValueById(COAP_OPTION_SIZE2,&buff_len,buff);

+    if(buff_len > 0)

+    {

+        *size2 = (uint16_t)atoi((char*)buff);

+        return 1;

+    }

+    else

+    {

+        return 0;

+    }

+}

+

+// Return 1 if get Size1 success,or 0 if fail.

+uint16_t coapGetRecvOptionSize1(uint16_t *size1)

+{

+    uint8_t buff[10];

+    uint16_t buff_len = 0;

+    memset(buff,0x0,10);

+    coapGetOptionValueById(COAP_OPTION_SIZE1,&buff_len,buff);

+    if(buff_len > 0)

+    {

+        *size1 = (uint16_t)atoi((char*)buff);

+        return 1;

+    }

+    else

+    {

+        return 0;

+    }

+}

+

+// Return 1 if get Block2 success,or 0 if fail.

+uint16_t coapGetRecvOptionBlock2(uint32_t *number,uint8_t *more_flag,uint8_t *size)

+{

+    uint8_t buff[10];

+    uint16_t buff_len = 0;

+    memset(buff,0x0,10);

+    coapGetOptionValueById(COAP_OPTION_BLOCK2,&buff_len,buff);

+    if(buff_len > 0)

+    {

+        if(buff_len == 1)

+        {

+            *size = (uint8_t)(buff[0] & 0x07);

+            *more_flag = (uint8_t)((buff[0] & 0x08) >> 3);

+            *number = (uint32_t)((buff[0] & 0xF0) >> 4);

+        }

+        else if(buff_len == 2)

+        {

+            *size = (uint8_t)(buff[1] & 0x07);

+            *more_flag = (uint8_t)((buff[1] & 0x08) >> 3);

+            *number = (uint32_t)(((buff[0] << 8) | (buff[1] & 0xF0)) >> 4);

+        }

+        else if(buff_len == 3)

+        {

+            *size = (uint8_t)(buff[2] & 0x07);

+            *more_flag = (uint8_t)((buff[2] & 0x08) >> 3);

+            *number = (uint32_t)(((buff[0] << 16) | (buff[1] << 8) | (buff[2] & 0xF0)) >> 4);

+        }

+        else

+        {

+            return 0;

+        }

+        return 1;

+    }

+    else

+    {

+        return 0;

+    }

+}

+

+// Return 1 if get Block1 success,or 0 if fail.

+uint16_t coapGetRecvOptionBlock1(uint32_t *number,uint8_t *more_flag,uint8_t *size)

+{

+    uint8_t buff[10];

+    uint16_t buff_len = 0;

+    memset(buff,0x0,10);

+    coapGetOptionValueById(COAP_OPTION_BLOCK1,&buff_len,buff);

+    if(buff_len > 0)

+    {

+        *size = (uint8_t)(buff[0] & 0x07);

+        *more_flag = (uint8_t)(buff[0] & 0x08);

+        if(buff_len == 1)

+        {

+            *number = (uint32_t)(buff[0] & 0xF0);

+        }

+        else if(buff_len == 2)

+        {

+            *number = (uint32_t)((buff[1] << 8) | (buff[0] & 0xF0));

+        }

+        else if(buff_len == 3)

+        {

+            *number = (uint32_t)((buff[2] << 16) | (buff[1] << 8) | (buff[0] & 0xF0));

+        }

+        else

+        {

+            return 0;

+        }

+        return 1;

+    }

+    else

+    {

+        return 0;

+    }

+}

+

+// Return 1 if get Content-Format success,or 0 if fail.

+uint16_t coapGetRecvOptionContentFormat(mbtk_content_format_type *type)

+{

+    uint8_t buff[10];

+    uint16_t buff_len = 0;

+    memset(buff,0x0,10);

+    coapGetOptionValueById(COAP_OPTION_CONTENT_FORMAT,&buff_len,buff);

+    if(buff_len > 0)

+    {

+        *type = (mbtk_content_format_type)atoi((char*)buff);

+        return 1;

+    }

+    else

+    {

+        return 0;

+    }

+}

+

+// Return 1 if get Accept success,or 0 if fail.

+uint16_t coapGetRecvOptionAccept(mbtk_content_format_type *type)

+{

+    uint8_t buff[10];

+    uint16_t buff_len = 0;

+    memset(buff,0x0,10);

+    coapGetOptionValueById(COAP_OPTION_ACCEPT,&buff_len,buff);

+    if(buff_len > 0)

+    {

+        *type = (mbtk_content_format_type)atoi((char*)buff);

+        return 1;

+    }

+    else

+    {

+        return 0;

+    }

+}

+

+

+int coapPrintRecvPayload(char *out)

+{

+    // print payload

+    int payloadLength = g_CoapRecvPointer->getPayloadLength();

+    if(payloadLength==0)

+    {

+        return 0;

+    }

+    else

+    {

+        uint8_t* payloadPointer = g_CoapRecvPointer->getPayloadPointer();

+        sprintf(out,"%s:%d\r\n",out, payloadLength*2);

+        for(int j=0; j<payloadLength; j++)

+        {

+            sprintf(out,"%s%.2X",out,payloadPointer[j]);

+        }

+        sprintf(out,"%s\r\n",out);

+        return 1;

+    }

+}

+

+const char* coapPrintHumanByIndex(int index)

+{

+    if(index == 0)

+    {

+        createCoapPDU();

+        return g_CoapPDU->printHuman();

+    }

+    else if(index == 1)

+        return g_CoapRecvPointer->printHuman();

+    return NULL;

+}

+

+const char* coapPrintHexByIndex(int index)

+{

+    if(index == 0)

+    {

+        createCoapPDU();

+        return g_CoapPDU->printHex();

+    }

+    else if(index == 1)

+        return g_CoapRecvPointer->printHex();

+    return NULL;

+}

+

+//}

+

+

diff --git a/mbtk/mbtk_lib/src/mbtk_coap_pdu.cpp b/mbtk/mbtk_lib/src/mbtk_coap_pdu.cpp
new file mode 100755
index 0000000..c9f046d
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_coap_pdu.cpp
@@ -0,0 +1,2065 @@
+/*

+Copyright (c) 2013, Ashley Mills.

+All rights reserved.

+

+Redistribution and use in source and binary forms, with or without

+modification, are permitted provided that the following conditions are met:

+

+1. Redistributions of source code must retain the above copyright notice, this

+   list of conditions and the following disclaimer.

+2. Redistributions in binary form must reproduce the above copyright notice,

+   this list of conditions and the following disclaimer in the documentation

+   and/or other materials provided with the distribution.

+

+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND

+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED

+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE

+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR

+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES

+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;

+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND

+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS

+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ */

+

+// version, 2 bits

+// type, 2 bits

+// 00 Confirmable

+// 01 Non-confirmable

+// 10 Acknowledgement

+// 11 Reset

+

+// token length, 4 bits

+// length of token in bytes (only 0 to 8 bytes allowed)

+

+#include <stdio.h>

+#include <stdlib.h>

+#include <stdint.h>

+#include <string.h>

+

+#include "mbtk_coap_pdu.h"

+

+//#include "mbtk_log.h"

+//#ifdef FEATURE_MBTK_ECOAP

+/// Memory-managed constructor. Buffer for PDU is dynamically sized and allocated by the object.

+/**

+ * When using this constructor, the CoapPDU class will allocate space for the PDU.

+ * Contrast this with the parameterized constructors, which allow the use of an external buffer.

+ *

+ * Note, the PDU container and space can be reused by issuing a CoapPDU::reset(). If the new PDU exceeds the

+ * space of the previously allocated memory, then further memory will be dynamically allocated.

+ *

+ * Deleting the object will free the Object container and all dynamically allocated memory.

+ *

+ * \note It would have been nice to use something like UDP_CORK or MSG_MORE, to allow separate buffers

+ * for token, options, and payload but these FLAGS aren't implemented for UDP in LwIP so stuck with one buffer for now.

+ *

+ * CoAP version defaults to 1.

+ *

+ * \sa CoapPDU::CoapPDU(uint8_t *pdu, int pduLength), CoapPDU::CoapPDU::(uint8_t *buffer, int bufferLength, int pduLength),

+ * CoapPDU:CoapPDU()~

+ *

+ */

+

+//#define printf(fmtstr) LOGD(fmtstr)

+//#define printf(fmtstr ,arg1) LOGD(fmtstr, arg1)

+//#define printf(fmtstr, arg1, arg2) LOGD(fmtstr , arg1, arg2)

+//#define printf(fmtstr, arg1, arg2, arg3) LOGD(fmtstr , arg1, arg2, arg3)

+

+/*

+void* operator new(unsigned int size, void* alloc, ds_appsrv_mem_e_type mem_type) throw()

+{

+// Do nothing

+    return alloc;

+}

+

+void operator delete(void* obj, void* alloc, ds_appsrv_mem_e_type mem_type) throw()

+{

+// Do nothing

+}

+

+*/

+

+CoapPDU::CoapPDU()

+{

+    // pdu

+    _pdu = (uint8_t*)calloc(4,sizeof(uint8_t));

+    _pduLength = 4;

+    _bufferLength = _pduLength;

+

+    //options

+    _numOptions = 0;

+    _maxAddedOptionNumber = 0;

+

+    // payload

+    _payloadPointer = NULL;

+    _payloadLength = 0;

+

+    _constructedFromBuffer = 0;

+

+    setVersion(1);

+}

+

+/// Construct object from external buffer that may be larger than actual PDU.

+/**

+ * This differs from CoapPDU::CoapPDU(uint8_t *pdu, int pduLength) in that the buffer may be larger

+ * than the actual CoAP PDU contained int the buffer. This is typically used when a large buffer is reused

+ * multiple times. Note that \b pduLength can be 0.

+ *

+ * If an actual CoAP PDU is passed in the buffer, \b pduLength should match its length. CoapPDU::validate() must

+ * be called to initiate the object before member functions can be used.

+ *

+ * A PDU constructed in this manner must be validated with CoapPDU::validate() before the member variables will be accessible.

+ *

+ * \warning The validation call parses the PDU structure to set some internal parameters. If you do

+ * not validate the PDU, then the behaviour of member access functions will be undefined.

+ *

+ * The buffer can be reused by issuing a CoapPDU::reset() but the class will not change the size of the buffer. If the

+ * newly constructed PDU exceeds the size of the buffer, the function called (for example CoapPDU::addOption) will fail.

+ *

+ * Deleting this object will only delete the Object container and will not delete the PDU buffer.

+ *

+ * \param buffer A buffer which either contains a CoAP PDU or is intended to be used to construct one.

+ * \param bufferLength The length of the buffer

+ * \param pduLength If the buffer contains a CoAP PDU, this specifies the length of the PDU within the buffer.

+ *

+ * \sa CoapPDU::CoapPDU(), CoapPDU::CoapPDU(uint8_t *pdu, int pduLength)

+ */

+CoapPDU::CoapPDU(uint8_t *buffer, int bufferLength, int pduLength)

+{

+    	printf("CoapPDU(),bufferLength:%d, pduLength;%d\n",bufferLength,pduLength);

+    // sanity

+    if(pduLength<4&&pduLength!=0)

+    {

+        printf("PDU cannot have a length less than 4");

+    }

+

+    // pdu

+    _pdu = buffer;

+    _bufferLength = bufferLength;

+    if(pduLength==0)

+    {

+        // this is actually a fresh pdu, header always exists

+        _pduLength = 4;

+        // make sure header is zeroed

+        _pdu[0] = 0x00;

+        _pdu[1] = 0x00;

+        _pdu[2] = 0x00;

+        _pdu[3] = 0x00;

+        setVersion(1);

+    }

+    else

+    {

+        _pduLength = pduLength;

+    }

+

+    _constructedFromBuffer = 1;

+

+    // options

+    _numOptions = 0;

+    _maxAddedOptionNumber = 0;

+

+    // payload

+    _payloadPointer = NULL;

+    _payloadLength = 0;

+}

+

+/// Reset CoapPDU container so it can be reused to build a new PDU.

+/**

+ * This resets the CoapPDU container, setting the pdu length, option count, etc back to zero. The

+ * PDU can then be populated as if it were newly constructed.

+ *

+ * Note that the space available will depend on how the CoapPDU was originally constructed:

+ * -# CoapPDU::CoapPDU()

+ *

+ *  Available space initially be \b _pduLength. But further space will be allocated as needed on demand,

+ *    limited only by the OS/environment.

+ *

+ * -# CoapPDU::CoapPDU(uint8_t *pdu, int pduLength)

+ *

+ *      Space is limited by the variable \b pduLength. The PDU cannot exceed \b pduLength bytes.

+ *

+ * -# CoapPDU::CoapPDU(uint8_t *buffer, int bufferLength, int pduLength)

+ *

+ *      Space is limited by the variable \b bufferLength. The PDU cannot exceed \b bufferLength bytes.

+ *

+ * \return 0 on success, 1 on failure.

+ */

+int CoapPDU::reset()

+{

+    // pdu

+    memset(_pdu,0x00,_bufferLength);

+    // packet always has at least a header

+    _pduLength = 4;

+

+    // options

+    _numOptions = 0;

+    _maxAddedOptionNumber = 0;

+    // payload

+    _payloadPointer = NULL;

+    _payloadLength = 0;

+    content.clear();

+    return 0;

+}

+

+/// Validates a PDU constructed using an external buffer.

+/**

+ * When a CoapPDU is constructed using an external buffer, the programmer must call this function to

+ * check that the received PDU is a valid CoAP PDU.

+ *

+ * \warning The validation call parses the PDU structure to set some internal parameters. If you do

+ * not validate the PDU, then the behaviour of member access functions will be undefined.

+ *

+ * \return 1 if the PDU validates correctly, 0 if not. XXX maybe add some error codes

+ */

+int CoapPDU::validate()

+{

+    if(_pduLength<4)

+    {

+        printf("PDU has to be a minimum of 4 bytes. This: %d bytes",_pduLength);

+        return 0;

+    }

+

+    // check header

+    //   0                   1                   2                   3

+    //  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1

+    // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+    // |Ver| T |  TKL  |      Code     |          Message ID           |

+    // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+    // |   Token (if any, TKL bytes) ...

+    // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+    // |   Options (if any) ...

+    // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+    // |1 1 1 1 1 1 1 1|    Payload (if any) ...

+    // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+

+    // version must be 1

+    int version = getVersion();

+    if (version != 1)

+    {

+        printf("Invalid version: %d", version);

+        return 0;

+    }

+    printf("Version: %d", version);

+    printf("Type: %d", getType());

+

+    // token length must be between 0 and 8

+    int tokenLength = getTokenLength();

+    if(tokenLength<0||tokenLength>8)

+    {

+        printf("Invalid token length: %d",tokenLength);

+        return 0;

+    }

+    printf("Token length: %d",tokenLength);

+    // check total length

+    if((COAP_HDR_SIZE+tokenLength)>_pduLength)

+    {

+        printf("Token length would make pdu longer than actual length.");

+        return 0;

+    }

+

+    // check that code is valid

+    int code = getCode();

+    if(code<COAP_EMPTY ||

+       (code>COAP_LASTMETHOD&&code<COAP_CREATED) ||

+       (code>COAP_CONTENT&&code<COAP_CONTINUE) ||

+       (code>COAP_CONTINUE&&code<COAP_BAD_REQUEST) ||

+       (code>COAP_NOT_ACCEPTABLE&&code<COAP_PRECONDITION_FAILED) ||

+       (code==0x8E) ||

+       (code>COAP_UNSUPPORTED_CONTENT_FORMAT&&code<COAP_INTERNAL_SERVER_ERROR) ||

+       (code>COAP_PROXYING_NOT_SUPPORTED) )

+    {

+        printf("Invalid CoAP code: %d",code);

+        return 0;

+    }

+    printf("CoAP code: %d",code);

+

+    // token can be anything so nothing to check

+

+    // check that options all make sense

+    uint16_t optionDelta =0, optionNumber = 0, optionValueLength = 0;

+    int totalLength = 0;

+

+    // first option occurs after token

+    int optionPos = COAP_HDR_SIZE + getTokenLength();

+

+    // may be 0 options

+    if(optionPos==_pduLength)

+    {

+        printf("No options. No payload.");

+        _numOptions = 0;

+        _payloadLength = 0;

+        return 1;

+    }

+

+    int bytesRemaining = _pduLength-optionPos;

+    int numOptions = 0;

+    uint8_t upperNibble = 0x00, lowerNibble = 0x00;

+

+    // walk over options and record information

+    while(1)

+    {

+        // check for payload marker

+        if(bytesRemaining>0)

+        {

+            uint8_t optionHeader = _pdu[optionPos];

+            if(optionHeader==0xFF)

+            {

+                // payload

+                if(bytesRemaining>1)

+                {

+                    _payloadPointer = &_pdu[optionPos+1];

+                    _payloadLength = (bytesRemaining-1);

+                    _numOptions = numOptions;

+                    printf("Payload found, length: %d",_payloadLength);

+                    return 1;

+                }

+                // payload marker but no payload

+                _payloadPointer = NULL;

+                _payloadLength = 0;

+                printf("Payload marker but no payload.");

+                return 0;

+            }

+

+            // check that option delta and option length are valid values

+            upperNibble = (optionHeader & 0xF0) >> 4;

+            lowerNibble = (optionHeader & 0x0F);

+            if(upperNibble==0x0F||lowerNibble==0x0F)

+            {

+                printf("Expected option header or payload marker, got: 0x%x%x",upperNibble,lowerNibble);

+                return 0;

+            }

+            printf("Option header byte appears sane: 0x%x%x",upperNibble,lowerNibble);

+        }

+        else

+        {

+            printf("No more data. No payload.");

+            _payloadPointer = NULL;

+            _payloadLength = 0;

+            _numOptions = numOptions;

+            return 1;

+        }

+

+        // skip over option header byte

+        bytesRemaining--;

+

+        // check that there is enough space for the extended delta and length bytes (if any)

+        int headerBytesNeeded = computeExtraBytes(upperNibble);

+        printf("%d extra bytes needed for extended delta",headerBytesNeeded);

+        if(headerBytesNeeded>bytesRemaining)

+        {

+            printf("Not enough space for extended option delta, needed %d, have %d.",headerBytesNeeded,bytesRemaining);

+            return 0;

+        }

+        headerBytesNeeded += computeExtraBytes(lowerNibble);

+        if(headerBytesNeeded>bytesRemaining)

+        {

+            printf("Not enough space for extended option length, needed %d, have %d.",

+                   (headerBytesNeeded-computeExtraBytes(upperNibble)),bytesRemaining);

+            return 0;

+        }

+        printf("Enough space for extended delta and length: %d, continuing.",headerBytesNeeded);

+

+        // extract option details

+        optionDelta = getOptionDelta(&_pdu[optionPos]);

+        optionNumber += optionDelta;

+        optionValueLength = getOptionValueLength(&_pdu[optionPos]);

+        printf("Got option: %d with length %d",optionNumber,optionValueLength);

+        // compute total length

+        totalLength = 1; // mandatory header

+        totalLength += computeExtraBytes(optionDelta);

+        totalLength += computeExtraBytes(optionValueLength);

+        totalLength += optionValueLength;

+        // check there is enough space

+        if(optionPos+totalLength>_pduLength)

+        {

+            printf("Not enough space for option payload, needed %d, have %d.",(totalLength-headerBytesNeeded-1),_pduLength-optionPos);

+            return 0;

+        }

+        printf("Enough space for option payload: %d %d",optionValueLength,(totalLength-headerBytesNeeded-1));

+

+        // recompute bytesRemaining

+        bytesRemaining -= totalLength;

+        bytesRemaining++; // correct for previous --

+

+        // move to next option

+        optionPos += totalLength;

+

+        // inc number of options XXX

+        numOptions++;

+    }

+

+    return 1;

+}

+

+/// Destructor. Does not free buffer if constructor passed an external buffer.

+/**

+ * The destructor acts differently, depending on how the object was initially constructed (from buffer or not):

+ *

+ * -# CoapPDU::CoapPDU()

+ *

+ *  Complete object is destroyed.

+ *

+ * -# CoapPDU::CoapPDU(uint8_t *pdu, int pduLength)

+ *

+ *      Only object container is destroyed. \b pdu is left intact.

+ *

+ * -# CoapPDU::CoapPDU(uint8_t *buffer, int bufferLength, int pduLength)

+ *

+ *      Only object container is destroyed. \b pdu is left intact.

+ *

+ */

+CoapPDU::~CoapPDU()

+{

+    if(!_constructedFromBuffer)

+    {

+        free(_pdu);

+    }

+}

+

+/// Returns a pointer to the internal buffer.

+uint8_t* CoapPDU::getPDUPointer()

+{

+    return _pdu;

+}

+

+/// Set the PDU length to the length specified.

+/**

+ * This is used when re-using a PDU container before calling CoapPDU::validate() as it

+ * is not possible to deduce the length of a PDU since the payload has no length marker.

+ * \param len The length of the PDU

+ */

+void CoapPDU::setPDULength(int len)

+{

+    _pduLength = len;

+}

+

+/// Shorthand function for setting a resource URI.

+/**

+ * Calls CoapPDU::setURI(uri,strlen(uri).

+ */

+int CoapPDU::setURI(char *uri)

+{

+    return setURI(uri,strlen(uri));

+}

+

+/// Shorthand function for setting a resource URI.

+/**

+ * This will parse the supplied \b uri and construct enough URI_PATH and URI_QUERY options to encode it.

+ * The options are added to the PDU.

+ *

+ * At present only simple URI formatting is handled, only '/','?', and '&' separators, and no port or protocol specificaiton.

+ *

+ * The function will split on '/' and create URI_PATH elements until it either reaches the end of the string

+ * in which case it will stop or if it reaches '?' it will start splitting on '&' and create URI_QUERY elements

+ * until it reaches the end of the string.

+ *

+ * Here is an example:

+ *

+ * /a/b/c/d?x=1&y=2&z=3

+ *

+ * Will be broken into four URI_PATH elements "a", "b", "c", "d", and three URI_QUERY elements "x=1", "y=2", "z=3"

+ *

+ * TODO: Add protocol extraction, port extraction, and some malformity checking.

+ *

+ * \param uri The uri to parse.

+ * \param urilen The length of the uri to parse.

+ *

+ * \return 0 on success, 1 on failure.

+ */

+int CoapPDU::setURI(char *uri, int urilen)

+{

+    // only '/', '?', '&' and ascii chars allowed

+

+    // sanitation

+    if(urilen<=0||uri==NULL)

+    {

+        printf("Null or zero-length uri passed.");

+        return 1;

+    }

+

+    // single character URI path (including '/' case)

+    if(urilen==1)

+    {

+        addOption(COAP_OPTION_URI_PATH,1,(uint8_t*)uri);

+        return 0;

+    }

+

+    // TODO, queries

+    // extract ? to mark where to stop processing path components

+    // and then process the query params

+

+    // local vars

+    char *startP=uri,*endP=NULL;

+    int oLen = 0;

+    char splitChar = '/';

+    int queryStageTriggered = 0;

+    uint16_t optionType = COAP_OPTION_URI_PATH;

+    while(1)

+    {

+        // stop at end of string or query

+        if(*startP==0x00||*(startP+1)==0x00)

+        {

+            break;

+        }

+

+        // ignore leading slash

+        if(*startP==splitChar)

+        {

+            printf("Skipping leading slash");

+            startP++;

+        }

+

+        // find next split point

+        endP = strchr(startP,splitChar);

+

+        // might not be another slash

+        if(endP==NULL)

+        {

+            printf("Ending out of slash");

+            // check if there is a ?

+            endP = strchr(startP,'?');

+            // done if no queries

+            if(endP==NULL)

+            {

+                endP = uri+urilen;

+            }

+            else

+            {

+                queryStageTriggered = 1;

+            }

+        }

+

+        // get length of segment

+        oLen = endP-startP;

+

+#ifdef DEBUG

+        char *b = (char*)malloc(oLen+1);

+        memcpy(b,startP,oLen);

+        b[oLen] = 0x00;

+        printf("Adding URI_PATH %s",b);

+        free(b);

+#endif

+

+        // add option

+        if(addOption(optionType,oLen,(uint8_t*)startP)!=0)

+        {

+            printf("Error adding option");

+            return 1;

+        }

+        startP = endP;

+

+        if(queryStageTriggered)

+        {

+            splitChar = '&';

+            optionType = COAP_OPTION_URI_QUERY;

+            startP++;

+            queryStageTriggered = false;

+        }

+    }

+

+    return 0;

+}

+

+/// Shorthand for adding a URI QUERY to the option list.

+/**

+ * Adds a new option to the CoAP PDU that encodes a URI_QUERY.

+ *

+ * \param query The uri query to encode.

+ * \return 0 on success, 1 on failure.

+ */

+int CoapPDU::addURIQuery(char *query)

+{

+    return addOption(COAP_OPTION_URI_QUERY,strlen(query),(uint8_t*)query);

+}

+

+/// Concatenates any URI_PATH elements and URI_QUERY elements into a single string.

+/**

+ * Parses the PDU options and extracts all URI_PATH and URI_QUERY elements,

+ * concatenating them into a single string with slash and amphersand separators accordingly.

+ *

+ * The produced string will be NULL terminated.

+ *

+ * \param dst Buffer into which to copy the concatenated path elements.

+ * \param dstlen Length of buffer.

+ * \param outLen Pointer to integer, into which URI length will be placed.

+ *

+ * \return 0 on success, 1 on failure. \b outLen will contain the length of the concatenated elements.

+ */

+int CoapPDU::getURI(char *dst, int dstlen, int *outLen)

+{

+    if(outLen==NULL)

+    {

+        printf("Output length pointer is NULL");

+        return 1;

+    }

+

+    if(dst==NULL)

+    {

+        printf("NULL destination buffer");

+        *outLen = 0;

+        return 1;

+    }

+

+    // check destination space

+    if(dstlen<=0)

+    {

+        *dst = 0x00;

+        *outLen = 0;

+        printf("Destination buffer too small (0)!");

+        return 1;

+    }

+    // check option count

+    if(_numOptions==0)

+    {

+        *dst = 0x00;

+        *outLen = 0;

+        return 0;

+    }

+    // get options

+    CoapPDU::CoapOption *options = getOptions();

+    if(options==NULL)

+    {

+        *dst = 0x00;

+        *outLen = 0;

+        return 0;

+    }

+    // iterate over options to construct URI

+    CoapOption *o = NULL;

+    int bytesLeft = dstlen-1; // space for 0x00

+    int oLen = 0;

+    // add slash at beggining

+    if(bytesLeft>=1)

+    {

+        *dst = '/';

+        dst++;

+        bytesLeft--;

+    }

+    else

+    {

+        printf("No space for initial slash needed 1, got %d",bytesLeft);

+        free(options);

+        return 1;

+    }

+

+    char separator = '/';

+    int firstQuery = 1;

+

+    for(int i=0; i<_numOptions; i++)

+    {

+        o = &options[i];

+        oLen = o->optionValueLength;

+        if(o->optionNumber==COAP_OPTION_URI_PATH||o->optionNumber==COAP_OPTION_URI_QUERY)

+        {

+            // if the option is a query, change the separator to &

+            if(o->optionNumber==COAP_OPTION_URI_QUERY)

+            {

+                if(firstQuery)

+                {

+                    // change previous '/' to a '?'

+                    *(dst-1) = '?';

+                    firstQuery = 0;

+                }

+                separator = '&';

+            }

+

+            // check space

+            if(oLen>bytesLeft)

+            {

+                printf("Destination buffer too small, needed %d, got %d",oLen,bytesLeft);

+                free(options);

+                return 1;

+            }

+

+            // case where single '/' exists

+            if(oLen==1&&o->optionValuePointer[0]=='/')

+            {

+                *dst = 0x00;

+                *outLen = 1;

+                free(options);

+                return 0;

+            }

+

+            // copy URI path or query component

+            memcpy(dst,o->optionValuePointer,oLen);

+

+            // adjust counters

+            dst += oLen;

+            bytesLeft -= oLen;

+

+            // add separator following (don't know at this point if another option is coming)

+            if(bytesLeft>=1)

+            {

+                *dst = separator;

+                dst++;

+                bytesLeft--;

+            }

+            else

+            {

+                printf("Ran out of space after processing option");

+                free(options);

+                return 1;

+            }

+        }

+    }

+

+    // remove terminating separator

+    dst--;

+    bytesLeft++;

+    // add null terminating byte (always space since reserved)

+    *dst = 0x00;

+    *outLen = (dstlen-1)-bytesLeft;

+    free(options);

+    return 0;

+}

+

+/// Sets the CoAP version.

+/**

+ * \param version CoAP version between 0 and 3.

+ * \return 0 on success, 1 on failure.

+ */

+int CoapPDU::setVersion(uint8_t version)

+{

+    if(version>3)

+    {

+        return 0;

+    }

+

+    _pdu[0] &= 0x3F;

+    _pdu[0] |= (version << 6);

+    return 1;

+}

+

+/**

+ * Gets the CoAP Version.

+ * @return The CoAP version between 0 and 3.

+ */

+uint8_t CoapPDU::getVersion()

+{

+    return (_pdu[0]&0xC0)>>6;

+}

+

+/**

+ * Sets the type of this CoAP PDU.

+ * \param mt The type, one of:

+ * - COAP_CONFIRMABLE

+ * - COAP_NON_CONFIRMABLE

+ * - COAP_ACKNOWLEDGEMENT

+ * - COAP_RESET.

+ */

+void CoapPDU::setType(int mt)

+{

+    _pdu[0] &= 0xCF;

+    _pdu[0] |= mt;

+}

+

+/// Returns the type of the PDU.

+int CoapPDU::getType()

+{

+    return (int)(_pdu[0]&0x30);

+}

+

+

+/// Set the token length.

+/**

+ * \param tokenLength The length of the token in bytes, between 0 and 8.

+ * \return 0 on success, 1 on failure.

+ */

+int CoapPDU::setTokenLength(uint8_t tokenLength)

+{

+    if(tokenLength>8)

+        return 1;

+

+    _pdu[0] &= 0xF0;

+    _pdu[0] |= tokenLength;

+    return 0;

+}

+

+/// Returns the token length.

+int CoapPDU::getTokenLength()

+{

+    return _pdu[0] & 0x0F;

+}

+

+/// Returns a pointer to the PDU token.

+uint8_t* CoapPDU::getTokenPointer()

+{

+    if(getTokenLength()==0)

+    {

+        return NULL;

+    }

+    return &_pdu[4];

+}

+

+/// Set the PDU token to the supplied byte sequence.

+/**

+ * This sets the PDU token to \b token and sets the token length to \b tokenLength.

+ * \param token A sequence of bytes representing the token.

+ * \param tokenLength The length of the byte sequence.

+ * \return 0 on success, 1 on failure.

+ */

+int CoapPDU::setToken(uint8_t *token, uint8_t tokenLength)

+{

+    printf("Setting token");

+    if(token==NULL)

+    {

+        printf("NULL pointer passed as token reference");

+        return 1;

+    }

+

+    if(tokenLength==0)

+    {

+        printf("Token has zero length");

+        return 1;

+    }

+

+    // if tokenLength has not changed, just copy the new value

+    uint8_t oldTokenLength = getTokenLength();

+    if(tokenLength==oldTokenLength)

+    {

+        memcpy((void*)&_pdu[4],token,tokenLength);

+        return 0;

+    }

+

+    // otherwise compute new length of PDU

+    uint8_t oldPDULength = _pduLength;

+    _pduLength -= oldTokenLength;

+    _pduLength += tokenLength;

+

+    // now, have to shift old memory around, but shift direction depends

+    // whether pdu is now bigger or smaller

+    if(_pduLength>oldPDULength)

+    {

+        // new PDU is bigger, need to allocate space for new PDU

+        if(!_constructedFromBuffer)

+        {

+            uint8_t *newMemory = (uint8_t*)realloc(_pdu,_pduLength);

+            if(newMemory==NULL)

+            {

+                // malloc failed

+                printf("Failed to allocate memory for token");

+                _pduLength = oldPDULength;

+                return 1;

+            }

+            _pdu = newMemory;

+            _bufferLength = _pduLength;

+        }

+        else

+        {

+            // constructed from buffer, check space

+            if(_pduLength>_bufferLength)

+            {

+                printf("Buffer too small to contain token, needed %d, got %d.",_pduLength-oldPDULength,_bufferLength-oldPDULength);

+                _pduLength = oldPDULength;

+                return 1;

+            }

+        }

+

+        // and then shift everything after token up to end of new PDU

+        // memory overlaps so do this manually so to avoid additional mallocs

+        int shiftOffset = _pduLength-oldPDULength;

+        int shiftAmount = _pduLength-tokenLength-COAP_HDR_SIZE; // everything after token

+        shiftPDUUp(shiftOffset,shiftAmount);

+

+        // now copy the token into the new space and set official token length

+        memcpy((void*)&_pdu[4],token,tokenLength);

+        setTokenLength(tokenLength);

+

+        // and return success

+        return 0;

+    }

+

+    // new PDU is smaller, copy the new token value over the old one

+    memcpy((void*)&_pdu[4],token,tokenLength);

+    // and shift everything after the new token down

+    int startLocation = COAP_HDR_SIZE+tokenLength;

+    int shiftOffset = oldPDULength-_pduLength;

+    int shiftAmount = oldPDULength-oldTokenLength-COAP_HDR_SIZE;

+    shiftPDUDown(startLocation,shiftOffset,shiftAmount);

+    // then reduce size of buffer

+    if(!_constructedFromBuffer)

+    {

+        uint8_t *newMemory = (uint8_t*)realloc(_pdu,_pduLength);

+        if(newMemory==NULL)

+        {

+            // malloc failed, PDU in inconsistent state

+            printf("Failed to shrink PDU for new token. PDU probably broken");

+            return 1;

+        }

+        _pdu = newMemory;

+        _bufferLength = _pduLength;

+    }

+

+    // and officially set the new tokenLength

+    setTokenLength(tokenLength);

+    return 0;

+}

+

+/// Sets the CoAP response code

+void CoapPDU::setCode(int code)

+{

+    _pdu[1] = code;

+    // there is a limited set of response codes

+}

+

+/// Gets the CoAP response code

+int CoapPDU::getCode()

+{

+    return (int)_pdu[1];

+}

+

+

+/// Converts a http status code as an integer, to a CoAP code.

+/**

+ * \param httpStatus the HTTP status code as an integer (e.g 200)

+ * \return The correct corresponding int on success,

+ * CoapPDU::COAP_UNDEFINED_CODE on failure.

+ */

+int CoapPDU::httpStatusToCode(int httpStatus)

+{

+    switch(httpStatus)

+    {

+        case 1:

+            return CoapPDU::COAP_GET;

+        case 2:

+            return CoapPDU::COAP_POST;

+        case 3:

+            return CoapPDU::COAP_PUT;

+        case 4:

+            return CoapPDU::COAP_DELETE;

+        case 201:

+            return CoapPDU::COAP_CREATED;

+        case 202:

+            return CoapPDU::COAP_DELETED;

+        case 203:

+            return CoapPDU::COAP_VALID;

+        case 204:

+            return CoapPDU::COAP_CHANGED;

+        case 205:

+            return CoapPDU::COAP_CONTENT;

+        case 400:

+            return CoapPDU::COAP_BAD_REQUEST;

+        case 401:

+            return CoapPDU::COAP_UNAUTHORIZED;

+        case 402:

+            return CoapPDU::COAP_BAD_OPTION;

+        case 403:

+            return CoapPDU::COAP_FORBIDDEN;

+        case 404:

+            return CoapPDU::COAP_NOT_FOUND;

+        case 405:

+            return CoapPDU::COAP_METHOD_NOT_ALLOWED;

+        case 406:

+            return CoapPDU::COAP_NOT_ACCEPTABLE;

+        case 412:

+            return CoapPDU::COAP_PRECONDITION_FAILED;

+        case 413:

+            return CoapPDU::COAP_REQUEST_ENTITY_TOO_LARGE;

+        case 415:

+            return CoapPDU::COAP_UNSUPPORTED_CONTENT_FORMAT;

+        case 500:

+            return CoapPDU::COAP_INTERNAL_SERVER_ERROR;

+        case 501:

+            return CoapPDU::COAP_NOT_IMPLEMENTED;

+        case 502:

+            return CoapPDU::COAP_BAD_GATEWAY;

+        case 503:

+            return CoapPDU::COAP_SERVICE_UNAVAILABLE;

+        case 504:

+            return CoapPDU::COAP_GATEWAY_TIMEOUT;

+        case 505:

+            return CoapPDU::COAP_PROXYING_NOT_SUPPORTED;

+        default:

+            return CoapPDU::COAP_UNDEFINED_CODE;

+    }

+}

+

+/// Set messageID to the supplied value.

+/**

+ * \param messageID A 16bit message id.

+ * \return 0 on success, 1 on failure.

+ */

+int CoapPDU::setMessageID(uint16_t messageID)

+{

+    // message ID is stored in network byte order

+    uint8_t *to = &_pdu[2];

+    endian_store16(to, messageID);

+    return 0;

+}

+

+/// Returns the 16 bit message ID of the PDU.

+uint16_t CoapPDU::getMessageID()

+{

+    // mesasge ID is stored in network byteorder

+    uint8_t *from = &_pdu[2];

+    uint16_t messageID = endian_load16(uint16_t, from);

+    return messageID;

+}

+

+/// Returns the length of the PDU.

+int CoapPDU::getPDULength()

+{

+    return _pduLength;

+}

+

+/// Return the number of options that the PDU has.

+int CoapPDU::getNumOptions()

+{

+    return _numOptions;

+}

+

+

+/**

+ * This returns the options as a sequence of structs.

+ */

+CoapPDU::CoapOption* CoapPDU::getOptions()

+{

+    printf("getOptions() called, %d options.",_numOptions);

+

+    uint16_t optionDelta =0, optionNumber = 0, optionValueLength = 0;

+    int totalLength = 0;

+

+    if(_numOptions==0)

+    {

+        return NULL;

+    }

+

+    // malloc space for options

+    CoapOption *options = (CoapOption*)malloc(_numOptions*sizeof(CoapOption));

+    if(options==NULL)

+    {

+        printf("Failed to allocate memory for options.");

+        return NULL;

+    }

+

+    // first option occurs after token

+    int optionPos = COAP_HDR_SIZE + getTokenLength();

+

+    // walk over options and record information

+    for(int i=0; i<_numOptions; i++)

+    {

+        // extract option details

+        optionDelta = getOptionDelta(&_pdu[optionPos]);

+        optionNumber += optionDelta;

+        optionValueLength = getOptionValueLength(&_pdu[optionPos]);

+        // compute total length

+        totalLength = 1; // mandatory header

+        totalLength += computeExtraBytes(optionDelta);

+        totalLength += computeExtraBytes(optionValueLength);

+        totalLength += optionValueLength;

+        // record option details

+        options[i].optionNumber = optionNumber;

+        options[i].optionDelta = optionDelta;

+        options[i].optionValueLength = optionValueLength;

+        options[i].totalLength = totalLength;

+        options[i].optionPointer = &_pdu[optionPos];

+        options[i].optionValuePointer = &_pdu[optionPos+totalLength-optionValueLength];

+        // move to next option

+        optionPos += totalLength;

+    }

+

+    return options;

+}

+

+/// Add an option to the PDU.

+/**

+ * Unlike other implementations, options can be added in any order, and in-memory manipulation will be

+ * performed to ensure the correct ordering of options (they use a delta encoding of option numbers).

+ * Re-ordering memory like this incurs a small performance cost, so if you care about this, then you

+ * might want to add options in ascending order of option number.

+ * \param optionNumber The number of the option, see the enum CoapPDU::Option for shorthand notations.

+ * \param optionLength The length of the option payload in bytes.

+ * \param optionValue A pointer to the byte sequence that is the option payload (bytes will be copied).

+ * \return 0 on success, 1 on failure.

+ */

+int CoapPDU::addOption(uint16_t insertedOptionNumber, uint16_t optionValueLength, uint8_t *optionValue)

+{

+    // this inserts the option in memory, and re-computes the deltas accordingly

+    // prevOption <-- insertionPosition

+    // nextOption

+

+    // find insertion location and previous option number

+    uint16_t prevOptionNumber = 0; // option number of option before insertion point

+    int insertionPosition = findInsertionPosition(insertedOptionNumber,&prevOptionNumber);

+    printf("inserting option at position %d, after option with number: %hu",insertionPosition,prevOptionNumber);

+

+    // compute option delta length

+    uint16_t optionDelta = insertedOptionNumber-prevOptionNumber;

+    uint8_t extraDeltaBytes = computeExtraBytes(optionDelta);

+

+    // compute option length length

+    uint16_t extraLengthBytes = computeExtraBytes(optionValueLength);

+

+    // compute total length of option

+    uint16_t optionLength = COAP_OPTION_HDR_BYTE + extraDeltaBytes + extraLengthBytes + optionValueLength;

+

+    // if this is at the end of the PDU, job is done, just malloc and insert

+    if(insertionPosition==_pduLength)

+    {

+        printf("Inserting at end of PDU");

+        // optionNumber must be biggest added

+        _maxAddedOptionNumber = insertedOptionNumber;

+

+        // set new PDU length and allocate space for extra option

+        int oldPDULength = _pduLength;

+        _pduLength += optionLength;

+        if(!_constructedFromBuffer)

+        {

+            uint8_t *newMemory = (uint8_t*)realloc(_pdu,_pduLength);

+            if(newMemory==NULL)

+            {

+                printf("Failed to allocate memory for option.");

+                _pduLength = oldPDULength;

+                // malloc failed

+                return 1;

+            }

+            _pdu = newMemory;

+            _bufferLength = _pduLength;

+        }

+        else

+        {

+            // constructed from buffer, check space

+            if(_pduLength>_bufferLength)

+            {

+                printf("Buffer too small for new option: needed %d, got %d.",_pduLength-oldPDULength,_bufferLength-oldPDULength);

+                _pduLength = oldPDULength;

+                return 1;

+            }

+        }

+

+        // insert option at position

+        insertOption(insertionPosition,optionDelta,optionValueLength,optionValue);

+        _numOptions++;

+        return 0;

+    }

+    // XXX could do 0xFF pdu payload case for changing of dynamically allocated application space SDUs < yeah, if you're insane

+

+    // the next option might (probably) needs it's delta changing

+    // I want to take this into account when allocating space for the new

+    // option, to avoid having to do two mallocs, first get info about this option

+    int nextOptionDelta = getOptionDelta(&_pdu[insertionPosition]);

+    int nextOptionNumber = prevOptionNumber + nextOptionDelta;

+    int nextOptionDeltaBytes = computeExtraBytes(nextOptionDelta);

+    printf("nextOptionDeltaBytes: %d",nextOptionDeltaBytes);

+    // recompute option delta, relative to inserted option

+    int newNextOptionDelta = nextOptionNumber-insertedOptionNumber;

+    int newNextOptionDeltaBytes = computeExtraBytes(newNextOptionDelta);

+    printf("newNextOptionDeltaBytes: %d",newNextOptionDeltaBytes);

+    // determine adjustment

+    int optionDeltaAdjustment = newNextOptionDeltaBytes-nextOptionDeltaBytes;

+

+    // create space for new option, including adjustment space for option delta

+    printf("Creating space");

+    int mallocLength = optionLength+optionDeltaAdjustment;

+    int oldPDULength = _pduLength;

+    _pduLength += mallocLength;

+

+    if(!_constructedFromBuffer)

+    {

+        uint8_t *newMemory = (uint8_t*)realloc(_pdu,_pduLength);

+        if(newMemory==NULL)

+        {

+            printf("Failed to allocate memory for option");

+            _pduLength = oldPDULength;

+            return 1;

+        }

+        _pdu = newMemory;

+        _bufferLength = _pduLength;

+    }

+    else

+    {

+        // constructed from buffer, check space

+        if(_pduLength>_bufferLength)

+        {

+            printf("Buffer too small to contain option, needed %d, got %d.",_pduLength-oldPDULength,_bufferLength-oldPDULength);

+            _pduLength = oldPDULength;

+            return 1;

+        }

+    }

+

+    // move remainder of PDU data up to create hole for new option

+    printf("Shifting PDU.");

+    shiftPDUUp(mallocLength,_pduLength-(insertionPosition+mallocLength));

+

+    // adjust option delta bytes of following option

+    // move the option header to the correct position

+    int nextHeaderPos = insertionPosition+mallocLength;

+    _pdu[nextHeaderPos-optionDeltaAdjustment] = _pdu[nextHeaderPos];

+    nextHeaderPos -= optionDeltaAdjustment;

+    // and set the new value

+    setOptionDelta(nextHeaderPos, newNextOptionDelta);

+

+    // new option shorter

+    // p p n n x x x x x

+    // p p n n x x x x x -

+    // p p - n n x x x x x

+    // p p - - n x x x x x

+    // p p o o n x x x x x

+

+    // new option longer

+    // p p n n x x x x x

+    // p p n n x x x x x - - -

+    // p p - - - n n x x x x x

+    // p p - - n n n x x x x x

+    // p p o o n n n x x x x x

+

+    // note, it can only ever be shorter or the same since if an option was inserted the delta got smaller

+    // but I'll leave that little comment in, just to show that it would work even if the delta got bigger

+

+    // now insert the new option into the gap

+    printf("Inserting new option...");

+    insertOption(insertionPosition,optionDelta,optionValueLength,optionValue);

+    printf("done\r\n");

+

+    // done, mark it with B!

+    _numOptions++;

+    return 0;

+}

+

+/// Allocate space for a payload.

+/**

+ * For dynamically constructed PDUs, this will allocate space for a payload in the object

+ * and return a pointer to it. If the PDU was constructed from a buffer, this doesn't

+ * malloc anything, it just changes the _pduLength and returns the payload pointer.

+ *

+ * \note The pointer returned points into the PDU buffer.

+ * \param len The length of the payload buffer to allocate.

+ * \return Either a pointer to the payload buffer, or NULL if there wasn't enough space / allocation failed.

+ */

+uint8_t* CoapPDU::mallocPayload(int len)

+{

+    printf("Entering mallocPayload");

+    // sanity checks

+    if(len==0)

+    {

+        printf("Cannot allocate a zero length payload");

+        return NULL;

+    }

+

+    // further sanity

+    if(len==_payloadLength)

+    {

+        printf("Space for payload of specified length already exists");

+        if(_payloadPointer==NULL)

+        {

+            printf("Garbage PDU. Payload length is %d, but existing _payloadPointer NULL",_payloadLength);

+            return NULL;

+        }

+        return _payloadPointer;

+    }

+

+    printf("_bufferLength: %d, _pduLength: %d, _payloadLength: %d",_bufferLength,_pduLength,_payloadLength);

+

+    // might be making payload bigger (including bigger than 0) or smaller

+    int markerSpace = 1;

+    int payloadSpace = len;

+    // is this a resizing?

+    if(_payloadLength!=0)

+    {

+        // marker already exists

+        markerSpace = 0;

+        // compute new payload length (can be negative if shrinking payload)

+        payloadSpace = len-_payloadLength;

+    }

+

+    // make space for payload (and payload marker if necessary)

+    int newLen = _pduLength+payloadSpace+markerSpace;

+    if(!_constructedFromBuffer)

+    {

+        uint8_t* newPDU = (uint8_t*)realloc(_pdu,newLen);

+        if(newPDU==NULL)

+        {

+            printf("Cannot allocate (or shrink) space for payload");

+            return NULL;

+        }

+        _pdu = newPDU;

+        _bufferLength = newLen;

+    }

+    else

+    {

+        // constructed from buffer, check space

+        printf("newLen: %d, _bufferLength: %d",newLen,_bufferLength);

+        if(newLen>_bufferLength)

+        {

+            printf("Buffer too small to contain desired payload, needed %d, got %d.",newLen-_pduLength,_bufferLength-_pduLength);

+            return NULL;

+        }

+    }

+

+    // deal with fresh allocation case separately

+    if(_payloadPointer==NULL)

+    {

+        // set payload marker

+        _pdu[_pduLength] = 0xFF;

+        // payload at end of old PDU

+        _payloadPointer = &_pdu[_pduLength+1];

+        _pduLength = newLen;

+        _payloadLength = len;

+        return _payloadPointer;

+    }

+

+    // otherwise, just adjust length of PDU

+    _pduLength = newLen;

+    _payloadLength = len;

+    printf("Leaving mallocPayload");

+    return _payloadPointer;

+}

+

+/// Set the payload to the byte sequence specified. Allocates memory in dynamic PDU if necessary.

+/**

+ * This will set the payload to \b payload. It will allocate memory in the case where the PDU was

+ * constructed without an external buffer.

+ *

+ * This will fail either if the fixed buffer isn't big enough, or if memory could not be allocated

+ * in the non-external-buffer case.

+ *

+ * \param payload Pointer to payload byte sequence.

+ * \param len Length of payload byte sequence.

+ * \return 0 on success, 1 on failure.

+ */

+int CoapPDU::setPayload(uint8_t *payload, int len)

+{

+    if(payload==NULL)

+    {

+        printf("NULL payload pointer.");

+        return 1;

+    }

+

+    uint8_t *payloadPointer = mallocPayload(len);

+    if(payloadPointer==NULL)

+    {

+        printf("Allocation of payload failed");

+        return 1;

+    }

+

+    // copy payload contents

+    memcpy(payloadPointer,payload,len);

+

+    return 0;

+}

+

+/// Returns a pointer to the payload buffer.

+uint8_t* CoapPDU::getPayloadPointer()

+{

+    return _payloadPointer;

+}

+

+/// Gets the length of the payload buffer.

+int CoapPDU::getPayloadLength()

+{

+    return _payloadLength;

+}

+

+/// Returns a pointer to a buffer which is a copy of the payload buffer (dynamically allocated).

+uint8_t* CoapPDU::getPayloadCopy()

+{

+    if(_payloadLength==0)

+    {

+        return NULL;

+    }

+

+    // malloc space for copy

+    uint8_t *payload = (uint8_t*)malloc(_payloadLength);

+    if(payload==NULL)

+    {

+        printf("Unable to allocate memory for payload");

+        return NULL;

+    }

+

+    // copy and return

+    memcpy(payload,_payloadPointer,_payloadLength);

+    return payload;

+}

+

+/// Shorthand for setting the content-format option.

+/**

+ * Sets the content-format to the specified value (adds an option).

+ * \param format The content format, one of:

+ *

+ * - COAP_CONTENT_FORMAT_TEXT_PLAIN

+ * - COAP_CONTENT_FORMAT_APP_LINK

+ * - COAP_CONTENT_FORMAT_APP_XML

+ * - COAP_CONTENT_FORMAT_APP_OCTET

+ * - COAP_CONTENT_FORMAT_APP_EXI

+ * - COAP_CONTENT_FORMAT_APP_JSON

+ *

+ * \return 0 on success, 1 on failure.

+ */

+int CoapPDU::setContentFormat(int format)

+{

+    if(format==0)

+    {

+        // minimal representation means null option value

+        if(addOption(CoapPDU::COAP_OPTION_CONTENT_FORMAT,0,NULL)!=0)

+        {

+            printf("Error setting content format");

+            return 1;

+        }

+        return 0;

+    }

+

+    uint8_t c[2];

+

+    // just use 1 byte if can do it

+    //if((uint16_t)format <= 0xffu) {

+    if(1)

+    {

+        c[0] = format;

+        if(addOption(CoapPDU::COAP_OPTION_CONTENT_FORMAT,1,c)!=0)

+        {

+            printf("Error setting content format");

+            return 1;

+        }

+        return 0;

+    }

+

+    uint8_t *to = c;

+    endian_store16(to, format);

+    if(addOption(CoapPDU::COAP_OPTION_CONTENT_FORMAT,2,c)!=0)

+    {

+        printf("Error setting content format");

+        return 1;

+    }

+    return 0;

+}

+

+// PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE

+// PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE

+

+/// Moves a block of bytes to end of PDU from given offset.

+/**

+ * This moves the block of bytes _pdu[_pduLength-1-shiftOffset-shiftAmount] ... _pdu[_pduLength-1-shiftOffset]

+ * to the end of the PDU.

+ * \param shiftOffset End of block to move, relative to end of PDU (-1).

+ * \param shiftAmount Length of block to move.

+ */

+void CoapPDU::shiftPDUUp(int shiftOffset, int shiftAmount)

+{

+    printf("shiftOffset: %d, shiftAmount: %d",shiftOffset,shiftAmount);

+    int destPointer = _pduLength-1;

+    int srcPointer  = destPointer-shiftOffset;

+    while(shiftAmount--)

+    {

+        _pdu[destPointer] = _pdu[srcPointer];

+        destPointer--;

+        srcPointer--;

+    }

+}

+

+/// Moves a block of bytes down a specified number of steps.

+/**

+ * Moves the block of bytes _pdu[startLocation+shiftOffset] ... _pdu[startLocation+shiftOffset+shiftAmount]

+ * down to \b startLocation.

+ * \param startLocation Index where to shift the block to.

+ * \param shiftOffset Where the block starts, relative to start index.

+ * \param shiftAmount Length of block to shift.

+ */

+void CoapPDU::shiftPDUDown(int startLocation, int shiftOffset, int shiftAmount)

+{

+    printf("startLocation: %d, shiftOffset: %d, shiftAmount: %d",startLocation,shiftOffset,shiftAmount);

+    int srcPointer = startLocation+shiftOffset;

+    while(shiftAmount--)

+    {

+        _pdu[startLocation] = _pdu[srcPointer];

+        startLocation++;

+        srcPointer++;

+    }

+}

+

+/// Gets the payload length of an option.

+/**

+ * \param option Pointer to location of option in PDU.

+ * \return The 16 bit option-payload length.

+ */

+uint16_t CoapPDU::getOptionValueLength(uint8_t *option)

+{

+    uint16_t delta = (option[0] & 0xF0) >> 4;

+    uint16_t length = (option[0] & 0x0F);

+    // no extra bytes

+    if(length<13)

+    {

+        return length;

+    }

+

+    // extra bytes skip header

+    int offset = 1;

+    // skip extra option delta bytes

+    if(delta==13)

+    {

+        offset++;

+    }

+    else if(delta==14)

+    {

+        offset+=2;

+    }

+

+    // process length

+    if(length==13)

+    {

+        return (option[offset]+13);

+    }

+    else

+    {

+        uint8_t *from = &option[offset];

+        uint16_t value = endian_load16(uint16_t, from);

+        return value+269;

+    }

+

+}

+

+/// Gets the delta of an option.

+/**

+ * \param option Pointer to location of option in PDU.

+ * \return The 16 bit delta.

+ */

+uint16_t CoapPDU::getOptionDelta(uint8_t *option)

+{

+    uint16_t delta = (option[0] & 0xF0) >> 4;

+    if(delta<13)

+    {

+        return delta;

+    }

+    else if(delta==13)

+    {

+        // single byte option delta

+        return (option[1]+13);

+    }

+    else if(delta==14)

+    {

+        uint8_t *from = &option[1];

+        uint16_t value = endian_load16(uint16_t, from);

+        return value+269;

+    }

+    else

+    {

+        // should only ever occur in payload marker

+        return delta;

+    }

+}

+

+/// Finds the insertion position in the current list of options for the specified option.

+/**

+ * \param optionNumber The option's number.

+ * \param prevOptionNumber A pointer to a uint16_t which will store the option number of the option previous

+ * to the insertion point.

+ * \return 0 on success, 1 on failure. \b prevOptionNumber will contain the option number of the option

+ * before the insertion position (for example 0 if no options have been inserted).

+ */

+int CoapPDU::findInsertionPosition(uint16_t optionNumber, uint16_t *prevOptionNumber)

+{

+    // zero this for safety

+    *prevOptionNumber = 0x00;

+

+    printf("_pduLength: %d",_pduLength);

+

+    // if option is bigger than any currently stored, it goes at the end

+    // this includes the case that no option has yet been added

+    if( (optionNumber >= _maxAddedOptionNumber) || (_pduLength == (COAP_HDR_SIZE+getTokenLength())) )

+    {

+        *prevOptionNumber = _maxAddedOptionNumber;

+        return _pduLength;

+    }

+

+    // otherwise walk over the options

+    int optionPos = COAP_HDR_SIZE + getTokenLength();

+    uint16_t optionDelta = 0, optionValueLength = 0;

+    uint16_t currentOptionNumber = 0;

+    while(optionPos<_pduLength && _pdu[optionPos]!=0xFF)

+    {

+        optionDelta = getOptionDelta(&_pdu[optionPos]);

+        currentOptionNumber += optionDelta;

+        optionValueLength = getOptionValueLength(&_pdu[optionPos]);

+        // test if this is insertion position

+        if(currentOptionNumber>optionNumber)

+        {

+            return optionPos;

+        }

+        // keep track of the last valid option number

+        *prevOptionNumber = currentOptionNumber;

+        // move onto next option

+        optionPos += computeExtraBytes(optionDelta);

+        optionPos += computeExtraBytes(optionValueLength);

+        optionPos += optionValueLength;

+        optionPos++; // (for mandatory option header byte)

+    }

+    return optionPos;

+

+}

+

+/// CoAP uses a minimal-byte representation for length fields. This returns the number of bytes needed to represent a given length.

+int CoapPDU::computeExtraBytes(uint16_t n)

+{

+    if(n<13)

+    {

+        return 0;

+    }

+

+    if(n<269)

+    {

+        return 1;

+    }

+

+    return 2;

+}

+

+/// Set the option delta to the specified value.

+/**

+ * This assumes space has been made for the option delta.

+ * \param optionPosition The index of the option in the PDU.

+ * \param optionDelta The option delta value to set.

+ */

+void CoapPDU::setOptionDelta(int optionPosition, uint16_t optionDelta)

+{

+    int headerStart = optionPosition;

+    // clear the old option delta bytes

+    _pdu[headerStart] &= 0x0F;

+

+    // set the option delta bytes

+    if(optionDelta<13)

+    {

+        _pdu[headerStart] |= (optionDelta << 4);

+    }

+    else if(optionDelta<269)

+    {

+        // 1 extra byte

+        _pdu[headerStart] |= 0xD0; // 13 in first nibble

+        _pdu[++optionPosition] &= 0x00;

+        _pdu[optionPosition] |= (optionDelta-13);

+    }

+    else

+    {

+        // 2 extra bytes, network byte order uint16_t

+        _pdu[headerStart] |= 0xE0; // 14 in first nibble

+        optionDelta -= 269;

+        uint8_t *to = &_pdu[++optionPosition];

+        endian_store16(to, optionDelta);

+    }

+}

+

+/// Insert an option in-memory at the specified location.

+/**

+ * This assumes that there is enough space at the location specified.

+ * \param insertionPosition Position in the PDU where the option should be placed.

+ * \param optionDelta The delta value for the option.

+ * \param optionValueLength The length of the option value.

+ * \param optionValue A pointer to the sequence of bytes representing the option value.

+ * \return 0 on success, 1 on failure.

+ */

+int CoapPDU::insertOption(

+    int insertionPosition,

+    uint16_t optionDelta,

+    uint16_t optionValueLength,

+    uint8_t *optionValue)

+{

+

+    int headerStart = insertionPosition;

+

+    // clear old option header start

+    _pdu[headerStart] &= 0x00;

+

+    // set the option delta bytes

+    if(optionDelta<13)

+    {

+        _pdu[headerStart] |= (optionDelta << 4);

+    }

+    else if(optionDelta<269)

+    {

+        // 1 extra byte

+        _pdu[headerStart] |= 0xD0; // 13 in first nibble

+        _pdu[++insertionPosition] &= 0x00;

+        _pdu[insertionPosition] |= (optionDelta-13);

+    }

+    else

+    {

+        // 2 extra bytes, network byte order uint16_t

+        _pdu[headerStart] |= 0xE0; // 14 in first nibble

+        optionDelta -= 269;

+        uint8_t *to = &_pdu[++insertionPosition];

+        endian_store16(to, optionDelta);

+        insertionPosition += 1;

+    }

+

+    // set the option value length bytes

+    if(optionValueLength<13)

+    {

+        _pdu[headerStart] |= (optionValueLength & 0x000F);

+    }

+    else if(optionValueLength<269)

+    {

+        _pdu[headerStart] |= 0x0D; // 13 in second nibble

+        _pdu[++insertionPosition] &= 0x00;

+        _pdu[insertionPosition] |= (optionValueLength-13);

+    }

+    else

+    {

+        _pdu[headerStart] |= 0x0E; // 14 in second nibble

+        // this is in network byte order

+        printf("optionValueLength: %u",optionValueLength);

+        uint8_t *to = &_pdu[++insertionPosition];

+        optionValueLength -= 269;

+        endian_store16(to, optionValueLength);

+        insertionPosition += 1;

+    }

+

+    // and finally copy the option value itself

+    memcpy(&_pdu[++insertionPosition],optionValue,optionValueLength);

+

+    return 0;

+}

+

+void CoapPDU::getOptionValueById

+(

+    uint16_t optionNumber,

+    uint16_t *optionValueLength,

+    uint8_t *optionValuePointer

+)

+{

+    if(_numOptions == 0)

+        return;

+    CoapOption* options = getOptions();

+    int i = 0;

+    for(; i < _numOptions; i++)

+    {

+        if(options[i].optionNumber == optionNumber)

+        {

+            *optionValueLength = options[i].optionValueLength;

+            memcpy(optionValuePointer, options[i].optionValuePointer, options[i].optionValueLength);

+        }

+    }

+    free(options);

+}

+

+int CoapPDU::hasOption

+(

+    uint16_t optionNumber

+)

+{

+    int res = 0;

+    if(_numOptions == 0)

+        return res;

+    CoapOption* options = getOptions();

+    int i = 0;

+    for(; i < _numOptions; i++)

+    {

+        if(options[i].optionNumber == optionNumber)

+        {

+            res = 1;

+            break;

+        }

+    }

+    free(options);

+    return res;

+}

+

+const char *CoapPDU::printHuman()

+{

+    content.clear();

+    char temp1[128];

+

+    sprintf(temp1,"PDU is %d bytes long\r\n",_pduLength);

+    content.append(temp1);

+

+    sprintf(temp1,"CoAP Version: %d\r\n",getVersion());

+    content.append("Message Type: ");

+    switch(getType())

+    {

+        case COAP_CONFIRMABLE:

+            content.append("Confirmable");

+            break;

+

+        case COAP_NON_CONFIRMABLE:

+            content.append("Non-Confirmable");

+            break;

+

+        case COAP_ACKNOWLEDGEMENT:

+            content.append("Acknowledgement");

+            break;

+

+        case COAP_RESET:

+            content.append("Reset");

+            break;

+    }

+

+    sprintf(temp1,"\r\nToken length: %d\r\n",getTokenLength());

+    content.append(temp1);

+    content.append("Code: ");

+    switch(getCode())

+    {

+        case COAP_EMPTY:

+            content.append("0.00 Empty");

+            break;

+        case COAP_GET:

+            content.append("0.01 GET");

+            break;

+        case COAP_POST:

+            content.append("0.02 POST");

+            break;

+        case COAP_PUT:

+            content.append("0.03 PUT");

+            break;

+        case COAP_DELETE:

+            content.append("0.04 DELETE");

+            break;

+        case COAP_CREATED:

+            content.append("2.01 Created");

+            break;

+        case COAP_DELETED:

+            content.append("2.02 Deleted");

+            break;

+        case COAP_VALID:

+            content.append("2.03 Valid");

+            break;

+        case COAP_CHANGED:

+            content.append("2.04 Changed");

+            break;

+        case COAP_CONTENT:

+            content.append("2.05 Content");

+            break;

+        case COAP_CONTINUE:

+            content.append("2.31 Continue");

+            break;

+        case COAP_BAD_REQUEST:

+            content.append("4.00 Bad Request");

+            break;

+        case COAP_UNAUTHORIZED:

+            content.append("4.01 Unauthorized");

+            break;

+        case COAP_BAD_OPTION:

+            content.append("4.02 Bad Option");

+            break;

+        case COAP_FORBIDDEN:

+            content.append("4.03 Forbidden");

+            break;

+        case COAP_NOT_FOUND:

+            content.append("4.04 Not Found");

+            break;

+        case COAP_METHOD_NOT_ALLOWED:

+            content.append("4.05 Method Not Allowed");

+            break;

+        case COAP_NOT_ACCEPTABLE:

+            content.append("4.06 Not Acceptable");

+            break;

+        case COAP_PRECONDITION_FAILED:

+            content.append("4.12 Precondition Failed");

+            break;

+        case COAP_REQUEST_ENTITY_TOO_LARGE:

+            content.append("4.13 Request Entity Too Large");

+            break;

+        case COAP_UNSUPPORTED_CONTENT_FORMAT:

+            content.append("4.15 Unsupported Content-Format");

+            break;

+        case COAP_INTERNAL_SERVER_ERROR:

+            content.append("5.00 Internal Server Error");

+            break;

+        case COAP_NOT_IMPLEMENTED:

+            content.append("5.01 Not Implemented");

+            break;

+        case COAP_BAD_GATEWAY:

+            content.append("5.02 Bad Gateway");

+            break;

+        case COAP_SERVICE_UNAVAILABLE:

+            content.append("5.03 Service Unavailable");

+            break;

+        case COAP_GATEWAY_TIMEOUT:

+            content.append("5.04 Gateway Timeout");

+            break;

+        case COAP_PROXYING_NOT_SUPPORTED:

+            content.append("5.05 Proxying Not Supported");

+            break;

+        default:

+            sprintf(temp1, "Undefined Code %u",(unsigned)(getCode()));

+            content.append(temp1);

+

+    }

+

+    // print message ID

+    sprintf(temp1,"\r\nMessage ID: %u\r\n",getMessageID());

+    content.append(temp1);

+

+    // print token value

+    int tokenLength = getTokenLength();

+    uint8_t *tokenPointer = getPDUPointer()+COAP_HDR_SIZE;

+    if(tokenLength==0)

+    {

+        content.append("No token.\r\n");

+    }

+    else

+    {

+        //memset(temp1,0,50);

+        sprintf(temp1,"Token of %d bytes.\r\n",tokenLength);

+        content.append(temp1);

+        content.append("Value: 0x");

+        char temp2[5];

+        for(int j=0; j<tokenLength; j++)

+        {

+            sprintf(temp2,"%.2X",tokenPointer[j]);

+            content.append(temp2);

+        }

+    }

+

+    // print options

+    CoapPDU::CoapOption* options = getOptions();

+    if(options==NULL)

+    {

+        content.append("\r\nNO options");

+    }

+    else

+    {

+        //memset(temp1,0,50);

+        sprintf(temp1,"\r\n%d options:",_numOptions);

+        content.append(temp1);

+    }

+

+    for(int i=0; i<_numOptions; i++)

+    {

+        char optionTemp[128];

+        sprintf(optionTemp,"\r\nOPTION (%d/%d)\r\n"

+                "Option number (delta): %hu (%hu)\r\n"

+                "Name: "

+                ,i + 1,_numOptions,

+                options[i].optionNumber,options[i].optionDelta);

+        content.append(optionTemp);

+        boolean asString = FALSE;

+        switch(options[i].optionNumber)

+        {

+            case COAP_OPTION_IF_MATCH:

+                content.append("IF_MATCH");

+                break;

+            case COAP_OPTION_URI_HOST:

+                content.append("URI_HOST");

+                asString = TRUE;

+                break;

+            case COAP_OPTION_ETAG:

+                content.append("ETAG");

+                break;

+            case COAP_OPTION_IF_NONE_MATCH:

+                content.append("IF_NONE_MATCH");

+                break;

+            case COAP_OPTION_OBSERVE:

+                content.append("OBSERVE");

+                break;

+            case COAP_OPTION_URI_PORT:

+                content.append("URI_PORT");

+                break;

+            case COAP_OPTION_LOCATION_PATH:

+                content.append("LOCATION_PATH");

+                break;

+            case COAP_OPTION_URI_PATH:

+                content.append("URI_PATH");

+                asString = TRUE;

+                break;

+            case COAP_OPTION_CONTENT_FORMAT:

+                content.append("CONTENT_FORMAT");

+                break;

+            case COAP_OPTION_MAX_AGE:

+                content.append("MAX_AGE");

+                break;

+            case COAP_OPTION_URI_QUERY:

+                content.append("URI_QUERY");

+                asString = TRUE;

+                break;

+            case COAP_OPTION_ACCEPT:

+                content.append("ACCEPT");

+                break;

+            case COAP_OPTION_LOCATION_QUERY:

+                content.append("LOCATION_QUERY");

+                asString = TRUE;

+                break;

+            case COAP_OPTION_PROXY_URI:

+                content.append("PROXY_URI");

+                asString = TRUE;

+                break;

+            case COAP_OPTION_PROXY_SCHEME:

+                content.append("PROXY_SCHEME");

+                asString = TRUE;

+                break;

+            case COAP_OPTION_BLOCK1:

+                content.append("BLOCK1");

+                break;

+            case COAP_OPTION_BLOCK2:

+                content.append("BLOCK2");

+                break;

+            case COAP_OPTION_SIZE1:

+                content.append("SIZE1");

+                break;

+            case COAP_OPTION_SIZE2:

+                content.append("SIZE2");

+                break;

+            default:

+                //memset(temp1,0,50);

+                sprintf(temp1,"Unknown option %u",(unsigned)options[i].optionNumber);

+                content.append(temp1);

+                break;

+        }

+        //memset(temp1,0,50);

+        sprintf(temp1,"\r\nValue length: %u\r\n",options[i].optionValueLength);

+        content.append(temp1);

+        if(asString)

+        {

+            content.append("Value: ");

+        }

+        else

+        {

+            content.append("Value: 0x");

+        }

+        char temp3[5];

+        for(int j=0; j<options[i].optionValueLength; j++)

+        {

+            if(asString)

+            {

+                char c = options[i].optionValuePointer[j];

+                if((c>='!'&&c<='~')||c==' ')

+                {

+                    sprintf(temp3,"%c", c);

+                }

+                else

+                {

+                    sprintf(temp3,"\\%.2d",c);

+                }

+                content.append(temp3);

+            }

+            else

+            {

+                sprintf(temp3,"%.2X",options[i].optionValuePointer[j]);

+                content.append(temp3);

+            }

+        }

+    }

+

+    // print payload

+    if(_payloadLength==0)

+    {

+        content.append("\r\nNo payload.\r\n");

+    }

+    else

+    {

+        //memset(temp1,0,50);

+        sprintf(temp1,"\r\nPayload of %d bytes\r\nValue: 0x",_payloadLength);

+        content.append(temp1);

+        char temp4[5];

+        for(int j=0; j<_payloadLength; j++)

+        {

+            sprintf(temp4,"%.2X",_payloadPointer[j]);

+            content.append(temp4);

+        }

+        content.append("\r\n");

+    }

+    if(options)

+        free(options);

+    return content.c_str();

+}

+

+const char * CoapPDU::printHex()

+{

+    content.clear();

+    char temp[5];

+    for(int i=0; i<_pduLength; i++)

+    {

+        sprintf(temp,"%.2X",_pdu[i]);

+        content.append(temp);

+    }

+    content.append("\r\n");

+    return content.c_str();

+}

+//#endif

diff --git a/mbtk/mbtk_lib/src/mbtk_dhcp.c b/mbtk/mbtk_lib/src/mbtk_dhcp.c
new file mode 100755
index 0000000..b764f34
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_dhcp.c
@@ -0,0 +1,885 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <poll.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <linux/if_ether.h>
+#include <linux/if_packet.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <unistd.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <time.h>
+
+#include "mbtk_log.h"
+#include "mbtk_type.h"
+#include "mbtk_ifc.h"
+#include "mbtk_dhcp.h"
+
+#define VERBOSE 2
+#define STATE_SELECTING  1
+#define STATE_REQUESTING 2
+
+#define TIMEOUT_INITIAL   4000
+#define TIMEOUT_MAX      32000
+
+static int verbose = 1;
+// static char errmsg[2048];
+
+static void *init_dhcp_msg(dhcp_msg *msg, int type, void *hwaddr, uint32_t xid)
+{
+    uint8_t *x;
+
+    memset(msg, 0, sizeof(dhcp_msg));
+
+    msg->op = OP_BOOTREQUEST;
+    msg->htype = HTYPE_ETHER;
+    msg->hlen = 6;
+    msg->hops = 0;
+
+    msg->flags = htons(FLAGS_BROADCAST);
+
+    msg->xid = xid;
+
+    memcpy(msg->chaddr, hwaddr, 6);
+
+    x = msg->options;
+
+    *x++ = OPT_COOKIE1;
+    *x++ = OPT_COOKIE2;
+    *x++ = OPT_COOKIE3;
+    *x++ = OPT_COOKIE4;
+
+    *x++ = OPT_MESSAGE_TYPE;
+    *x++ = 1;
+    *x++ = type;
+
+    return x;
+}
+
+static int init_dhcp_discover_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid)
+{
+    uint8_t *x;
+
+    x = init_dhcp_msg(msg, DHCPDISCOVER, hwaddr, xid);
+
+    *x++ = OPT_PARAMETER_LIST;
+    *x++ = 4;
+    *x++ = OPT_SUBNET_MASK;
+    *x++ = OPT_GATEWAY;
+    *x++ = OPT_DNS;
+    *x++ = OPT_BROADCAST_ADDR;
+
+    *x++ = OPT_END;
+
+    return DHCP_MSG_FIXED_SIZE + (x - msg->options);
+}
+
+static int init_dhcp_request_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid,
+                                 uint32_t ipaddr, uint32_t serveraddr)
+{
+    uint8_t *x;
+
+    x = init_dhcp_msg(msg, DHCPREQUEST, hwaddr, xid);
+
+    *x++ = OPT_PARAMETER_LIST;
+    *x++ = 4;
+    *x++ = OPT_SUBNET_MASK;
+    *x++ = OPT_GATEWAY;
+    *x++ = OPT_DNS;
+    *x++ = OPT_BROADCAST_ADDR;
+
+    *x++ = OPT_REQUESTED_IP;
+    *x++ = 4;
+    memcpy(x, &ipaddr, 4);
+    x +=  4;
+
+    *x++ = OPT_SERVER_ID;
+    *x++ = 4;
+    memcpy(x, &serveraddr, 4);
+    x += 4;
+
+    *x++ = OPT_END;
+
+    return DHCP_MSG_FIXED_SIZE + (x - msg->options);
+}
+
+
+static msecs_t get_msecs(void)
+{
+    struct timespec ts;
+
+    if (clock_gettime(CLOCK_MONOTONIC, &ts))
+    {
+        return 0;
+    }
+    else
+    {
+        return (((msecs_t) ts.tv_sec) * ((msecs_t) 1000)) +
+               (((msecs_t) ts.tv_nsec) / ((msecs_t) 1000000));
+    }
+}
+
+static int open_raw_socket(const char *ifname __attribute__((unused)), uint8_t *hwaddr, int if_index)
+{
+    int s;
+    struct sockaddr_ll bindaddr;
+
+    if((s = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0)
+    {
+        LOGE("socket(PF_PACKET)");
+        return -1;
+    }
+
+    memset(&bindaddr, 0, sizeof(bindaddr));
+    bindaddr.sll_family = AF_PACKET;
+    bindaddr.sll_protocol = htons(ETH_P_IP);
+    bindaddr.sll_halen = ETH_ALEN;
+    memcpy(bindaddr.sll_addr, hwaddr, ETH_ALEN);
+    bindaddr.sll_ifindex = if_index;
+
+    if (bind(s, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0)
+    {
+        LOGE("Cannot bind raw socket to interface");
+        return -1;
+    }
+
+    return s;
+}
+
+static uint32_t checksum(void *buffer, unsigned int count, uint32_t startsum)
+{
+    uint16_t *up = (uint16_t *)buffer;
+    uint32_t sum = startsum;
+    uint32_t upper16;
+
+    while (count > 1)
+    {
+        sum += *up++;
+        count -= 2;
+    }
+    if (count > 0)
+    {
+        sum += (uint16_t) *(uint8_t *)up;
+    }
+    while ((upper16 = (sum >> 16)) != 0)
+    {
+        sum = (sum & 0xffff) + upper16;
+    }
+    return sum;
+}
+
+static uint32_t finish_sum(uint32_t sum)
+{
+    return ~sum & 0xffff;
+}
+
+static int send_packet(int s, int if_index, dhcp_msg *msg, int size,
+                       uint32_t saddr, uint32_t daddr, uint32_t sport, uint32_t dport)
+{
+    struct iphdr ip;
+    struct udphdr udp;
+    struct iovec iov[3];
+    uint32_t udpsum;
+    uint16_t temp;
+    struct msghdr msghdr;
+    struct sockaddr_ll destaddr;
+
+    ip.version = IPVERSION;
+    ip.ihl = sizeof(ip) >> 2;
+    ip.tos = 0;
+    ip.tot_len = htons(sizeof(ip) + sizeof(udp) + size);
+    ip.id = 0;
+    ip.frag_off = 0;
+    ip.ttl = IPDEFTTL;
+    ip.protocol = IPPROTO_UDP;
+    ip.check = 0;
+    ip.saddr = saddr;
+    ip.daddr = daddr;
+    ip.check = finish_sum(checksum(&ip, sizeof(ip), 0));
+
+    udp.source = htons(sport);
+    udp.dest = htons(dport);
+    udp.len = htons(sizeof(udp) + size);
+    udp.check = 0;
+
+    /* Calculate checksum for pseudo header */
+    udpsum = checksum(&ip.saddr, sizeof(ip.saddr), 0);
+    udpsum = checksum(&ip.daddr, sizeof(ip.daddr), udpsum);
+    temp = htons(IPPROTO_UDP);
+    udpsum = checksum(&temp, sizeof(temp), udpsum);
+    temp = udp.len;
+    udpsum = checksum(&temp, sizeof(temp), udpsum);
+
+    /* Add in the checksum for the udp header */
+    udpsum = checksum(&udp, sizeof(udp), udpsum);
+
+    /* Add in the checksum for the data */
+    udpsum = checksum(msg, size, udpsum);
+    udp.check = finish_sum(udpsum);
+
+    iov[0].iov_base = (char *)&ip;
+    iov[0].iov_len = sizeof(ip);
+    iov[1].iov_base = (char *)&udp;
+    iov[1].iov_len = sizeof(udp);
+    iov[2].iov_base = (char *)msg;
+    iov[2].iov_len = size;
+    memset(&destaddr, 0, sizeof(destaddr));
+    destaddr.sll_family = AF_PACKET;
+    destaddr.sll_protocol = htons(ETH_P_IP);
+    destaddr.sll_ifindex = if_index;
+    destaddr.sll_halen = ETH_ALEN;
+    memcpy(destaddr.sll_addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN);
+
+    msghdr.msg_name = &destaddr;
+    msghdr.msg_namelen = sizeof(destaddr);
+    msghdr.msg_iov = iov;
+    msghdr.msg_iovlen = sizeof(iov) / sizeof(struct iovec);
+    msghdr.msg_flags = 0;
+    msghdr.msg_control = 0;
+    msghdr.msg_controllen = 0;
+    return sendmsg(s, &msghdr, 0);
+}
+
+static int receive_packet(int s, dhcp_msg *msg)
+{
+    int nread;
+    int is_valid;
+    struct dhcp_packet
+    {
+        struct iphdr ip;
+        struct udphdr udp;
+        dhcp_msg dhcp;
+    } packet;
+    int dhcp_size;
+    uint32_t sum;
+    uint16_t temp;
+    uint32_t saddr, daddr;
+
+    nread = read(s, &packet, sizeof(packet));
+    if (nread < 0)
+    {
+        return -1;
+    }
+    /*
+     * The raw packet interface gives us all packets received by the
+     * network interface. We need to filter out all packets that are
+     * not meant for us.
+     */
+    is_valid = 0;
+    if (nread < (int)(sizeof(struct iphdr) + sizeof(struct udphdr)))
+    {
+#if VERBOSE
+        LOGD("Packet is too small (%d) to be a UDP datagram", nread);
+#endif
+    }
+    else if (packet.ip.version != IPVERSION || packet.ip.ihl != (sizeof(packet.ip) >> 2))
+    {
+#if VERBOSE
+        LOGD("Not a valid IP packet");
+#endif
+    }
+    else if (nread < ntohs(packet.ip.tot_len))
+    {
+#if VERBOSE
+        LOGD("Packet was truncated (read %d, needed %d)", nread, ntohs(packet.ip.tot_len));
+#endif
+    }
+    else if (packet.ip.protocol != IPPROTO_UDP)
+    {
+#if VERBOSE
+        LOGD("IP protocol (%d) is not UDP", packet.ip.protocol);
+#endif
+    }
+    else if (packet.udp.dest != htons(PORT_BOOTP_CLIENT))
+    {
+#if VERBOSE
+        LOGD("UDP dest port (%d) is not DHCP client", ntohs(packet.udp.dest));
+#endif
+    }
+    else
+    {
+        is_valid = 1;
+    }
+
+    if (!is_valid)
+    {
+        return -1;
+    }
+
+    /* Seems like it's probably a valid DHCP packet */
+    /* validate IP header checksum */
+    sum = finish_sum(checksum(&packet.ip, sizeof(packet.ip), 0));
+    if (sum != 0)
+    {
+        LOGW("IP header checksum failure (0x%x)", packet.ip.check);
+        return -1;
+    }
+    /*
+     * Validate the UDP checksum.
+     * Since we don't need the IP header anymore, we "borrow" it
+     * to construct the pseudo header used in the checksum calculation.
+     */
+    dhcp_size = ntohs(packet.udp.len) - sizeof(packet.udp);
+    /*
+     * check validity of dhcp_size.
+     * 1) cannot be negative or zero.
+     * 2) src buffer contains enough bytes to copy
+     * 3) cannot exceed destination buffer
+     */
+    if ((dhcp_size <= 0) ||
+        ((int)(nread - sizeof(struct iphdr) - sizeof(struct udphdr)) < dhcp_size) ||
+        ((int)sizeof(dhcp_msg) < dhcp_size))
+    {
+#if VERBOSE
+        LOGD("Malformed Packet");
+#endif
+        return -1;
+    }
+    saddr = packet.ip.saddr;
+    daddr = packet.ip.daddr;
+    nread = ntohs(packet.ip.tot_len);
+    memset(&packet.ip, 0, sizeof(packet.ip));
+    packet.ip.saddr = saddr;
+    packet.ip.daddr = daddr;
+    packet.ip.protocol = IPPROTO_UDP;
+    packet.ip.tot_len = packet.udp.len;
+    temp = packet.udp.check;
+    packet.udp.check = 0;
+    sum = finish_sum(checksum(&packet, nread, 0));
+    packet.udp.check = temp;
+    if (!sum)
+        sum = finish_sum(sum);
+    if (temp != sum)
+    {
+        LOGW("UDP header checksum failure (0x%x should be 0x%x)", sum, temp);
+        return -1;
+    }
+    memcpy(msg, &packet.dhcp, dhcp_size);
+    return dhcp_size;
+}
+
+static void hex2str(char *buf, size_t buf_size, const unsigned char *array, int len)
+{
+    int i;
+    char *cp = buf;
+    char *buf_end = buf + buf_size;
+    for (i = 0; i < len; i++)
+    {
+        cp += snprintf(cp, buf_end - cp, " %02x ", array[i]);
+    }
+}
+
+static const char *ipaddr(in_addr_t addr)
+{
+    struct in_addr in_addr;
+
+    in_addr.s_addr = addr;
+    return inet_ntoa(in_addr);
+}
+
+static const char *dhcp_type_to_name(uint32_t type)
+{
+    switch(type)
+    {
+        case DHCPDISCOVER:
+            return "discover";
+        case DHCPOFFER:
+            return "offer";
+        case DHCPREQUEST:
+            return "request";
+        case DHCPDECLINE:
+            return "decline";
+        case DHCPACK:
+            return "ack";
+        case DHCPNAK:
+            return "nak";
+        case DHCPRELEASE:
+            return "release";
+        case DHCPINFORM:
+            return "inform";
+        default:
+            return "???";
+    }
+}
+
+static void dump_dhcp_msg(dhcp_msg *msg, int len)
+{
+    unsigned char *x;
+    unsigned int n,c;
+    int optsz;
+    const char *name;
+    char buf[2048];
+
+    LOGD("===== DHCP message:");
+    if (len < DHCP_MSG_FIXED_SIZE)
+    {
+        LOGD("Invalid length %d, should be %d", len, DHCP_MSG_FIXED_SIZE);
+        return;
+    }
+
+    len -= DHCP_MSG_FIXED_SIZE;
+
+    if (msg->op == OP_BOOTREQUEST)
+        name = "BOOTREQUEST";
+    else if (msg->op == OP_BOOTREPLY)
+        name = "BOOTREPLY";
+    else
+        name = "????";
+    LOGD("op = %s (%d), htype = %d, hlen = %d, hops = %d",
+         name, msg->op, msg->htype, msg->hlen, msg->hops);
+    LOGD("xid = 0x%08x secs = %d, flags = 0x%04x optlen = %d",
+         ntohl(msg->xid), ntohs(msg->secs), ntohs(msg->flags), len);
+    LOGD("ciaddr = %s", ipaddr(msg->ciaddr));
+    LOGD("yiaddr = %s", ipaddr(msg->yiaddr));
+    LOGD("siaddr = %s", ipaddr(msg->siaddr));
+    LOGD("giaddr = %s", ipaddr(msg->giaddr));
+
+    c = msg->hlen > 16 ? 16 : msg->hlen;
+    hex2str(buf, sizeof(buf), msg->chaddr, c);
+    LOGD("chaddr = {%s}", buf);
+
+    for (n = 0; n < 64; n++)
+    {
+        unsigned char x = msg->sname[n];
+        if ((x < ' ') || (x > 127))
+        {
+            if (x == 0) break;
+            msg->sname[n] = '.';
+        }
+    }
+    msg->sname[63] = 0;
+
+    for (n = 0; n < 128; n++)
+    {
+        unsigned char x = msg->file[n];
+        if ((x < ' ') || (x > 127))
+        {
+            if (x == 0) break;
+            msg->file[n] = '.';
+        }
+    }
+    msg->file[127] = 0;
+
+    LOGD("sname = '%s'", msg->sname);
+    LOGD("file = '%s'", msg->file);
+
+    if (len < 4) return;
+    len -= 4;
+    x = msg->options + 4;
+
+    while (len > 2)
+    {
+        if (*x == 0)
+        {
+            x++;
+            len--;
+            continue;
+        }
+        if (*x == OPT_END)
+        {
+            break;
+        }
+        len -= 2;
+        optsz = x[1];
+        if (optsz > len) break;
+        if (x[0] == OPT_DOMAIN_NAME || x[0] == OPT_MESSAGE)
+        {
+            if ((unsigned int)optsz < sizeof(buf) - 1)
+            {
+                n = optsz;
+            }
+            else
+            {
+                n = sizeof(buf) - 1;
+            }
+            memcpy(buf, &x[2], n);
+            buf[n] = '\0';
+        }
+        else
+        {
+            hex2str(buf, sizeof(buf), &x[2], optsz);
+        }
+        if (x[0] == OPT_MESSAGE_TYPE)
+            name = dhcp_type_to_name(x[2]);
+        else
+            name = NULL;
+        LOGD("op %d len %d {%s} %s", x[0], optsz, buf, name == NULL ? "" : name);
+        len -= optsz;
+        x = x + optsz + 2;
+    }
+}
+
+static int send_message(int sock, int if_index, dhcp_msg  *msg, int size)
+{
+#if VERBOSE > 1
+    dump_dhcp_msg(msg, size);
+#endif
+    return send_packet(sock, if_index, msg, size, INADDR_ANY, INADDR_BROADCAST,
+                       PORT_BOOTP_CLIENT, PORT_BOOTP_SERVER);
+}
+
+// static dhcp_info last_good_info;
+static int dhcp_configure(const char *ifname, dhcp_info *info)
+{
+    //last_good_info = *info;
+    return mbtk_ifc_configure1(ifname, info->ipaddr, info->prefixLength, info->gateway, 0);
+}
+
+static int is_valid_reply(dhcp_msg *msg, dhcp_msg *reply, int sz)
+{
+    if (sz < DHCP_MSG_FIXED_SIZE)
+    {
+        if (verbose) LOGD("Wrong size %d != %d\n", sz, DHCP_MSG_FIXED_SIZE);
+        return 0;
+    }
+    if (reply->op != OP_BOOTREPLY)
+    {
+        if (verbose) LOGD("Wrong Op %d != %d\n", reply->op, OP_BOOTREPLY);
+        return 0;
+    }
+    if (reply->xid != msg->xid)
+    {
+        if (verbose) LOGD("Wrong Xid 0x%x != 0x%x\n", ntohl(reply->xid),
+                              ntohl(msg->xid));
+        return 0;
+    }
+    if (reply->htype != msg->htype)
+    {
+        if (verbose) LOGD("Wrong Htype %d != %d\n", reply->htype, msg->htype);
+        return 0;
+    }
+    if (reply->hlen != msg->hlen)
+    {
+        if (verbose) LOGD("Wrong Hlen %d != %d\n", reply->hlen, msg->hlen);
+        return 0;
+    }
+    if (memcmp(msg->chaddr, reply->chaddr, msg->hlen))
+    {
+        if (verbose) LOGD("Wrong chaddr %x != %x\n", *(reply->chaddr),*(msg->chaddr));
+        return 0;
+    }
+    return 1;
+}
+
+int ipv4NetmaskToPrefixLength(in_addr_t mask)
+{
+    int prefixLength = 0;
+    uint32_t m = (uint32_t)ntohl(mask);
+    while (m & 0x80000000)
+    {
+        prefixLength++;
+        m = m << 1;
+    }
+    return prefixLength;
+}
+
+static int decode_dhcp_msg(dhcp_msg *msg, int len, dhcp_info *info)
+{
+    uint8_t *x;
+    unsigned int opt;
+    int optlen;
+
+    memset(info, 0, sizeof(dhcp_info));
+    if (len < (DHCP_MSG_FIXED_SIZE + 4)) return -1;
+
+    len -= (DHCP_MSG_FIXED_SIZE + 4);
+
+    if (msg->options[0] != OPT_COOKIE1) return -1;
+    if (msg->options[1] != OPT_COOKIE2) return -1;
+    if (msg->options[2] != OPT_COOKIE3) return -1;
+    if (msg->options[3] != OPT_COOKIE4) return -1;
+
+    x = msg->options + 4;
+
+    while (len > 2)
+    {
+        opt = *x++;
+        if (opt == OPT_PAD)
+        {
+            len--;
+            continue;
+        }
+        if (opt == OPT_END)
+        {
+            break;
+        }
+        optlen = *x++;
+        len -= 2;
+        if (optlen > len)
+        {
+            break;
+        }
+        switch(opt)
+        {
+            case OPT_SUBNET_MASK:
+                if (optlen >= 4)
+                {
+                    in_addr_t mask;
+                    memcpy(&mask, x, 4);
+                    info->prefixLength = ipv4NetmaskToPrefixLength(mask);
+                }
+                break;
+            case OPT_GATEWAY:
+                if (optlen >= 4) memcpy(&info->gateway, x, 4);
+                break;
+            case OPT_DNS:
+                if (optlen >= 4) memcpy(&info->dns1, x + 0, 4);
+                if (optlen >= 8) memcpy(&info->dns2, x + 4, 4);
+                break;
+            case OPT_LEASE_TIME:
+                if (optlen >= 4)
+                {
+                    memcpy(&info->lease, x, 4);
+                    info->lease = ntohl(info->lease);
+                }
+                break;
+            case OPT_SERVER_ID:
+                if (optlen >= 4) memcpy(&info->serveraddr, x, 4);
+                break;
+            case OPT_MESSAGE_TYPE:
+                info->type = *x;
+                break;
+            default:
+                break;
+        }
+        x += optlen;
+        len -= optlen;
+    }
+
+    info->ipaddr = msg->yiaddr;
+
+    return 0;
+}
+
+void dump_dhcp_info(dhcp_info *info)
+{
+    char addr[20], gway[20];
+    LOGD("--- dhcp %s (%d) ---",
+    dhcp_type_to_name(info->type), info->type);
+    strcpy(addr, ipaddr(info->ipaddr));
+    strcpy(gway, ipaddr(info->gateway));
+    LOGD("ip %s gw %s prefixLength %d", addr, gway, info->prefixLength);
+    if (info->dns1) LOGD("dns1: %s", ipaddr(info->dns1));
+    if (info->dns2) LOGD("dns2: %s", ipaddr(info->dns2));
+    LOGD("server %s, lease %d seconds",
+         ipaddr(info->serveraddr), info->lease);
+}
+
+static int dhcp_init_ifc(const char *ifname)
+{
+    dhcp_msg discover_msg;
+    dhcp_msg request_msg;
+    dhcp_msg reply;
+    dhcp_msg *msg;
+    dhcp_info info;
+    int s, r, size;
+    int valid_reply;
+    uint32_t xid;
+    unsigned char hwaddr[6];
+    struct pollfd pfd;
+    unsigned int state;
+    unsigned int timeout;
+    int if_index;
+
+    xid = (uint32_t) get_msecs();
+    if (mbtk_ifc_get_hwaddr(ifname, hwaddr))
+    {
+        LOGE("cannot obtain interface address");
+        return -1;
+    }
+    if (mbtk_ifc_get_ifindex(ifname, &if_index))
+    {
+        LOGE("cannot obtain interface index");
+        return -1;
+    }
+
+    s = open_raw_socket(ifname, hwaddr, if_index);
+
+    timeout = TIMEOUT_INITIAL;
+    state = STATE_SELECTING;
+    info.type = 0;
+    goto transmit;
+
+    for (;;)
+    {
+        pfd.fd = s;
+        pfd.events = POLLIN;
+        pfd.revents = 0;
+        r = poll(&pfd, 1, timeout);
+
+        if (r == 0)
+        {
+#if VERBOSE
+            LOGE("TIMEOUT");
+#endif
+            if (timeout >= TIMEOUT_MAX)
+            {
+                LOGE("timed out");
+                if ( info.type == DHCPOFFER )
+                {
+                    LOGE("no acknowledgement from DHCP server\nconfiguring %s with offered parameters", ifname);
+                    return dhcp_configure(ifname, &info);
+                }
+                errno = ETIME;
+                close(s);
+                return -1;
+            }
+            timeout = timeout * 2;
+
+        transmit:
+            size = 0;
+            msg = NULL;
+            switch(state)
+            {
+                case STATE_SELECTING:
+                    msg = &discover_msg;
+                    size = init_dhcp_discover_msg(msg, hwaddr, xid);
+                    break;
+                case STATE_REQUESTING:
+                    msg = &request_msg;
+                    size = init_dhcp_request_msg(msg, hwaddr, xid, info.ipaddr, info.serveraddr);
+                    break;
+                default:
+                    r = 0;
+            }
+            if (size != 0)
+            {
+                r = send_message(s, if_index, msg, size);
+                if (r < 0)
+                {
+                    LOGE("error sending dhcp msg: %s\n", strerror(errno));
+                }
+            }
+            continue;
+        }
+
+        if (r < 0)
+        {
+            if ((errno == EAGAIN) || (errno == EINTR))
+            {
+                continue;
+            }
+            LOGE("poll failed");
+            return -1;
+        }
+
+        errno = 0;
+        r = receive_packet(s, &reply);
+        if (r < 0)
+        {
+            if (errno != 0)
+            {
+                LOGD("receive_packet failed (%d): %s", r, strerror(errno));
+                if (errno == ENETDOWN || errno == ENXIO)
+                {
+                    return -1;
+                }
+            }
+            continue;
+        }
+
+#if VERBOSE > 1
+        dump_dhcp_msg(&reply, r);
+#endif
+        decode_dhcp_msg(&reply, r, &info);
+
+        if (state == STATE_SELECTING)
+        {
+            valid_reply = is_valid_reply(&discover_msg, &reply, r);
+        }
+        else
+        {
+            valid_reply = is_valid_reply(&request_msg, &reply, r);
+        }
+        if (!valid_reply)
+        {
+            LOGE("invalid reply");
+            continue;
+        }
+
+        if (verbose)
+            dump_dhcp_info(&info);
+
+        switch(state)
+        {
+            case STATE_SELECTING:
+                if (info.type == DHCPOFFER)
+                {
+                    state = STATE_REQUESTING;
+                    timeout = TIMEOUT_INITIAL;
+                    xid++;
+                    goto transmit;
+                }
+                break;
+            case STATE_REQUESTING:
+                if (info.type == DHCPACK)
+                {
+                    LOGE("configuring %s", ifname);
+                    close(s);
+                    return dhcp_configure(ifname, &info);
+                }
+                else if (info.type == DHCPNAK)
+                {
+                    LOGE("configuration request denied");
+                    close(s);
+                    return -1;
+                }
+                else
+                {
+                    LOGE("ignoring %s message in state %d",
+                         dhcp_type_to_name(info.type), state);
+                }
+                break;
+        }
+    }
+    close(s);
+    return 0;
+}
+
+
+int mbtk_do_dhcp(const char *name)
+{
+    int ret = 0;
+    if(mbtk_ifc_open())
+    {
+        LOGE("mbtk_ifc_open() fail.");
+        ret = -1;
+        goto return_result;
+    }
+
+    if (mbtk_ifc_set_addr(name, 0, 0))
+    {
+        LOGE("failed to set ip addr for %s to 0.0.0.0: %s", name, strerror(errno));
+        ret = -1;
+        goto return_result;
+    }
+
+    if (mbtk_ifc_up(name))
+    {
+        LOGE("failed to bring up interface %s: %s", name, strerror(errno));
+        ret = -1;
+        goto return_result;
+    }
+
+    if(dhcp_init_ifc(name))
+    {
+        LOGE("dhcp_init_ifc() fail.");
+        ret = -1;
+        goto return_result;
+    }
+
+return_result:
+
+    mbtk_ifc_close();
+
+    return ret;
+}
+
+
diff --git a/mbtk/mbtk_lib/src/mbtk_file.c b/mbtk/mbtk_lib/src/mbtk_file.c
new file mode 100755
index 0000000..44f3954
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_file.c
@@ -0,0 +1,128 @@
+#include "mbtk_file.h"
+
+/**
+* Return TRUE if file exist,FLASE or not.
+*/
+bool file_exist(const char *path)
+{
+    return (access(path, F_OK) == 0) ? true : false;
+}
+
+/*
+ * Return file descriptor if open file success, return -1 or not.
+ *
+ * flag : File open flag.
+ *  O_RDONLY 以只读方式打开文件
+ *  O_WRONLY 以只写方式打开文件
+ *  O_RDWR 以可读写方式打开文件
+ *      上述三种旗标是互斥
+ *
+ *  O_CREAT 若欲打开的文件不存在则自动建立该文件。
+ *  O_EXCL 如果 O_CREAT 也被设置,此指令会去检查文件是否存在。文件若不存在则
+ *      建立该文件,否则将导致打开文件错误。此外,若 O_CREAT 与 O_EXCL 同时设置,
+ *      并且欲打开的文件为符号连接,则会打开文件失败。
+ *  O_NOCTTY 如果欲打开的文件为终端机设备时,则不会将该终端机当成进程控制终端机。
+ *  O_TRUNC 若文件存在并且以可写的方式打开时,此旗标会令文件长度清为 0,而原来存于该文件的资料也会消失。
+ *  O_APPEND 当读写文件时会从文件尾开始移动,也就是所写入的数据会以附加的方式加入到文件后面。
+ *  O_NONBLOCK 以不可阻断的方式打开文件,也就是无论有无数据读取或等待,都会立即返回进程之中。
+ *  O_NDELAY 同 O_NONBLOCK。
+ *  O_SYNC 以同步的方式打开文件。
+ *  O_NOFOLLOW 如果参数 pathname 所指的文件为一符号连接,则会令打开文件失败。
+ *  O_DIRECTORY 如果参数 pathname 所指的文件并非为一目录,则会令打开文件失败
+ */
+int file_open(const char *path, int flag)
+{
+    // Only for create file : rwxrw-rx-
+    int result = open(path, flag, 0766);
+    if(result == -1) {
+        LOGE("Open file[%s] fail:%d", path, errno);
+    }
+    return result;
+}
+
+/*
+* Return file size,or -1 if get file size fail.
+*/
+int file_length(int fd)
+{
+    int cur=  lseek(fd, 0, SEEK_CUR);
+    if(cur < 0)
+    {
+        LOGE("lseek(SEEK_CUR) error: %d", errno);
+        return -1;
+    }
+    int len = lseek(fd, 0, SEEK_END);
+    if (len < 0)
+    {
+        LOGE("lseek(SEEK_END) error: %d", errno);
+        return -1;
+    }
+
+    // Reset position
+    if(cur != len) {
+        if(lseek(fd, cur, SEEK_SET))
+        {
+            LOGE("lseek(SEEK_SET) error: %d", errno);
+            return -1;
+        }
+    }
+    return len;
+}
+
+// Read data of specified length.
+int file_read(int fd, void *buf, int nbyte)
+{
+    int count = 0;
+    int len = 0;
+    while (true)
+    {
+        len = read(fd, buf + count, nbyte - count);
+        if (len > 0)
+        {
+            count += len;
+        }
+        else
+        {
+            break;
+        }
+
+        if (count == nbyte)
+            break;
+    }
+
+    return count;
+}
+
+// Write data of specified length.
+int file_write(int fd, void *buf, int nbyte)
+{
+    int count = 0;
+    int len = 0;
+    while (true)
+    {
+        len = write(fd, buf + count, nbyte - count);
+        if (len > 0)
+        {
+            count += len;
+        }
+        else
+        {
+            LOGE("write() fail,ret = %d,errno = %d", len, errno);
+            break;
+        }
+
+        if (count == nbyte)
+            break;
+    }
+
+    return count;
+}
+
+int file_close(int fd)
+{
+    int result = close(fd);
+    if(result == -1) {
+        LOGE("Close file fail:%d", errno);
+    }
+    return result;
+}
diff --git a/mbtk/mbtk_lib/src/mbtk_ftp.c b/mbtk/mbtk_lib/src/mbtk_ftp.c
new file mode 100755
index 0000000..8c1340c
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_ftp.c
@@ -0,0 +1,3040 @@
+/*************************************************************
+ Description:
+ $file_description$
+ Author:
+ LiuBin
+ Date:
+ 2020/10/28 11:49:08
+ *************************************************************/
+#include "mbtk_ftp.h"
+#include "mbtk_list.h"
+#include "mbtk_sock.h"
+#include "mbtk_str.h"
+#include "mbtk_sock2.h"
+#include "mbtk_info.h"
+#include<linux/msg.h>
+
+/*************************************************************
+ Constants and Macros
+ *************************************************************/
+#define FTP_HANDLE_MAX 5
+#define FTP_ANONYMOUS_USER "Anonymous"
+#define FTP_ANONYMOUS_PASS "@"
+
+//#define FTP_RESUME_DEBUG
+
+#ifdef MBTK_PLATFORM_QCOMM
+#define FTP_TIMEOUT 60000    // 60s
+#else
+#define FTP_TIMEOUT 3000    // 3s
+#endif
+
+/*************************************************************
+ typedef struct:local at
+ *************************************************************/
+typedef struct
+{
+    char host[64];
+    uint16 port;
+    mbtk_ftp_auth_type_enum auth_type;
+    bool ftp_type;
+    bool use_cert;
+    char name[FTP_SERVER_USER_NAME_MAX+1];
+    char pass[FTP_SERVER_USER_PASS_MAX+1];
+    mbtk_ftp_handle at_ftp_handle;
+    char remote_path[MBTK_FTP_FILE_NAME_MAX+1];
+    char local_path[MBTK_FTP_FILE_NAME_MAX+1];
+    int rest_size;
+    int read_size;
+    int put_len;
+} mbtk_at_init_parameter;
+
+mbtk_at_init_parameter mbtk_at_ftp_par={0};
+
+/*************************************************************
+ Variables:local
+ *************************************************************/
+static list_node_t *ftp_client_list = NULL;
+
+/*************************************************************
+ Variables:public
+ *************************************************************/
+
+/*************************************************************
+ Local Function Declaration
+ *************************************************************/
+
+/*************************************************************
+ Local Function Definitions
+ *************************************************************/
+static bool ftp_ch_is_space(char ch)
+{
+    if (ch == ' ' || ch == '\r' || ch == '\n' || ch == '\t')
+        return true;
+
+    return false;
+}
+
+static const char* ftp_str_next(const char* str, char *result, uint32 len)
+{
+    memset(result, 0x0, len);
+    const char* ptr = str;
+    const char* ptr_result;
+    while (*ptr && ftp_ch_is_space(*ptr))
+    {
+        ptr++;
+    }
+    if (*ptr == '\0')
+    {
+        return NULL;
+    }
+    // Point to first no space char.
+    ptr_result = ptr;
+    while (*ptr && !ftp_ch_is_space(*ptr))
+    {
+        ptr++;
+    }
+
+    // Point to first space char or '\0'.
+    memcpy(result, ptr_result, ptr - ptr_result);
+
+    if (*ptr == '\0')
+    {
+        return NULL;
+    }
+
+    return ptr;
+}
+
+static int ftp_cmd_handle(mbtk_ftp_info_s *info, const void *cmd, void *rsp,
+                          uint32 *rsp_len)
+{
+    int err;
+    int rsp_code;
+    int len = strlen((char*) cmd);
+    // Write cmd
+    if(info->auth_type != 0)
+    {
+        mbtk_sock_write(info->ftp_ssl_handle,info->session,cmd,len,60000,&err);
+        if(err!=0)
+        {
+            printf("\nmbtk_sock_write error:%d\n",err);
+            return err;
+        }
+    }
+    else
+    {
+        if (sock_write(&info->net_info, &info->sock_info[FTP_SOCK_CTRL], cmd, len, 60000, &err)
+            != len)
+        {
+            LOGE("Socket write fail[err = %d].", err);
+            return -1;
+        }
+    }
+
+    // Read cmd response
+    if (rsp != NULL && *rsp_len > 0)
+    {
+    read_angin_1:
+        memset(rsp, 0x0, *rsp_len);
+        if(info->auth_type != 0)
+        {
+            unsigned char mbtk_ftp_ssl_read_buf[16384 + 1];
+            len = mbtk_sock_read(info->ftp_ssl_handle,info->session,mbtk_ftp_ssl_read_buf,sizeof(mbtk_ftp_ssl_read_buf),FTP_TIMEOUT,&err);
+            if(err != 0)
+            {
+                printf("\n mbtk_sock_read error:%d \n",err);
+            }
+            rsp_code = atoi(mbtk_ftp_ssl_read_buf);
+            printf("%s -> Code:%d; RSP:%s\n", (char*)cmd, rsp_code, (char* )mbtk_ftp_ssl_read_buf);
+            if (rsp_code == 0)
+            {
+                goto read_angin_1;
+            }
+            memcpy((char* )rsp, mbtk_ftp_ssl_read_buf, strlen(mbtk_ftp_ssl_read_buf));
+            //printf("\nrsp = %s\n",(char* )rsp);
+            return rsp_code;
+        }
+        else
+        {
+            len = sock_readline(&info->net_info, &info->sock_info[FTP_SOCK_CTRL], rsp, *rsp_len, FTP_TIMEOUT,&err);
+        }
+        if (len <= 0)
+        {
+            LOGE("Socket read fail[len = %d].", len);
+            return -1;
+        }
+        rsp_code = atoi(rsp);
+        LOGI("%s -> Code:%d; RSP:%s", (char*)cmd, rsp_code, (char* )rsp);
+        //log_hex("FTP_RSP",(char* )rsp,len);
+
+        if (rsp_code == 0)
+        {
+            goto read_angin_1;
+        }
+
+        *rsp_len = len;
+    }
+    else
+    {
+        char buff[1024];
+
+    read_angin_2:
+        memset(buff, 0x0, 1024);
+        if(info->auth_type != 0)
+        {
+            unsigned char mbtk_ftp_ssl_read_buf[16384 + 1];
+            len = mbtk_sock_read(info->ftp_ssl_handle,info->session,mbtk_ftp_ssl_read_buf,sizeof(mbtk_ftp_ssl_read_buf),FTP_TIMEOUT,&err);
+            if(err != 0)
+            {
+                printf("\nmbtk_sock_read error:%d\n",err);
+            }
+            rsp_code = atoi(mbtk_ftp_ssl_read_buf);
+            printf("%s -> Code:%d; RSP:%s\n", (char*)cmd, rsp_code, (char* )mbtk_ftp_ssl_read_buf);
+            char *mbtk_ftp_ssl_read_buf_p = NULL;
+            mbtk_ftp_ssl_read_buf_p = strstr(mbtk_ftp_ssl_read_buf,"\n");
+            if(mbtk_ftp_ssl_read_buf_p!=NULL);
+            {
+                if(strlen(mbtk_ftp_ssl_read_buf_p)>1)
+                {
+                    mbtk_ftp_ssl_read_buf_p++;
+                    rsp_code = atoi(mbtk_ftp_ssl_read_buf_p);
+                }
+            }
+            if (rsp_code == 0)
+            {
+                goto read_angin_2;
+            }
+            return rsp_code;
+        }
+        else
+        {
+            len = sock_readline(&info->net_info, &info->sock_info[FTP_SOCK_CTRL], buff, 1024, FTP_TIMEOUT,
+                            &err);
+        }
+        if (len <= 0)
+        {
+            LOGE("Socket read fail[len = %d].", len);
+            return -1;
+        }
+
+        rsp_code = atoi(buff);
+        LOGI("%s -> Code:%d; RSP:%s", (char*)cmd, rsp_code, buff);
+        //log_hex("FTP_RSP",buff,len);
+
+        if (rsp_code == 0)
+        {
+            goto read_angin_2;
+        }
+    }
+
+    return rsp_code;
+}
+
+// Create data socket service and waitting client connect.
+static mbtk_ftp_error_enum ftp_data_sock_service_create(mbtk_ftp_sock_s *sock)
+{
+    return FTP_ERR_SUCCESS;
+}
+
+static bool ftp_internal_ipv4_check(void *ipv4)
+{
+    char *ip = (char*) ipv4;
+    int ip1 = atoi(ip);
+    ip = strstr(ip, ".");
+    if (!ip)
+        return FALSE;
+    int ip2 = atoi(ip + 1);
+
+    if (ip1 == 10)   // 10.x.x.x
+    {
+        return TRUE;
+    }
+    else if (ip1 == 172 && (ip2 >= 16 && ip2 <= 31))     // 172.16.0.0 ~ 172.31.255.255
+    {
+        return TRUE;
+    }
+    else if (ip1 == 192 && ip2 == 168)     // 192.168.x.x
+    {
+        return TRUE;
+    }
+    else
+    {
+        return FALSE;
+    }
+}
+
+static mbtk_ftp_error_enum ftp_sock_set_by_port(mbtk_ftp_info_s *info,
+        mbtk_ftp_sock_s *sock)
+{
+    // port = port1 * 256 + port2
+    // "PORT ip1,ip2,ip3,ip4,port1,port2\r\n"
+    int code;
+    char cmd_buff[100];
+    memset(cmd_buff, 0x0, 100);
+    if (info->sock_info[FTP_SOCK_CTRL].addr_family == MBTK_ADDR_IPV6 || sock->port <= 1024
+        || strlen((char*) sock->host) == 0)
+    {
+        LOGE("FTP params set error.");
+        return FTP_ERR_PARAM_SET;
+    }
+
+    if (ftp_data_sock_service_create(sock) != FTP_ERR_SUCCESS)
+    {
+        LOGE("FTP data services create fail.");
+        return FTP_ERR_UNKNOWN;
+    }
+
+    uint8 *ptr = sock->host;
+    while (*ptr)
+    {
+        if (*ptr == '.')
+            *ptr = ',';
+        ptr++;
+    }
+    // Waitting client connect.
+    snprintf(cmd_buff, 100, "PORT %s,%d,%d\r\n", sock->host, sock->port / 256,
+             sock->port % 256);
+    code = ftp_cmd_handle(info, cmd_buff, NULL, NULL);
+    if (code / 100 != 2)   // Not 2xx
+    {
+        LOGE("PORT rsp error[code = %d].", code);
+        return FTP_ERR_UNKNOWN;
+    }
+
+    return FTP_ERR_SUCCESS;
+}
+
+static mbtk_ftp_error_enum ftp_sock_set_by_eprt(mbtk_ftp_info_s *info,
+        mbtk_ftp_sock_s *sock)
+{
+    int code;
+    char cmd_buff[100];
+    memset(cmd_buff, 0x0, 100);
+    if (sock->port <= 1024 || strlen((char*) sock->host) == 0)
+    {
+        LOGE("FTP params set error.");
+        return FTP_ERR_PARAM_SET;
+    }
+    if (ftp_data_sock_service_create(sock) != FTP_ERR_SUCCESS)
+    {
+        LOGE("FTP data services create fail.");
+        return FTP_ERR_UNKNOWN;
+    }
+
+    // "EPRT |2|1080::8:800:200C:417A|5282|"
+    if (info->sock_info[FTP_SOCK_CTRL].addr_family == MBTK_ADDR_IPV6)
+    {
+        uint8 *ptr = NULL;
+        if (sock->host[strlen((char*) sock->host) - 1] == ']')
+            sock->host[strlen((char*) sock->host) - 1] = '\0';
+
+        if (sock->host[0] == '[')
+            ptr = sock->host + 1;
+        else
+            ptr = sock->host;
+        snprintf(cmd_buff, 100, "EPRT |2|%s|%d|\r\n", ptr, sock->port);
+    }
+    else       // IPV4 "EPRT |1|132.235.1.2|6275|"
+    {
+        snprintf(cmd_buff, 100, "EPRT |1|%s|%d|\r\n", sock->host, sock->port);
+    }
+
+    code = ftp_cmd_handle(info, cmd_buff, NULL, NULL);
+    if (code / 100 != 2)   // Not 2xx
+    {
+        LOGE("PORT rsp error[code = %d].", code);
+        return FTP_ERR_UNKNOWN;
+    }
+
+    return FTP_ERR_SUCCESS;
+}
+
+static mbtk_ftp_error_enum ftp_sock_get_by_pasv(mbtk_ftp_info_s *info,
+        mbtk_ftp_sock_s *sock)
+{
+    int code;
+    uint8 rsp[1024];
+    uint32 rsp_len = 1024;
+    memset(sock, 0x0, sizeof(mbtk_ftp_sock_s));
+    code = ftp_cmd_handle(info, "PASV\r\n", rsp, &rsp_len);
+    /* Found reply-strings include:
+     * "227 Entering Passive Mode (127,0,0,1,4,51)"
+     * "227 Data transfer will passively listen to 127,0,0,1,4,51"
+     * "227 Entering passive mode. 127,0,0,1,4,51"
+     */
+    if (code != 227)
+    {
+        LOGE("PASV rsp error[code = %d].", code);
+        if(code == 421)
+        {
+            printf("\n code:%d\n",code);
+            return FTP_ERR_UNKNOWN+1;
+        }
+        return FTP_ERR_UNKNOWN;
+    }
+
+    // Get IPv4 and port
+    uint8 *ptr = rsp + 4;
+    while (*ptr)
+    {
+        if (isdigit(*ptr))
+            break;
+        ptr++;
+    }
+    // ptr point to first digit.
+    memcpy(sock->host, ptr, strlen((char*) ptr));
+    ptr = sock->host;
+    int count = 0;
+    while (*ptr)
+    {
+        if (*ptr == ',')
+        {
+            *ptr = '.';
+            count++;
+        }
+        if (count == 4)
+        {
+            *ptr = '\0';
+            break;
+        }
+        ptr++;
+    }
+    ptr++;
+
+    // ptr point to first port digit.
+    int port1 = atoi((char*) ptr);
+    while (*ptr)
+    {
+        if (*ptr == ',')
+            break;
+        ptr++;
+    }
+    ptr++;
+
+    // ptr point to second port digit.
+    int port2 = atoi((char*) ptr);
+    sock->port = port1 * 256 + port2;
+
+    LOGI("PASV: %s:%d", sock->host, sock->port);
+
+    if(ftp_internal_ipv4_check(sock->host))
+    {
+        memset(sock->host,0x0,MBTK_FTP_IP_MAX + 1);
+        memcpy(sock->host,info->sock_info[FTP_SOCK_CTRL].host,strlen(info->sock_info[FTP_SOCK_CTRL].host));
+        LOGI("This is internal ipv4,so use IP - %s:%d",sock->host, sock->port);
+    }
+
+    return FTP_ERR_SUCCESS;
+}
+
+static mbtk_ftp_error_enum ftp_sock_get_by_epsv(mbtk_ftp_info_s *info,
+        mbtk_ftp_sock_s *sock)
+{
+    int code;
+    char rsp[1024];
+    uint32 rsp_len = 1024;
+    memset(sock, 0x0, sizeof(mbtk_ftp_sock_s));
+    // |||6446|
+    code = ftp_cmd_handle(info, "EPSV\r\n", rsp, &rsp_len);
+    if (code != 229)
+    {
+        LOGE("EPSV rsp error[code = %d].", code);
+        return FTP_ERR_UNKNOWN;
+    }
+
+    /* positive EPSV response */
+    char *ptr = strchr(rsp, '(');
+    if (ptr)
+    {
+        unsigned int num;
+        char separator[4];
+        ptr++;
+        if (5
+            == sscanf(ptr, "%c%c%c%u%c", &separator[0], &separator[1],
+                      &separator[2], &num, &separator[3]))
+        {
+            const char sep1 = separator[0];
+            int i;
+
+            /* The four separators should be identical, or else this is an oddly
+             formatted reply and we bail out immediately. */
+            for (i = 1; i < 4; i++)
+            {
+                if (separator[i] != sep1)
+                {
+                    ptr = NULL; /* set to NULL to signal error */
+                    break;
+                }
+            }
+            if (num > 0xffff)
+            {
+                LOGE("Illegal port number in EPSV reply");
+                return FTP_ERR_UNKNOWN;
+            }
+            if (ptr)
+            {
+                sock->port = (uint32) (num & 0xffff);
+                memcpy(sock->host, info->sock_info[FTP_SOCK_CTRL].host,
+                       strlen(info->sock_info[FTP_SOCK_CTRL].host));
+            }
+        }
+        else
+        {
+            ptr = NULL;
+        }
+    }
+    if (!ptr)
+    {
+        LOGE("Weirdly formatted EPSV reply");
+        return FTP_ERR_UNKNOWN;
+    }
+
+    LOGI("PASV: %s:%d", sock->host, sock->port);
+
+    return FTP_ERR_SUCCESS;
+}
+
+/*
+ * 04-26-20  02:13PM               379193 audio_0426.zip
+ * 09-10-20  02:31PM              4660645 Log.zip
+ * 10-28-20  01:53PM       <DIR>          PicLibs
+ */
+
+ /*
+ *-rw-r--r-- 1 ftp ftp     2097152000 Feb 18 17:28 ASR1803_Linux.tar.gz_aa
+ *-rw-r--r-- 1 ftp ftp     2097152000 Feb 18 17:21 ASR1803_Linux.tar.gz_ab
+ *-rw-r--r-- 1 ftp ftp     1198057917 Feb 18 17:29 ASR1803_Linux.tar.gz_ac
+ *-rw-r--r-- 1 ftp ftp         445043 Apr 06  2021 data
+ *drwxr-xr-x 1 ftp ftp              0 Apr 10  2021 L306
+ */
+static mbtk_ftp_error_enum ftp_cmd_list_parse(mbtk_ftp_info_s *info,
+        mbtk_ftp_file_info_s *file_list)
+{
+    char line[1024];
+    char line_buf[1024];
+    int len, err;
+    mbtk_ftp_file_info_s file_ptr;
+    memset(file_list, 0x0, sizeof(mbtk_ftp_file_info_s)-sizeof(void *));
+
+    // Save file number.
+    file_list->size = 0;
+    len = 1;
+    int read_line_count=0;
+    while (len > 0)
+    {
+        if(info->auth_type!=0)
+        {
+            len = mbtk_sock_readline(info->ftp_ssl_handle,info->session_data,line_buf,1024,FTP_TIMEOUT,&err,&read_line_count,line);
+        }
+        else
+        {
+            len = sock_readline(&info->net_info, &info->sock_info[FTP_SOCK_DATA], line, 1024,FTP_TIMEOUT, &err);
+        }
+        LOGD("Line:%s", line);
+        if(len <= 0)
+            {
+                if (err == MBTK_SOCK_SUCCESS)
+                    return FTP_ERR_SUCCESS;
+                break;
+            }
+        else
+            len = strlen(line) - 1;
+        if (line[0] >= '0' && line[0] <= '9')
+        {
+            /*
+             * 04-26-20  02:13PM               379193 audio_0426.zip
+             * 09-10-20  02:31PM              4660645 Log.zip
+             * 10-28-20  01:53PM       <DIR>          PicLibs
+             */
+            line[len] = '\0';
+            /*
+            file_ptr = (mbtk_ftp_file_info_s*) malloc(
+                           sizeof(mbtk_ftp_file_info_s));
+            if (!file_ptr)
+            {
+                LOGE("malloc() fail.");
+                return FTP_ERR_UNKNOWN;
+            }
+            */
+            memset(&file_ptr, 0x0, sizeof(mbtk_ftp_file_info_s));
+
+            char str[MBTK_FTP_FILE_NAME_MAX];
+            const char *temp = line;
+            // Data
+            temp = ftp_str_next(temp, str, MBTK_FTP_FILE_NAME_MAX);
+            if (strlen(str) > 0)
+            {
+                LOGV("Data:%s", str);
+            }
+
+            // Time
+            temp = ftp_str_next(temp, str, MBTK_FTP_FILE_NAME_MAX);
+            if (strlen(str) > 0)
+            {
+                LOGV("Time:%s", str);
+            }
+
+            // <DIR> or file size
+            temp = ftp_str_next(temp, str, MBTK_FTP_FILE_NAME_MAX);
+            if (strlen(str) > 0)
+            {
+                LOGV("<DIR>/size:%s", str);
+                if (!strcmp("<DIR>", str))
+                {
+                    file_ptr.is_file = false;
+                    file_ptr.size = 0;
+                }
+                else
+                {
+                    file_ptr.is_file = true;
+                    file_ptr.size = atoi(str);
+                }
+            }
+
+            const char *name = temp;
+            while (*name && ftp_ch_is_space(*name))
+            {
+                name++;
+            }
+
+            // Name
+            if (strlen(name) > 0)
+            {
+                LOGV("Name:%s",name);
+                memset(file_ptr.name,0,strlen(file_ptr.name)+1);
+                memcpy(file_ptr.name, name, strlen(name));
+                char *temp = (char*) file_ptr.name
+                             + strlen((char*) file_ptr.name) - 1;
+                while (ftp_ch_is_space(*temp))
+                {
+                    *temp = '\0';
+                    temp--;
+                }
+            }
+            else
+            {
+                LOGE("Can not get name[%s].", line);
+                return FTP_ERR_UNKNOWN;
+            }
+
+            (file_list->ftp_ls_cb_typedef)(&file_ptr);
+            //file_ptr->next = file_list->next;
+            //file_list->next = file_ptr;
+            //file_list->size++;
+            //free(file_ptr);
+        } else if(!ftp_ch_is_space(line[0])) {
+             /*
+             *-rw-r--r-- 1 ftp ftp     2097152000 Feb 18 17:28 ASR1803_Linux.tar.gz_aa
+             *-rw-r--r-- 1 ftp ftp     2097152000 Feb 18 17:21 ASR1803_Linux.tar.gz_ab
+             *-rw-r--r-- 1 ftp ftp     1198057917 Feb 18 17:29 ASR1803_Linux.tar.gz_ac
+             *-rw-r--r-- 1 ftp ftp         445043 Apr 06  2021 data
+             *drwxr-xr-x 1 ftp ftp              0 Apr 10  2021 L306
+             */
+            line[len] = '\0';
+             /*
+            file_ptr = (mbtk_ftp_file_info_s*) malloc(
+                           sizeof(mbtk_ftp_file_info_s));
+            if (!file_ptr)
+            {
+                LOGE("malloc() fail.");
+                return FTP_ERR_UNKNOWN;
+            }
+            */
+            memset(&file_ptr, 0x0, sizeof(mbtk_ftp_file_info_s));
+
+            char str[MBTK_FTP_FILE_NAME_MAX];
+            const char *temp = line;
+            // rwx
+            temp = ftp_str_next(temp, str, MBTK_FTP_FILE_NAME_MAX);
+            if (strlen(str) > 0)
+            {
+                LOGV("rwx:%s", str);
+                file_ptr.is_file = (str[0] == '-' ? true : false);
+            }
+
+            // 1 ftp ftp
+            temp = ftp_str_next(temp, str, MBTK_FTP_FILE_NAME_MAX);
+            temp = ftp_str_next(temp, str, MBTK_FTP_FILE_NAME_MAX);
+            temp = ftp_str_next(temp, str, MBTK_FTP_FILE_NAME_MAX);
+
+            // size
+            temp = ftp_str_next(temp, str, MBTK_FTP_FILE_NAME_MAX);
+            if (strlen(str) > 0)
+            {
+                LOGV("Size:%s", str);
+                file_ptr.size = atoi(str);
+            }
+
+            // Feb 18 17:28
+            // or
+            // Apr 10  2021
+            temp = ftp_str_next(temp, str, MBTK_FTP_FILE_NAME_MAX);
+            temp = ftp_str_next(temp, str, MBTK_FTP_FILE_NAME_MAX);
+            temp = ftp_str_next(temp, str, MBTK_FTP_FILE_NAME_MAX);
+
+            const char *name = temp;
+            while (*name && ftp_ch_is_space(*name))
+            {
+                name++;
+            }
+
+            // Name
+            if (strlen(name) > 0)
+            {
+                LOGV("Name:%s",name);
+                memset(file_ptr.name,0,strlen(file_ptr.name)+1);
+                memcpy(file_ptr.name, name, strlen(name));
+                char *temp = (char*) file_ptr.name
+                             + strlen((char*) file_ptr.name) - 1;
+                while (ftp_ch_is_space(*temp))
+                {
+                    *temp = '\0';
+                    temp--;
+                }
+            }
+            else
+            {
+                LOGE("Can not get name[%s].", line);
+                return FTP_ERR_UNKNOWN;
+            }
+
+            (file_list->ftp_ls_cb_typedef)(&file_ptr);
+            //file_ptr->next = file_list->next;
+            //file_list->next = file_ptr;
+            //file_list->size++;
+            //free(file_ptr);
+        }else {
+            LOGE("Data error.");
+            return FTP_ERR_UNKNOWN;
+        }
+    }
+    return FTP_ERR_SUCCESS;
+}
+
+static mbtk_ftp_error_enum ftp_download_process(mbtk_ftp_info_s *info)
+{
+#define READ_BUF_SIZE 2048
+    char buff[READ_BUF_SIZE];
+    int len, err, len_read;
+    int read_count = info->file_trans.size_send;
+
+#ifdef FTP_RESUME_DEBUG
+    int count = 0;
+#endif
+
+    if (info->file_trans.size_count - read_count > READ_BUF_SIZE)
+        len_read = READ_BUF_SIZE;
+    else
+        len_read = info->file_trans.size_count - read_count;
+
+    if(info->auth_type != 0)
+        len = mbtk_sock_read(info->ftp_ssl_handle,info->session_data,buff,len_read,FTP_TIMEOUT,&err);
+    else
+        len = sock_read(&info->net_info, &info->sock_info[FTP_SOCK_DATA], buff, len_read,FTP_TIMEOUT, &err);
+    /*
+    while (len_read > 0 && (len = sock_read(&info->net_info, &info->sock_info[FTP_SOCK_DATA], buff, len_read,
+                                    FTP_TIMEOUT, &err)) > 0)
+    */
+    while (len_read > 0 && len > 0)
+    {
+        read_count += len;
+
+#ifdef FTP_RESUME_DEBUG
+        count++;
+        if (count <= 1000)
+        {
+#endif
+            if (info->file_trans.data_cb)
+            {
+                info->file_trans.data_cb(buff, len);
+            }
+            else     // Write to efs.
+            {
+                if (len != file_write(info->file_trans.fd, buff, len))
+                {
+                    LOGE("EFS write fail.");
+                    return FTP_ERR_EFS_FILE;
+                }
+            }
+            memset(buff,0,sizeof(buff));
+            info->file_trans.size_send = read_count;
+#ifdef FTP_RESUME_DEBUG
+        }
+#endif
+
+        if (info->file_trans.size_count - read_count > READ_BUF_SIZE)
+            len_read = READ_BUF_SIZE;
+        else
+            len_read = info->file_trans.size_count - read_count;
+
+        if(info->auth_type != 0)
+            len = mbtk_sock_read(info->ftp_ssl_handle,info->session_data,buff,len_read,FTP_TIMEOUT,&err);
+        else
+            len = sock_read(&info->net_info, &info->sock_info[FTP_SOCK_DATA], buff, len_read,FTP_TIMEOUT, &err);
+    }
+
+    return FTP_ERR_SUCCESS;
+}
+
+static mbtk_ftp_error_enum ftp_update_process(mbtk_ftp_info_s *info)
+{
+#define READ_BUF_SIZE 2048
+    char buff[READ_BUF_SIZE];
+    int len, err, len_write;
+	int file_total_size = 0;
+    int write_count = 0;
+
+	if(info->file_trans.fd > 0)
+	{
+		file_total_size = file_length(info->file_trans.fd);
+        info->file_trans.size_count = file_total_size;
+		while((len_write = file_read(info->file_trans.fd, buff, READ_BUF_SIZE)) > 0 )
+		{
+			LOGE("file_read.len:%d, buf;%s", len_write, buff);
+            if(info->auth_type != 0)
+                len = mbtk_sock_write(info->ftp_ssl_handle,info->session_data,buff,len_write,FTP_TIMEOUT,&err);
+            else
+			    len = sock_write(&info->net_info, &info->sock_info[FTP_SOCK_DATA], buff, len_write,
+                                    FTP_TIMEOUT, &err);
+			if(len < 0)
+			{
+				LOGE("EFS write fail.len:%d, err;%d", len, err);
+                return FTP_ERR_EFS_FILE;
+			}
+
+			write_count += len;
+		}
+
+        info->file_trans.size_send = write_count;
+		if( write_count != file_total_size)
+		{
+			LOGE("socket write fail.,file_total_size:%d, write_count:%d", file_total_size,write_count);
+            return FTP_ERR_NET_WRITE;
+		}
+		else{
+			LOGE("socket write success.,file_total_size:%d, write_count:%d", file_total_size, write_count);
+		}
+
+	}
+	else
+	{
+		LOGE("EFS write fail.");
+        return FTP_ERR_EFS_FILE;
+	}
+
+	return FTP_ERR_SUCCESS;
+}
+
+
+/*
+ * audio_0426.zip
+ * Log.zip
+ * PicLibs
+ */
+static mbtk_ftp_error_enum ftp_cmd_nlst_parse(mbtk_ftp_info_s *info,
+        mbtk_ftp_file_info_s *file_list)
+{
+    char line[1024];
+    int len, err;
+    mbtk_ftp_file_info_s *file_ptr = NULL;
+    while ((len = sock_readline(&info->net_info, &info->sock_info[FTP_SOCK_DATA], line, 1024,
+                                FTP_TIMEOUT, &err)) > 0)
+    {
+        if (!ftp_ch_is_space(line[0]))
+        {
+            file_ptr = (mbtk_ftp_file_info_s*) malloc(
+                           sizeof(mbtk_ftp_file_info_s));
+            if (!file_ptr)
+            {
+                LOGE("malloc() fail.");
+                return FTP_ERR_UNKNOWN;
+            }
+            memset(file_ptr, 0x0, sizeof(mbtk_ftp_file_info_s));
+            memcpy(file_ptr->name, line, strlen(line));
+            char *temp = (char*) file_ptr->name + strlen((char*) file_ptr->name)
+                         - 1;
+            while (ftp_ch_is_space(*temp))
+            {
+                *temp = '\0';
+                temp--;
+            }
+
+            file_ptr->next = file_list->next;
+            file_list->next = file_ptr;
+        }
+    }
+    return FTP_ERR_SUCCESS;
+}
+
+static mbtk_ftp_error_enum ftp_data_sock_read(mbtk_ftp_info_s *info,
+        mbtk_ftp_cmd_enum cmd, void *req, void *rsp)
+{
+    int err, len, code;
+    mbtk_ftp_error_enum result = FTP_ERR_SUCCESS;
+    char buff[1024];
+    if (info->is_data_sock_busy)
+    {
+        LOGW("Data socket is busy!");
+        return FTP_ERR_DATA_SOCK_BUSY;
+    }
+
+    info->is_data_sock_busy = true;
+    // Connect to service by data socket.
+    if (info->data_mode == FTP_MODE_PASSIVE)
+    {
+        mbtk_ftp_sock_s sock;
+        if(info->auth_type != FTP_AUTH_TYPE_NON) {
+            // PROT P
+            char cmd_ssl[50];
+            memset(cmd_ssl,0,50);
+            snprintf(cmd_ssl, 50, "PROT P\r\n");
+            code = ftp_cmd_handle(info, cmd_ssl, NULL,NULL);
+            if (code/100 != 2)
+            {
+                LOGE("PROT P error[code = %d].", code);
+                goto end;
+            }
+        }
+
+        if (info->sock_info[FTP_SOCK_CTRL].addr_family == MBTK_ADDR_IPV6)
+        {
+            if ((result = ftp_sock_get_by_epsv(info, &sock))
+                != FTP_ERR_SUCCESS)
+            {
+                goto end;
+            }
+        }
+        else
+        {
+            if ((result = ftp_sock_get_by_pasv(info, &sock))
+                != FTP_ERR_SUCCESS)
+            {
+                printf("\nftp_sock_get_by_pasv end.result=%d\n",result);
+            }
+            else
+            {
+                printf("\nftp_sock_get_by_pasv ok!\n");
+            }
+        }
+
+        if(info->auth_type != FTP_AUTH_TYPE_NON)
+        {
+            info->ftp_ssl_handle_data = mbtk_sock_init(&info->ftp_ssl_info_data);
+            if(info->ftp_ssl_handle_data == -1)
+            {
+                printf("mbtk_sock_init error\n");
+            }
+            info->ftp_sock_ssl_info_data = (mbtk_sock_info*) malloc(sizeof(mbtk_sock_info));
+            memset(info->ftp_sock_ssl_info_data, 0x0, sizeof(mbtk_sock_info));
+            info->ftp_sock_ssl_info_data->is_support_ssl = info->auth_type;
+            info->ftp_sock_ssl_info_data->ftp_ssl_support=1;
+            info->ftp_sock_ssl_info_data->port = sock.port;
+            info->ftp_sock_ssl_info_data->type = MBTK_SOCK_TCP;
+            info->ftp_sock_ssl_info_data->ingnore_cert = ~(info->sock_info[FTP_SOCK_CTRL].use_cert);
+            memcpy(info->ftp_sock_ssl_info_data->address, sock.host, strlen(sock.host));
+            if(info->ftp_sock_ssl_info_data->is_support_ssl != 0)
+            {
+                info->ftp_sock_ssl_info_data->is_support_ssl = 0;
+            }
+            info->session_data = mbtk_sock_open(info->ftp_ssl_handle,info->ftp_sock_ssl_info_data,60000,&err);
+            if(err!=0)
+            {
+                printf("mbtk_sock_open error : %d\n",err);
+            }
+            else
+            {
+                info->ftp_sock_ssl_info_data->is_support_ssl = info->auth_type;
+                //printf("\ninfo->session_data = %d \n",info->session_data);
+            }
+        }
+        else
+        {
+            memcpy(info->sock_info[FTP_SOCK_DATA].host, sock.host, strlen((char*)sock.host));
+            info->sock_info[FTP_SOCK_DATA].port = sock.port;
+            info->sock_info[FTP_SOCK_DATA].is_ssl = (info->auth_type != FTP_AUTH_TYPE_NON);
+            info->sock_info[FTP_SOCK_DATA].addr_family = info->sock_info[FTP_SOCK_CTRL].addr_family;
+            info->sock_info[FTP_SOCK_DATA].use_cert = info->sock_info[FTP_SOCK_CTRL].use_cert;
+            info->net_info.keep_alive = FALSE;
+            info->net_info.recv_buff_size = 32 * 1024; // 32K
+            info->sock_info[FTP_SOCK_DATA].fd = sock_open(&info->net_info, &info->sock_info[FTP_SOCK_DATA],
+                                                    FTP_TIMEOUT, &err);
+        }
+
+    }
+    else    // Active mode
+    {
+        if (req == NULL)
+        {
+            result = FTP_ERR_PARAM_SET;
+            goto end;
+        }
+        mbtk_ftp_sock_s *sock = (mbtk_ftp_sock_s*) req;
+
+        if (info->sock_info[FTP_SOCK_CTRL].addr_family == MBTK_ADDR_IPV6)
+        {
+            if ((result = ftp_sock_set_by_eprt(info, sock))
+                != FTP_ERR_SUCCESS)
+            {
+                goto end;
+            }
+        }
+        else
+        {
+            if ((result = ftp_sock_set_by_port(info, sock))
+                != FTP_ERR_SUCCESS)
+            {
+                goto end;
+            }
+        }
+
+        // Wait for client[service] connect.
+
+    }
+    if(info->auth_type != FTP_AUTH_TYPE_NON)
+    {
+        if(info->session_data < 0)
+        {
+               //printf("Socket open/connect ssl fail[err = %d].", err);
+               result = FTP_ERR_NET_CONN;
+               goto read_end;
+        }
+    }
+    else
+    {
+        if (info->sock_info[FTP_SOCK_DATA].fd < 0)
+        {
+            LOGE("Socket open/connect fail[err = %d].", err);
+            result = FTP_ERR_NET_CONN;
+            goto read_end;
+        }
+
+        if(info->auth_type == FTP_AUTH_TYPE_IMPLICIT) {
+            info->sock_info[FTP_SOCK_DATA].is_ssl = true;
+        }
+    }
+
+    if (cmd == FTP_CMD_GET)   // Is download
+    {
+        char cmd[100];
+        if (info->file_trans.size_send > 0)   // Resume transmission
+        {
+            // REST size
+            memset(cmd, 0x0, 100);
+            snprintf(cmd, 100, "REST %ld\r\n", info->file_trans.size_send);
+            code = ftp_cmd_handle(info, cmd, NULL, NULL);
+            if (code != 350)
+            {
+                LOGE("REST rsp error[code = %d].", code);
+                result = FTP_ERR_UNKNOWN;
+                goto end;
+            }
+        }
+
+        memset(cmd, 0x0, 100);
+        snprintf(cmd, 100, "RETR %s\r\n", info->file_trans.remote_name);
+        code = ftp_cmd_handle(info, cmd, NULL, NULL);
+        if (code != 125 && code != 150)
+        {
+            LOGE("RETR %s rsp error[code = %d].", info->file_trans.remote_name,
+                 code);
+            result = FTP_ERR_UNKNOWN;
+            goto end;
+        }
+        
+        int mbtk_errno;
+        if(info->auth_type != 0)
+        {
+            mbtk_errno = mbtk_ssl_init_func(info->ftp_ssl_handle,info->ftp_sock_ssl_info_data->ingnore_cert,info->session_data);
+            if(mbtk_errno != 0)
+            {
+                printf("\nmbtk_ssl_init_func error = %d",mbtk_errno);
+                goto end;
+            }
+            
+        }
+        result = ftp_download_process(info);
+        if(info->auth_type != 0)
+        {
+            char mbtk_ftp_ssl_read_buf[256];
+            memset(mbtk_ftp_ssl_read_buf,0,sizeof(mbtk_ftp_ssl_read_buf));
+            mbtk_sock_read(info->ftp_ssl_handle,info->session,
+                                       mbtk_ftp_ssl_read_buf,
+                                       sizeof(mbtk_ftp_ssl_read_buf),
+                                       6000,
+                                       &mbtk_errno);
+            printf("\nmbtk_sock_read:\n%s\n",mbtk_ftp_ssl_read_buf);
+        }
+        if(info->auth_type != 0)
+            goto read_end;
+    }
+	else if (cmd == FTP_CMD_PUT)   // Is download
+    {
+        if(info->auth_type != 0)
+        {
+                int mbtk_errno;
+                char cmd[100];
+                memset(cmd, 0x0, 100);
+                snprintf(cmd, 100, "STOR %s\r\n", info->file_trans.remote_name);
+        		LOGE("STOR %s .name:%s ", cmd, info->file_trans.remote_name);
+                code = ftp_cmd_handle(info, cmd, NULL, NULL);
+                if (code != 125 && code != 150)
+                {
+                    LOGE("RETR %s rsp error[code = %d].", info->file_trans.remote_name,
+                         code);
+                    //printf("RETR %s rsp error[code = %d].\n", info->file_trans.remote_name,code);
+                    result = FTP_ERR_UNKNOWN;
+                    goto end;
+                }
+                
+                mbtk_errno = mbtk_ssl_init_func(info->ftp_ssl_handle,info->ftp_sock_ssl_info_data->ingnore_cert,info->session_data);
+                if(mbtk_errno != 0)
+                {
+                    printf("\nmbtk_ssl_init_func error = %d",mbtk_errno);
+                    goto end;
+                }
+                if(info->file_trans.size_count == 0)
+            	{
+            	    result = ftp_update_process(info);
+            	    goto read_end;
+            	}
+            	else 
+            	{
+                    //printf("FTP_SOCK_DATA,fd:%d\n",info->file_trans.size_count);
+            	    return FTP_ERR_SUCCESS;
+            	}
+            goto end;
+        }
+        else
+        {
+            char cmd[100];
+            memset(cmd, 0x0, 100);
+            snprintf(cmd, 100, "STOR %s\r\n", info->file_trans.remote_name);
+    		LOGE("STOR %s .name:%s ", cmd, info->file_trans.remote_name);
+            code = ftp_cmd_handle(info, cmd, NULL, NULL);
+            if (code != 125 && code != 150)
+            {
+                LOGE("RETR %s rsp error[code = %d].", info->file_trans.remote_name,
+                     code);
+                result = FTP_ERR_UNKNOWN;
+                goto end;
+            }
+    		
+        	if(info->file_trans.size_count == 0)
+        	{
+        	    result = ftp_update_process(info);
+        	    goto read_end;
+        	}
+        	else 
+        	{
+                    LOGE("FTP_SOCK_DATA,fd:%d", info->sock_info[FTP_SOCK_DATA].fd);
+        	    return FTP_ERR_SUCCESS;
+        	}
+        }
+    }
+    else if (cmd == FTP_CMD_LIST)
+    {
+        if(info->auth_type != 0)
+        {
+        	int mbtk_errno;
+        	code = ftp_cmd_handle(info, "LIST\r\n", NULL, NULL);
+        	if (code != 125 && code != 150)
+            {
+            	LOGE("LIST rsp error[code = %d].", code);
+                result = FTP_ERR_UNKNOWN;
+                goto read_end;
+            }
+
+            mbtk_errno = mbtk_ssl_init_func(info->ftp_ssl_handle,info->ftp_sock_ssl_info_data->ingnore_cert,info->session_data);
+            if(mbtk_errno != 0)
+            {
+            	printf("\nmbtk_ssl_init_func error = %d",mbtk_errno);
+                goto read_end;
+            }
+
+            result = ftp_cmd_list_parse(info, (mbtk_ftp_file_info_s*) rsp);
+            char mbtk_ftp_ssl_read_buf[1024];
+            memset(mbtk_ftp_ssl_read_buf,0,sizeof(mbtk_ftp_ssl_read_buf));
+            mbtk_sock_read(info->ftp_ssl_handle,info->session,
+                                mbtk_ftp_ssl_read_buf,
+                                sizeof(mbtk_ftp_ssl_read_buf),
+                                6000,
+                                &mbtk_errno);
+            printf("\nmbtk_sock_read:\n%s\n",mbtk_ftp_ssl_read_buf);
+            int rsp_code = atoi(mbtk_ftp_ssl_read_buf);
+            if(rsp_code / 200)
+            	result = FTP_ERR_SUCCESS;
+            goto read_end;
+        }
+        else
+        {
+            code = ftp_cmd_handle(info, "LIST\r\n", NULL, NULL);
+            if (code != 125 && code != 150)
+            {
+                LOGE("LIST rsp error[code = %d].", code);
+                result = FTP_ERR_UNKNOWN;
+                goto end;
+            }
+
+            result = ftp_cmd_list_parse(info, (mbtk_ftp_file_info_s*) rsp);
+        }
+    }
+    else if (cmd == FTP_CMD_NLST)
+    {
+        code = ftp_cmd_handle(info, "NLST\r\n", NULL, NULL);
+        if (code != 125 && code != 150)
+        {
+            LOGE("LIST rsp error[code = %d].", code);
+            result = FTP_ERR_UNKNOWN;
+            goto end;
+        }
+
+        result = ftp_cmd_nlst_parse(info, (mbtk_ftp_file_info_s*) rsp);
+    }
+    else
+    {
+        LOGE("No support this cmd[%d].", cmd);
+        result = FTP_ERR_UNKNOWN;
+        goto read_end;
+    }
+
+    // Read [226 Transfer complete.]
+read_code:
+    memset(buff, 0x0, 1024);
+    len = sock_readline(&info->net_info, &info->sock_info[FTP_SOCK_CTRL], buff, 1024, FTP_TIMEOUT,
+                    &err);
+    if (len <= 0)
+    {
+        LOGE("Socket read fail[len = %d].", len);
+        result = FTP_ERR_NET_READ;
+        goto sock_error;
+    }
+    LOGI("RSP[len-%d]:%s",len,buff);
+    //log_hex("FTP_RSP",buff,len);
+    code = atoi(buff);
+    if (code == 0)
+    {
+        goto read_code;
+    }
+#if 1
+    // 426 Connection closed; aborted transfer of "/files/test"
+    else if (code == 426)
+    {
+        LOGE("Connection closed,restart download...");
+        result = FTP_ERR_UNKNOWN;
+        goto sock_error;
+    }
+#endif
+    else if (code != 226)
+    {
+        LOGE("Code not be 226[%s].", buff);
+        result = FTP_ERR_UNKNOWN;
+        goto read_end;
+    }
+
+    goto read_end;
+sock_error:
+    {
+        if (info->sock_info[FTP_SOCK_CTRL].fd > 0)
+        {
+            if (sock_close(&info->net_info, &info->sock_info[FTP_SOCK_CTRL], FTP_TIMEOUT, &err))
+            {
+                LOGE("Close ctrl socket fail[%d].", err);
+            }
+        }
+        info->state = FTP_STATE_NON;
+        info->sock_info[FTP_SOCK_CTRL].fd = -1;
+    }
+read_end:
+    // Close data socket.
+    if (info->sock_info[FTP_SOCK_DATA].fd > 0)
+    {
+        if (sock_close(&info->net_info, &info->sock_info[FTP_SOCK_DATA], FTP_TIMEOUT, &err))
+        {
+            LOGE("Close data socket fail[%d].", err);
+            result = FTP_ERR_NET_CLOSE;
+        }
+        else
+        {
+            info->sock_info[FTP_SOCK_DATA].fd = -1;
+        }
+    }
+    if(info->auth_type != 0)
+    {
+        if(mbtk_sock_close(info->ftp_ssl_handle,info->session_data,6000,&err))
+        {
+            LOGE("Close ssl data socket fail[%d].", err);
+            result = FTP_ERR_NET_CLOSE;
+        }
+        else
+        {
+        //    info->sock_info[FTP_SOCK_DATA].fd = -1;
+        }
+    }
+    if (info->data_mode == FTP_MODE_PASSIVE)
+    {
+
+    }
+    else
+    {
+
+    }
+
+end:
+    // Default is passive.
+    info->data_mode = FTP_MODE_PASSIVE;
+    info->is_data_sock_busy = false;
+
+    return result;
+}
+
+static mbtk_ftp_error_enum ftp_cmd_process_pwd(mbtk_ftp_info_s *info,
+        char *path)
+{
+    int code;
+    char rsp[100];
+    uint32 rsp_len = 100;
+    code = ftp_cmd_handle(info, "PWD\r\n", rsp, &rsp_len);
+    if (code != 257)
+    {
+        LOGE("PWD rsp error[code = %d].", code);
+        return FTP_ERR_UNKNOWN;
+    }
+
+    // "path" is current ....
+    char *ptr = strchr(rsp, '"');
+    if (!ptr)
+    {
+        LOGE("PWD rsp error[%d].", code);
+        return FTP_ERR_UNKNOWN;
+    }
+
+    ptr++;
+    char *ptr_temp = ptr;
+    while (*ptr_temp)
+    {
+        if (*ptr_temp == '"')
+        {
+            *ptr_temp = '\0';
+            break;
+        }
+        ptr_temp++;
+    }
+    memcpy(path, ptr, strlen(ptr));
+    path[strlen(ptr)] = '\0';
+
+    return FTP_ERR_SUCCESS;
+}
+
+static mbtk_ftp_error_enum ftp_cmd_process_cwd(mbtk_ftp_info_s *info,
+        const char *path)
+{
+    int code;
+    char cmd[100] = { 0 };
+    snprintf(cmd, 100, "CWD %s\r\n", path);
+    code = ftp_cmd_handle(info, cmd, NULL, NULL);
+    if (code != 250)
+    {
+        LOGE("CWD rsp error[code = %d].", code);
+        return FTP_ERR_UNKNOWN;
+    }
+
+    return FTP_ERR_SUCCESS;
+}
+
+static mbtk_ftp_error_enum ftp_cmd_process_mkd(mbtk_ftp_info_s *info,
+        const char *path)
+{
+    int code;
+    char cmd[100] = { 0 };
+    snprintf(cmd, 100, "MKD %s\r\n", path);
+    code = ftp_cmd_handle(info, cmd, NULL, NULL);
+    if (code == 257)
+    {
+        return FTP_ERR_SUCCESS;
+    }
+    else if (code == 550)
+    {
+        LOGE("Dir has exist[%s].", path);
+        return FTP_ERR_FILE_EXIST;
+    }
+    else
+    {
+        LOGE("MKD rsp error[code = %d].", code);
+        return FTP_ERR_UNKNOWN;
+    }
+}
+
+static mbtk_ftp_error_enum ftp_cmd_process_rmd(mbtk_ftp_info_s *info,
+        const char *path)
+{
+    int code;
+    char cmd[100] = { 0 };
+    snprintf(cmd, 100, "RMD %s\r\n", path);
+    code = ftp_cmd_handle(info, cmd, NULL, NULL);
+    if (code == 250)
+    {
+        return FTP_ERR_SUCCESS;
+    }
+    else if (code == 550)
+    {
+        LOGE("Dir not exist or not empty[%s].", path);
+        return FTP_ERR_FILE_NO_FOUND;
+    }
+    else
+    {
+        LOGE("RMD rsp error[code = %d].", code);
+        return FTP_ERR_UNKNOWN;
+    }
+}
+
+static mbtk_ftp_error_enum ftp_cmd_process_stat(mbtk_ftp_info_s *info,
+        void *status)
+{
+    int code;
+    char rsp[1024];
+    uint32 rsp_len = 1024;
+    code = ftp_cmd_handle(info, "STAT\r\n", rsp, &rsp_len);
+    if (code != 211)
+    {
+        LOGE("STAT rsp error[code = %d].", code);
+        return FTP_ERR_UNKNOWN;
+    }
+
+    memcpy(status, rsp, rsp_len);
+
+    return FTP_ERR_SUCCESS;
+}
+
+static mbtk_ftp_error_enum ftp_cmd_process_type(mbtk_ftp_info_s *info,
+        mbtk_ftp_data_type_enum type)
+{
+    int code;
+    if (type == FTP_DATA_TYPE_I)
+        code = ftp_cmd_handle(info, "TYPE I\r\n", NULL, NULL);
+    else if (type == FTP_DATA_TYPE_E)
+        code = ftp_cmd_handle(info, "TYPE E\r\n", NULL, NULL);
+    else
+        code = ftp_cmd_handle(info, "TYPE A\r\n", NULL, NULL);
+
+    if (code != 200)
+    {
+        LOGE("TYPE rsp error[code = %d].", code);
+        return FTP_ERR_UNKNOWN;
+    }
+
+    return FTP_ERR_SUCCESS;
+}
+
+static mbtk_ftp_error_enum ftp_cmd_process_size(mbtk_ftp_info_s *info,
+        const char *path, uint32 *size)
+{
+    int code;
+    char cmd[100] = { 0 };
+    char rsp[100];
+    uint32 rsp_len = 100;
+    snprintf(cmd, 100, "SIZE %s\r\n", path);
+    code = ftp_cmd_handle(info, cmd, rsp, &rsp_len);
+
+    if (code == 213)
+    {
+        // "213 4660645"
+        *size = atoi(rsp + 4);
+    }
+    else if (code == 550)
+    {
+        LOGW("No found file[%s].", path);
+        return FTP_ERR_FILE_NO_FOUND;
+    }
+    else
+    {
+        LOGE("SIZE rsp error[code = %d].", code);
+        return FTP_ERR_UNKNOWN;
+    }
+
+    return FTP_ERR_SUCCESS;
+}
+
+static mbtk_ftp_error_enum ftp_cmd_process_mdtm(mbtk_ftp_info_s *info,
+        const char *path, char *time)
+{
+    int code;
+    char cmd[100] = { 0 };
+    char rsp[100];
+    uint32 rsp_len = 100;
+    snprintf(cmd, 100, "MDTM %s\r\n", path);
+    code = ftp_cmd_handle(info, cmd, rsp, &rsp_len);
+
+    if (code == 213)
+    {
+        // "213 20181017014716"
+        memcpy(time, rsp + 4, 14);
+    }
+    else if (code == 550)
+    {
+        LOGW("No found file[%s].", path);
+        return FTP_ERR_FILE_NO_FOUND;
+    }
+    else
+    {
+        LOGE("MDTM rsp error[code = %d].", code);
+        return FTP_ERR_UNKNOWN;
+    }
+
+    return FTP_ERR_SUCCESS;
+}
+
+static mbtk_ftp_error_enum ftp_cmd_process_dele(mbtk_ftp_info_s *info,
+        const char *path)
+{
+    int code;
+    char cmd[100] = { 0 };
+    snprintf(cmd, 100, "DELE %s\r\n", path);
+    code = ftp_cmd_handle(info, cmd, NULL, NULL);
+    if (code == 250)
+    {
+        return FTP_ERR_SUCCESS;
+    }
+    else if (code == 550)
+    {
+        LOGW("No found file[%s].", path);
+        return FTP_ERR_FILE_NO_FOUND;
+    }
+    else
+    {
+        LOGE("DELE rsp error[code = %d].", code);
+        return FTP_ERR_UNKNOWN;
+    }
+}
+
+static mbtk_ftp_error_enum ftp_cmd_process_quit(mbtk_ftp_info_s *info)
+{
+    int code;
+    code = ftp_cmd_handle(info, "QUIT\r\n", NULL, NULL);
+    if (code != 221)
+    {
+        LOGE("CWD rsp error[code = %d].", code);
+        return FTP_ERR_UNKNOWN;
+    }
+
+    return FTP_ERR_SUCCESS;
+}
+
+static mbtk_ftp_error_enum ftp_cmd_process(mbtk_ftp_info_s *info,
+        mbtk_ftp_cmd_enum cmd, void *req, void *rsp)
+{
+    mbtk_ftp_error_enum result = FTP_ERR_SUCCESS;
+    if (info->state == FTP_STATE_READY)   // FTP login
+    {
+        info->state = FTP_STATE_CMD_PROCESS;
+        switch (cmd)
+        {
+            case FTP_CMD_LIST:
+            {
+                result = ftp_data_sock_read(info, FTP_CMD_LIST, req, rsp);
+                break;
+            }
+            case FTP_CMD_NLST:
+            {
+                result = ftp_data_sock_read(info, FTP_CMD_NLST, req, rsp);
+                break;
+            }
+            case FTP_CMD_PWD:
+            {
+                result = ftp_cmd_process_pwd(info, rsp);
+                break;
+            }
+            case FTP_CMD_CWD:
+            {
+                result = ftp_cmd_process_cwd(info, (char*) req);
+                break;
+            }
+            case FTP_CMD_MKD:
+            {
+                result = ftp_cmd_process_mkd(info, (char*) req);
+                break;
+            }
+            case FTP_CMD_RMD:
+            {
+                result = ftp_cmd_process_rmd(info, (char*) req);
+                break;
+            }
+            case FTP_CMD_STAT:
+            {
+                result = ftp_cmd_process_stat(info, rsp);
+                break;
+            }
+            case FTP_CMD_TYPE:
+            {
+                mbtk_ftp_data_type_enum *type = (mbtk_ftp_data_type_enum*) req;
+                result = ftp_cmd_process_type(info, *type);
+                break;
+            }
+            case FTP_CMD_SIZE:
+            {
+                result = ftp_cmd_process_size(info, (char*) req, (uint32*) rsp);
+                break;
+            }
+            case FTP_CMD_MDTM:
+            {
+                result = ftp_cmd_process_mdtm(info, (char*) req, (char*) rsp);
+                break;
+            }
+            case FTP_CMD_DELE:
+            {
+                result = ftp_cmd_process_dele(info, (char*) req);
+                break;
+            }
+            case FTP_CMD_QUIT:
+            {
+                result = ftp_cmd_process_quit(info);
+                break;
+            }
+            default:
+            {
+                LOGE("Unknown cmd:%d", cmd);
+                result = FTP_ERR_UNKNOWN;
+                break;
+            }
+        }
+        info->state = FTP_STATE_READY;
+    }
+    else
+    {
+        LOGW("FTP state error[%d].", info->state);
+        result = FTP_ERR_UNKNOWN;
+    }
+
+    return result;
+}
+
+static mbtk_ftp_error_enum ftp_login(mbtk_ftp_info_s *info,mbtk_ftp_user_info_s *user)
+{
+    // Open net in the first.
+    if(info->net_info.net_id <= 0) {
+        if(sock_net_open(&info->net_info,info->sock_info[FTP_SOCK_CTRL].addr_family))
+        {
+            LOGE("sock_net_open() fail.");
+            return FTP_ERR_NET_CONN;
+        }
+    }
+
+    int err;
+    mbtk_ftp_error_enum ftp_err = FTP_ERR_SUCCESS;
+    info->state = FTP_STATE_CONNECTING;
+    info->net_info.keep_alive = TRUE;
+    info->net_info.recv_buff_size = 0; // Default
+    info->sock_info[FTP_SOCK_CTRL].fd = sock_open(&info->net_info, &info->sock_info[FTP_SOCK_CTRL], FTP_TIMEOUT, &err);
+    if (info->sock_info[FTP_SOCK_CTRL].fd < 0)   // Fail
+    {
+        LOGE("Socket open/connect fail[err = %d].", err);
+        ftp_err = FTP_ERR_NET_CONN;
+        goto login_fail;
+    }
+
+    if(info->auth_type == FTP_AUTH_TYPE_IMPLICIT)
+        info->sock_info[FTP_SOCK_CTRL].is_ssl = true;
+
+    // Ctrl socket connect success.
+    info->state = FTP_STATE_CONNECTED;
+    LOGI("FTP ctrl socket connected.");
+
+    char buff[1024];
+    int len;
+    char *ptr = NULL;
+
+    // 220-FileZilla Server version 0.9.43 beta
+    // 220-written by Tim Kosse (tim.kosse@filezilla-project.org)
+    // 220 Please visit http://sourceforge.net/projects/filezilla/
+    while(TRUE) {
+        memset(buff, 0x0, 1024);
+        len = sock_readline(&info->net_info, &info->sock_info[FTP_SOCK_CTRL], buff, 1024, FTP_TIMEOUT,
+                            &err);
+        if (len <= 0)
+        {
+            LOGE("Socket read fail[err = %d].", err);
+            ftp_err = FTP_ERR_NET_READ;
+            goto login_fail;
+        } else {// xxx yyyyyy
+            if((ptr = strstr(buff,"220 ")) || (ptr = strstr(buff,"230 "))) {
+                LOGI("RSP:%s",ptr);
+                break;
+            }
+        }
+    }
+
+    int code = atoi(ptr);
+    if (code == 230)   // Has logged in.
+    {
+        info->state = FTP_STATE_READY;
+        LOGI("FTP Has logged in.");
+        return FTP_ERR_SUCCESS;
+    }
+    else if (code == 220)       // Should logn in.
+    {
+        int len;
+        char cmd_buff[50];
+
+#if 0
+        // Read other data.
+        char buff[1024];
+        memset(buff,0x0,1024);
+        while((len = sock_read(&info->net_info,info->ctrl_sock.fd, buff, 1024, info->ssl_enable, 1000,
+                                &err)) > 0)
+        {
+            LOGI("RSP[%d]:%s",len,buff);
+            memset(buff,0x0,1024);
+        }
+#endif
+
+        if(info->auth_type == FTP_AUTH_TYPE_EXPLICIT_SSL
+            || info->auth_type == FTP_AUTH_TYPE_EXPLICIT_TLS) {
+            if(info->auth_type == FTP_AUTH_TYPE_EXPLICIT_SSL) {
+                len = snprintf(cmd_buff, 50, "AUTH SSL\r\n");
+            } else {
+                len = snprintf(cmd_buff, 50, "AUTH TLS\r\n");
+            }
+            cmd_buff[len] = '\0';
+            code = ftp_cmd_handle(info, cmd_buff, NULL, NULL);
+            if (code != 234 && code != 334)
+            {
+                LOGE("AUTH SSL/TLS fail[code = %d].", code);
+                ftp_err = FTP_ERR_LOGIN_DENIED;
+                goto login_fail;
+            }
+
+#if 0
+            if(sock_ssl_enable(&info->net_info,info->ctrl_sock.fd
+                ,info->ctrl_sock.host,info->ctrl_sock.port,info->use_cert,FTP_TIMEOUT)){
+                LOGE("sock_ssl_enable() fail.");
+                ftp_err = FTP_ERR_LOGIN_DENIED;
+                goto login_fail;
+            }
+#endif
+            info->sock_info[FTP_SOCK_CTRL].is_ssl = true;
+        }
+
+        if(info->auth_type != FTP_AUTH_TYPE_NON) {
+            len = snprintf(cmd_buff, 50, "PBSZ 0\r\n");
+            cmd_buff[len] = '\0';
+            code = ftp_cmd_handle(info, cmd_buff, NULL, NULL);
+            if (code != 200 && code != 220)
+            {
+                LOGE("PBSZ 0 fail[code = %d].", code);
+                ftp_err = FTP_ERR_LOGIN_DENIED;
+                goto login_fail;
+            }
+        }
+
+        len = snprintf(cmd_buff, 50, "USER %s\r\n", user->name);
+        cmd_buff[len] = '\0';
+        code = ftp_cmd_handle(info, cmd_buff, NULL, NULL);
+        if (code == 331) // USER is 331
+        {
+            len = snprintf(cmd_buff, 50, "PASS %s\r\n", user->pass);
+            cmd_buff[len] = '\0';
+            code = ftp_cmd_handle(info, cmd_buff, NULL, NULL);
+        }
+
+        if (code / 100 == 2)   // USER/PASS is 2xx
+        {
+            info->state = FTP_STATE_READY;
+            LOGI("FTP logn in success.");
+            return FTP_ERR_SUCCESS;
+        }
+        else if (code == 332)   // // USER/PASS is 332
+        {
+            LOGW("Should set ACCT.");
+            ftp_err = FTP_ERR_UNKNOWN;
+            goto login_fail;
+        }
+        else
+        {
+            LOGE("FTP login denied[code = %d].", code);
+            ftp_err = FTP_ERR_LOGIN_DENIED;
+            goto login_fail;
+        }
+    }
+    else
+    {
+        LOGE("FTP code error[%d].", code);
+        ftp_err = FTP_ERR_UNKNOWN;
+        goto login_fail;
+    }
+
+login_fail:
+    info->state = FTP_STATE_NON;
+    return ftp_err;
+}
+
+static mbtk_ftp_info_s* ftp_info_find(mbtk_ftp_handle handle)
+{
+    if (!ftp_client_list)
+    {
+        LOGE("FTP Client List not init.");
+        return NULL;
+    }
+
+    mbtk_ftp_info_s *result = NULL;
+    list_first(ftp_client_list);
+    while ((result = (mbtk_ftp_info_s*) list_next(ftp_client_list)))
+    {
+        if (result->handle == handle)
+            break;
+    }
+
+    return result;
+}
+
+mbtk_ftp_handle mbtk_ftp_upload_end(mbtk_ftp_handle handle)
+{
+    mbtk_ftp_info_s *info = ftp_info_find(handle);
+    if (!info)
+    {
+        printf("No such FTP handle:%d\n", handle);
+    }
+    
+    char buff[1024]={0};
+    int err=0;
+    int len=0;
+    int result;
+
+    if (info->sock_info[FTP_SOCK_DATA].fd > 0)
+    {
+        if (sock_close(&info->net_info, &info->sock_info[FTP_SOCK_DATA], FTP_TIMEOUT, &err))
+        {
+            LOGE("Close data socket fail[%d].", err);
+            result = FTP_ERR_NET_CLOSE;
+        }
+        else
+        {
+        //    info->sock_info[FTP_SOCK_DATA].fd = -1;
+        }
+    }
+    if(info->auth_type != 0)
+    {
+        if(mbtk_sock_close(info->ftp_ssl_handle,info->session_data,6000,&err))
+        {
+            LOGE("Close ssl data socket fail[%d].", err);
+            result = FTP_ERR_NET_CLOSE;
+        }
+        else
+        {
+        //    info->sock_info[FTP_SOCK_DATA].fd = -1;
+        }
+    }
+    info->data_mode = FTP_MODE_PASSIVE;
+    info->is_data_sock_busy = false;
+    info->file_trans.size_count = 0;
+    info->file_trans.size_send = 0;
+    
+    char line_buf[1024];
+    if(info->auth_type != 0)
+        len = mbtk_sock_read(info->ftp_ssl_handle,info->session,buff,sizeof(buff),FTP_TIMEOUT,&err);
+    else
+        len = sock_readline(&info->net_info, &info->sock_info[FTP_SOCK_CTRL], buff, 1024, FTP_TIMEOUT, &err);
+    if(len > 0)
+    {
+        printf("\n%s\n",buff);
+        if(err != 0)
+            printf("socket read error: %d\n",err);
+    }
+    else
+    {
+        printf("socket_read error:%d\n",err);
+        result = -1;
+    }
+
+    return result;
+}
+
+static mbtk_ftp_handle ftp_next_handle()
+{
+    if (!ftp_client_list)
+    {
+        LOGE("FTP Client List not init.");
+        return -1;
+    }
+
+    mbtk_ftp_info_s *info = NULL;
+    mbtk_ftp_handle handle = 1;
+    bool used;
+    while (handle <= FTP_HANDLE_MAX)
+    {
+        used = false;
+        // This handle used?
+        list_first(ftp_client_list);
+        while ((info = (mbtk_ftp_info_s*) list_next(ftp_client_list)))
+        {
+            if (handle == info->handle)
+            {
+                used = true;
+                break;
+            }
+        }
+
+        if (!used)
+        {
+            break;
+        }
+
+        handle++;
+    }
+
+    LOGI("Get free handle:%d", handle);
+
+    return handle;
+}
+
+static void ftp_free_func(void *data)
+{
+    if (data)
+    {
+        mbtk_ftp_info_s *info = (mbtk_ftp_info_s*) data;
+        LOGI("Free FTP client[handle = %d].", info->handle);
+        free(info);
+    }
+}
+
+/*************************************************************
+ Public Function Definitions
+ *************************************************************/
+mbtk_ftp_handle mbtk_ftp_init(const void* host, uint16 port, mbtk_ftp_auth_type_enum auth_type,
+                              bool is_ipv6, bool use_cert)
+{
+    if (!ftp_client_list)
+        ftp_client_list = list_create(ftp_free_func);
+
+    if (!ftp_client_list)
+    {
+        LOGE("FTP Client List not init.");
+        return -1;
+    }
+
+    if (list_size(ftp_client_list) > FTP_HANDLE_MAX)
+    {
+        LOGE("FTP client is full.");
+        return -1;
+    }
+
+    if (host == NULL || strlen(host) == 0)
+    {
+        LOGE("Host error.");
+        return -1;
+    }
+
+    if (port == 0)
+    {
+        port = FTP_SERVICE_PORT_DEF;
+    }
+
+    mbtk_ftp_info_s *info = (mbtk_ftp_info_s*) malloc(sizeof(mbtk_ftp_info_s));
+    if (!info)
+    {
+        LOGE("malloc() fail.");
+        return -1;
+    }
+    memset(info, 0x0, sizeof(mbtk_ftp_info_s));
+    list_add(ftp_client_list, info);
+    memcpy(info->sock_info[FTP_SOCK_CTRL].host, host, strlen(host));
+    info->sock_info[FTP_SOCK_CTRL].port = port;
+    // info->auth_type == FTP_AUTH_TYPE_IMPLICIT, info->is_ipv6 ? MBTK_ADDR_IPV6 : MBTK_ADDR_IPV4,info->use_cert,
+    info->sock_info[FTP_SOCK_CTRL].is_ssl = (auth_type == FTP_AUTH_TYPE_IMPLICIT);
+    info->sock_info[FTP_SOCK_CTRL].addr_family = is_ipv6 ? MBTK_ADDR_IPV6: MBTK_ADDR_IPV4;
+    info->sock_info[FTP_SOCK_CTRL].use_cert = use_cert;
+
+    info->handle = ftp_next_handle();
+    info->state = FTP_STATE_NON;
+    info->data_mode = FTP_MODE_PASSIVE;
+    info->sock_info[FTP_SOCK_CTRL].fd = -1;
+    info->sock_info[FTP_SOCK_DATA].fd = -1;
+    //info->is_ipv6 = is_ipv6;
+    info->auth_type = auth_type;
+    //info->use_cert = use_cert;
+
+    LOGI("FTP info#host:%s,port:%d,ipv6:%d,ssl:%d,cert:%d",info->sock_info[FTP_SOCK_CTRL].host,
+            info->sock_info[FTP_SOCK_CTRL].port,
+            is_ipv6,info->auth_type,use_cert);
+
+    if(auth_type != FTP_AUTH_TYPE_NON)
+    {
+        info->ftp_ssl_handle = mbtk_sock_init(&info->ftp_ssl_info);
+        if(info->ftp_ssl_handle == -1)
+        {
+            printf("mbtk_sock_init error\n");
+        }
+        
+        info->ftp_sock_ssl_info = (mbtk_sock_info*) malloc(sizeof(mbtk_sock_info));
+        memset(info->ftp_sock_ssl_info, 0x0, sizeof(mbtk_sock_info));
+        info->ftp_sock_ssl_info->is_support_ssl = (auth_type != FTP_AUTH_TYPE_NON);
+        info->ftp_sock_ssl_info->ftp_ssl_support = 1;
+        info->ftp_sock_ssl_info->port = port;
+        info->ftp_sock_ssl_info->type = MBTK_SOCK_TCP;
+        info->ftp_sock_ssl_info->ingnore_cert = ~use_cert;
+        memcpy(info->ftp_sock_ssl_info->address, host, strlen(host));
+    }
+    return info->handle;
+}
+
+mbtk_ftp_error_enum mbtk_ftp_reconfig(mbtk_ftp_handle handle,const void* host, uint16 port, mbtk_ftp_auth_type_enum auth_type,
+                                      bool is_ipv6, bool use_cert)
+{
+    mbtk_ftp_info_s *info = ftp_info_find(handle);
+    if (!info)
+    {
+        LOGE("No such FTP handle:%d", handle);
+        return FTP_ERR_UNKNOWN;
+    }
+    if(auth_type != FTP_AUTH_TYPE_NON)
+    {
+        info->ftp_sock_ssl_info = (mbtk_sock_info*) malloc(sizeof(mbtk_sock_info));
+        memset(info->ftp_sock_ssl_info, 0x0, sizeof(mbtk_sock_info));
+        if(host && strlen(host) > 0)
+        {
+            memcpy(info->ftp_sock_ssl_info->address, host, strlen(host));
+            info->ftp_sock_ssl_info->address[strlen(host)] = '\0';
+        }
+        info->ftp_sock_ssl_info->is_support_ssl = (auth_type != FTP_AUTH_TYPE_NON);
+        info->ftp_sock_ssl_info->ftp_ssl_support = 1;
+        info->ftp_sock_ssl_info->port = port;
+        info->ftp_sock_ssl_info->ingnore_cert = ~use_cert;
+        info->ftp_sock_ssl_info->type = MBTK_SOCK_TCP;
+    }
+    if(info->state == FTP_STATE_NON)
+    {
+        if(host && strlen(host) > 0)
+        {
+            memcpy(info->sock_info[FTP_SOCK_CTRL].host, host, strlen(host));
+            info->sock_info[FTP_SOCK_CTRL].host[strlen(host)] = '\0';
+        }
+        info->sock_info[FTP_SOCK_CTRL].port = port;
+        info->sock_info[FTP_SOCK_CTRL].addr_family = is_ipv6 ? MBTK_ADDR_IPV6 : MBTK_ADDR_IPV4;
+        info->auth_type = auth_type;
+        info->sock_info[FTP_SOCK_CTRL].use_cert = use_cert;
+
+        return FTP_ERR_SUCCESS;
+    }
+    else
+    {
+        LOGE("Not reset FTP config.The state is %d", info->state);
+        return FTP_ERR_UNKNOWN;
+    }
+}
+
+mbtk_ftp_error_enum mbtk_ftp_user_set(mbtk_ftp_handle handle,void* user,void* pass)
+{
+    mbtk_ftp_info_s *info = ftp_info_find(handle);
+    if (!info)
+    {
+        LOGE("No such FTP handle:%d", handle);
+        return FTP_ERR_UNKNOWN;
+    }
+
+    if(info->state == FTP_STATE_NON && !str_empty(user) && !str_empty(pass))
+    {
+        memset(&info->user,0x0,sizeof(mbtk_ftp_user_info_s));
+        memcpy(info->user.name, user, strlen(user));
+        memcpy(info->user.pass, pass, strlen(pass));
+        return FTP_ERR_SUCCESS;
+    }
+    else
+    {
+        LOGE("Not reset FTP config.The state is %d", info->state);
+        return FTP_ERR_UNKNOWN;
+    }
+}
+
+mbtk_ftp_error_enum mbtk_ftp_deinit(mbtk_ftp_handle handle)
+{
+    mbtk_ftp_info_s *info = ftp_info_find(handle);
+    if (!info)
+    {
+        LOGE("No such FTP handle:%d", handle);
+        return FTP_ERR_UNKNOWN;
+    }
+
+    if (list_remove(ftp_client_list, info))
+    {
+        int err;
+        sock_close(&info->net_info, &info->sock_info[FTP_SOCK_CTRL], FTP_TIMEOUT, &err);
+        sock_close(&info->net_info, &info->sock_info[FTP_SOCK_DATA], FTP_TIMEOUT, &err);
+        ftp_free_func(info);
+    }
+
+    return FTP_ERR_SUCCESS;
+}
+
+/*
+ * Quit FTP service.
+ */
+mbtk_ftp_error_enum mbtk_ftp_quit(mbtk_ftp_handle handle)
+{
+    mbtk_ftp_info_s *info = ftp_info_find(handle);
+    if (!info)
+    {
+        LOGE("No such FTP handle:%d", handle);
+        return FTP_ERR_UNKNOWN;
+    }
+
+    int err = 0;
+    if(info->sock_info[FTP_SOCK_CTRL].fd > 0) {
+        mbtk_ftp_error_enum ftp_err = ftp_cmd_process(info, FTP_CMD_QUIT, NULL,
+                                      NULL);
+        if (ftp_err != FTP_ERR_SUCCESS)
+        {
+            LOGE("FTP QUIT fail[%d].", ftp_err);
+            //return ftp_err;
+        }
+
+        // FTP quit success.
+        info->state = FTP_STATE_CONNECTED;
+    }
+
+    if (sock_close(&info->net_info, &info->sock_info[FTP_SOCK_CTRL], FTP_TIMEOUT, &err))
+    {
+        LOGE("Close ctrl socket fail[%d].", err);
+        return FTP_ERR_NET_CLOSE;
+    }
+
+    if (sock_close(&info->net_info, &info->sock_info[FTP_SOCK_DATA], FTP_TIMEOUT, &err))
+    {
+        LOGE("Close data socket fail[%d].", err);
+        return FTP_ERR_NET_CLOSE;
+    }
+
+    info->state = FTP_STATE_NON;
+    info->data_mode = FTP_MODE_PASSIVE;
+    memset(&info->file_trans, 0x0, sizeof(mbtk_ftp_file_trans_info_s));
+    info->sock_info[FTP_SOCK_CTRL].fd = -1;
+    info->sock_info[FTP_SOCK_DATA].fd = -1;
+
+    return FTP_ERR_SUCCESS;
+}
+
+mbtk_ftp_error_enum mbtk_ftp_net_close(mbtk_ftp_handle handle)
+{
+    mbtk_ftp_info_s *info = ftp_info_find(handle);
+    if (!info)
+    {
+        LOGE("No such FTP handle:%d", handle);
+        return FTP_ERR_UNKNOWN;
+    }
+
+    if(info->net_info.net_id > 0) {
+        int err;
+        if(sock_net_close(&info->net_info, FTP_TIMEOUT,&err))
+        {
+            LOGE("sock_net_close() fail[%ld].",err);
+            return FTP_ERR_NET_CLOSE;
+        }
+
+        info->net_info.net_id = -1;
+    }
+
+    return FTP_ERR_SUCCESS;
+}
+
+
+mbtk_ftp_info_s* mbtk_ftp_info_get(mbtk_ftp_handle handle)
+{
+    mbtk_ftp_info_s *info = ftp_info_find(handle);
+    if (!info)
+    {
+        LOGE("No such FTP handle:%d", handle);
+        return NULL;
+    }
+
+    return info;
+}
+
+/*
+ * Login specified FTP service.
+ */
+mbtk_ftp_error_enum mbtk_ftp_login(mbtk_ftp_handle handle, void *name,
+                                   void *pass)
+{
+    mbtk_ftp_info_s *info = ftp_info_find(handle);
+    if (!info)
+    {
+        LOGE("No such FTP handle:%d", handle);
+        return FTP_ERR_UNKNOWN;
+    }
+
+    if (info->state == FTP_STATE_NON)
+    {
+        mbtk_ftp_user_info_s user;
+        memset(&user,0x0,sizeof(mbtk_ftp_user_info_s));
+        if (!str_empty(name) && !str_empty(pass))
+        {
+            memcpy(user.name, name, strlen(name));
+            memcpy(user.pass, pass, strlen(pass));
+            memcpy(info->user.name, name, strlen(name));
+            memcpy(info->user.pass, pass, strlen(pass));
+        }
+        else
+        {
+            memcpy(user.name, FTP_ANONYMOUS_USER, strlen(FTP_ANONYMOUS_USER));
+            memcpy(user.pass, FTP_ANONYMOUS_PASS, strlen(FTP_ANONYMOUS_PASS));
+            memcpy(info->user.name, FTP_ANONYMOUS_USER, strlen(FTP_ANONYMOUS_USER));
+            memcpy(info->user.pass, FTP_ANONYMOUS_PASS, strlen(FTP_ANONYMOUS_PASS));
+        }
+
+        LOGI("FTP login#user:%s,pass:%s",user.name,user.pass);
+        
+        if(info->auth_type != 0)
+        {
+            int mbtk_errno=-1;
+            info->session = mbtk_sock_open(info->ftp_ssl_handle,info->ftp_sock_ssl_info,60000,&mbtk_errno);
+            if(mbtk_errno!=0)
+            {
+                printf("mbtk_sock_open error : %d\n",mbtk_errno);
+            }
+            else
+            {
+                unsigned char mbtk_ftp_ssl_read_buf[16384 + 1];
+                char cmd[50];
+                memset(cmd,0,50);
+                int len_ssl;
+                
+
+                memset(cmd,0,50);
+                snprintf(cmd, 50, "PBSZ 0\r\n");
+                mbtk_sock_write(info->ftp_ssl_handle,info->session,
+                                    cmd,
+                                    sizeof(cmd),
+                                    60000,
+                                    &mbtk_errno);
+                memset(mbtk_ftp_ssl_read_buf,0,sizeof(mbtk_ftp_ssl_read_buf));
+                mbtk_sock_read(info->ftp_ssl_handle,info->session,
+                            mbtk_ftp_ssl_read_buf,
+                            sizeof(mbtk_ftp_ssl_read_buf),
+                            60000,
+                            &mbtk_errno);
+                printf("\nmbtk_sock_read PBSZ 0:\n%s\n",mbtk_ftp_ssl_read_buf);
+                memset(cmd,0,50);
+                snprintf(cmd, 50, "PROT P\r\n");
+                mbtk_sock_write(info->ftp_ssl_handle,info->session,
+                                    cmd,
+                                    sizeof(cmd),
+                                    60000,
+                                    &mbtk_errno);
+                memset(mbtk_ftp_ssl_read_buf,0,sizeof(mbtk_ftp_ssl_read_buf));
+                mbtk_sock_read(info->ftp_ssl_handle,info->session,
+                            mbtk_ftp_ssl_read_buf,
+                            sizeof(mbtk_ftp_ssl_read_buf),
+                            60000,
+                            &mbtk_errno);
+                printf("\nmbtk_sock_read PROT P:\n%s\n",mbtk_ftp_ssl_read_buf);
+                memset(cmd,0,50);
+                snprintf(cmd, 50, "USER %s\r\n",info->user.name);
+                mbtk_sock_write(info->ftp_ssl_handle,info->session,
+                                    cmd,
+                                    sizeof(cmd),
+                                    60000,
+                                    &mbtk_errno);
+                memset(mbtk_ftp_ssl_read_buf,0,sizeof(mbtk_ftp_ssl_read_buf));
+                mbtk_sock_read(info->ftp_ssl_handle,info->session,
+                            mbtk_ftp_ssl_read_buf,
+                            sizeof(mbtk_ftp_ssl_read_buf),
+                            60000,
+                            &mbtk_errno);
+                printf("\nmbtk_sock_read USER:\n%s\n",mbtk_ftp_ssl_read_buf);
+                memset(cmd,0,50);
+                snprintf(cmd, 50, "PASS %s\r\n",info->user.pass);
+                mbtk_sock_write(info->ftp_ssl_handle,info->session,
+                                    cmd,
+                                    sizeof(cmd),
+                                    60000,
+                                    &mbtk_errno);
+                memset(mbtk_ftp_ssl_read_buf,0,sizeof(mbtk_ftp_ssl_read_buf));
+                mbtk_sock_read(info->ftp_ssl_handle,info->session,
+                            mbtk_ftp_ssl_read_buf,
+                            sizeof(mbtk_ftp_ssl_read_buf),
+                            60000,
+                            &mbtk_errno);
+                printf("\nmbtk_sock_read PASS:\n%s\n",mbtk_ftp_ssl_read_buf);
+                char *ptr = NULL;
+                if((ptr = strstr(mbtk_ftp_ssl_read_buf,"220 ")) || (ptr = strstr(mbtk_ftp_ssl_read_buf,"230 "))) {
+                    LOGI("RSP:%s",ptr);
+                    printf("RSP:%s\n",ptr);
+                }
+                else
+                {
+                    printf("\nptr error.\n");
+                    return FTP_ERR_UNKNOWN;
+                }
+                int code = atoi(ptr);
+                if (code / 100 == 2)   // USER/PASS is 2xx
+                {
+                    info->state = FTP_STATE_READY;
+                    LOGI("FTP logn in success.");
+                    printf("FTP logn in success.\n");
+                }
+                else if (code == 332)   // // USER/PASS is 332
+                {
+                    LOGW("Should set ACCT.");
+                    printf("Should set ACCT.\n");
+                    return FTP_ERR_UNKNOWN;
+                }
+                else
+                {
+                    LOGE("FTP login denied[code = %d].", code);
+                    printf("FTP login denied[code = %d].\n", code);
+                    return FTP_ERR_UNKNOWN;
+                }
+                
+                memset(cmd,0,50);
+                snprintf(cmd, 50, "PWD\r\n");
+                mbtk_sock_write(info->ftp_ssl_handle,info->session,
+                                    cmd,
+                                    sizeof(cmd),
+                                    60000,
+                                    &mbtk_errno);
+                memset(mbtk_ftp_ssl_read_buf,0,sizeof(mbtk_ftp_ssl_read_buf));
+                mbtk_sock_read(info->ftp_ssl_handle,info->session,
+                            mbtk_ftp_ssl_read_buf,
+                            sizeof(mbtk_ftp_ssl_read_buf),
+                            60000,
+                            &mbtk_errno);
+                printf("\nmbtk_sock_read PWD:\n%s\n",mbtk_ftp_ssl_read_buf);
+            }
+            return mbtk_errno;
+        }
+        
+        return ftp_login(info,&user);
+    }
+    else
+    {
+        LOGD("Has login.");
+        return FTP_ERR_SUCCESS;
+    }
+}
+
+/*
+ * Get current directory's path.
+ */
+mbtk_ftp_error_enum mbtk_ftp_pwd(mbtk_ftp_handle handle, void *path)
+{
+    if (!path)
+    {
+        LOGE("No set path");
+        return FTP_ERR_PARAM_SET;
+    }
+    mbtk_ftp_info_s *info = ftp_info_find(handle);
+    if (!info)
+    {
+        LOGE("No such FTP handle:%d", handle);
+        return FTP_ERR_UNKNOWN;
+    }
+
+    return ftp_cmd_process(info, FTP_CMD_PWD, NULL, path);
+}
+
+/*
+ * Go to specified directory.
+ */
+mbtk_ftp_error_enum mbtk_ftp_cd(mbtk_ftp_handle handle, void *path)
+{
+    if (!path)
+    {
+        LOGE("No set path");
+        return FTP_ERR_PARAM_SET;
+    }
+    mbtk_ftp_info_s *info = ftp_info_find(handle);
+    if (!info)
+    {
+        LOGE("No such FTP handle:%d", handle);
+        return FTP_ERR_UNKNOWN;
+    }
+
+    return ftp_cmd_process(info, FTP_CMD_CWD, path, NULL);
+}
+
+/*
+ * Get the native ip and free port.
+ */
+mbtk_ftp_error_enum mbtk_ftp_get_ip_and_port(char *ipBuf_out,
+                                    int *port,int iptype)
+{
+    char psz_port_cmd[128];
+    int i=0;
+
+    *port = rand() % (60000 - 50000 + 1) + 50000;
+    sprintf(psz_port_cmd, "netstat -an | grep :%d > /dev/null", *port);
+
+        char ipBuf[32] = "";
+        FILE *fstream=NULL;
+
+        char buff[1024];  
+        char iptype_str[8];
+        memset(buff,0,sizeof(buff));    
+        /*eth0可以换成eth1、docker0、em1、lo等*/
+        if(iptype == MBTK_ADDR_IPV6)
+        {
+            
+            if(NULL==(fstream=popen("ifconfig ccinet0 | grep \"inet6 addr: 2\" | awk '{print $3}'","r")))    
+            {   
+                snprintf(ipBuf, 39, "%s","0:0:0:0:0:0:0:0");  
+            }       
+            if(NULL!=fgets(buff, sizeof(buff), fstream))   
+            {   
+                snprintf(ipBuf, 39, "%s",buff);  
+            }   
+            else  
+            {  
+                snprintf(ipBuf, 39, "%s","0:0:0:0:0:0:0:0");  
+                pclose(fstream);  
+            }
+        }
+        else
+        {
+            if(NULL==(fstream=popen("ifconfig ccinet0 | grep \"inet addr:\" | awk \'{print $2}\' | cut -c 6-","r")))    
+            {   
+                snprintf(ipBuf, 18, "%s","0.0.0.0");  
+            }       
+            if(NULL!=fgets(buff, sizeof(buff), fstream))   
+            {   
+                snprintf(ipBuf, 18, "%s",buff);  
+            }   
+            else  
+            {  
+                snprintf(ipBuf, 18, "%s","0.0.0.0");  
+                pclose(fstream);  
+            }  
+        }
+            pclose(fstream);  
+            
+        printf("ip:%s\n", ipBuf);
+        memcpy(ipBuf_out, ipBuf, 32);
+        return 0;
+}
+/*
+ * Get current directory's subdirectory.
+ */
+mbtk_ftp_error_enum mbtk_ftp_dir_ls(mbtk_ftp_handle handle,
+                                    mbtk_ftp_file_info_s *list_head)
+{
+    if (!list_head)
+    {
+        LOGE("No set file list.");
+        return FTP_ERR_PARAM_SET;
+    }
+    mbtk_ftp_info_s *info = ftp_info_find(handle);
+    if (!info)
+    {
+        LOGE("No such FTP handle:%d", handle);
+        return FTP_ERR_UNKNOWN;
+    }
+
+    return ftp_cmd_process(info, FTP_CMD_LIST, NULL, list_head);
+}
+
+/*
+ * Get specified file's size.
+ */
+uint32 mbtk_ftp_file_size(mbtk_ftp_handle handle, void *path)
+{
+    if (!path)
+    {
+        LOGE("No set path");
+        return 0;
+    }
+    mbtk_ftp_info_s *info = ftp_info_find(handle);
+    if (!info)
+    {
+        LOGE("No such FTP handle:%d", handle);
+        return 0;
+    }
+    uint32 size = 0;
+    if (ftp_cmd_process(info, FTP_CMD_SIZE, path, &size) != FTP_ERR_SUCCESS)
+        return 0;
+
+    return size;
+}
+
+/*
+ * Get specified file's modify time.
+ */
+mbtk_ftp_error_enum mbtk_ftp_file_time(mbtk_ftp_handle handle, void *path,
+                                       void *time)
+{
+    if (!path)
+    {
+        LOGE("No set path");
+        return FTP_ERR_PARAM_SET;
+    }
+
+    mbtk_ftp_info_s *info = ftp_info_find(handle);
+    if (!info)
+    {
+        LOGE("No such FTP handle:%d", handle);
+        return FTP_ERR_UNKNOWN;
+    }
+
+    return ftp_cmd_process(info, FTP_CMD_MDTM, path, time);
+}
+
+/*
+ * Delete specified file.
+ */
+mbtk_ftp_error_enum mbtk_ftp_file_del(mbtk_ftp_handle handle, void *path)
+{
+    if (!path)
+    {
+        LOGE("No set path");
+        return FTP_ERR_PARAM_SET;
+    }
+    mbtk_ftp_info_s *info = ftp_info_find(handle);
+    if (!info)
+    {
+        LOGE("No such FTP handle:%d", handle);
+        return FTP_ERR_UNKNOWN;
+    }
+
+    return ftp_cmd_process(info, FTP_CMD_DELE, path, NULL);
+}
+
+/*
+ * Create specified directory.
+ */
+mbtk_ftp_error_enum mbtk_ftp_dir_mkdir(mbtk_ftp_handle handle, void *path)
+{
+    if (!path)
+    {
+        LOGE("No set path");
+        return FTP_ERR_PARAM_SET;
+    }
+    mbtk_ftp_info_s *info = ftp_info_find(handle);
+    if (!info)
+    {
+        LOGE("No such FTP handle:%d", handle);
+        return FTP_ERR_UNKNOWN;
+    }
+
+    return ftp_cmd_process(info, FTP_CMD_MKD, path, NULL);
+}
+
+/*
+ * Delete specified directory.
+ */
+mbtk_ftp_error_enum mbtk_ftp_dir_rmdir(mbtk_ftp_handle handle, void *path)
+{
+    if (!path)
+    {
+        LOGE("No set path");
+        return FTP_ERR_PARAM_SET;
+    }
+    mbtk_ftp_info_s *info = ftp_info_find(handle);
+    if (!info)
+    {
+        LOGE("No such FTP handle:%d", handle);
+        return FTP_ERR_UNKNOWN;
+    }
+
+    return ftp_cmd_process(info, FTP_CMD_RMD, path, NULL);
+}
+
+/*
+ * Set data type.
+ */
+mbtk_ftp_error_enum mbtk_ftp_data_type_set(mbtk_ftp_handle handle,
+        mbtk_ftp_data_type_enum data_type)
+{
+    mbtk_ftp_info_s *info = ftp_info_find(handle);
+    if (!info)
+    {
+        LOGE("No such FTP handle:%d", handle);
+        return FTP_ERR_UNKNOWN;
+    }
+
+    return ftp_cmd_process(info, FTP_CMD_TYPE, &data_type, NULL);
+}
+
+/*
+ * Set FTP mode.
+ */
+mbtk_ftp_error_enum mbtk_ftp_mode_set(mbtk_ftp_handle handle,
+                                      mbtk_ftp_mode_enum mode)
+{
+    mbtk_ftp_info_s *info = ftp_info_find(handle);
+    if (!info)
+    {
+        LOGE("No such FTP handle:%d", handle);
+        return FTP_ERR_UNKNOWN;
+    }
+
+    info->data_mode = mode;
+
+    return FTP_ERR_SUCCESS;
+}
+
+uint32 mbtk_ftp_download_start(mbtk_ftp_handle handle, void *remote_path,
+                               void *local_path, mbtk_data_cb_func data_cb)
+{
+    if (!remote_path || (local_path && data_cb) || (!local_path && !data_cb))
+    {
+        LOGE("Param set error.");
+        return 0;
+    }
+
+    mbtk_ftp_info_s *info = ftp_info_find(handle);
+    if (!info)
+    {
+        LOGE("No such FTP handle:%d", handle);
+        return 0;
+    }
+
+    if (info->state >= FTP_STATE_READY && !info->is_trans)
+    {
+        int retry_time = 0;
+        memset(&info->file_trans, 0x0, sizeof(mbtk_ftp_file_trans_info_s));
+
+        // Set data type to "I"
+        if (FTP_ERR_SUCCESS
+            != mbtk_ftp_data_type_set(handle, FTP_DATA_TYPE_I))
+        {
+            LOGE("Set data type to I fail.");
+            return 0;
+        }
+
+        // Get file size.
+        info->file_trans.size_count = mbtk_ftp_file_size(handle, remote_path);
+        if (info->file_trans.size_count > 0)
+        {
+            LOGI("File size:%d", info->file_trans.size_count);
+        }
+        else
+        {
+            LOGE("File not exist.");
+            return 0;
+        }
+
+        //Get file modify time.
+        if (FTP_ERR_SUCCESS
+            != mbtk_ftp_file_time(handle, remote_path,
+                                  (char*) info->file_trans.modify_time))
+        {
+            LOGE("Get file modify time fail.");
+            return 0;
+        }
+
+        memcpy(info->file_trans.remote_name, remote_path,
+               strlen((char*) remote_path));
+        if (local_path)
+        {
+            memcpy(info->file_trans.local_name, local_path,
+                   strlen((char*) local_path));
+            info->file_trans.data_cb = NULL;
+        }
+        else
+        {
+            info->file_trans.data_cb = data_cb;
+        }
+        info->file_trans.size_send = 0;
+        info->file_trans.is_download = true;
+        info->file_trans.fd = -1;
+        info->is_trans = true;
+
+        if (!info->file_trans.data_cb)   // Save to efs
+        {
+            info->file_trans.fd = file_open((const char*)info->file_trans.local_name,
+                                            O_WRONLY | O_CREAT | O_TRUNC);
+            if (info->file_trans.fd <= 0)
+            {
+                LOGE("Can not open EFS file[%s].", info->file_trans.local_name);
+                return 0;
+            }
+        }
+
+        do {
+            // Start download file.
+            if (FTP_ERR_SUCCESS
+                != ftp_data_sock_read(info, FTP_CMD_GET, NULL, NULL))
+            {
+                LOGW("ftp_data_sock_read() fail.");
+            }
+
+            if(info->sock_info[FTP_SOCK_CTRL].fd <= 0) {
+                // Download fail.
+                LOGW("Ctrl socket error,should login angin.");
+                break;
+            } else if (info->file_trans.size_send == info->file_trans.size_count) {
+                // Download success
+                break;
+            }
+
+            // Should redownload without quit.
+            char time[20] = { 0 };
+            if (FTP_ERR_SUCCESS
+                != mbtk_ftp_file_time(handle, info->file_trans.remote_name,
+                                      time))
+            {
+                LOGE("Get file modify time fail.");
+                break;
+            }
+
+            if (strcmp(time, (char*) info->file_trans.modify_time))
+            {
+                LOGW("Service file changed.");
+                break;
+            }
+
+            retry_time++;
+        } while(retry_time < 5);
+
+
+        if (!info->file_trans.data_cb && info->file_trans.fd > 0)   // Save to efs
+        {
+            if (file_close(info->file_trans.fd))
+            {
+                LOGE("EFS close fail.");
+                return 0;
+            }
+        }
+
+        // Download success.
+        if (info->file_trans.size_send == info->file_trans.size_count)
+        {
+            LOGI("Download %s success[%d].", (char* )remote_path,
+                 info->file_trans.size_count);
+
+            // Reset download configs.
+            info->is_trans = false;
+            // memset(&info->file_trans, 0x0, sizeof(mbtk_ftp_file_trans_info_s));
+        }
+        else
+        {
+            LOGW("Download %s fail[%d / %d].", (char* )remote_path,
+                 info->file_trans.size_send, info->file_trans.size_count);
+        }
+
+        return info->file_trans.size_send;
+    }
+    else
+    {
+        LOGE("FTP state error[%d].", info->state);
+        return 0;
+    }
+}
+
+uint32 mbtk_ftp_download_continue(mbtk_ftp_handle handle)
+{
+    mbtk_ftp_info_s *info = ftp_info_find(handle);
+    if (!info)
+    {
+        LOGE("No such FTP handle:%d", handle);
+        return 0;
+    }
+
+    if(info->state == FTP_STATE_NON) {
+        if(FTP_ERR_SUCCESS != ftp_login(info,&info->user)) {
+            LOGE("FTP login fail.");
+            return 0;
+        }
+    }
+
+    if (info->state >= FTP_STATE_READY && info->is_trans
+        && info->file_trans.is_download
+        && info->file_trans.size_send < info->file_trans.size_count)
+    {
+        // Set data type to "I"
+        if (FTP_ERR_SUCCESS
+            != mbtk_ftp_data_type_set(handle, FTP_DATA_TYPE_I))
+        {
+            LOGE("Set data type to I fail.");
+            return 0;
+        }
+
+        // Get file size.
+        uint32 size = mbtk_ftp_file_size(handle, info->file_trans.remote_name);
+        if (size > 0)
+        {
+            LOGI("File size:%d", size);
+        }
+        else
+        {
+            LOGE("File not exist.");
+            return 0;
+        }
+        if (size != info->file_trans.size_count)
+        {
+            LOGW("Service file changed.");
+            return 0;
+        }
+
+        //Get file modify time.
+        char time[20] = { 0 };
+        if (FTP_ERR_SUCCESS
+            != mbtk_ftp_file_time(handle, info->file_trans.remote_name,
+                                  time))
+        {
+            LOGE("Get file modify time fail.");
+            return 0;
+        }
+        if (strcmp(time, (char*) info->file_trans.modify_time))
+        {
+            LOGW("Service file changed.");
+            return 0;
+        }
+
+        if (!info->file_trans.data_cb)   // Save to efs
+        {
+            info->file_trans.fd = file_open((const char*)info->file_trans.local_name,
+                                            O_WRONLY | O_CREAT | O_APPEND);
+            if (info->file_trans.fd <= 0)
+            {
+                LOGE("Can not open EFS file[%s].", info->file_trans.local_name);
+                return FTP_ERR_EFS_FILE;
+            }
+        }
+
+        uint32 size_last = info->file_trans.size_send;
+        // Start download file.
+        if (FTP_ERR_SUCCESS
+            != ftp_data_sock_read(info, FTP_CMD_GET, NULL, NULL))
+        {
+            LOGW("Data download fail.");
+        }
+
+        if (!info->file_trans.data_cb && info->file_trans.fd > 0)
+        {
+            if (file_close(info->file_trans.fd))
+            {
+                LOGE("EFS close fail.");
+                return 0;
+            }
+        }
+
+        // Download success.
+        if (info->file_trans.size_send == info->file_trans.size_count)
+        {
+            LOGI("Download %s success[%d].", info->file_trans.remote_name,
+                 info->file_trans.size_count);
+
+            // Reset download configs.
+            info->is_trans = false;
+            //memset(&info->file_trans, 0x0, sizeof(mbtk_ftp_file_trans_info_s));
+        }
+        else
+        {
+            LOGW("Download %s fail[%d / %d].", info->file_trans.remote_name,
+                 info->file_trans.size_send, info->file_trans.size_count);
+        }
+
+        return info->file_trans.size_send - size_last;
+    }
+    else
+    {
+        LOGE("FTP state error[%d].", info->state);
+        return 0;
+    }
+}
+
+/*
+* Upload EFS:  local_path is efs path;size_byte is 0.
+* Upload data: local_path is NULL;size_byte is data size.
+*/
+int mbtk_ftp_upload_start(mbtk_ftp_handle handle, const void *remote_path,
+                          const void *local_path, uint32 size_byte)
+{
+    if (!remote_path || (size_byte == 0 && !local_path)
+        || (size_byte > 0 && local_path))
+    {
+        LOGE("Param set error.");
+        return -1;
+    }
+
+	int result = 0;
+    mbtk_ftp_info_s *info = ftp_info_find(handle);
+    if (!info)
+    {
+        LOGE("No such FTP handle:%d", handle);
+        return -1;
+    }
+
+    LOGE("info->state:%d, info->is_trans:%d", info->state,info->is_trans);
+
+    if (info->state >= FTP_STATE_READY && !info->is_trans)
+    {
+		        // Set data type to "I"
+        if (FTP_ERR_SUCCESS
+            != mbtk_ftp_data_type_set(handle, FTP_DATA_TYPE_I))
+        {
+            LOGE("Set data type to I fail.");
+            return -1;
+        }
+	
+        memset(&info->file_trans, 0x0, sizeof(mbtk_ftp_file_trans_info_s));
+        info->file_trans.is_download = false;
+
+        memcpy(info->file_trans.remote_name, remote_path,
+               strlen((char*) remote_path));
+
+        if (local_path)
+        {
+            memcpy(info->file_trans.local_name, local_path,
+                   strlen((char*) local_path));
+            info->file_trans.fd = file_open((const char*)info->file_trans.local_name,
+                                            O_RDONLY);
+            if (info->file_trans.fd <= 0)
+            {
+                LOGE("Can not open EFS file[%s].", info->file_trans.local_name);
+                return -1;
+            }
+		
+        }
+        info->file_trans.size_count = size_byte;
+        info->file_trans.size_send = 0;
+        // Start upload data.
+
+
+	    // Start update file.
+        if (FTP_ERR_SUCCESS != ftp_data_sock_read(info, FTP_CMD_PUT, NULL, NULL))
+        {
+            LOGW("ftp_data_sock_read() fail.");
+			if(info->file_trans.size_count == info->file_trans.size_send && info->file_trans.size_send != 0)
+			{
+				result = FTP_ERR_SUCCESS;
+			}
+			else
+			{
+			    mbtk_at_ftp_par.rest_size = info->file_trans.size_send;
+				result = FTP_ERR_UNKNOWN;
+			} 
+        }
+
+		if (info->file_trans.fd > 0 )   // Save to efs
+		{
+            info->file_trans.size_count = 0;
+			info->file_trans.size_send = 0;
+			if (file_close(info->file_trans.fd))
+			{
+				LOGE("EFS close fail.");
+				return -1;
+			}
+		}
+
+    }
+    else
+    {
+        LOGE("FTP state error[%d].", info->state);
+        return -1;
+    }
+
+    return result;
+}
+
+/*
+* This only for upload data(No for upload efs).
+*/
+int mbtk_ftp_upload_send(mbtk_ftp_handle handle, const void *data,uint16 data_len)
+{
+    if (!data || data_len == 0)
+    {
+        LOGE("Param set error.");
+        return -1;
+    }
+
+    int err;
+	int result = 0;
+    mbtk_ftp_info_s *info = ftp_info_find(handle);
+    if (!info)
+    {
+        LOGE("No such FTP handle:%d", handle);
+        return -1;
+    }
+
+    if(info->file_trans.fd > 0)   // Is upload from efs.
+    {
+        LOGE("Not upload from EFS.");
+        return -1;
+    }
+
+//LOGE("1socket:%d, data:%s, data_len:%d", info->sock_info[FTP_SOCK_DATA].fd, data, data_len );
+    if((info->file_trans.size_send + data_len) > info->file_trans.size_count)
+	{
+		printf("send over set length\n");
+        result = FTP_ERR_UNKNOWN;
+        goto overlong;
+    }
+    int len;
+    if(info->auth_type != 0)
+        len = mbtk_sock_write(info->ftp_ssl_handle,info->session_data,data,data_len,FTP_TIMEOUT,&err);
+    else
+	    len = sock_write(&info->net_info, &info->sock_info[FTP_SOCK_DATA], data, data_len,
+                            FTP_TIMEOUT, &err);
+	if(len < 0)
+	{
+		LOGE("EFS write fail.len:%d, err;%d", len, err);
+        return FTP_ERR_EFS_FILE;
+	}
+
+	info->file_trans.size_send += len;
+    mbtk_at_ftp_par.rest_size = info->file_trans.size_send;
+    
+    LOGE("size_count:%d, size_send:%d.", info->file_trans.size_count,info->file_trans.size_send);
+
+    if((info->file_trans.size_count <= info->file_trans.size_send) )
+	{
+	    printf("\nClose data socket begin!\n ");
+			    // Close data socket.
+overlong:    if (info->sock_info[FTP_SOCK_DATA].fd > 0)
+	    {
+	        if (sock_close(&info->net_info, &info->sock_info[FTP_SOCK_DATA], FTP_TIMEOUT, &err))
+	        {
+	            LOGE("Close data socket fail[%d].", err);
+                printf("\nClose data socket fail[%d].\n", err);
+	            result = FTP_ERR_NET_CLOSE;
+	        }
+	        else
+	        {
+	            printf("\nClose data socket ok[%d].\n", err);
+	        //    info->sock_info[FTP_SOCK_DATA].fd = -1;
+	        }
+	    }
+        if(info->auth_type != 0)
+        {
+            if(mbtk_sock_close(info->ftp_ssl_handle,info->session_data,6000,&err))
+            {
+	            LOGE("Close ssl data socket fail[%d].", err);
+                printf("\nClose ssl data socket fail[%d].\n", err);
+	            result = FTP_ERR_NET_CLOSE;
+	        }
+	        else
+	        {
+	            printf("\nClose ssl data socket ok[%d].\n", err);
+	        //    info->sock_info[FTP_SOCK_DATA].fd = -1;
+	        }
+        }
+        info->data_mode = FTP_MODE_PASSIVE;
+        info->is_data_sock_busy = false;
+        info->file_trans.size_count = 0;
+        info->file_trans.size_send = 0;
+	}
+	else
+	{
+		LOGE("size_count:%d, size_send:%d.", info->file_trans.size_count,info->file_trans.size_send);
+	}
+
+
+    // Start update data.
+
+    return result;
+}
+
+mbtk_ftp_error_enum mbtk_ftp_trans_reset(mbtk_ftp_handle handle)
+{
+    mbtk_ftp_info_s *info = ftp_info_find(handle);
+    if (!info)
+    {
+        LOGE("No such FTP handle:%d", handle);
+        return FTP_ERR_UNKNOWN;
+    }
+
+    info->is_trans = false;
+    memset(&info->file_trans, 0x0, sizeof(mbtk_ftp_file_trans_info_s));
+
+    return FTP_ERR_SUCCESS;
+}
+
+
diff --git a/mbtk/mbtk_lib/src/mbtk_ftp_at.c b/mbtk/mbtk_lib/src/mbtk_ftp_at.c
new file mode 100755
index 0000000..58c2d07
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_ftp_at.c
@@ -0,0 +1,594 @@
+/*************************************************************
+ Description:
+ $file_description$
+ Author:
+ LiuBin
+ Date:
+ 2020/10/28 11:49:08
+ *************************************************************/
+#include "mbtk_ftp.h"
+#include "mbtk_list.h"
+#include "mbtk_sock.h"
+#include "mbtk_str.h"
+#include "mbtk_sock2.h"
+#include "mbtk_info.h"
+#include<linux/msg.h>
+
+/*************************************************************
+ typedef struct:local at
+ *************************************************************/
+typedef struct
+{
+    char host[64];
+    uint16 port;
+    mbtk_ftp_auth_type_enum auth_type;
+    bool ftp_type;
+    bool use_cert;
+    char name[FTP_SERVER_USER_NAME_MAX+1];
+    char pass[FTP_SERVER_USER_PASS_MAX+1];
+    mbtk_ftp_handle at_ftp_handle;
+    char remote_path[MBTK_FTP_FILE_NAME_MAX+1];
+    char local_path[MBTK_FTP_FILE_NAME_MAX+1];
+    int rest_size;
+    int read_size;
+    int put_len;
+} mbtk_at_init_parameter;
+
+mbtk_at_init_parameter mbtk_at_ftp_par_at={0};
+
+/*************************************************************
+ Variables:local
+ *************************************************************/
+static list_node_t *ftp_client_list = NULL;
+
+/*************************************************************
+ Variables:public
+ *************************************************************/
+
+/*************************************************************
+ Local Function Declaration
+ *************************************************************/
+
+/*************************************************************
+ Local Function Definitions
+ *************************************************************/
+
+//at
+struct my_msg   //消息队列结构体
+{
+	long int my_msg_type;
+    char *ptr;
+}mbtk_at_msg;
+
+int msgid;
+
+int mbtk_at_msgid(int *at_msgid)
+{
+    msgid = *at_msgid;
+    return 0;
+}
+
+void mbtk_at_printf(void *data)
+{
+    char *ptr = malloc(strlen(data)+1);
+    memset(ptr,0,strlen(data)+1);
+    memcpy(ptr, data, strlen(data)+1);
+    mbtk_at_msg.ptr = ptr;
+	mbtk_at_msg.my_msg_type=3;
+	msgsnd(msgid,&mbtk_at_msg,sizeof(&mbtk_at_msg),0);   //发送数据到缓冲区
+}
+
+int mbtk_at_ftp(int parameter,int port,char *addr, char *pass, char *name,int at_auth_type ,bool ftp_type, bool use_cert)
+{
+    int at_ftp_ret = 0;
+
+    LOGI("%s %d", __FUNCTION__, __LINE__);
+    switch (parameter) {
+    case 0: {
+        mbtk_at_ftp_par_at.port = port;
+        break;
+        }
+    case 1: {
+        memset(mbtk_at_ftp_par_at.host, 0, strlen(mbtk_at_ftp_par_at.host)+1);
+        memcpy(mbtk_at_ftp_par_at.host, addr, strlen(addr)+1);
+        break;
+        }
+    case 2: {
+        memset(mbtk_at_ftp_par_at.pass, 0, strlen(mbtk_at_ftp_par_at.pass)+1);
+        memcpy(mbtk_at_ftp_par_at.pass, pass, strlen(pass)+1);
+        break;
+        }
+    case 3: {
+        memset(mbtk_at_ftp_par_at.name, 0, strlen(mbtk_at_ftp_par_at.name)+1);
+        memcpy(mbtk_at_ftp_par_at.name, name, strlen(name)+1);
+        break;
+        }
+    case 4: {
+        mbtk_at_ftp_par_at.auth_type = at_auth_type;
+        mbtk_at_ftp_par_at.use_cert = use_cert;
+        break;
+        }
+    case 5: {
+        mbtk_at_ftp_par_at.ftp_type = ftp_type;
+        break;
+        }
+    default :
+        at_ftp_ret = -1;
+        return at_ftp_ret;
+    }
+    return at_ftp_ret;
+}
+
+int mbtk_at_get_ftp_info(int parameter,int *port,char *addr, char *pass, char *name, int *at_auth_type ,int *ftp_type, int *use_cert)
+{
+    int at_ftp_ret = 0;
+
+    LOGI("%s %d", __FUNCTION__, __LINE__);
+    switch (parameter) {
+    case 0: {
+        *port = mbtk_at_ftp_par_at.port;
+        break;
+        }
+    case 1: {
+        memset(addr, 0, strlen(addr)+1);
+        memcpy(addr, mbtk_at_ftp_par_at.host, strlen(mbtk_at_ftp_par_at.host)+1);
+        break;
+        }
+    case 2: {
+        memset(pass, 0, strlen(pass)+1);
+        memcpy(pass, mbtk_at_ftp_par_at.pass, strlen(mbtk_at_ftp_par_at.pass)+1);
+        break;
+        }
+    case 3: {
+        memset(name, 0, strlen(name)+1);
+        memcpy(name, mbtk_at_ftp_par_at.name, strlen(mbtk_at_ftp_par_at.name)+1);
+        break;
+        }
+    case 4: {
+        *at_auth_type = mbtk_at_ftp_par_at.auth_type;
+        *use_cert = mbtk_at_ftp_par_at.use_cert;
+        break;
+        }
+    case 5: {
+        *ftp_type = mbtk_at_ftp_par_at.ftp_type;
+        break;
+        }
+    default :
+        at_ftp_ret = -1;
+    }
+    return at_ftp_ret;
+}
+
+int mbtk_at_ftp_mkrmdel(int cmd,char *dir)
+{
+    int at_ftp_ret = 0;
+
+    LOGI("%s %d", __FUNCTION__, __LINE__);
+
+    char send_buf[1024];
+    
+    mbtk_at_ftp_par_at.at_ftp_handle = mbtk_ftp_init(mbtk_at_ftp_par_at.host, mbtk_at_ftp_par_at.port, mbtk_at_ftp_par_at.auth_type,
+                                    0, mbtk_at_ftp_par_at.use_cert);
+    mbtk_ftp_data_type_set(mbtk_at_ftp_par_at.at_ftp_handle,mbtk_at_ftp_par_at.ftp_type);//FTP_DATA_TYPE_I = 2 FTP_DATA_TYPE_A = 0
+    if(mbtk_at_ftp_par_at.at_ftp_handle < 0)
+            at_ftp_ret = -1;
+    
+    at_ftp_ret = mbtk_ftp_login(mbtk_at_ftp_par_at.at_ftp_handle,mbtk_at_ftp_par_at.name,mbtk_at_ftp_par_at.pass);
+    if(at_ftp_ret != 0)
+    {
+        printf("ftp_login err:%d\n",at_ftp_ret);
+    }
+    memset(mbtk_at_ftp_par_at.remote_path, 0, strlen(mbtk_at_ftp_par_at.remote_path)+1);
+    memcpy(mbtk_at_ftp_par_at.remote_path,dir,strlen(dir)+1);
+    switch (cmd)
+    {
+        case 0:
+        {
+            at_ftp_ret = mbtk_ftp_dir_mkdir(mbtk_at_ftp_par_at.at_ftp_handle,dir);
+            if(at_ftp_ret != 0)
+            {
+                printf("+CFTPMKD: FAIL,<%d>\n",at_ftp_ret);
+                sprintf(send_buf,"+CFTPMKD: FAIL,<%d>\n",at_ftp_ret);
+            }
+            else
+            {
+                printf("+CFTPMKD: SUCCESS\n");
+                sprintf(send_buf,"+CFTPMKD: SUCCESS\n");
+            }
+            break;
+        }
+        case 1:
+        {
+            at_ftp_ret = mbtk_ftp_dir_rmdir(mbtk_at_ftp_par_at.at_ftp_handle,dir);
+            if(at_ftp_ret != 0)
+            {
+                printf("+CFTPRMD: FAIL,<%d>\n",at_ftp_ret);
+                sprintf(send_buf,"+CFTPRMD: FAIL,<%d>\n",at_ftp_ret);
+            }
+            else
+            {
+                printf("+CFTPRMD: SUCCESS\n");
+                sprintf(send_buf,"+CFTPRMD: SUCCESS\n");
+            }
+            break;
+        }
+        case 2:
+        {
+            at_ftp_ret = mbtk_ftp_file_del(mbtk_at_ftp_par_at.at_ftp_handle,dir);
+            if(at_ftp_ret != 0)
+            {
+                printf("+CFTPDELE: FAIL,<%d>\n",at_ftp_ret);
+                sprintf(send_buf,"+CFTPDELE: FAIL,<%d>\n",at_ftp_ret);
+            }
+            else
+            {
+                printf("+CFTPDELE: SUCCESS\n");
+                sprintf(send_buf,"+CFTPDELE: SUCCESS\n");
+            }
+            break;
+        }
+        default:
+            break;
+    }
+    
+    mbtk_at_printf(send_buf);
+    mbtk_at_printf("end");
+    mbtk_ftp_quit(mbtk_at_ftp_par_at.at_ftp_handle);
+    mbtk_ftp_deinit(mbtk_at_ftp_par_at.at_ftp_handle);
+    return at_ftp_ret;
+}
+
+int mbtk_at_ftp_upload(char *remote_path,char *local_path, int size_byte, int rest_size)
+{
+    int at_ftp_ret=-1;
+    int len_size;
+    char send_buf[1024];
+    
+    mbtk_at_ftp_par_at.at_ftp_handle = mbtk_ftp_init(mbtk_at_ftp_par_at.host, mbtk_at_ftp_par_at.port, mbtk_at_ftp_par_at.auth_type,
+                                    0, mbtk_at_ftp_par_at.use_cert);
+    mbtk_ftp_data_type_set(mbtk_at_ftp_par_at.at_ftp_handle,mbtk_at_ftp_par_at.ftp_type);//FTP_DATA_TYPE_I = 2 FTP_DATA_TYPE_A = 0
+    if(mbtk_at_ftp_par_at.at_ftp_handle < 0)
+        at_ftp_ret = -1;
+    
+    at_ftp_ret = mbtk_ftp_login(mbtk_at_ftp_par_at.at_ftp_handle,mbtk_at_ftp_par_at.name,mbtk_at_ftp_par_at.pass);
+    if(at_ftp_ret != 0)
+    {
+        printf("ftp_login err:%d\n",at_ftp_ret);
+    }
+    
+    memset(mbtk_at_ftp_par_at.remote_path, 0, strlen(mbtk_at_ftp_par_at.remote_path)+1);
+    memcpy(mbtk_at_ftp_par_at.remote_path, remote_path, strlen(remote_path)+1);
+    if(local_path!=NULL)
+    {
+        memset(mbtk_at_ftp_par_at.local_path, 0, strlen(mbtk_at_ftp_par_at.local_path)+1);
+        memcpy(mbtk_at_ftp_par_at.local_path, local_path, strlen(local_path)+1);
+    }
+    mbtk_at_ftp_par_at.put_len = size_byte;
+    mbtk_at_ftp_par_at.rest_size = rest_size;
+
+    {
+        FILE *fp;
+        fp=fopen(local_path,"r");
+        if(NULL==fp)
+            printf("Read file error\n");
+        else
+        {
+            printf("Open file success\n");
+            fseek(fp,0,SEEK_END); //将文件读写指针移动到文件尾
+            len_size=ftell(fp); //ftell函数获取从文件尾移动到文件开头的偏移量
+            pclose(fp);
+        }
+    }
+    if(local_path == NULL)
+    {
+        at_ftp_ret = mbtk_ftp_upload_start(mbtk_at_ftp_par_at.at_ftp_handle, remote_path,
+                          NULL, size_byte);
+        if(at_ftp_ret != 0)
+        {
+            printf("+CFTPPUT: FAIL,<%d>\n",at_ftp_ret);
+            sprintf(send_buf,"+CFTPPUT: FAIL,<%d>\n",at_ftp_ret);
+        }
+        else 
+        {
+            at_ftp_ret = mbtk_ftp_upload_send(mbtk_at_ftp_par_at.at_ftp_handle,"" ,size_byte);
+            if(at_ftp_ret != 0)
+            {
+                printf("+CFTPPUT: FAIL,<%d>\n",at_ftp_ret);
+                sprintf(send_buf,"+CFTPPUT: FAIL,<%d>\n",at_ftp_ret);
+            }
+            else
+            {
+                printf("+CFTPPUT: SUCCESS,<%d>\n",len_size);
+                sprintf(send_buf,"+CFTPPUT: SUCCESS,<%d>\n",len_size);
+            }
+        }
+        mbtk_ftp_upload_end(mbtk_at_ftp_par_at.at_ftp_handle);
+    }
+    else
+    {
+        at_ftp_ret = mbtk_ftp_upload_start(mbtk_at_ftp_par_at.at_ftp_handle, remote_path,
+                          local_path, size_byte);
+        if(at_ftp_ret != 0)
+        {
+            printf("+CFTPPUT: FAIL,<%d>\n",at_ftp_ret);
+            sprintf(send_buf,"+CFTPPUT: FAIL,<%d>\n",at_ftp_ret);
+        }
+        else
+        {
+            printf("+CFTPPUT: SUCCESS,<%d>\n",len_size);
+            sprintf(send_buf,"+CFTPPUT: SUCCESS,<%d>\n",len_size);
+        }
+    }
+    mbtk_at_printf(send_buf);
+    mbtk_at_printf("end");
+    mbtk_ftp_quit(mbtk_at_ftp_par_at.at_ftp_handle);
+    mbtk_ftp_deinit(mbtk_at_ftp_par_at.at_ftp_handle);
+    return at_ftp_ret;
+}
+
+void mbtk_at_ftp_data_cb(void *data, uint32 data_len)
+{
+    char resp_buf[2150];
+    printf("\n=====data_cb data[%s]=====\n=====data_cb data_len[%d]=====\n",data, data_len);
+
+    memset(resp_buf,0,2150);
+    sprintf(resp_buf, "+CFTPGET:");
+    mbtk_at_printf(resp_buf);
+
+    if(data_len > 1021)
+    {
+        memset(resp_buf,0,2150);
+        memcpy(resp_buf,data,1021);
+        mbtk_at_printf(resp_buf);
+        memset(resp_buf,0,2150);
+        sprintf(resp_buf, "<%d>", 1021);
+        mbtk_at_printf(resp_buf);
+
+        memset(resp_buf,0,2150);
+        sprintf(resp_buf, "+CFTPGET:");
+        mbtk_at_printf(resp_buf);
+        memset(resp_buf,0,2150);
+        memcpy(resp_buf,data+1021,data_len-1021);
+        mbtk_at_printf(resp_buf);
+        memset(resp_buf,0,2150);
+        sprintf(resp_buf, "<%d>", data_len-1021);
+        mbtk_at_printf(resp_buf);
+    }
+    else
+    {
+        memset(resp_buf,0,2150);
+        memcpy(resp_buf,data,data_len);
+        mbtk_at_printf(resp_buf);
+        
+        memset(resp_buf,0,2150);
+        sprintf(resp_buf, "<%d>", data_len);
+        mbtk_at_printf(resp_buf);
+    }
+
+}
+
+int mbtk_at_ftp_download(void *remote_path, void *local_path, int data_cbc, int rest_size, int read_size)
+{
+
+    int at_ftp_ret=-1;
+    int len_size;
+    
+    mbtk_at_ftp_par_at.at_ftp_handle = mbtk_ftp_init(mbtk_at_ftp_par_at.host, mbtk_at_ftp_par_at.port, mbtk_at_ftp_par_at.auth_type,
+                                    0, mbtk_at_ftp_par_at.use_cert);
+    mbtk_ftp_data_type_set(mbtk_at_ftp_par_at.at_ftp_handle,mbtk_at_ftp_par_at.ftp_type);//FTP_DATA_TYPE_I = 2 FTP_DATA_TYPE_A = 0
+    if(mbtk_at_ftp_par_at.at_ftp_handle < 0)
+        at_ftp_ret = -1;
+    
+    at_ftp_ret = mbtk_ftp_login(mbtk_at_ftp_par_at.at_ftp_handle,mbtk_at_ftp_par_at.name,mbtk_at_ftp_par_at.pass);
+    if(at_ftp_ret != 0)
+    {
+        printf("ftp_login err:%d\n",at_ftp_ret);
+    }
+
+    memset(mbtk_at_ftp_par_at.remote_path, 0, strlen(mbtk_at_ftp_par_at.remote_path)+1);
+    memcpy(mbtk_at_ftp_par_at.remote_path, remote_path, strlen(remote_path)+1);
+    if(local_path!=NULL)
+    {
+        memset(mbtk_at_ftp_par_at.local_path, 0, strlen(mbtk_at_ftp_par_at.local_path)+1);
+        memcpy(mbtk_at_ftp_par_at.local_path, local_path, strlen(local_path)+1);
+    }
+    mbtk_at_ftp_par_at.rest_size = rest_size;
+    mbtk_at_ftp_par_at.read_size = read_size;
+    
+    // Download file: /data
+    uint32 len_count = 0;
+    uint32 len;
+    int download_time = 0;
+    char resp_buf[1024];
+    int ret;
+    
+    uint32 file_size = mbtk_ftp_file_size(mbtk_at_ftp_par_at.at_ftp_handle, remote_path);
+    if(file_size > 0)
+    {
+        if(rest_size > file_size)
+        {
+            printf("+CTPGETFILE: FAIL,<err>");
+            memset(resp_buf,0,1024);
+            sprintf(resp_buf, "+CTPGETFILE: FAIL,<%d>\r\n",901);
+            mbtk_at_printf(resp_buf);
+            //ret = ATRESP(30163, 0, 0, resp_buf);
+            goto exit;
+        }
+        printf("Will download file:/data[%d]\n", file_size);
+        /*
+        memset(resp_buf,0,1024);
+        sprintf(resp_buf, "Will download file:/data[%d]",file_size);
+        mbtk_at_printf(resp_buf);
+        */
+        // Start download
+        if(data_cbc == 0)//to file
+        {
+            len = mbtk_ftp_download_start(mbtk_at_ftp_par_at.at_ftp_handle, remote_path, local_path, NULL);
+            /*
+            memset(resp_buf,0,1024);
+            sprintf(resp_buf, "download size:/len[%d]",len);
+            mbtk_at_printf(resp_buf);
+            */
+        }
+        else if(data_cbc == 1)
+        {
+            len = mbtk_ftp_download_start(mbtk_at_ftp_par_at.at_ftp_handle, remote_path, NULL, mbtk_at_ftp_data_cb);
+            /*
+            memset(resp_buf,0,1024);
+            sprintf(resp_buf, "download size:/len[%d]",len);
+            mbtk_at_printf(resp_buf);
+            */
+        }
+        else
+        {
+            printf("+CTPGETFILE: FAIL,<err>");
+            memset(resp_buf,0,1024);
+            sprintf(resp_buf, "+CTPGETFILE: FAIL,<%d>\r\n",902);
+            mbtk_at_printf(resp_buf);
+            //ret = ATRESP(30163, 0, 0, resp_buf);
+            goto exit;
+        }
+        if(len > 0)
+        {
+            len_count += len;
+            download_time++;
+            printf("Download[time-%d] size:[%d / %d]\n", download_time, len_count, file_size);
+            /*
+            memset(resp_buf,0,1024);
+            sprintf(resp_buf,"Download[time-%d] size:[%d / %d]\n", download_time, len_count, file_size);
+            mbtk_at_printf(resp_buf);
+            */
+            while (len_count < file_size
+                   && download_time <= 10 // Try 10 times.
+                   && (len = mbtk_ftp_download_continue(mbtk_at_ftp_par_at.at_ftp_handle)) > 0)
+            {
+                len_count += len;
+                download_time++;
+                printf("Download[time-%d] size:[%d / %d]\n", download_time, len_count, file_size);
+                /*
+                memset(resp_buf,0,1024);
+                sprintf(resp_buf,"Download[time-%d] size:[%d / %d]\n", download_time, len_count, file_size);
+                mbtk_at_printf(resp_buf);
+                */
+            }
+    
+            printf("Download complete - [%d / %d].\n",len_count, file_size);
+            memset(resp_buf,0,1024);
+            sprintf(resp_buf, "+CFTPGETFILE: SUCCESS,<%d>\r\n",len_count);
+            mbtk_at_printf(resp_buf);
+            //ret = ATRESP(30163, 0, 0, resp_buf);
+        }
+        else
+        {
+            printf("FTP download fail[%d / %d].\n",len_count, file_size);
+            memset(resp_buf,0,1024);
+            sprintf(resp_buf, "+CTPGETFILE: FAIL,<%d / %d> len=%d",len_count, file_size,len);
+            mbtk_at_printf(resp_buf);
+            //ret = ATRESP(30163, 0, 0, resp_buf);
+            goto exit;
+        }
+    }else {
+        printf("File error.\n");
+        memset(resp_buf,0,1024);
+        sprintf(resp_buf, "+CTPGETFILE: FAIL,<%d>\r\n",904);
+        mbtk_at_printf(resp_buf);
+        //ret = ATRESP(30163, 0, 0, resp_buf);
+        goto exit;
+    }
+
+    
+exit:
+    if(file_size != len_count)
+        mbtk_at_ftp_par_at.rest_size = len_count;
+    
+    //mbtk_at_printf(resp_buf);
+    mbtk_at_printf("end");
+    mbtk_ftp_quit(mbtk_at_ftp_par_at.at_ftp_handle);
+    mbtk_ftp_deinit(mbtk_at_ftp_par_at.at_ftp_handle);
+    return at_ftp_ret;
+
+}
+
+void ftp_ls_at_cb_func(void *file_list_cb)
+{
+    int ret;
+    char resp_buf[1024 + 1];
+    printf("+CFTPLIST:\r\n%s\n", ((mbtk_ftp_file_info_s *)file_list_cb)->name);
+    sprintf(resp_buf, "%s",((mbtk_ftp_file_info_s *)file_list_cb)->name);
+    mbtk_at_printf(resp_buf);
+    //ret = ATRESP(30163, 0, 0, resp_buf);
+}
+
+
+int mbtk_at_ftp_list(char *ftp_path)
+{
+    int at_ftp_ret=-1;
+    int len_size;
+    char resp_buf[1024];
+    
+    mbtk_at_ftp_par_at.at_ftp_handle = mbtk_ftp_init(mbtk_at_ftp_par_at.host, mbtk_at_ftp_par_at.port, mbtk_at_ftp_par_at.auth_type,
+                                    0, mbtk_at_ftp_par_at.use_cert);
+    mbtk_ftp_data_type_set(mbtk_at_ftp_par_at.at_ftp_handle,mbtk_at_ftp_par_at.ftp_type);//FTP_DATA_TYPE_I = 2 FTP_DATA_TYPE_A = 0
+    if(mbtk_at_ftp_par_at.at_ftp_handle < 0)
+        at_ftp_ret = -1;
+    
+    at_ftp_ret = mbtk_ftp_login(mbtk_at_ftp_par_at.at_ftp_handle,mbtk_at_ftp_par_at.name,mbtk_at_ftp_par_at.pass);
+    if(at_ftp_ret != 0)
+    {
+        printf("ftp_login err:%d\n",at_ftp_ret);
+    }
+
+    memset(mbtk_at_ftp_par_at.remote_path, 0, strlen(mbtk_at_ftp_par_at.remote_path)+1);
+    memcpy(mbtk_at_ftp_par_at.remote_path, ftp_path, strlen(ftp_path)+1);
+    
+    at_ftp_ret = mbtk_ftp_cd(mbtk_at_ftp_par_at.at_ftp_handle, ftp_path);
+    if(FTP_ERR_SUCCESS != at_ftp_ret) {
+        printf("mbtk_ftp_cd() fail:%d\n", at_ftp_ret);
+        sprintf(resp_buf,"+CFTPLIST: FAIL:%d\n", at_ftp_ret);
+        goto exit;
+    }
+    mbtk_at_printf("+CFTPLIST:");
+    mbtk_ftp_file_info_s list_head;
+    list_head.ftp_ls_cb_typedef = ftp_ls_at_cb_func;
+    at_ftp_ret = mbtk_ftp_dir_ls(mbtk_at_ftp_par_at.at_ftp_handle, &list_head);
+    if(FTP_ERR_SUCCESS != at_ftp_ret) {
+        printf("mbtk_ftp_dir_ls() fail:%d\n", at_ftp_ret);
+        sprintf(resp_buf,"+CFTPLIST: FAIL:%d\n", at_ftp_ret);
+        goto exit;
+    }
+    else
+    {
+        printf("+CFTPLIST: SUCCESS\r\n");
+        sprintf(resp_buf,"+CFTPLIST: SUCCESS\r\n");
+    }
+exit:
+    mbtk_at_printf(resp_buf);
+    mbtk_at_printf("end");
+    mbtk_ftp_quit(mbtk_at_ftp_par_at.at_ftp_handle);
+    mbtk_ftp_deinit(mbtk_at_ftp_par_at.at_ftp_handle);
+    return at_ftp_ret;
+}
+
+int mbtk_at_get_ftp_data_info(char *remote_path, char *local_path, int *rest_size, int *read_size, int *put_len)
+{
+    if(remote_path != NULL)
+    {
+        memset(remote_path, 0, strlen(remote_path)+1);
+        memcpy(remote_path, mbtk_at_ftp_par_at.remote_path, strlen(mbtk_at_ftp_par_at.remote_path)+1);
+    }
+    if(local_path != NULL)
+    {
+        memset(local_path, 0, strlen(local_path)+1);
+        memcpy(local_path, mbtk_at_ftp_par_at.local_path, strlen(mbtk_at_ftp_par_at.local_path)+1);
+    }
+    if(rest_size != NULL)
+        *rest_size = mbtk_at_ftp_par_at.rest_size;
+    if(read_size != NULL)
+        *read_size = mbtk_at_ftp_par_at.read_size;
+    if(put_len != NULL)
+        *put_len = mbtk_at_ftp_par_at.put_len;
+    return 0;
+}
+
+
+
diff --git a/mbtk/mbtk_lib/src/mbtk_gnss.c b/mbtk/mbtk_lib/src/mbtk_gnss.c
new file mode 100755
index 0000000..72c207c
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_gnss.c
@@ -0,0 +1,2214 @@
+/**
+ *   \file mbtk_gnss.c
+ *   \brief gnss module.
+ *
+ *  Detailed description
+ *   \Author:  Sniper <js.wang@mobiletek.cn>
+ *   \Version: 1.0.0
+ *   \Date: 2022-03-16
+ */
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <termios.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <termios.h>
+#include <time.h>
+#include <sys/ioctl.h>
+#include "mbtk_type.h"
+#include "mbtk_gnss.h"
+#include "mbtk_http.h"
+#include "mbtk_log.h"
+#include "ringbuffer.h"
+
+// #define DEBUG 1
+
+#ifdef DEBUG
+    #define gnss_log(...)                    printf(__VA_ARGS__)
+#else
+    #define gnss_log(...)
+#endif
+
+// 默认为 9600,打开为 115200,但是 AT+MGPSCMD 会重启。
+#define BAUDRATE_115200  1
+
+#define TTFF_TEST   0
+
+#define MBTK_GNSS_DEV  "/dev/ttyS2"
+
+#define MBTK_UART_RECV_BUFFER_SIZE 1024
+#define MBTK_UART_SEND_BUFFER_MAX 128
+
+/******************************************************************************
+ * 时间处理相关的宏
+ *****************************************************************************/
+// 获取当前时间
+#define GET_TIME()  { gettimeofday(&time_m, NULL); \
+    time_m.tv_sec += TIMEOUT_SEC;\
+}
+// 设置从循环中退出的时间
+#define SET_TIME_OUT(x)  { gettimeofday(&time_m, NULL); \
+    time_m.tv_sec += x;\
+}
+// 检测时间是否超时,超时则退出当前函数
+#define CHK_TIME()  { gettimeofday(&time_n, NULL); \
+    if(time_n.tv_sec > time_m.tv_sec) { \
+        printf("\ntimeout!!!\n\n");\
+        close(fd); \
+        return ret; \
+    } \
+}
+// 检测时间是否超时,超时则退出当前循环
+#define CHK_TIME_BREAK()  { gettimeofday(&time_n, NULL); \
+    if(time_n.tv_sec > time_m.tv_sec) { \
+        printf("\ntimeout!!!\n\n");\
+        break; \
+    } \
+}
+// 检测延时是否到达,到达则退出当前循环
+#define DELAY_TIME_BREAK()  { gettimeofday(&time_n, NULL); \
+    if(time_n.tv_sec > time_m.tv_sec) { \
+        break; \
+    } \
+}
+
+typedef void (*gnss_msg_func_t)
+(
+    int index,
+    char *in_data,
+    void *out_ptr
+);
+
+struct mbtk_gnss_cmd_msg_t
+{
+    int index; // 序号
+    char *cmd_str; // 匹配字符
+    gnss_msg_func_t gnss_msg_func; // 回调函数
+    int is_continue; // 是否随NEMA数据一起输出
+};
+
+typedef enum
+{
+    E_MT_LOC_MSG_ID_STATUS_INFO = 0,        /**<  pv_data = &E_QL_LOC_STATUS_VALUE_T  */
+    E_MT_LOC_MSG_ID_LOCATION_INFO,          /**<  pv_data = &QL_LOC_LOCATION_INFO_T  */
+    E_MT_LOC_MSG_ID_SV_INFO,                /**<  pv_data = &QL_LOC_SV_STATUS_T  */
+    E_MT_LOC_MSG_ID_NMEA_INFO,              /**<  pv_data = &QL_LOC_NMEA_INFO_T  */
+    E_MT_LOC_MSG_ID_CAPABILITIES_INFO,      /**<  pv_data = &E_QL_LOC_CAPABILITIES_T  */
+    E_MT_LOC_MSG_ID_AGPS_STATUS,            /**<  pv_data = &QL_LOC_AGPS_STATUS_T  */
+    E_MT_LOC_MSG_ID_NI_NOTIFICATION,        /**<  pv_data = &QL_LOC_NI_NOTIFICATION_INTO_T  */
+    E_MT_LOC_MSG_ID_XTRA_REPORT_SERVER,     /**<  pv_data = &QL_LOC_XTRA_REPORT_SERVER_INTO_T  */
+}e_mt_loc_msg_id_t;
+
+typedef struct
+{
+    int64_t     timestamp;                          /**<   System Timestamp, marked for when got the nmea data */
+    int         length;                             /**<   NMEA string length. */
+    char        nmea[255 + 1];   /**<   NMEA string.*/
+}mopen_gnss_nmea_info_t;  /* Message */
+
+struct mbtk_gnss_handle_t
+{
+    int dev_fd;
+    pthread_t  uart_pthread;
+    pthread_t  gnss_pthread;
+    mbtk_gnss_handler_func_t gnss_handler_func;
+    int mode; // 0 - stop, 1 - single, 2 - periodic, 3 - start
+    pthread_mutex_t _cond_mutex;
+    int reset_state;
+    int inited;
+    ring_buffer_t ring_buffer;
+    int getap_status;
+    char *rb;
+
+#if TTFF_TEST
+    pthread_t ttff_pid;
+    int location_state;
+#endif
+    /********************
+    存储handle的地址指针
+    phandle = &handle
+    handle  = mbtk_gnss_handle
+    *********************/
+    uint32 *phandle; // handle的地址指针
+};
+
+#if TTFF_TEST
+struct mbtk_gnss_ttff_t
+{
+    int type;
+    int timeout_sec;
+    int test_count;
+    int average_loc_time;
+};
+
+// ttff 测试 flag
+static int location_test = 0;
+static pthread_mutex_t loc_cond_mutex_r = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t loc_sync_cond = PTHREAD_COND_INITIALIZER;
+#endif
+
+struct mopen_gnss_device_info_t
+{
+    int      device_info_valid;
+    int      usrt_bandrate;
+    char     product_name[10];
+    char     dev_config[6];
+    char     hw_ver[6];
+    char     fw_ver[32];
+    char     pn[16];
+    char     sn[16];
+    char     nmea_ver[4];
+};
+
+static struct mopen_gnss_device_info_t mopen_gnss_device_info;
+static struct mbtk_gnss_handle_t *mbtk_gnss_handle = NULL;
+static int firmware_extren_state = 0;
+
+static char g_no_sv = 0;// 参与定位的卫星数量
+
+int mopen_gnss_get_nmea_config(uint32 h_gnss);
+int mopen_gnss_get_ant_state_info(uint32 h_gnss);
+
+static void get_gnss_time_info(int cmd, char *str, void *data);
+static void get_gnss_agnss_state(int cmd, char *str, void *data);
+static void get_gnss_device_info(int type, char *str, void *usr_ptr);
+static void gnss_uart_info(int cmd, char *str, void *data);
+static void gnss_gsa_info(int cmd, char *str, void *data);
+static int mopen_uart_change(int fd, int check);
+
+static int select_read( int fd, int timeout );
+ssize_t deal_read(int fd, void *buf, size_t count);
+
+static struct mbtk_gnss_cmd_msg_t mbtk_gnss_cmd_msg_map[] = {
+{ 1,     "$OK",         NULL,                 0},
+{ 2,     "$Fail",       NULL,                 0},
+{ 3,     "RMC",         get_gnss_time_info,   1},
+{ 4,     "GGA",         get_gnss_time_info,   1},
+{ 5,     "$PDTINFO",    get_gnss_device_info, 0},
+{ 6,     "$CFGNMEA",    get_gnss_device_info, 0},
+{ 7,     "$CFGPRT",     gnss_uart_info,       0},
+{ 8,     "$CFGAID",     get_gnss_agnss_state, 0},
+{ 9,     "$ANTSTAT",    NULL,                 0},
+#if TTFF_TEST
+{10,     "GSA",         gnss_gsa_info,        1},
+#endif
+};
+
+/**
+ *  \brief strstr_n
+ *
+ *  find string return number
+ *
+ *  \param param
+ *  \return return type
+ */
+int strstr_n(const char *s1, const char *s2)
+{
+    int n;
+    int strlen = 0;
+
+    if(*s2)
+    {
+        while(*s1)
+        {
+            for(n = 0; *(s1+n) == *(s2 + n); n++)
+            {
+                if(!*(s2 + n + 1))
+                {
+                    strlen++;
+                    return strlen;
+                }
+            }
+            s1++;
+            strlen++;
+        }
+        return 0;
+    }
+    else
+        return 0;
+}
+
+/**
+ * @brief      gnss_get_para_from_nmea
+ *
+ * @details    从buf里面得到第num个逗号所在的位置
+ *
+ * @param      param
+ *
+ * @return     0~0xfe,代表逗号所在位置的偏移.
+ *             0xff,代表不存在第cx个逗号
+ */
+static int gnss_get_para_from_nmea(const char *data, char *out_data, int num)
+{
+    int i = 0;
+    int n[2] = {0};
+    int tmp;
+
+    // 找到第num个",",结果放到 n[0]
+    for (i = 0; i < num; ++i) {
+        tmp = strstr_n(&data[n[0]], ",");
+        if(0 == tmp) {
+            gnss_log("%s %d : error\n", __func__, __LINE__);
+            gnss_log("error: [%d] %s\n", num, data);
+            return -1;
+        }
+        n[0] += tmp;
+    }
+    if ((n[1] = strstr_n(&data[n[0]], ",")) ||
+        (n[1] = strstr_n(&data[n[0]], "*")) &&
+        (n[1] > 1)) {
+        memcpy(out_data, &data[n[0]], n[1] - 1);
+    } else {
+        gnss_log("%s %d : error [%d]\n" , __func__, __LINE__, n[1]);
+        gnss_log("error: [%d] %s\n", num, data);
+        return -1;
+    }
+
+    return 0;
+}
+/**
+* @brief      get_timestamp
+*
+* @details
+*
+* @param      param
+*
+* @return     return type
+*/
+static time_t get_timestamp(char *time)
+{
+    char tmp_char[4] = {0};
+    struct tm* tmp_time = (struct tm*)malloc(sizeof(struct tm));
+
+    memset(tmp_time, 0, sizeof(struct tm));
+    memset(tmp_char, 0, sizeof(tmp_char));
+    memcpy(tmp_char, &time[4], 2);
+    tmp_time->tm_sec = atoi(tmp_char);
+    memcpy(tmp_char, &time[2], 2);
+    tmp_time->tm_min = atoi(tmp_char);
+    memcpy(tmp_char, &time[0], 2);
+    tmp_time->tm_hour = atoi(tmp_char);
+    memcpy(tmp_char, &time[6], 2);
+    tmp_time->tm_mday = atoi(tmp_char);
+    memcpy(tmp_char, &time[8], 2);
+    tmp_time->tm_mon = atoi(tmp_char);
+    memcpy(tmp_char, &time[10], 2);
+    tmp_time->tm_year = 100 + atoi(tmp_char);
+
+    time_t _t = mktime(tmp_time);//按当地时区解析tmp_time
+    // gnss_log("timestamp: %ld\n",_t);
+    free(tmp_time);
+
+    return _t;
+}
+/**
+ * @brief      get_gnss_device_info
+ *
+ * @details    获取设备信息
+ *
+ * @param      type: 5-从$PDTINFO获取
+ *                   6-从$CFGNMEA获取
+ *
+ * @return     return type
+ */
+static void get_gnss_device_info(int type, char *str, void *usr_ptr)
+{
+    char tmp_str[32] = {0};
+    int i, ret;
+
+    if(5 == type) // define  mbtk_gnss_cmd_msg_map
+    {
+        // $PDTINFO get product info
+        char *tmp_ptr[6] = {mopen_gnss_device_info.product_name,
+                            mopen_gnss_device_info.dev_config,
+                            mopen_gnss_device_info.hw_ver,
+                            mopen_gnss_device_info.fw_ver,
+                            mopen_gnss_device_info.pn,
+                            mopen_gnss_device_info.sn
+        };
+        for (i = 0; i < 6; ++i) {
+            memset(tmp_str, 0, sizeof(tmp_str));
+            // get product name
+            ret = gnss_get_para_from_nmea(str, tmp_str, i + 1);
+            if(ret)
+                continue;
+            memcpy(tmp_ptr[i], tmp_str, strlen(tmp_str));
+        }
+        gnss_log("*************************\n");
+        gnss_log("-Pn: %s\n dc: %s\n hv: %s\n fw: %s\n pn: %s\n sn: %s\n ",
+                            mopen_gnss_device_info.product_name,
+                            mopen_gnss_device_info.dev_config,
+                            mopen_gnss_device_info.hw_ver,
+                            mopen_gnss_device_info.fw_ver,
+                            mopen_gnss_device_info.pn,
+                            mopen_gnss_device_info.sn);
+    }
+    if(6 == type) // define  mbtk_gnss_cmd_msg_map
+    {
+        // $CFGNMEA get nmea version
+        memset(tmp_str, 0, sizeof(tmp_str));
+        ret = gnss_get_para_from_nmea(str, tmp_str, 1);
+        if(ret)
+            return;
+        memcpy(mopen_gnss_device_info.nmea_ver, tmp_str, strlen(tmp_str));
+        mopen_gnss_device_info.device_info_valid = TRUE;
+    }
+}
+
+static short int from_hex(char a)
+{
+    if (a >= 'A' && a <= 'F')
+        return a - 'A' + 10;
+    else if (a >= 'a' && a <= 'f')
+        return a - 'a' + 10;
+    else
+        return a - '0';
+}
+
+static int str_to_hex(char *str)
+{
+    unsigned char str_len = strlen(str);
+    int i;
+    int ret = 0;
+
+    for (i = 0; i < str_len; ++i) {
+        ret = ret * 16 + from_hex(str[i]);
+    }
+    return ret;
+}
+
+static void get_gnss_agnss_state(int cmd, char *str, void *data)
+{
+    int ret;
+    char tmp_str[10] = {0};
+    int agps;
+    static int count = 0;
+
+    ret = gnss_get_para_from_nmea(str, tmp_str, 2);
+    if(ret) {
+        printf("\n%s[%d] error!\n" , __FUNCTION__, __LINE__);
+    }
+    agps = str_to_hex(tmp_str);
+    gnss_log("\n%s[%d] agnss: %s[%x]\n" , __FUNCTION__, __LINE__, tmp_str, agps);
+    if(0 == agps && count < 5) {
+        mopen_gnss_get_aidinfo(mbtk_gnss_handle);
+        count++;
+    } else {
+        printf("\nagnss: %s\n", str);
+        count = 0;
+    }
+    //  $CFGAID,0,00000000,00000000,00*3E
+}
+// 1节=1海里/小时=1.852公里/小时
+/**
+ * @brief      function description
+ *
+ * @details    获取位置信息
+ *
+ * @param      type: 1-从$RMC获取
+ *                   2-从$GGA获取
+ * @return     return type
+ */
+static void get_gnss_loc_info(int type, char *str,
+                              struct mbtk_gnss_location_info_t *_mopen_location_info)
+
+{
+    char tmp_str[32] = {0};
+    int ret;
+
+    if(1 == type)
+    {
+        // $PDTINFO get product info
+        memset(tmp_str, 0, sizeof(tmp_str));
+        // get product name
+        ret = gnss_get_para_from_nmea(str, tmp_str, 7);
+        if(ret)
+            return;
+        _mopen_location_info->speed = atof(tmp_str);
+        memset(tmp_str, 0, sizeof(tmp_str));
+        // get product name
+        ret = gnss_get_para_from_nmea(str, tmp_str, 8);
+        if(ret)
+            return;
+        _mopen_location_info->bearing = atof(tmp_str);
+    }
+    else if(2 == type)
+    {
+        // $XXGGA get product info
+        memset(tmp_str, 0, sizeof(tmp_str));
+        // get product name
+        ret = gnss_get_para_from_nmea(str, tmp_str, 2);
+        if(ret)
+            return;
+        _mopen_location_info->latitude = atof(tmp_str);
+        memset(tmp_str, 0, sizeof(tmp_str));
+        // get product name
+        ret = gnss_get_para_from_nmea(str, tmp_str, 4);
+        if(ret)
+            return;
+        _mopen_location_info->longitude = atof(tmp_str);
+        memset(tmp_str, 0, sizeof(tmp_str));
+        // get product name
+        ret = gnss_get_para_from_nmea(str, tmp_str, 9);
+        if(ret)
+            return;
+        _mopen_location_info->altitude = atof(tmp_str);
+    }
+}
+
+static void get_gnss_time_info(int cmd, char *str, void *data)
+{
+    int ret;
+    char param[36] = {0};
+    struct mbtk_gnss_location_info_t *mopen_location_info_ptr = (struct mbtk_gnss_location_info_t *)data;
+
+    if (3 == cmd) {
+        memset(param, 0, sizeof(param));
+        // get time
+        ret = gnss_get_para_from_nmea(str, param, 1);
+        if(ret)
+            return;
+        // get date
+        ret = gnss_get_para_from_nmea(str, &param[6], 9);
+        if(ret)
+            return;
+
+        mopen_location_info_ptr->timestamp = get_timestamp(param);
+        get_gnss_loc_info(1, str, mopen_location_info_ptr);
+    } else if(4 == cmd) /* GGA */{
+        get_gnss_loc_info(2, str, mopen_location_info_ptr);
+        ret = gnss_get_para_from_nmea(str, param, 7);
+        if(ret)
+            return;
+        char no_sv = (char)atoi(param);
+        gnss_log("SV number: %d, %d\n", g_no_sv, no_sv);
+        /*
+          只能在临时固件下,才能获取APdata星历数据
+          在6颗卫星保存文件,每增加2颗保存一次。
+         */
+        if (1 == firmware_extren_state &&
+            g_no_sv < (no_sv - 1) && no_sv > 5) {
+
+            g_no_sv = no_sv;
+            mbtk_gnss_get_ap_data();
+        }
+    }
+}
+
+static void gnss_uart_info(int cmd, char *str, void *data)
+{
+    int ret;
+    char tmp_str[12] = {0};
+
+    // $CFGPRT,1,h0,9600,129,3*57
+    ret = gnss_get_para_from_nmea(str, tmp_str, 3);
+    if(ret)
+        return;
+    mopen_gnss_device_info.usrt_bandrate = atoi(tmp_str);
+    gnss_log("CFGPRT: %s\n", str);
+    gnss_log("Uart bandrate: %d\n" , mopen_gnss_device_info.usrt_bandrate);
+    gnss_log("*************************\n");
+}
+/**
+ *  \brief function description
+ *
+ *  Detailed 处理gnss数据
+ *
+ *  \param param
+ *  \return return type
+ */
+static void process_gnss_callback(struct mbtk_gnss_handle_t *handle,
+                                  const char *data, int data_len)
+{
+    int ret = 0;
+    int i = 0;
+    static struct mbtk_gnss_location_info_t mopen_location_info;
+    static int64_t tmp_time = 0;
+    mopen_gnss_nmea_info_t nmea_info;
+
+    memset(&nmea_info, 0, sizeof(nmea_info));
+    if(0 == tmp_time)
+        memset(&mopen_location_info, 0, sizeof(mopen_location_info));
+
+    for (i = 0;
+         i < (sizeof(mbtk_gnss_cmd_msg_map) / sizeof(struct mbtk_gnss_cmd_msg_t));
+         ++i) {
+        if(strstr_n(data, mbtk_gnss_cmd_msg_map[i].cmd_str)) {
+            if(mbtk_gnss_cmd_msg_map[i].gnss_msg_func)
+                mbtk_gnss_cmd_msg_map[i].gnss_msg_func(mbtk_gnss_cmd_msg_map[i].index,
+                                                   data, &mopen_location_info);
+            break;
+        }
+    }
+    if(0 == mbtk_gnss_cmd_msg_map[i].is_continue)
+        return;
+
+    tmp_time = mopen_location_info.timestamp;
+    nmea_info.timestamp = mopen_location_info.timestamp;
+    nmea_info.length = data_len;
+    memcpy(nmea_info.nmea, data, data_len);
+    gnss_log("nmea:[%d] %s", data_len, data);
+    if(handle->gnss_handler_func && handle->mode == 3 &&
+       nmea_info.timestamp)
+        handle->gnss_handler_func(handle, E_MT_LOC_MSG_ID_NMEA_INFO, &nmea_info, NULL);
+    if(handle->gnss_handler_func && handle->mode == 1 &&
+            mopen_location_info.latitude &&
+            mopen_location_info.longitude &&
+            mopen_location_info.altitude &&
+            mopen_location_info.timestamp &&
+            mopen_location_info.speed)
+    {
+        handle->gnss_handler_func(handle, E_MT_LOC_MSG_ID_LOCATION_INFO, &mopen_location_info, NULL);
+        memset(&mopen_location_info, 0, sizeof(mopen_location_info));
+    }
+
+    return;
+}
+
+/**
+ *  \brief get_gnss_from_str
+ *
+ *  Detailed 从串口数据解析出每条消息
+ *
+ *  \param param
+ *  \return return type
+ */
+static int get_gnss_from_str(struct mbtk_gnss_handle_t *handle,
+                             const char *data, int data_len)
+{
+    char *tail = NULL;
+    static int seek = 0;
+    // 等待 OK, 如果20条结果没有等到,就异常
+    static int reset_count = 0;
+    int i = 0, ret = -1;
+
+    if (handle->reset_state)
+    {
+        // 等待 reset 回复的 OK
+        if(0 != memcmp(data, "$OK", 3) && reset_count < 20) {
+            printf("gnss reset invalid: [%s]\n", data);
+            reset_count++;
+            return -1;
+        }
+        if (reset_count > 19) {
+            printf("%s: device reset timeout!!!\n", __FUNCTION__);
+            LOGI("%s: device reset timeout!!!\n", __FUNCTION__);
+        }
+        reset_count = 0;
+        gnss_log("reset ok: %s\n", data);
+#if BAUDRATE_115200
+        ret = mopen_uart_change(handle->dev_fd, 0);
+        if(ret) {
+            printf("reset Uart set 115200 error\n");
+        }
+#endif
+        pthread_mutex_lock(&handle->_cond_mutex);
+        handle->reset_state = 0;
+        pthread_mutex_unlock(&handle->_cond_mutex);
+    }
+
+    if((data[0] == '$' || data[0] == '#') &&
+       data[data_len - 1] == '\n' &&
+       data_len < 128) {
+        process_gnss_callback(handle, data, data_len);
+    } else {
+        gnss_log("nmea error: %s\n", data);
+    }
+
+    return 1;
+}
+
+void mopen_gnss_NonBlock(int fd, int cmd)
+{
+    int flags;
+
+    flags = fcntl(fd, F_GETFL, 0);
+    if(cmd)
+        flags |= O_NONBLOCK;
+    else
+        flags &= ~O_NONBLOCK;
+
+    fcntl(fd, F_SETFL, flags);
+}
+
+int set_baudrate(int fd, int baudrate)
+{
+    struct termios options, oldtio;
+
+    if(fcntl(fd, F_SETFL, 0) < 0) {
+        printf("fcntl failed!\n");
+        return -1;
+    }
+
+    if(tcgetattr(fd, &oldtio) != 0) {
+        printf("setup serial error!\n");
+        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))
+    {
+        printf("get options error!\n");
+        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)
+    {
+        printf("setup serial error!\n");
+        return -1 ;
+    }
+
+    return 0 ;
+}
+/*
+  自适应波特率设置
+ */
+static int auto_set_uart_baudrate(int fd)
+{
+    char rbuf[512];
+    int rByte = 0;
+    int b[3] = {B115200, B9600, 0};
+    int ret = B9600;
+    struct timeval time_m, time_n;
+    // 时间超时标志
+    int timeout_sign = 0;
+    // 先测试默认的9600波特率
+    SET_TIME_OUT(3);
+    do {
+        gettimeofday(&time_n, NULL);
+        if(time_n.tv_sec > time_m.tv_sec) {
+            printf("Baudrate--test-9600--- timeout!\n");
+            if(timeout_sign)
+                break;
+            set_baudrate(fd, B115200);
+            ret = B115200;
+            timeout_sign = 1;
+            SET_TIME_OUT(3);
+            continue;
+        }
+
+        if(select_read(fd, 1) > 0)
+            usleep(50000);
+        else
+            continue;
+
+        rByte = deal_read(fd,&rbuf,sizeof(rbuf));
+        if(rByte > 0) {
+            gnss_log("Auto Baudrate[%d]%s\n", rByte, rbuf);
+            if(strstr(rbuf, "$"))
+                return ret;
+            memset(rbuf, 0, sizeof(rbuf));
+        } else {
+            printf("*** read error\n");
+        }
+	}while(1);
+
+    return -1;
+}
+
+int mopen_gnss_open(char *dev, int baudrate)
+{
+    int ret;
+    int fd = 0;
+
+    fd = open(dev, O_RDWR | O_NOCTTY | O_NDELAY);
+    if(fd < 0) {
+        return -1;
+    }
+    gnss_log("curent dev: %s, fd: %d \n", dev, fd);
+
+    if(baudrate) {
+        gnss_log("set baudrate: %d \n", baudrate);
+        ret = set_baudrate(fd, baudrate);
+        if(-1 == ret) {
+            close(fd);
+            return -1;
+        }
+    } else {
+        set_baudrate(fd, B9600);
+    }
+
+    return fd;
+}
+
+static int mopen_gnss_read(int fd, char* buf, unsigned int buf_len)
+{
+     buf_len=(buf_len > MBTK_UART_RECV_BUFFER_SIZE ? MBTK_UART_RECV_BUFFER_SIZE : buf_len);
+     return read(fd, buf, buf_len);
+}
+
+int mopen_gnss_write(int fd, const char* buf, unsigned int buf_len)
+{
+    size_t size;
+    size_t size_to_wr;
+    ssize_t size_written;
+    if(MBTK_UART_SEND_BUFFER_MAX < buf_len)
+    {
+        return -1;
+    }
+    for(size = 0; size < buf_len;)
+    {
+        size_to_wr = buf_len - size;
+        if( size_to_wr > MBTK_UART_SEND_BUFFER_MAX)
+            size_to_wr = MBTK_UART_SEND_BUFFER_MAX;
+
+        size_written = write(fd, &buf[size], size_to_wr);
+        if (size_written==-1)
+        {
+            return -1;
+        }
+        gnss_log("send cmd: %s", &buf[size]);
+        size += size_written;
+        if(size_written != size_to_wr)
+        {
+            return size;
+        }
+    }
+    return size;
+}
+
+int mopen_gnss_close(int fd)
+{
+    return close(fd);
+}
+
+static void gnss_info_pthread(void* hdl)
+{
+    struct mbtk_gnss_handle_t *gnss_handle = (struct mbtk_gnss_handle_t *)hdl;
+    int ret = 0, i;
+    char tmp;
+    char tmp_arr[128] = {0};
+
+    pthread_detach(pthread_self());
+
+    memset(tmp_arr, 0, sizeof(tmp_arr));
+    while(mbtk_gnss_handle->inited)
+    {
+        for (i = 0; i < 256; ++i) {
+            if (0 == mbtk_gnss_handle->inited)
+                goto exit;
+            ret = ring_buffer_peek(&gnss_handle->ring_buffer, &tmp, i);
+            if (0 == ret) {
+                usleep(300000);
+                gnss_log("ring_buffer_peek ringbuffer read error\n");
+                i--;
+                continue;
+            }
+            if (tmp == '\n') {
+                break;
+            }
+        }
+
+        if (i > (256 - 2))
+            continue;
+
+        ret = ring_buffer_dequeue_arr(&gnss_handle->ring_buffer, tmp_arr, i + 1);
+
+        if(ret > 0 && 0 == mbtk_gnss_handle->getap_status) {
+            // gnss_log("NEMA:[%d] %s", ret, tmp_arr);
+            get_gnss_from_str(gnss_handle, tmp_arr, ret);
+            memset(tmp_arr, 0, sizeof(tmp_arr));
+        } else {
+            gnss_log("ringbuffer read error\n");
+        }
+        usleep(5000);
+    }
+ exit:
+    pthread_exit(NULL);
+}
+
+static void gnss_uart_pthread(void* hdl)
+{
+    struct mbtk_gnss_handle_t *gnss_handle = (struct mbtk_gnss_handle_t *)hdl;
+    int ret = 0;
+    char buf[MBTK_UART_RECV_BUFFER_SIZE] = {0};
+
+    pthread_detach(pthread_self());
+
+    memset(buf, 0, sizeof(buf));
+    while(mbtk_gnss_handle->inited)
+    {
+        while(mbtk_gnss_handle->getap_status){
+            // 在读AP_DATA星历时,不能输出NMEA
+            printf("g");
+            usleep(100000);
+        }
+        ret = mopen_gnss_read(gnss_handle->dev_fd, buf, MBTK_UART_RECV_BUFFER_SIZE);
+        if(ret > 0) {
+            // gnss_log("read: [%d] %s\n", ret, buf);
+            ring_buffer_queue_arr(&gnss_handle->ring_buffer, buf, ret);
+            memset(buf, 0, sizeof(buf));
+        } else {
+            gnss_log("read error\n");
+        }
+        usleep(100000);
+    }
+
+    pthread_exit(NULL);
+}
+
+ssize_t deal_read(int fd, void *buf, size_t count)
+{
+    int ret = 0;
+
+#if 0
+    ret = read(fd, buf, count);
+    return ret;
+#else
+    while (1)
+    {
+        ret = read(fd, buf, count);
+        if (ret == 0) {
+            printf("read serial return 0, please check serial device.\n");
+            exit(-5);
+        }
+        if(ret < 0) {
+            if ((errno == EAGAIN) || (errno == EINTR)) {
+                printf("read serial return -1, errno = %d, retry.\n", errno);
+                continue;
+            } else {
+                printf("read serial return -1, errno = %d, please check serial device.\n", errno);
+                exit(-5);
+            }
+        }
+        return ret;
+    }
+#endif
+}
+
+static int select_read( int fd, int timeout ) //1ms
+{
+    fd_set set;
+    struct timeval t;
+    int ret;
+    int i = timeout;
+
+    do {
+        FD_ZERO(&set);
+        FD_SET(fd, &set);
+        t.tv_sec = 0;
+        t.tv_usec = 100;
+
+        ret = select(FD_SETSIZE, &set, NULL, NULL, &t );
+        if(ret == 0) continue;
+        if(ret < 0 && errno == EINTR)continue;
+        else return ret;
+    } while(i--);
+
+    return ret;
+}
+
+// check: 是否需要校验
+static int mopen_uart_change(int fd, int check)
+{
+    int rByte = 0, i = 20;
+    char name[32];
+    char rbuf[1024];
+
+    sprintf(name,"$CFGPRT,1,h0,115200,129,3\r\n");
+    rByte = write( fd, name, strlen(name));
+    tcdrain(fd);
+
+    set_baudrate(fd, B115200);
+    usleep(200000);
+    tcflush(fd, TCIFLUSH);
+
+    if (0 == check)
+        return 0;
+    do{
+        rByte = 0;
+        memset(rbuf, 0, sizeof(rbuf));
+        if(select_read(fd, 1) > 0) {
+            rByte = deal_read(fd, &rbuf, sizeof(rbuf) - 1);
+            rbuf[rByte] = 0;
+            gnss_log("%s: %s", __FUNCTION__, rbuf);
+        }
+        if(strstr(rbuf, "$")) {
+            return 0;
+        } else {
+            gnss_log("%d rByte = %d, [%s]\n", 20 - i, rByte, rbuf);
+        }
+        usleep(5000 * 100);
+    }while(i--);
+
+    return -1;
+}
+
+#define GPS_DEV "/sys/devices/soc.0/d4000000.apb/mbtk-dev-op/gps_power"
+
+static int mopen_open_gps(int state)
+{
+    int fd, ret;
+    char s[4] = "on";
+    fd = open(GPS_DEV, O_RDWR | O_TRUNC, 0644);
+    if(fd < 0) {
+        LOGE("[%s]  file [%s] open error\n", __FUNCTION__, GPS_DEV);
+        return -1;
+    }
+    if(0 == state)
+    {
+        memcpy(s, "off", 3);
+    }
+    ret = write(fd, s, 4);
+    if (ret < 0) {
+        LOGE("%s: error writing to file!\n", __FUNCTION__);
+        close(fd);
+        return -2;
+    }
+
+    close(fd);
+    return 0;
+}
+
+int mbtk_gnss_client_init(uint32 *ph_gnss)
+{
+    int ret;
+
+    if(ph_gnss == NULL) {
+        printf("ARG error or has inited.");
+        return -1;
+    }
+
+    if (mbtk_gnss_handle) {
+        printf("GNSS has inited.");
+        *ph_gnss = (uint32)mbtk_gnss_handle;
+        return 0;
+    }
+    mbtk_gnss_handle = malloc(sizeof(struct mbtk_gnss_handle_t));
+    if(NULL == mbtk_gnss_handle)
+    {
+        printf("malloc memory error\n");
+        return -3;
+    }
+    memset(mbtk_gnss_handle, 0, sizeof(struct mbtk_gnss_handle_t));
+    memset(&mopen_gnss_device_info, 0, sizeof(mopen_gnss_device_info));
+
+    ret = mopen_open_gps(1);
+    if(ret) {
+        printf("GNSS open init error\n");
+        return -4;
+    }
+    sleep(1);
+
+    mbtk_gnss_handle->dev_fd = mopen_gnss_open(MBTK_GNSS_DEV, 0);
+
+    printf("Gnss Config Uart Baudrate Start -> \n");
+    ret = auto_set_uart_baudrate(mbtk_gnss_handle->dev_fd);
+    if(-1 == ret) {
+        ret = -2;
+        goto err;
+    } else if (B9600 == ret) {
+        ret = mopen_uart_change(mbtk_gnss_handle->dev_fd, 1);
+        if(ret)
+        {
+            printf("GNSS Uart set B115200 error\n");
+            mopen_gnss_close(mbtk_gnss_handle->dev_fd);
+            mopen_open_gps(0);
+            return -1;
+        }
+    }
+    printf("Gnss Config Uart Baudrate Successful.\n");
+
+    mbtk_gnss_handle->rb = malloc(MBTK_UART_RECV_BUFFER_SIZE);
+    if(NULL == mbtk_gnss_handle->rb)
+    {
+        printf("malloc memory error\n");
+        return -1;
+    }
+
+    ring_buffer_init(&mbtk_gnss_handle->ring_buffer,
+                     mbtk_gnss_handle->rb,
+                     MBTK_UART_RECV_BUFFER_SIZE);
+
+    mbtk_gnss_handle->inited = 1;
+
+    pthread_mutex_init(&mbtk_gnss_handle->_cond_mutex, NULL);
+    pthread_create(&mbtk_gnss_handle->uart_pthread, NULL, (void *)gnss_uart_pthread, (void *)mbtk_gnss_handle);
+    pthread_create(&mbtk_gnss_handle->gnss_pthread, NULL, (void *)gnss_info_pthread, (void *)mbtk_gnss_handle);
+
+    mopen_gnss_get_ant_state_info((uint32)mbtk_gnss_handle);
+    mopen_gnss_get_device_info((uint32)mbtk_gnss_handle);
+    mopen_gnss_get_nmea_config((uint32)mbtk_gnss_handle);
+    mopen_gnss_get_uart((uint32)mbtk_gnss_handle);
+    mopen_gnss_get_aidinfo((uint32)mbtk_gnss_handle);
+
+    *ph_gnss = (uint32)mbtk_gnss_handle;
+    mbtk_gnss_handle->phandle = ph_gnss;
+
+    return 0;
+ err:
+    mopen_gnss_close(mbtk_gnss_handle->dev_fd);
+    mopen_open_gps(0);
+    firmware_extren_state = 0;
+    if (mbtk_gnss_handle) free(mbtk_gnss_handle);
+    mbtk_gnss_handle = NULL;
+
+    return ret;
+}
+static int _kill_pthread(pthread_t pid, int kill)
+{
+    int ret;
+
+    if (kill) {
+        ret = pthread_cancel(pid);
+        pthread_join(pid, NULL);
+    }
+    do{
+        ret = pthread_kill(pid, 0);
+        if(ret == ESRCH)
+            gnss_log("The specified thread does not exist or has terminated\n");
+        else if(ret == EINVAL)
+            gnss_log("Useless signal\n");
+        else
+            gnss_log("The thread exists\n");
+        usleep(100000);
+    }while(0 == ret);
+
+    return 0;
+}
+int mbtk_gnss_client_deinit(uint32 h_gnss)
+{
+    int ret;
+    struct mbtk_gnss_handle_t *gnss_handle = (struct mbtk_gnss_handle_t *)h_gnss;
+
+    if(h_gnss == NULL)
+    {
+        gnss_log("ARG error or not inited.");
+        return -1;
+    }
+    gnss_handle->inited = 0;
+#if TTFF_TEST
+    // ttff测试线程在运行,而且不是临时固件模式
+    if (gnss_handle->ttff_pid &&
+        0 == firmware_extren_state &&
+        0 == location_test) {
+        gnss_log("kill thread ttff.\n");
+        _kill_pthread(gnss_handle->ttff_pid, 1);
+    }
+#endif
+    gnss_log("kill thread info 0.\n");
+    _kill_pthread(gnss_handle->gnss_pthread, 0);
+
+    gnss_log("kill thread uart.\n");
+    _kill_pthread(gnss_handle->uart_pthread, 0);
+
+    mopen_gnss_close(gnss_handle->dev_fd);
+
+    ret = mopen_open_gps(0);
+    if(ret)
+    {
+        printf("GNSS close init error\n");
+        return -1;
+    }
+
+    firmware_extren_state = 0;
+    if (gnss_handle->rb) free(gnss_handle->rb);
+    free(h_gnss);
+    mbtk_gnss_handle = NULL;
+
+    return 0;
+}
+
+int mopen_gnss_send_cmd(uint32 h_gnss, const char *cmd, int cmd_len)
+{
+    struct mbtk_gnss_handle_t *gnss_handle = (struct mbtk_gnss_handle_t *)h_gnss;
+    return  mopen_gnss_write(gnss_handle->dev_fd, cmd, cmd_len);
+}
+
+/**
+ *  \brief mbtk_gnss_dev_reset
+ *
+ *  Detailed description
+ *
+ *  \param
+ *         type: 0 软件复位
+ *               1 芯片级复位(看门狗)
+ *               2 板级复位
+ *               3 接收机停止
+ *         mode :
+ *         0 : 热启动
+ *         1 : 温启动
+ *         2 : 冷启动
+ *  \return return type
+ */
+int mbtk_gnss_dev_reset(uint32 h_gnss, int type, int mode)
+{
+    int ret;
+    struct mbtk_gnss_handle_t *handle = (struct mbtk_gnss_handle_t *)h_gnss;
+    // h00 热启动
+    // h01 温启动
+    // h85 冷启动
+    char send_buf[24] = {0};
+
+    if(0 == h_gnss){
+        printf("%s handler invalid.\n", __func__);
+        return -1;
+    }
+
+    if (1 == handle->reset_state) {
+        printf("%s already reset_state.\n", __func__);
+        return -2;
+    }
+
+    if (0 == mode || 1 == mode) {
+        snprintf(send_buf, sizeof(send_buf), "$RESET,%d,h0%d\r\n", type, mode);
+    } else if (2 == mode) {
+        snprintf(send_buf, sizeof(send_buf), "$RESET,%d,h85\r\n", type);
+    } else if (3 == mode) {
+        snprintf(send_buf, sizeof(send_buf), "$RESET,%d,hFF\r\n", type);
+    } else {
+        printf("%s reset mode invalid.\n", __func__);
+        return -2;
+    }
+
+    if ( 1 == firmware_extren_state ) {
+        if (mode > 1) {
+            mbtk_gnss_firmware_update();
+        } else {
+            memset(send_buf, 0, sizeof(send_buf));
+            // 在有GLONASS固件的情况下,冷启动指令为: $CFGSYS,H101
+            // 只发$RESET,0,hFF, 会重置波特率,待验证
+            snprintf(send_buf, sizeof(send_buf), "$CFGSYS,H101\r\n");
+        }
+    }
+
+    gnss_log("%s : %s\n", __FUNCTION__, send_buf);
+    LOGI("%s : %s", __FUNCTION__, send_buf);
+    pthread_mutex_lock(&handle->_cond_mutex);
+    handle->reset_state = 1;
+    pthread_mutex_unlock(&handle->_cond_mutex);
+    ret = mopen_gnss_send_cmd(h_gnss, send_buf, strlen(send_buf));
+    if(ret < 0)
+    {
+        printf("%s %d FAIL.	ret:%d\n", __FUNCTION__, __LINE__, ret);
+        return -1;
+    }
+
+    // 加载GLONASS固件后,波特率为115200
+    if ( 0 == firmware_extren_state ) {
+        set_baudrate(handle->dev_fd, B9600);
+        gnss_log("%s : set B9600\n", __FUNCTION__);
+    }
+
+    return 0;
+}
+
+int mopen_gnss_get_device_info(uint32 h_gnss)
+{
+    int ret;
+    char *send_buf = "$PDTINFO\r\n";
+    if(0 == h_gnss)
+    {
+        printf("%s handler invalid.\n", __func__);
+        return -1;
+    }
+
+    ret = mopen_gnss_send_cmd(h_gnss, send_buf, strlen(send_buf));
+    if(ret < 0)
+    {
+        printf("mopen_gnss_client_init FAIL.	ret:%d\n",ret);
+        return -1;
+    }
+
+    return 0;
+}
+/**
+ * @brief      mopen_gnss_set_system_config
+ *
+ * @details    设置卫星系统配置
+ *
+ * @param      mode
+ *             0 -> H01(1) –GPS L1+SBAS+QZSS
+ *             1 -> H10 – BDS B1
+ *             2 -> H101 2 – GPS+GLONASS+GALILEO+SBAS+QZSS
+ *             3 -> H11 3 – GPS+BDS+GALILEO+SBAS+QZSS
+ * @return     return type
+ */
+int mbtk_gnss_set_system_config(uint32 h_gnss, int mode)
+{
+    int ret;
+    char send_buf[20] = "$CFGSYS,H10\r\n";
+    char *str_mode[4] = {"H01", "H10", "H101", "H11"};
+    if(0 == h_gnss)
+    {
+        printf("%s handler invalid.\n", __func__);
+        return -1;
+    }
+    if( mode > 3 )
+    {
+        printf("%s param invalid.\n", __func__);
+        return -2;
+    }
+    snprintf(send_buf, sizeof(send_buf), "$CFGSYS,%s\r\n", str_mode[mode]);
+    ret = mopen_gnss_send_cmd(h_gnss, send_buf, strlen(send_buf));
+    if(ret < 0)
+    {
+        printf("%s FAIL. ret:%d\n", __FUNCTION__, ret);
+        return -3;
+    }
+
+    return 0;
+}
+/**
+ * @brief      mopen_gnss_set_nema_config
+ *
+ * @details    设定NMEA 配置
+ *             输出的NMEA 协议版本
+ *             h30 - 在 NMEA 标准 version 3.0 基础上扩展北斗相关的语句(NMEA 3.0)
+ *             h51 - 在标准NMEA4.1 基础上扩展北斗相关语(NMEA 4.1)
+ *             默认配置: h51
+ *
+ * @param      mode
+ *             0 -> h30
+ *             1 -> h51
+ *
+ * @return     return type
+ */
+int mbtk_gnss_set_nema_config(uint32 h_gnss, int mode)
+{
+    int ret;
+    char send_buf[16] = "$CFGNMEA,h30\r\n";
+    if(0 == h_gnss)
+    {
+        printf("%s handler invalid.\n", __func__);
+        return -1;
+    }
+
+    if (mode) {
+        send_buf[10] = '5';
+        send_buf[11] = '1';
+    }
+    ret = mopen_gnss_send_cmd(h_gnss, send_buf, strlen(send_buf));
+    if(ret < 0)
+    {
+        printf("%s FAIL. ret:%d\n", __FUNCTION__, ret);
+        return -3;
+    }
+
+    return 0;
+}
+int mopen_gnss_aidpos(uint32 h_gnss)
+{
+    int ret;
+    char *send_buf = "$AIDPOS,4002.229934,N,11618.096855,E,37.254\r\n";
+    if(0 == h_gnss)
+    {
+        printf("%s handler invalid.\n", __func__);
+        return -1;
+    }
+
+    ret = mopen_gnss_send_cmd(h_gnss, send_buf, strlen(send_buf));
+    if(ret < 0)
+    {
+        printf("mopen_gnss_client_init FAIL.	ret:%d\n",ret);
+        return -1;
+    }
+    return 0;
+}
+/**
+ * @brief      mopen_gnss_get_aidinfo
+ *
+ * @details    查询辅助数据状态
+ *             $CFGAID,0,D7FBFBDF,00000000,08*47
+ * @param      param
+ *
+ * @return     return type
+ */
+int mopen_gnss_get_aidinfo(uint32 h_gnss)
+{
+    int ret;
+    // char *send_buf = "$AIDINFO\r\n";
+    char *send_buf = "$CFGAID,0\r\n";
+    if(0 == h_gnss)
+    {
+        printf("%s handler invalid.\n", __func__);
+        return -1;
+    }
+
+    ret = mopen_gnss_send_cmd(h_gnss, send_buf, strlen(send_buf));
+    if(ret < 0)
+    {
+        printf("%s FAIL. ret:%d\n", __FUNCTION__, ret);
+        return -1;
+    }
+    return 0;
+}
+/**
+ * @brief      mopen_gnss_get_uart
+ *
+ * @details    get uart config info.
+ *             $CFGPRT,1,h0,9600,129,3*57
+ *
+ * @param      param
+ *
+ * @return     return type
+ */
+int mopen_gnss_get_uart(uint32 h_gnss)
+{
+    int ret;
+    char *send_buf = "$CFGPRT,1\r\n";
+    // char *send_buf = "$CFGPRT,2\r\n";
+    if(0 == h_gnss)
+    {
+        printf("%s handler invalid.\n", __func__);
+        return -1;
+    }
+
+    ret = mopen_gnss_send_cmd(h_gnss, send_buf, strlen(send_buf));
+    if(ret < 0)
+    {
+        printf("mopen_gnss_client_init FAIL.	ret:%d\n",ret);
+        return -1;
+    }
+    return 0;
+}
+
+int mopen_gnss_set_uart(uint32 h_gnss, int baudrate)
+{
+    int ret;
+    char send_buf[28] = {0};
+    // char *send_buf = "$CFGPRT,1,h0,9600,1,3\r\n";
+
+    if(0 == h_gnss)
+    {
+        printf("%s handler invalid.\n", __func__);
+        return -1;
+    }
+    sprintf(send_buf, "$CFGPRT,1,h0,%d,1,3\r\n", baudrate);
+    gnss_log("%s %s", __FUNCTION__, send_buf);
+    ret = mopen_gnss_send_cmd(h_gnss, send_buf, strlen(send_buf));
+    if(ret < 0)
+    {
+        printf("mopen_gnss_client_init FAIL.	ret:%d\n",ret);
+        return -1;
+    }
+    return 0;
+}
+
+int mopen_gnss_get_msg_output(uint32 h_gnss)
+{
+    int ret;
+    char *send_buf = "$CFGMSG,0,1\r\n"; // msg class, msg id
+
+    if(0 == h_gnss)
+    {
+        printf("%s handler invalid.\n", __func__);
+        return -1;
+    }
+    ret = mopen_gnss_send_cmd(h_gnss, send_buf, strlen(send_buf));
+    if(ret < 0)
+    {
+        printf("%s FAIL. ret:%d\n", __func__, ret);
+        return -1;
+    }
+    return 0;
+}
+
+int mopen_gnss_set_msg_output(uint32 h_gnss)
+{
+    int ret;
+    char *send_buf = "$CFGMSG,0,1,1\r\n";// msg class, msg id, msg switch
+
+    if(0 == h_gnss)
+    {
+        printf("%s handler invalid.\n", __func__);
+        return -1;
+    }
+    ret = mopen_gnss_send_cmd(h_gnss, send_buf, strlen(send_buf));
+    if(ret < 0)
+    {
+        gnss_log("%s FAIL. ret:%d\n", __func__, ret);
+        return -1;
+    }
+    return 0;
+}
+
+int mopen_gnss_set_lowpower(uint32 h_gnss)
+{
+    int ret;
+    char *send_buf = "$CFGLOWPOWER,0\r\n";// 0 - nomale, 1 - lowpower
+
+    if(0 == h_gnss)
+    {
+        printf("%s handler invalid.\n", __func__);
+        return -1;
+    }
+    ret = mopen_gnss_send_cmd(h_gnss, send_buf, strlen(send_buf));
+    if(ret < 0)
+    {
+        gnss_log("%s FAIL. ret:%d\n", __func__, ret);
+        return -1;
+    }
+    return 0;
+}
+
+int mopen_gnss_get_ant_state_info(uint32 h_gnss)
+{
+    int ret;
+    char *cmd1_buf = "$ANTSTAT,1\r\n";
+    char *cmd2_buf = "$ANTSTAT1\r\n";
+
+    if(0 == h_gnss)
+    {
+        printf("%s handler invalid.\n", __func__);
+        return -1;
+    }
+    ret = mopen_gnss_send_cmd(h_gnss, cmd1_buf, strlen(cmd1_buf));
+    if(ret < 0)
+    {
+        gnss_log("%s FAIL. ret:%d\n", __func__, ret);
+        return -1;
+    }
+    ret = mopen_gnss_send_cmd(h_gnss, cmd2_buf, strlen(cmd2_buf));
+    if(ret < 0)
+    {
+        gnss_log("%s FAIL. ret:%d\n", __func__, ret);
+        return -1;
+    }
+    return 0;
+}
+
+int mopen_gnss_get_nmea_config(uint32 h_gnss)
+{
+    int ret;
+    char *send_buf = "$CFGNMEA\r\n";
+
+    if(0 == h_gnss)
+    {
+        printf("%s handler invalid.\n", __func__);
+        return -1;
+    }
+    ret = mopen_gnss_send_cmd(h_gnss, send_buf, strlen(send_buf));
+    if(ret < 0)
+    {
+        gnss_log("%s FAIL. ret:%d\n", __func__, ret);
+        return -1;
+    }
+    return 0;
+}
+
+int mbtk_gnss_add_rx_msg_handler(uint32 h_gnss, mbtk_gnss_handler_func_t handler_ptr)
+{
+    struct mbtk_gnss_handle_t *gnss_handle = (struct mbtk_gnss_handle_t *)h_gnss;
+
+    if(0 == h_gnss && NULL == handler_ptr)
+    {
+        printf("%s handler invalid.\n", __func__);
+        return -1;
+    }
+    gnss_handle->gnss_handler_func = handler_ptr;
+
+    return 0;
+}
+
+#define AGNSS_TLE_FILE  "/tmp/agnss_tle"
+
+static void http_data_cb_func(
+    int session_id, mbtk_http_data_type_enum type,
+    void *data,int data_len)
+{
+    static int agnss_fd = 0;
+    int ret = 0;
+
+    if(type == MBTK_HTTP_DATA_HEADER) {
+        gnss_log("Header(%d):%s\n", data_len, (char*)data);
+        if(agnss_fd > 0)
+            return;
+        unlink(AGNSS_TLE_FILE);
+        agnss_fd = open(AGNSS_TLE_FILE, O_RDWR|O_CREAT|O_TRUNC, 0644);
+        if (agnss_fd <= 0)
+            gnss_log("file open error\n");
+        gnss_log("agnss file open: %d\n", agnss_fd);
+    } else if(type == MBTK_HTTP_DATA_CONTENT) {
+        gnss_log("http Data(%d)\n", data_len);
+
+        ret = write(agnss_fd, (char*)data, data_len);
+        if (ret < 0) {
+            gnss_log("%s: error writing to file!\n", __FUNCTION__);
+        } else if (ret < data_len) {
+            gnss_log("%s: wrote less the buffer size!\n", __FUNCTION__);
+        }
+    } else {
+        gnss_log(">>>>>Complete<<<<<\n");
+        if(agnss_fd <= 0)
+            return;
+        close(agnss_fd);
+        agnss_fd = 0;
+    }
+}
+
+static int gnss_http_requst(char *id, char *pw)
+{
+    char tmp[128] = {0};
+
+    int http_handle = mbtk_http_handle_get(TRUE, http_data_cb_func);
+    if(http_handle < 0)
+    {
+        printf("mbtk_http_handle_get() fail.");
+        return -1;
+    }
+
+    int http_session = mbtk_http_session_create(http_handle, HTTP_OPTION_POST, HTTP_VERSION_1_1);
+    if(http_handle < 0)
+    {
+        printf("mbtk_http_session_create() fail.");
+        return -2;
+    }
+
+    if(mbtk_http_session_url_set(http_handle, http_session, "http://unicore-api.rx-networks.cn/rxn-api/locationApi/rtcm")) {
+        printf("mbtk_http_session_url_set() fail.\n");
+        return -3;
+    }
+
+    char* post_data = "[{\"rtAssistance\":{\"format\":\"rtcm\",\"msgs\":[\"GPS:2NAF\",\"BDS:2NAF\",\"QZS:2NAF\"]}}]\r\n";
+
+    mbtk_http_session_head_add(http_handle, http_session, \
+    "Host", "unicore-api.rx-networks.cn");
+
+    sprintf(tmp, "RXN-BASIC cId=%s,mId=Unicore,dId=12-23-34-45-58,pw=%s", id, pw);
+    mbtk_http_session_head_add(http_handle, http_session, \
+    "Authorization", tmp);
+
+    mbtk_http_session_head_add(http_handle, http_session, \
+            "Content-Type", "application/json");
+
+    mbtk_http_session_head_add(http_handle, http_session, \
+            "Accept", "application/octet-stream");
+    mbtk_http_session_content_set(http_handle, http_session,
+                                  post_data, strlen(post_data));
+
+    if(mbtk_http_session_start(http_handle, http_session)) {
+        printf("mbtk_http_session_start() fail.\n");
+        return -4;
+    }
+
+    if(mbtk_http_handle_free(http_handle))
+    {
+        printf("mbtk_http_handle_free() fail.");
+        return -5;
+    }
+
+    return 0;
+}
+
+/**********************************
+
+ ID1: TempID1Expire20221031
+ Base 64 PW1: RlJYdkFTNE9DWXJhN2ZWTA==
+**************************************/
+#define AGNSS_CONFIG_FILE  "/etc/mbtk/gps.conf"
+
+/**
+ * @brief      mopen_gnss_download_tle
+ *
+ * @details    下载星历数据
+ *             (卫星星历,又称为两行轨道数据(TLE,Two-Line Orbital Element))
+ *             保存到文件:AGNSS_TLE_FILE
+ * @param      param
+ *
+ * @return     return type
+ */
+int mbtk_gnss_download_tle(void)
+{
+    FILE *fp;
+    char StrLine[64];
+    char _id[24] = {0};
+    char _passwd[28] = {0};
+    int i;
+    if((fp = fopen(AGNSS_CONFIG_FILE, "r")) == NULL)
+    {
+        printf("open %s error!\n", AGNSS_CONFIG_FILE);
+        return -1;
+    }
+
+    while (!feof(fp))
+    {
+        memset(StrLine, 0, 64);
+        fgets(StrLine, 64, fp);
+        gnss_log("%s\n", StrLine);
+        i = strstr_n(StrLine, ": ");
+        if(i && strstr_n(StrLine, "ID"))
+        {
+            memcpy(_id, &StrLine[i + 1], strlen(StrLine) - i - 2);
+        }
+        else if( i && strstr_n(StrLine, "Base 64"))
+        {
+            memcpy(_passwd, &StrLine[i + 1], strlen(StrLine) - i - 2);
+        }
+    }
+    fclose(fp);
+    gnss_log("%s : %s[%d], %s[%d]\n", __FUNCTION__, _id, strlen(_id), _passwd, strlen(_passwd));
+
+    return gnss_http_requst(_id, _passwd);
+}
+
+/**
+ * @brief      mopen_gnss_injects_aidpos
+ *
+ * @details    注入星历, 128 bytes
+ *
+ * @param      param
+ *
+ * @return     return type
+ */
+int mbtk_gnss_injects_aidpos(uint32 h_gnss)
+{
+    int ret;
+    int agnss_fd = 0;
+    int size = 0;
+
+    if(0 == h_gnss)
+    {
+        printf("%s handler invalid.\n", __func__);
+        return -1;
+    }
+
+    agnss_fd = open(AGNSS_TLE_FILE, O_RDWR);
+    if (agnss_fd <= 0)
+    {
+        printf("%s open file FAIL. errno:%d\n", __FUNCTION__, errno);
+        return -1;
+    }
+    char* databuf = (char*)malloc(128);
+    if(databuf == NULL)
+    {
+        gnss_log("%s malloc() fail.", __FUNCTION__);
+        return -1;
+    }
+    memset(databuf, 0, 128);
+    while(0 < (size = read(agnss_fd, databuf, 128)))
+    {
+        gnss_log("%s Write[%d]\r\n", __FUNCTION__, size);
+        ret = mopen_gnss_send_cmd(h_gnss, databuf, size);
+        if(ret < 0)
+        {
+            printf("%s send cmd FAIL. ret:%d\n", __FUNCTION__, ret);
+            break;
+        }
+        memset(databuf, 0, 128);
+    }
+    close(agnss_fd);
+    free(databuf);
+    mopen_gnss_get_aidinfo(h_gnss);
+
+    return 0;
+}
+/**
+ * @brief      mopen_gnss_set_mode
+ *
+ * @details    detailed description
+ *
+ * @param      mode
+ *                  0 : stop
+ *                  1 : 输出一次坐标
+ *                  2 : stop
+ *                  3 : 输出nmea数据到回调函数
+ *    0 - stop, 1 - single, 2 - periodic, 3 - start
+ *
+ * @return     return type
+ */
+int mbtk_gnss_set_mode(uint32 h_gnss, int mode)
+{
+    struct mbtk_gnss_handle_t *gnss_handle = (struct mbtk_gnss_handle_t *)h_gnss;
+    if(0 == h_gnss)
+    {
+        printf("%s handler invalid.\n", __func__);
+        return -1;
+    }
+    gnss_handle->mode = mode;
+
+    return 0;
+}
+
+int mbtk_gnss_print_version(uint32 h_gnss)
+{
+    printf("*************************\n");
+    printf("-Pn: %s\n dc: %s\n hv: %s\n fw: %s\n pn: %s\n sn: %s\n ",
+                        mopen_gnss_device_info.product_name,
+                        mopen_gnss_device_info.dev_config,
+                        mopen_gnss_device_info.hw_ver,
+                        mopen_gnss_device_info.fw_ver,
+                        mopen_gnss_device_info.pn,
+                        mopen_gnss_device_info.sn);
+    printf("Uart bandrate: %d\n" , mopen_gnss_device_info.usrt_bandrate);
+    printf("*************************\n");
+
+    return 0;
+}
+
+/**
+* @brief 使用popen调用终端并获取执行结果
+*
+* @param[in] cmd 命令内容
+* @param[out] result 保存结果的地址
+* @return 0或1 执行状态,成功或失败
+*/
+int exec_cmd(const char *cmd, char *result)
+{
+    FILE *pipe = popen(cmd, "r");
+    if(!pipe)
+        return -1;
+
+    char buffer[256] = {0};
+    while(!feof(pipe))
+    {
+        if(fgets(buffer, 256, pipe))
+        {
+            printf("%s", buffer);
+            memset(buffer, 0, sizeof(buffer));
+        }
+    }
+    pclose(pipe);
+    return 0;
+}
+
+#define GNSS_AP_DATA_FILE  "/etc/mbtk/rtm.bin"
+
+int mbtk_gnss_get_ap_data(void)
+{
+    int state = 0;
+    uint32 *ph_gnss = NULL;
+    mbtk_gnss_handler_func_t cb;
+    int current_mode;
+    const char* cmd = "mbtk_gnss_update getap -d /dev/ttyS2 -b 115200 -a /etc/mbtk/rtm.bin";
+
+	if(access(GNSS_AP_DATA_FILE, F_OK) != -1) {
+        unlink(GNSS_AP_DATA_FILE);
+    }
+    mbtk_gnss_handle->getap_status = 1;
+    sleep(1);
+    printf("Mopen Gnss Get Ap Data -> \n");
+    int ret = exec_cmd(cmd, NULL);
+
+    usleep(100000);
+    mbtk_gnss_handle->getap_status = 0;
+    if(0 != ret) {
+        printf("Gnss getap result: %x\n", ret);
+        return -1;
+    }
+    LOGI("%s %d: %d.\n", __FUNCTION__, __LINE__, ret);
+    return ret;
+}
+/*
+  sync : 1
+ */
+int mbtk_gnss_firmware_update(void)
+{
+    int state = 0;
+    uint32 *ph_gnss = NULL;
+    mbtk_gnss_handler_func_t cb;
+    int current_mode;
+	int fd = -1;
+	int ret = 0;
+
+    const char* cmd_1 = "mbtk_gnss_update downbl -d /dev/ttyS2 \
+                       -l /etc/mbtk/bootloader_r3.0.0_build6773_uartboot_921600.pkg";
+
+    const char* cmd_2 = "mbtk_gnss_update sendap -d /dev/ttyS2 -b 921600 -a /etc/mbtk/rtm.bin";
+
+    const char* cmd_3 = "mbtk_gnss_update downfw -d /dev/ttyS2 -b 921600\
+                       -f /etc/mbtk/UC6228CI-R3.4.21.0Build16211_G1L1E1_mfg.pkg";
+                       // /etc/mbtk/UC6228CI-R3.4.0.0Build7258_mfg.pkg
+
+    if (mbtk_gnss_handle) {
+        printf("%s gnss thread runing!!!\n", __func__);
+        if (mbtk_gnss_handle->gnss_handler_func)
+            cb = mbtk_gnss_handle->gnss_handler_func;
+
+        ph_gnss = mbtk_gnss_handle->phandle;
+        current_mode = mbtk_gnss_handle->mode;
+        // 主线程是否在运行
+        if (mbtk_gnss_handle->gnss_pthread)
+            state = 1;
+        mbtk_gnss_client_deinit(mbtk_gnss_handle);
+    }
+    printf("Mopen Gnss Bootloader Update -> \n");
+    //int ret = exec_cmd(cmd_1, NULL);
+
+	fd=initial_serialPort("/dev/ttyS2");
+	ret = downloadBL(fd, "/etc/mbtk/bootloader_r3.0.0_build6773_uartboot_921600.pkg");
+    if(0 != ret) {
+        printf("Gnss update result: %x\n", ret);
+		close(fd);
+        return -1;
+    }
+
+    if(access(GNSS_AP_DATA_FILE, F_OK) != -1)
+    {
+        printf("Mopen Gnss Send AP Data  -> \n");
+        //ret = exec_cmd(cmd_2, NULL);
+
+		set_baudrate(fd, B921600);
+		ret = sendAPData(fd, "/etc/mbtk/rtm.bin");
+		
+        if(0 != ret) {
+			close(fd);
+            printf("Gnss update result: %x\n", ret);
+        }
+    }
+
+    printf("Mopen Gnss Firmware Update -> \n");
+    //ret = exec_cmd(cmd_3, NULL);
+	set_baudrate(fd, B921600);
+	ret = downloadFW(fd, "/etc/mbtk/UC6228CI-R3.4.21.0Build16211_G1L1E1_mfg.pkg");
+	close(fd);
+
+    if(0 != ret) {
+        printf("Gnss update result: %x\n", ret);
+        return -2;
+    }
+
+    if (0 == firmware_extren_state)
+        firmware_extren_state = 1;
+    if (state && ph_gnss) {
+        ret = mbtk_gnss_client_init(ph_gnss);
+        if (cb) {
+            mbtk_gnss_handle->gnss_handler_func = cb;
+            mbtk_gnss_handle->mode = current_mode;
+        }
+    }
+
+    LOGI("%s %d: %d.\n", __FUNCTION__, __LINE__, ret);
+    return ret;
+}
+
+int mbtk_at_gnss(int start_stop, void *cb)
+{
+    static uint32 loc = 0;
+    int ret = -1;
+
+    LOGI("%s %d", __FUNCTION__, __LINE__);
+    switch (start_stop) {
+    case 0: {
+        ret = mbtk_gnss_set_mode(loc, 0);
+        if(ret)
+            return -3;
+        ret = mbtk_gnss_client_deinit(loc);
+        if(ret)
+            return -3;
+        loc = 0;
+        return 0;
+    }
+    case 1: {
+        if (0 != loc)
+            return -1;
+
+        ret = mbtk_gnss_client_init(&loc);
+        if(ret || 0 == loc)
+            return -1;
+
+        if(NULL == cb)
+            return -2;
+
+        ret = mbtk_gnss_add_rx_msg_handler(loc, (mbtk_gnss_handler_func_t)cb);
+        if(ret)
+            return -2;
+        ret = mbtk_gnss_set_mode(loc, 3);
+        break;
+    }
+    case 2: {
+        ret = mbtk_gnss_set_mode(loc, 0);
+        break;
+    }
+    case 3: {
+        ret = mbtk_gnss_set_mode(loc, 3);
+        break;
+    }
+    case 4: {
+        ret = mopen_uart_change(((struct mbtk_gnss_handle_t *)loc)->dev_fd, 1);
+        if(ret) {
+            printf("reset Uart set 115200 error\n");
+        }
+        break;
+    }
+    case 6: {
+        ret = mbtk_gnss_firmware_update();
+        if(ret) {
+            printf("gnss firmware update error!!\n");
+        }
+		break;
+    }
+	case 11:{
+		ret = mbtk_gnss_set_system_config((uint32)mbtk_gnss_handle,3);//GPS+BD
+		break;
+	}
+	case 10:{
+		ret = mbtk_gnss_set_system_config((uint32)mbtk_gnss_handle,0);// only GPS
+		break;
+	}
+	case 9:{
+		ret = mbtk_gnss_set_system_config((uint32)mbtk_gnss_handle,2);//GPS+GLONASS
+		break;
+	}	
+    default:
+        break;
+    }
+
+    return ret;
+}
+/*
+    0     热启动
+    1     冷启动
+    2     温启动
+ */
+int mbtk_at_gnss_reset(int type)
+{
+    switch (type)
+    {
+    case 0: {
+        // $RESET,0,h0
+        return mbtk_gnss_dev_reset((uint32)mbtk_gnss_handle, 0, 0);
+    }
+    case 1: {
+        // $RESET,0,h85
+        return mbtk_gnss_dev_reset((uint32)mbtk_gnss_handle, 0, 2);
+    }
+    case 2: {
+        // $RESET,0,h01
+        return mbtk_gnss_dev_reset((uint32)mbtk_gnss_handle, 0, 1);
+    }
+    case 3: {
+        return mbtk_gnss_dev_reset((uint32)mbtk_gnss_handle, 0, 3);
+    }
+    case 4: {
+        return mbtk_gnss_dev_reset((uint32)mbtk_gnss_handle, 3, 0);
+    }
+    case 5: {
+        return mbtk_gnss_dev_reset((uint32)mbtk_gnss_handle, 1, 0);
+    }
+    default:
+        break;
+    }
+    return -9;
+}
+
+#if TTFF_TEST
+static void gnss_gsa_info(int cmd, char *str, void *data)
+{
+    char tmp_str[32] = {0};
+    int ret;
+
+    gnss_log("[ GSA ]: ");
+    // $xxGSA
+    ret = gnss_get_para_from_nmea(str, tmp_str, 1);
+    if(0 == ret && ('A' == tmp_str[0])){
+        gnss_log("Smode: %s, ", tmp_str);
+        memset(tmp_str, 0, 32);
+    } else {
+        printf("%s [exit]: %s\n", __FUNCTION__, str);
+        return;
+    }
+
+    /* 定位模式:
+            1-未定位
+            2-2D 定位
+            3-3D 定位
+     */
+    ret = gnss_get_para_from_nmea(str, tmp_str, 2);
+    if(ret){
+        gnss_log("L%d %s error! \n");
+        return;
+    }
+
+    gnss_log("fs: %s - [%d]", tmp_str, atoi(tmp_str));
+    if ( atoi(tmp_str) != mbtk_gnss_handle->location_state ) {
+        mbtk_gnss_handle->location_state = atoi(tmp_str);
+        if (mbtk_gnss_handle->location_state > 1 && location_test) {
+            pthread_mutex_lock(&loc_cond_mutex_r);
+            location_test = 0;
+            pthread_cond_signal(&loc_sync_cond);
+            pthread_mutex_unlock(&loc_cond_mutex_r);
+        }
+    }
+    gnss_log(" -- \n");
+}
+
+int mbtk_gnss_test_ttff(int type, int timeout_sec)
+{
+    struct timeval tnow;
+    struct timespec tout;
+    long t_start, t_end;
+    int ret;
+
+    pthread_mutex_lock(&loc_cond_mutex_r);
+    location_test = 1;
+    pthread_mutex_unlock(&loc_cond_mutex_r);
+
+    switch (type)
+    {
+    case 0: {
+        // $RESET,0,h0
+        mbtk_gnss_dev_reset((uint32)mbtk_gnss_handle, 0, 0);
+        break;
+    }
+    case 1: {
+        // $RESET,0,hFF
+        mbtk_gnss_dev_reset((uint32)mbtk_gnss_handle, 0, 3);
+        break;
+    }
+    case 2: {
+        // $RESET,0,h01
+        mbtk_gnss_dev_reset((uint32)mbtk_gnss_handle, 0, 1);
+        break;
+    }
+    case 3: {
+        // 在有GLONASS固件的情况下,冷启动指令为: $CFGSYS,H101
+        // 只发$RESET,0,hFF, 会重置波特率,待验证
+        mopen_gnss_set_system_config((uint32)mbtk_gnss_handle, 2);
+        break;
+    }
+    case 4: {
+        if ( 0 == firmware_extren_state )
+            return -2;
+
+        ret = mopen_gnss_firmware_update();
+        if(ret) {
+            printf("gnss firmware update error!!\n");
+        }
+        break;
+    }
+    default:
+        break;
+    }
+    if (0 == timeout_sec )
+        timeout_sec = 60 * 3;
+    gettimeofday(&tnow, NULL);
+    t_start = tnow.tv_sec * 1000 * 1000 + tnow.tv_usec;
+    tout.tv_sec = tnow.tv_sec + timeout_sec;
+    tout.tv_nsec = tnow.tv_usec;
+    if (tout.tv_nsec > 1000000000)
+    {
+        tout.tv_sec += 1;
+        tout.tv_nsec -= 1000000000;
+    }
+    pthread_mutex_lock(&loc_cond_mutex_r);
+    ret = pthread_cond_timedwait(&loc_sync_cond, &loc_cond_mutex_r, &tout);
+    pthread_mutex_unlock(&loc_cond_mutex_r);
+    gettimeofday(&tnow, NULL);
+    t_end = tnow.tv_sec * 1000 * 1000 + tnow.tv_usec;
+    gnss_log("gnss ttff time:%ld\n", t_end - t_start);
+    if(ret == ETIMEDOUT) {
+        location_test = 0;
+        return -1;
+    }
+
+    return (t_end - t_start)/1000;
+}
+
+#define GNSS_TEST_FILE  "/tmp/gnss_test"
+#define INSERT_CUT_OFF_RULE() { memset(buffer, 0, sizeof(buffer)); \
+    sprintf(buffer, "----------------------------------\r\n"); \
+    ret = write(fd, buffer, strlen(buffer)); \
+    if (ret < 0) { \
+        printf("%s write error !!\n", __func__); \
+    } \
+}
+
+void *gnss_ttff_thread(void *data)
+{
+    struct mbtk_gnss_ttff_t *handle = (struct mbtk_gnss_ttff_t *)data;
+    int fd, ret, i;
+    int index, time_ms;
+    float loc_time;
+    char buffer[128] = {0};
+
+    pthread_detach(pthread_self());
+
+	if ( !access(GNSS_TEST_FILE, F_OK) ){
+        unlink(GNSS_TEST_FILE);
+    }
+
+    pthread_cond_init(&loc_sync_cond, NULL);
+    pthread_mutex_init(&loc_cond_mutex_r, NULL);
+    fd = open(GNSS_TEST_FILE, O_RDWR|O_CREAT|O_TRUNC, 0644);
+    if (fd <= 0) {
+        gnss_log("file open error\n");
+    }
+    INSERT_CUT_OFF_RULE()
+    sprintf(buffer, "type: %d, timeout: %d, count: %d\r\n", handle->type,
+                                                          handle->timeout_sec,
+                                                          handle->test_count);
+    ret = write(fd, buffer, strlen(buffer));
+    if (ret < 0) {
+        printf("%s write error !!\n", __func__);
+    }
+    INSERT_CUT_OFF_RULE()
+    for (i = 0; i < handle->test_count; ++i) {
+        memset(buffer, 0, sizeof(buffer));
+        time_ms = mbtk_gnss_test_ttff(handle->type, handle->timeout_sec);
+        if (-1 == time_ms)
+            loc_time = time_ms;
+        else
+            loc_time = ((float)time_ms) / 1000;
+        sprintf(buffer, "\t %d - [ %f s ]\r\n", i + 1, loc_time);
+        printf("\t %d - %f\n", i + 1, loc_time);
+        ret = write(fd, buffer, strlen(buffer));
+        if (ret < 0) {
+            printf("%s write error !!\n", __func__);
+        }
+    }
+
+    INSERT_CUT_OFF_RULE()
+    close(fd);
+    mbtk_gnss_handle->ttff_pid = 0;
+    pthread_cond_destroy(&loc_sync_cond);
+    pthread_mutex_destroy(&loc_cond_mutex_r);
+    pthread_exit(NULL);
+}
+#endif
+
+int mbtk_at_gnss_start_ttff(int type, int timeout_sec, int count)
+{
+#if TTFF_TEST
+    int ret;
+    static struct mbtk_gnss_ttff_t mbtk_gnss_ttff;
+
+    LOGI("%s %d, %d, %d", __FUNCTION__, type, timeout_sec, count);
+    if (NULL == mbtk_gnss_handle) {
+        printf("%s not init!!!\n", __func__);
+        return -1;
+    }
+    if (mbtk_gnss_handle->ttff_pid) {
+        printf("%s busy!!!\n", __func__);
+        return -2;
+    }
+    mbtk_gnss_ttff.type = type;
+    mbtk_gnss_ttff.timeout_sec = timeout_sec;
+    mbtk_gnss_ttff.test_count = count;
+    ret = pthread_create(&mbtk_gnss_handle->ttff_pid, NULL, (void *)gnss_ttff_thread, &mbtk_gnss_ttff);
+    if (ret != 0)
+    {
+        fprintf(stderr, "\n%s: Failed create pthread\n", __FUNCTION__);
+        return ret;
+    }
+
+#endif
+    return 0;
+}
diff --git a/mbtk/mbtk_lib/src/mbtk_gnss_update.c b/mbtk/mbtk_lib/src/mbtk_gnss_update.c
new file mode 100755
index 0000000..2c0657e
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_gnss_update.c
@@ -0,0 +1,1283 @@
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <time.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/times.h>
+#include <sys/types.h>
+#include <termios.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <ctype.h>
+#include <sched.h>
+#include <limits.h>
+#include <linux/serial.h>
+
+#define RTM_FILE_NAME "rtm.bin"
+
+#define PARA_ERR                -1
+#define FILE_CHECK_ERR          -2
+#define ENTER_UPDATE_MODE_ERR   -3
+#define UPDATE_ERR              -4
+#define UART_DEV_ERR            -5
+
+#define XMODEM_SOH 0x01
+#define XMODEM_STX 0x02
+#define XMODEM_EOT 0x04
+#define XMODEM_ACK 0x06
+#define XMODEM_NAK 0x15
+#define XMODEM_CAN 0x18
+#define XMODEM_CRC_CHR	'C'
+#define XMODEM_CRC_SIZE 2		/* Crc_High Byte + Crc_Low Byte */
+#define XMODEM_FRAME_ID_SIZE 2 		/* Frame_Id + 255-Frame_Id */
+#define XMODEM_DATA_SIZE_SOH 128  	/* for Xmodem protocol */
+#define XMODEM_DATA_SIZE_STX 1024 	/* for 1K xmodem protocol */
+#define USE_1K_XMODEM 1  		/* 1 for use 1k_xmodem 0 for xmodem */
+#define	TIMEOUT_USEC	0
+#define	TIMEOUT_SEC		10
+
+#if (USE_1K_XMODEM)
+#define XMODEM_DATA_SIZE 	XMODEM_DATA_SIZE_STX
+#define XMODEM_HEAD		XMODEM_STX
+#else
+#define XMODEM_DATA_SIZE 	XMODEM_DATA_SIZE_SOH
+#define XMODEM_HEAD 		XMODEM_SOH
+#endif
+
+
+/******************************************************************************
+ * 时间处理相关的宏
+ *****************************************************************************/
+// 时间超时标志
+int timeout_sign = 1;
+// 获取当前时间
+#define GET_TIME()  { gettimeofday(&time_m, NULL); \
+	time_m.tv_sec += TIMEOUT_SEC;\
+}
+// 设置从循环中退出的时间
+#define SET_TIME_OUT(x)  { gettimeofday(&time_m, NULL); \
+	time_m.tv_sec += x;\
+}
+// 检测时间是否超时,超时则退出当前函数
+#define CHK_TIME()  { gettimeofday(&time_n, NULL); \
+	if(time_n.tv_sec > time_m.tv_sec) { \
+		printf("\ntimeout!!!\n\n");\
+		close(fd); \
+		return ret; \
+	} \
+}
+// 检测时间是否超时,超时则退出当前循环
+#define CHK_TIME_BREAK()  { gettimeofday(&time_n, NULL); \
+	if(time_n.tv_sec > time_m.tv_sec) { \
+		timeout_sign = 1; 	\
+		printf("\ntimeout!!!\n\n");\
+		break; \
+	} \
+}
+// 检测延时是否到达,到达则退出当前循环
+#define DELAY_TIME_BREAK()  { gettimeofday(&time_n, NULL); \
+	if(time_n.tv_sec > time_m.tv_sec) { \
+		timeout_sign = 1; 	\
+		break; \
+	} \
+}
+
+// 检测时间是否超时,超时则退出当前函数
+#define CHK_TIME1()  { gettimeofday(&time_n, NULL); \
+	if(time_n.tv_sec > time_m.tv_sec) { \
+		printf("\ntimeout!!!\n\n");\
+		if(datafile != NULL) \
+		fclose(datafile); \
+		return ret; \
+	} \
+}
+
+
+/******************************************************************************
+ * APData 相关定义、声明
+ *****************************************************************************/
+// APData 数据头部定义
+typedef struct {
+	unsigned int  ih_magic;		// Image Header Magic Number
+	unsigned int  ih_dcrc;		// Image Data CRC checksum
+	unsigned int  ih_time;		// Image Creation Timestamp
+	unsigned int  ih_size;		// Image Data Size
+	unsigned int  ih_compress;		// Image Header compress or not:0, not compress
+	unsigned int  pkg_flash_addr;	// flash memory offset for package
+	unsigned int  pkg_run_addr;		// Run time address for this package
+	unsigned int  ih_hcrc;		// Image Header CRC checksum
+} uc_image_header_t;
+
+// APData 数据头部长度
+#define HEADERLEN 32
+// APData 接收状态类型
+typedef enum ReceStat{
+	WAIT_FD = 1,
+	WAIT_FC,
+	WAIT_FB,
+	WAIT_FA,
+	RECE_HEADER,
+	RECE_DATA,
+} RecvStat_t;
+// APData 接收状态变量
+static RecvStat_t recvStat = WAIT_FD;
+// APData 接收状态变量
+static int isStartReceive = 0;
+// APData 开始接收
+void af_start_receive();
+// APData 停止接收,在数据接收完成或出错时调用
+void af_stop_receive();
+// 获取isStartReceive 变量值
+int af_is_start_receive();
+// APData 数据接收入口
+int af_add_char(char *file, unsigned char c);
+// 校验 APData 数据头
+int af_check_header(unsigned char *pbuf, unsigned int len);
+// 校验 APData 数据区
+int af_check_data(unsigned int * pbuf, unsigned int len);
+// 获取 APData 数据长度
+int af_get_data_len();
+// APData 数据接收缓存
+unsigned int recv_buf[1024 * 2];
+unsigned char *pbuf;
+int data_len;
+// 校验文件
+int check_file(char *fname);
+
+/******************************************************************************
+ * 与接收机串口通讯相关定义、声明
+ *****************************************************************************/
+// select功能使用变量
+static int use_select = 1;
+// 初始化串口资源
+int initial_serialPort(char * serial_device);
+// 设置串口波特率
+static int set_baudrate(int fd, int baudrate);
+// 检测串口数据
+static int select_read(int fd, int timeout);
+// 读取串口数据
+static ssize_t deal_read(int fd, void *buf, size_t count);
+// 向接收机发送串口数据
+static void gps_dev_send(int fd, char *msg);
+// 通过xmodem协议向接收机发送串口数据
+int xmodem_send(int fd, char *fname);
+
+/******************************************************************************
+ * apflash 功能函数
+ *****************************************************************************/
+// 检测接收机发送的'YC'信号,如果收到,则标志着接收机进入boot模式,并可以通过xmodem发送数据
+int check_YC(char newchar);
+// 获取 APData 数据, 应该在接收机定位且星历数据接收完整时获取
+int getAPData(int fd, char *file);
+// 向接收机发送 APData 数据,有效的APData数据可以让CI模块迅速定位
+int sendAPData(int fd, char *ap);
+// 校验 APData 数据文件
+int checkAPData(char *apFile);
+// 为接收机加载BootLoader,该流程执行时,需要根据终端提示,给接收机下电和上电,确保接收机进入boot模式
+int downloadBL(int fd, char *bl);
+// 为接收机加载Firmware,该流程必须在加载BootLoader之后进行
+int downloadFW(int fd, char *fw);
+
+void printGetapUsage(char *app)
+{
+	printf("\n%s getap -d receiverPort [-b baudrate] [-h] -a apfile \n", app);
+	printf("\tfunction: read APData from receiver\n");
+	printf("\tparas:\n");
+	printf("\t\treceiverPort: Port that connected to receiver\n");
+	printf("\t\tbaudrate: baudrate of receiverPort\n");
+}
+
+void printSendapUsage(char *app)
+{
+	printf("\n%s sendap -d receiverPort [-b baudrate] [-h] -a apfile\n", app);
+	printf("\tfunction: send APData to receiver\n");
+	printf("\tparas:\n");
+	printf("\t\treceiverPort: Port that connected to receiver\n");
+	printf("\t\tbaudrate: baudrate of receiverPort\n");
+}
+
+void printCheckapUsage(char *app)
+{
+	printf("\n%s checkap -a apfile [-h]\n", app);
+	printf("\tfunction: check APData\n");
+	printf("\tparas:\n");
+	printf("\t\tapfile: APData file\n");
+}
+
+void printDownloadblUsage(char *app)
+{
+	printf("\n%s downbl -d receiverPort [-b baudrate] -l bootloader [-h]\n", app);
+	printf("\tfunction: download bootloader to receiver\n");
+	printf("\tparas:\n");
+	printf("\t\treceiverPort: Port that connected to receiver\n");
+	printf("\t\tbaudrate: baudrate of receiverPort\n");
+	printf("\t\tbootloader: bootloader file\n");
+}
+
+void printDownloadfwUsage(char *app)
+{
+	printf("\n%s downfw -d receiverPort [-b baudrate] -f firmware [-h]\n", app);
+	printf("\tfunction: download firmware to receiver\n");
+	printf("\tparas:\n");
+	printf("\t\treceiverPort: Port that connected to receiver\n");
+	printf("\t\tbaudrate: baudrate of receiverPort\n");
+	printf("\t\tfirmware: firmware file\n");
+}
+
+void printUsage(char *app)
+{
+	printGetapUsage(app);
+	printSendapUsage(app);
+	printCheckapUsage(app);
+	printDownloadblUsage(app);
+	printDownloadfwUsage(app);
+}
+
+#define GPS_DEV "/sys/devices/soc.0/d4000000.apb/mbtk-dev-op/gps_power"
+
+static int mopen_open_gps(int state)
+{
+    int fd, ret;
+    char s[4] = "on";
+    fd = open(GPS_DEV, O_RDWR | O_TRUNC, 0644);
+    if(fd < 0) {
+        printf("[%s]  file [%s] open error\n", __FUNCTION__, GPS_DEV);
+        return -1;
+    }
+    if(0 == state)
+    {
+        memcpy(s, "off", 3);
+    }
+    ret = write(fd, s, 4);
+    if (ret < 0) {
+        printf("%s: error writing to file!\n", __FUNCTION__);
+        close(fd);
+        return -2;
+    }
+
+    close(fd);
+    return 0;
+}
+
+int main(int argc, char *argv[])
+{
+	int c;
+	int ret = 0;
+	int fd = -1;
+	int paraMask = 0;
+	int baud = 115200;
+	char devPort[200] = {0};
+	char fw[200] = {0};
+	char bl[200] = {0};
+	char ap[200] = {0};
+	int func = 0; // 1:getap, 2:sendap, 3:downbl, 4:downfw, 5:checkap
+
+	// Verify arguments
+	if (argc < 2) {
+		printf("Usage:\n");
+		printUsage(argv[0]);
+		exit(1);
+	}
+
+	if(strcmp(argv[1], "getap") == 0) {
+		func = 1;
+		paraMask = 1;
+	} else if (strcmp(argv[1], "sendap") == 0) {
+		func = 2;
+		paraMask = 1;
+	} else if (strcmp(argv[1], "downbl") == 0) {
+		func = 3;
+		paraMask = 9;
+	} else if (strcmp(argv[1], "downfw") == 0) {
+		func = 4;
+		paraMask = 5;
+	} else if (strcmp(argv[1], "checkap") == 0) {
+		func = 5;
+		paraMask = 16;
+	} else {
+		printf("Usage:\n");
+		printUsage(argv[0]);
+		exit(1);
+	}
+
+	for(;;) {
+		opterr = 0;
+		c = getopt(argc, argv, "d:b:f:l:a:h");
+		if(c < 0)
+			break;
+		switch(c) {
+			case 'd':
+				snprintf(devPort, 200, "%s", optarg);
+				printf("receiver port: %s\n", devPort);
+				paraMask &= ~1;
+				break;
+			case 'b':
+				baud = atoi(optarg);
+				printf("baud rate: %d\n", baud);
+				break;
+			case 'f':
+				snprintf(fw, 200, "%s", optarg);
+				printf("firmware: %s\n", fw);
+				paraMask &= ~4;
+				break;
+			case 'l':
+				snprintf(bl, 200, "%s", optarg);
+				printf("bootloader: %s\n", bl);
+				paraMask &= ~8;
+				break;
+			case 'a':
+				snprintf(ap, 200, "%s", optarg);
+				printf("apdata file: %s\n", ap);
+				paraMask &= ~16;
+				break;
+			case 'h':
+			default:
+				printf("Usage:\n");
+				if (func == 1)
+					printGetapUsage(argv[0]);
+				else if (func == 2)
+					printSendapUsage(argv[0]);
+				else if (func == 3)
+					printDownloadblUsage(argv[0]);
+				else if (func == 4)
+					printDownloadfwUsage(argv[0]);
+				else if (func == 5)
+					printCheckapUsage(argv[0]);
+				else
+					printUsage(argv[0]);
+				exit(1);
+		}
+	}
+	if (paraMask) {
+		if (func == 1)
+			printGetapUsage(argv[0]);
+		else if (func == 2)
+			printSendapUsage(argv[0]);
+		else if (func == 3)
+			printDownloadblUsage(argv[0]);
+		else if (func == 4)
+			printDownloadfwUsage(argv[0]);
+		else if (func == 5)
+			printCheckapUsage(argv[0]);
+		else
+			printUsage(argv[0]);
+		exit(1);
+	}
+
+	// Open serial port
+	if (func != 5) {
+		if ((fd = initial_serialPort(devPort)) == -1)
+		{
+			printf("Can't open COM\n");
+			return UART_DEV_ERR;
+		}
+		if (baud == 115200) {
+			set_baudrate(fd, B115200);
+		} else if (baud == 921600) {
+			set_baudrate(fd, B921600);
+		} else if (baud == 1843200) {
+			set_baudrate(fd, B1500000);
+		} else {
+			printf("baudrate %d not supported\n", baud);
+			close(fd);
+			exit(1);
+		}
+	}
+
+	// execute function
+	switch(func) {
+		case 1:
+			ret = getAPData(fd, ap);
+			break;
+		case 2:
+			ret = sendAPData(fd, ap);
+			break;
+		case 3:
+			ret = downloadBL(fd, bl);
+			break;
+		case 4:
+			ret = downloadFW(fd, fw);
+			break;
+		case 5:
+			ret = checkAPData(ap);
+			break;
+		default:break;
+	}
+	close(fd);
+	return ret;
+
+}
+
+int getAPData(int fd, char *file)
+{
+	int rByte = 0;
+	char rbuf[4096];
+	int ret = 0, i;
+	struct timeval time_m, time_n;
+
+    if (NULL == file) {
+	    printf("Please input file!! \n");
+		return 1;
+    }
+	if (!fd)
+		return 1;
+	printf("Get apdata\n");
+	gps_dev_send(fd, "$ReqRecvFlash\r\n");
+	af_start_receive();
+	GET_TIME();
+	while(1) {
+		CHK_TIME();
+		if(select_read(fd,1) > 0)
+			usleep(50000);
+		else
+			continue;
+
+		rByte = deal_read(fd,&rbuf,sizeof(rbuf));
+		if(rByte >= 1){
+			if(af_is_start_receive()) {
+				for(i = 0; i < rByte; i++)
+					af_add_char(file, rbuf[i]);
+			} else {
+				break;
+			}
+		}
+	}
+
+	checkAPData(file);
+	return 0;
+}
+
+int sendAPData(int fd, char *apFile)
+{
+	int rByte = 0;
+	char rbuf[4096];
+	int ret = 0;
+	struct timeval time_m, time_n;
+
+	if(access(apFile, F_OK) != -1)
+	{
+		// Download APData only if the file exits
+		printf("Download APData...\n");
+		if(xmodem_send(fd, apFile))
+		{
+			printf("xmodem error!\n");
+			close(fd);
+			return ENTER_UPDATE_MODE_ERR;
+		}
+		// Waiting for 'C'
+		GET_TIME();
+		while(1)
+		{
+			CHK_TIME();
+			if(select_read(fd,1) > 0)
+				usleep(500000);
+			else
+				continue;
+			rByte = deal_read(fd,&rbuf,sizeof(rbuf)-1);
+			rbuf[rByte] = 0;
+			if(rByte > 0)
+			{
+				if(rbuf[rByte - 1] == 'C')
+					break;
+			}
+		}
+		printf("download APData success\n");
+	}
+	else
+	{
+		printf("file err!\n");
+		return 1;
+	}
+
+	return 0;
+}
+
+int checkAPData(char *apFile)
+{
+	printf("Checking %s\n", apFile);
+	if(check_file(apFile))
+	{
+		printf("file error!\n");
+		return FILE_CHECK_ERR;
+
+	}
+	else
+	{
+		printf("pass\r\n");
+	}
+	return 0;
+}
+
+int downloadFW(int fd, char *fw)
+{
+    int ret;
+	struct timeval time_m, time_n;
+	char rbuf[512];
+
+    if(NULL == fw) {
+	    printf("Error: Can't find firmware.\n");
+        return -1;
+    }
+	printf("Download firmware...\n");
+	if(xmodem_send(fd, fw) < 0) {
+		printf("xmodem error! send firmware failed!\n");
+		close(fd);
+		return UPDATE_ERR;
+	}
+
+	printf("Download firmware success\n");
+	return 0;
+}
+
+int downloadBL(int fd, char *bl)
+{
+	int rByte = 0;
+	char rbuf[4096];
+	char name[128];
+	int ret = 0;
+	struct timeval time_m, time_n;
+
+    if(NULL == bl) {
+	    printf("Error: Can't find bootloader.\n");
+        return -1;
+    }
+	printf("------Please Powerdown the receiver!\n");
+    mopen_open_gps(0);
+	SET_TIME_OUT(3);
+	while(1) {
+		DELAY_TIME_BREAK(); //
+		if(select_read(fd,1) > 0)
+			usleep(50000);
+		else
+			continue;
+
+		rByte = deal_read(fd,&rbuf,sizeof(rbuf));
+	}
+	int start = 0,finish = 0;
+
+	memset(name, 0, sizeof(name));
+	sprintf(name,"M!T");
+	printf("waiting for YC, timeout is %d s\n", TIMEOUT_SEC);
+	printf("-------Please Powerup the receiver!\n");
+    mopen_open_gps(1);
+	// Waiting for 'YC'
+	GET_TIME();
+	while(1) {
+		int finish = 0, i;
+		CHK_TIME_BREAK(); //
+		rByte = write( fd, name, strlen(name));
+		rByte = select_read(fd,1);
+		if(rByte <= 0)
+			continue;
+		rByte = deal_read(fd, rbuf, sizeof(rbuf) - 1);
+		rbuf[rByte] = 0;
+		for (i = 0 ; i < rByte; i++)
+		{
+			if (check_YC(rbuf[i])) {
+				printf("Receive 'YC'\n");
+				finish = 1;
+				break;
+			}
+		}
+		if (finish)
+			break;
+	}
+	//wait 'YC' timeout deal
+	if (timeout_sign == 1)
+	{
+		//wait NAK
+		GET_TIME();
+		while(1)
+		{
+			CHK_TIME();
+			if(select_read(fd,1) <= 0)
+				continue;
+
+			rByte = deal_read(fd, rbuf,sizeof(rbuf));
+			if (rbuf[rByte-1] == 'C')
+			{
+				printf("###read xmodem start character 'C'.\n");
+				break;
+			}
+		}
+	}
+	use_select = 1;
+	printf("download bootloader...\n");
+
+	// Transfer bootloader via xmodem protocal
+	// if(xmodem_send(fd, "./bootloader.bin")) {
+	if(xmodem_send(fd, bl)) {
+		printf("xmodem error!\n");
+		close(fd);
+		return ENTER_UPDATE_MODE_ERR;
+	}
+	printf("download bootloader success\n");
+	return 0;
+}
+
+int check_YC(char newchar)
+{
+	int static state = 0;
+	int ret = 0;
+	switch (state) {
+		case 0:
+			if (newchar == 'Y')
+				state = 1;
+			break;
+		case 1:
+			if (newchar == 'C') {
+				state = 1;
+				ret = 1;
+			}
+			break;
+		default:
+			state = 0;
+	}
+	return ret;
+}
+
+const unsigned short CRC16_Table[256] = {
+	0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
+	0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
+	0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
+	0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
+	0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
+	0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
+	0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
+	0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
+	0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
+	0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
+	0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
+	0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
+	0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
+	0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
+	0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
+	0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
+	0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
+	0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
+	0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
+	0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
+	0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
+	0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
+	0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
+	0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
+	0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
+	0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
+	0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
+	0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
+	0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
+	0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
+	0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
+	0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
+};
+
+unsigned short crc16_ccitt(const unsigned char *buf, int len)
+{
+	register int counter;
+	register unsigned short crc = 0;
+	for (counter = 0; counter < len; counter++)
+		crc = (crc << 8) ^ CRC16_Table[((crc >> 8) ^ *(char *)buf++) & 0x00FF];
+	return crc;
+}
+
+
+unsigned int checksum32(unsigned int * pbuf, unsigned int len)
+{
+	unsigned int i, sumValue = 0;
+	len >>=2;
+
+	for(i=0;i<len;i++)
+	{
+		sumValue += *pbuf++;
+	}
+	return sumValue;
+}
+
+void af_start_receive()
+{
+	recvStat = WAIT_FD;
+	isStartReceive = 1;
+	return;
+}
+
+void af_stop_receive()
+{
+    int i;
+	printf("%s:%d\r\n", __FUNCTION__, __LINE__);
+	printf("%s:%d  recvStat = %d\r\n", __FUNCTION__, __LINE__, recvStat);
+	pbuf = (unsigned char *)recv_buf;
+	for(i = 0; i < 4636; i++){
+		if(i % 32 == 0)printf("\r\n");
+		printf("%02X ", pbuf[i]);
+	}
+	printf("\r\n");
+	isStartReceive = 0;
+	return;
+}
+
+int af_is_start_receive()
+{
+	return isStartReceive;
+}
+
+int af_check_header(unsigned char *pbuf, unsigned int len)
+{
+	unsigned int crc = crc16_ccitt(pbuf, len-4);
+
+	if(crc == *(unsigned int *)(pbuf + len - 4))
+		return 1;
+	else
+		return 0;
+}
+
+int af_check_data(unsigned int * pbuf, unsigned int len)
+{
+	unsigned int cs = checksum32(pbuf + 8, len);
+	if(cs == pbuf[1])
+		return 1;
+	else
+		return 0;
+}
+
+int af_get_data_len()
+{
+	int len = *(int *)(recv_buf+3);
+	return len;
+}
+
+int af_add_char(char *file, unsigned char c)
+{
+	int ret = 0;
+	switch(recvStat){
+		case WAIT_FD:
+			if(c == 0xfd){
+				pbuf = (unsigned char *)recv_buf;
+				pbuf[0] = c;
+				recvStat = WAIT_FC;
+				printf("------------received 0xfd\r\n");
+			}
+			break;
+		case WAIT_FC:
+			if(c == 0xfc){
+				pbuf[1] = c;
+				recvStat = WAIT_FB;
+				printf("------------received 0xfc\r\n");
+			}else{
+				af_stop_receive();
+			}
+			break;
+		case WAIT_FB:
+			if(c == 0xfb){
+				pbuf[2] = c;
+				recvStat = WAIT_FA;
+				printf("------------received 0xfb\r\n");
+			}else{
+				af_stop_receive();
+			}
+			break;
+		case WAIT_FA:
+			if(c == 0xfa){
+				pbuf[3] = c;
+				recvStat = RECE_HEADER;
+				pbuf += 4;
+				printf("------------received 0xfa\r\n");
+			}else{
+				af_stop_receive();
+			}
+			break;
+		case RECE_HEADER:
+			*pbuf = c;
+			pbuf++;
+			if((pbuf - (unsigned char *)recv_buf) == HEADERLEN){
+				if(af_check_header((unsigned char *)recv_buf, HEADERLEN)){
+					recvStat = RECE_DATA;
+					data_len = af_get_data_len();
+				}else{
+					af_stop_receive();
+				}
+			}
+			break;
+		case RECE_DATA:
+			*pbuf = c;
+			pbuf++;
+			if((pbuf - (unsigned char *)recv_buf) == data_len + HEADERLEN){
+				if(af_check_data(recv_buf, recv_buf[3])){
+					int fd = open(file, O_WRONLY|O_CREAT, S_IRWXU);
+					write(fd, recv_buf, pbuf - (unsigned char *)recv_buf);
+					printf("%s:%d rtm len = %ld\r\n", __FUNCTION__, __LINE__, pbuf-(unsigned char *)recv_buf);
+					close(fd);
+					printf("receive rtm\n");
+				}else{
+					printf("af_check_data false!");
+				}
+				af_stop_receive();
+			}
+			ret = 1;
+			break;
+		default:
+			printf("%s:recvStat = %d\r\n", __FUNCTION__, recvStat);
+			break;
+	}
+	return ret;
+}
+
+
+
+ssize_t deal_read(int fd, void *buf, size_t count)
+{
+	int ret = 0;
+
+	while (1)
+	{
+		ret = read(fd, buf, count);
+		if (ret == 0)
+		{
+			printf("read serial return 0, please check serial device.\n");
+			exit(UART_DEV_ERR);
+		}
+		if(ret < 0)
+		{
+			if ((errno == EAGAIN) || (errno == EINTR))
+			{
+				printf("read serial return -1, errno = %d, retry.\n", errno);
+				continue;
+			}
+			else
+			{
+				printf("read serial return -1, errno = %d, please check serial device.\n", errno);
+				exit(UART_DEV_ERR);
+			}
+		}
+		return ret;
+	}
+}
+
+unsigned short get_crc16 ( char *ptr, unsigned short count )
+{
+	unsigned short 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);
+}
+
+void dump_u(void *buf, int len)
+{
+	unsigned char *p = (unsigned char *)buf;
+	int i;
+
+	for(i = 0; i < len; i++) {
+		if(i % 16 == 0) printf("%04x:", i);
+		if(i % 16 == 8) printf(" -");
+		printf(" %02x", p[i]);
+		if(i % 16 == 15) printf("\n");
+	}
+	if(i % 16) printf("\n");
+}
+
+unsigned int get_file_size(const char * name)
+{
+	struct stat statbuff;
+	//unsigned int size, checksum;
+	if(stat(name, &statbuff) < 0)
+		return -1;
+	else
+		return statbuff.st_size;
+}
+
+
+int check_file(char * fname)
+{
+	FILE *fd;
+	uc_image_header_t header;
+	unsigned int buf[1024];
+	unsigned int fsize, checksum, i;
+	unsigned int len;
+	unsigned short crc;
+	size_t rByte;
+
+	if((fd=fopen(fname,"rb"))==NULL)
+	{
+		printf("\n can't open (%s) or not exist!(errno=%d:%s) \n", fname, errno, strerror(errno));
+		return -1;
+	}
+
+	fsize = get_file_size(fname);
+
+	printf("file size [%d]\n",fsize);
+
+	if(fsize == 0)
+		return -1;
+
+	while(fsize > sizeof(header)) {
+		rByte = fread((char *)&header, sizeof(char), sizeof(header), fd);
+
+		dump_u((char *)&header, sizeof(header));
+
+		crc = get_crc16 ( (char *) &header, sizeof(header)-4);
+		printf("crc16  [%08x]\n", crc);
+
+		if((header.ih_hcrc & 0xFFFF) != crc) {
+			fclose(fd);
+			return -1;
+		}
+
+		fsize -= sizeof(header);
+		fsize -= header.ih_size;
+		checksum = 0;
+		len = header.ih_size;
+		while(len > 0)
+		{
+			if(len >= 1024 )
+				rByte = 1024;
+			else
+				rByte = len;
+
+			memset(buf, 0, sizeof(buf));
+			rByte = fread((char *)buf, 1, rByte, fd);
+			for(i = 0; i < (rByte+3)/4; i++)
+				checksum += buf[i];
+
+			len -= rByte;
+		}
+		printf("checksum  [%08x]\n\n",checksum);
+
+		if( checksum != header.ih_dcrc) {
+			fclose(fd);
+			return -1;
+		}
+	}
+
+	fclose(fd);
+	return 0;
+}
+
+static int select_read(int fd, int timeout) //1ms
+{
+	fd_set set;
+	struct timeval t;
+	int ret;
+	int i = timeout;
+
+	if(use_select) {
+		do {
+			FD_ZERO(&set);
+			FD_SET(fd, &set);
+			t.tv_sec = 0;
+			t.tv_usec = 100;
+
+			ret = select(FD_SETSIZE, &set, NULL, NULL, &t );
+			if(ret == 0) continue;
+			if(ret < 0 && errno == EINTR)continue;
+			else return ret;
+		} while(i--);
+	} else {
+		struct timeval t0, t1;
+		long dt = 0;
+		int c;
+
+		gettimeofday(&t0, NULL);
+		do {
+			c = 0;
+			ret = ioctl(fd, FIONREAD, &c);
+			if(c > 0) { ret = c; break; }
+
+			gettimeofday(&t1, NULL);
+			dt = t1.tv_usec - t0.tv_usec;
+			dt += (t1.tv_sec-t0.tv_sec)*1000*1000;
+		} while(dt/1000 < timeout);
+	}
+
+	return ret;
+}
+
+int set_baudrate(int fd, int baudrate)
+{
+	struct termios options, oldtio;
+
+	if(fcntl(fd, F_SETFL, 0) < 0) {
+		printf("fcntl failed!\n");
+		return -1;
+	}
+
+	if(tcgetattr(fd, &oldtio) != 0) {
+		printf("setup serial error!\n");
+		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))
+	{
+		printf("get options error!\n");
+		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)
+	{
+		printf("setup serial error!\n");
+		return -1 ;
+	}
+
+	return 0 ;
+}
+
+int initial_serialPort(char * serial_device)
+{
+	int fd;
+	fd = open( serial_device , O_RDWR );
+	if ( fd == -1 )
+	{
+		/* open error! */
+		printf("Can't open serial port(%s)!(errno=%d:%s) \n", serial_device, errno, strerror(errno));
+		return -1;
+	}
+
+	set_baudrate(fd, B9600);
+
+	return fd ;
+}
+
+static void gps_dev_send(int fd, char *msg)
+{
+	int i, n, ret;
+
+	i = strlen(msg);
+
+	n = 0;
+
+	printf("function gps_dev_send: %s", msg);
+	do {
+
+		ret = write(fd, msg + n, i - n);
+
+		if (ret < 0 && errno == EINTR) {
+			continue;
+		}
+
+		n += ret;
+
+	} while (n < i);
+
+	// drain cmd
+	tcdrain(fd);
+
+	return;
+}
+
+int xmodem_send(int fd, char *fname)
+{
+	char packet_data[XMODEM_DATA_SIZE];
+	char frame_data[XMODEM_DATA_SIZE + XMODEM_CRC_SIZE + XMODEM_FRAME_ID_SIZE + 1];
+	int ret = -1;
+
+	FILE *datafile;
+	int complete,retry_num,pack_counter,read_number,write_number,i;
+	unsigned short crc_value;
+	unsigned char ack_id = 'C';
+	struct timeval time_m, time_n;
+
+	datafile = NULL;
+	pack_counter = 0;	// 包计数器清零
+	complete = 0;
+	retry_num = 0;
+
+	printf("[%s]\n",fname);
+	//只读方式打开一个准备发送的文件,如果不存在就报错,退出程序。
+	if((datafile=fopen(fname,"rb"))==NULL)
+	{
+		printf("\n can't open (%s) or not exist!(errno=%d:%s) \n", fname, errno, strerror(errno));
+		return -1;
+	}
+	else
+	{
+		printf("Ready to send the file:%s\n",fname);
+	}
+
+	printf("Waiting for signal C/NAK!\n");
+	GET_TIME();
+	while(1)
+	{
+		CHK_TIME1();
+		if(select_read(fd,1) > 0)
+			usleep(10000);
+		else
+			continue;
+
+		//read(fd,&ack_id,1);
+		deal_read(fd,&ack_id,1);
+		if(ack_id == 'C')
+			break;
+	}
+
+	printf("The signal NAK: %02x ok!!!\n",ack_id);//打印接收到的NAK信息
+
+	while(!complete)
+	{
+		switch(ack_id)
+		{
+			case XMODEM_CRC_CHR:	// 接收到字符'C'开始启动传输,并使用CRC校验
+				printf("begining to Send file %s...\n",fname);
+
+			case XMODEM_ACK:        //0x06
+				retry_num = 0;
+				pack_counter++;
+
+				read_number = fread(packet_data, sizeof(char), XMODEM_DATA_SIZE, datafile);
+				//从打开的datafile指向的文件中读取
+				//XMODEM_DATA_SIZE 个(char)数据,
+				//放到packet_data这个数组中
+				if(read_number > 0)//read_number为返回的读取实际字节数
+				{
+					//printf("test:read_number:%d\n", read_number);
+					if(read_number < XMODEM_DATA_SIZE_STX)
+					{
+						printf("Start filling the last frame!\n");
+						for(; read_number < XMODEM_DATA_SIZE; read_number++)
+							packet_data[read_number] = 0x1A;  // 不足128字节用0x1A填充
+						//printf("replenish data.\n");
+					}
+
+					frame_data[0] = XMODEM_HEAD;  // 帧开始字符
+					frame_data[1] = (char)pack_counter;  // 信息包序号
+					frame_data[2] = (char)(255 - frame_data[1]);  // 信息包序号的补码
+
+					for(i=0; i < XMODEM_DATA_SIZE; i++)  // 128字节的数据段
+						frame_data[i+3] = packet_data[i];//把收到的字符和信息头一起打包
+
+					crc_value = get_crc16(packet_data, XMODEM_DATA_SIZE); // 16位crc校验
+					frame_data[XMODEM_DATA_SIZE+3] = (unsigned char)(crc_value >> 8);// 高八位数据
+					frame_data[XMODEM_DATA_SIZE+4] = (unsigned char)(crc_value);     //低八位数据
+
+					/* 发送133字节数据 */
+					write_number = write( fd, frame_data, XMODEM_DATA_SIZE + 5);//向串口写一个包数据,即133字节数据
+					printf("."); //ADD: process
+					fflush(stdout);
+					//printf("waiting for next ACK... \n......\n");
+
+					GET_TIME();
+					while(1)
+					{
+						CHK_TIME1();
+						if(select_read(fd,1) > 0)
+							usleep(10000);
+						else
+							continue;
+
+						//read(fd,&ack_id,1);
+						deal_read(fd,&ack_id,1);
+						break;
+					}
+
+					if(ack_id == XMODEM_ACK) {
+						//printf("ACK Ok!!Ready sending next pack!\n");
+						;
+					}
+					else
+					{
+						printf("ACK Error!\n");
+						printf("0x%02X\n",ack_id);
+						//printf("pack_counter = %d\n", pack_counter);
+					}
+				}
+
+				else  // 文件发送完成
+				{
+					ack_id = XMODEM_EOT;
+					complete = 1;
+					printf("Complete ACK\n");
+
+					GET_TIME();
+					while(ack_id != XMODEM_ACK)
+					{
+						CHK_TIME1();
+						ack_id = XMODEM_EOT;
+						write_number = write(fd,&ack_id,1);
+						while((deal_read(fd, &ack_id, 1)) <= 0);
+					}
+					printf("Send file successful!!!\n");
+					fclose(datafile);
+					datafile = NULL;
+				}
+				break;
+
+			case XMODEM_NAK:
+				if( retry_num++ > 10)
+				{
+					printf("Retry too many times,Quit!\n");
+					complete = 1;
+				}
+				else //重试,发送
+				{
+					write_number = write(fd, frame_data, XMODEM_DATA_SIZE + 5);
+					printf("Retry for ACK,%d,%d...", pack_counter, write_number);
+
+					GET_TIME();
+					while(1)
+					{
+						CHK_TIME1();
+						if(select_read(fd,1) > 0)
+							usleep(100);
+						else
+							continue;
+
+						//read(fd,&ack_id,1);
+						deal_read(fd,&ack_id,1);
+						break;
+					}
+
+					if( ack_id == XMODEM_ACK )
+						printf("OK\n");
+					else
+						printf("Error!\n");
+				}
+				break;
+			default:
+				printf("Fatal Error! %d\n", ack_id);
+				complete = 1;
+				return -1;
+				break;
+		}
+	}
+
+	if( datafile != NULL )
+		fclose(datafile);
+
+	return 0;
+}
diff --git a/mbtk/mbtk_lib/src/mbtk_gpio.c b/mbtk/mbtk_lib/src/mbtk_gpio.c
new file mode 100755
index 0000000..c761476
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_gpio.c
@@ -0,0 +1,532 @@
+/**
+ *   \file gpio-test.c
+ *   \brief A Documented file.
+ *
+ *  Detailed description
+ *   \Author:  Sniper <js.wang@mobiletek.cn>
+ *   \Version: 1.0.0
+ *   \Date: 2022-04-26
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <sys/mount.h>
+#include "mbtk_log.h"
+#include <sys/mman.h>
+#include <ctype.h>
+#include <cutils/properties.h>
+#include "gpio-define.h"
+
+#define gpio_log(...)     if(gpio_debug)printf(__VA_ARGS__)
+
+static int gpio_debug = 0;
+
+struct gpio_register_function gpio_func_register[128] = {
+    {GPIO_FUNC_GPIO_00, 0},
+    {GPIO_FUNC_GPIO_01, 0},
+    {GPIO_FUNC_GPIO_02, 0},
+    {GPIO_FUNC_GPIO_03, 0},
+    {GPIO_FUNC_GPIO_04, 0},
+    {GPIO_FUNC_GPIO_05, 0},
+    {GPIO_FUNC_GPIO_06, 0},
+    {GPIO_FUNC_GPIO_07, 0},
+    {GPIO_FUNC_GPIO_08, 0},
+    {GPIO_FUNC_GPIO_09, 0},
+    {GPIO_FUNC_GPIO_10, 0},
+    {GPIO_FUNC_GPIO_11, 0},
+    {GPIO_FUNC_GPIO_12, 0},
+    {GPIO_FUNC_GPIO_13, 0},
+    {GPIO_FUNC_GPIO_14, 0},
+    {GPIO_FUNC_GPIO_15, 0},
+    {GPIO_FUNC_GPIO_16, 0},
+    {GPIO_FUNC_GPIO_17, 0},
+    {GPIO_FUNC_GPIO_18, 0},
+    {GPIO_FUNC_GPIO_19, 0},
+    {GPIO_FUNC_GPIO_20, 0},
+    {GPIO_FUNC_GPIO_21, 0},
+    {GPIO_FUNC_GPIO_22, 0},
+    {GPIO_FUNC_GPIO_23, 0},
+    {GPIO_FUNC_GPIO_24, 0},
+    {GPIO_FUNC_GPIO_25, 0},
+    {GPIO_FUNC_GPIO_26, 0},
+    {GPIO_FUNC_GPIO_27, 0},
+    {GPIO_FUNC_GPIO_28, 0},
+    {GPIO_FUNC_GPIO_29, 0},
+    {GPIO_FUNC_GPIO_30, 0},
+    {GPIO_FUNC_GPIO_31, 0},
+    {GPIO_FUNC_GPIO_32, 0},
+    {GPIO_FUNC_GPIO_33, 0},
+    {GPIO_FUNC_GPIO_34, 0},
+    {GPIO_FUNC_GPIO_35, 0},
+    {GPIO_FUNC_GPIO_36,   0},
+    {GPIO_FUNC_MMC1_DAT3, 5}, // GPIO_37
+    {GPIO_FUNC_MMC1_DAT2, 5}, // GPIO_38
+    {GPIO_FUNC_MMC1_DAT1, 5}, // GPIO_39
+    {GPIO_FUNC_MMC1_DAT0, 5}, // GPIO_40
+    {GPIO_FUNC_MMC1_CMD, 5},  //GPIO_41
+    {GPIO_FUNC_MMC1_CLK, 5},  //GPIO_42
+    {GPIO_FUNC_MMC1_CD , 1},  //GPIO_43
+    {0,0},{0,0},{0,0},{0,0},
+    {GPIO_FUNC_SDIO_DAT3, 1}, //GPIO_48
+    {GPIO_FUNC_GPIO_49, 0},
+    {GPIO_FUNC_GPIO_50, 0},
+    {GPIO_FUNC_GPIO_51, 0},
+    {GPIO_FUNC_GPIO_52, 0},
+    {GPIO_FUNC_GPIO_53, 0},
+    {GPIO_FUNC_GPIO_54, 0},
+    {GPIO_FUNC_SDIO_DAT2, 1}, //GPIO_55
+    {GPIO_FUNC_SDIO_DAT1, 1}, //GPIO_56
+    {GPIO_FUNC_SDIO_DAT0, 1}, //GPIO_57
+    {GPIO_FUNC_SDIO_CMD, 1},  //GPIO_58
+    {GPIO_FUNC_SDIO_CLK, 1},  //GPIO_59
+    {GPIO_FUNC_GPIO_60, 0},
+    {0,0},{0,0},{0,0},{0,0},
+    {0,0},{0,0},
+    {GPIO_FUNC_DVL_0, 1},//GPIO_67
+    {GPIO_FUNC_DVL_1, 1},//GPIO_68
+    {GPIO_FUNC_GPIO_69, 0},
+    {GPIO_FUNC_GPIO_70, 0},
+    {GPIO_FUNC_QSPI_DAT3, 1}, //GPIO_71
+    {GPIO_FUNC_QSPI_DAT2, 1}, //GPIO_72
+    {GPIO_FUNC_QSPI_DAT1, 1}, //GPIO_73
+    {GPIO_FUNC_QSPI_DAT0, 1}, //GPIO_74
+    {GPIO_FUNC_QSPI_CLK, 1},  //GPIO_75
+    {GPIO_FUNC_QSPI_CS1, 1},  //GPIO_76
+    {GPIO_FUNC_GPIO_77, 0},
+    {GPIO_FUNC_GPIO_78, 0},
+    {GPIO_FUNC_GPIO_79, 0},
+    {GPIO_FUNC_GPIO_80, 0},
+    {GPIO_FUNC_USIM_UCLK, 1},//GPIO_81
+    {GPIO_FUNC_USIM_UIO, 1},//GPIO_82
+    {GPIO_FUNC_USIM_URSTn, 1},//GPIO_83
+    {0,0},{0,0},{0,0},{0,0},
+    {0,0},{0,0},{0,0},{0,0},
+    {0,0},{0,0},{0,0},{0,0},
+    {0,0},{0,0},{0,0},
+    {GPIO_FUNC_USB_ID, 1},//GPIO_99
+    {0,0},{0,0},{0,0},{0,0},
+    {0,0},{0,0},{0,0},{0,0},
+    {0,0},{0,0},{0,0},{0,0},
+    {0,0},{0,0},{0,0},{0,0},
+    {0,0},
+    {GPIO_FUNC_PRI_TDI, 1},         //GPIO_117
+    {GPIO_FUNC_PRI_TMS, 1},         //GPIO_118
+    {GPIO_FUNC_PRI_TCK, 1},         //GPIO_119
+    {GPIO_FUNC_PRI_TDO, 1},         //GPIO_120
+    {GPIO_FUNC_QSPI_VMODE_GPIO, 1}, //GPIO_121
+    {GPIO_FUNC_VBUS_DRV, 1},        //GPIO_122
+    {GPIO_FUNC_CLK_REQ, 1},         //GPIO_123
+    {0,0},
+    {GPIO_FUNC_VCXO_REQ, 1},        //GPIO_125
+    {GPIO_FUNC_VCXO_OUT, 1},        //GPIO_126
+    {0,0},
+};
+#if 0
+/**
+ *  \brief strstr_n
+ *
+ *  find string return number
+ *
+ *  \param param
+ *  \return return type
+ */
+int strstr_n(const char *s1, const char *s2)
+{
+    int n;
+    int strlen = 0;
+
+    if(*s2)
+    {
+        while(*s1)
+        {
+            for(n = 0; *(s1+n) == *(s2 + n); n++)
+            {
+                if(!*(s2 + n + 1))
+                {
+                    strlen++;
+                    return strlen;
+                }
+            }
+            s1++;
+            strlen++;
+        }
+        return 0;
+    }else
+        return 0;
+}
+static short int from_hex(char a)
+{
+    if (a >= 'A' && a <= 'F')
+        return a - 'A' + 10;
+    else if (a >= 'a' && a <= 'f')
+        return a - 'a' + 10;
+    else
+        return a - '0';
+}
+
+int str_to_hex(char *str)
+{
+    unsigned char str_len = strlen(str);
+    int i;
+    int ret = 0;
+
+    if(str[0] != '0' || (str[1] != 'x' && str[1] != 'X'))
+    {
+        printf("%s error, %s!\n", __FUNCTION__, str);
+        return ret;
+    }
+    for (i = 2; i < str_len; ++i) {
+        ret = ret * 16 + from_hex(str[i]);
+    }
+    return ret;
+}
+#endif
+
+#define HWMAP_DEVICE "/dev/hwmap"
+#define PAGE_OFFS_BITS(pgsz) ((unsigned int)(pgsz)-1)
+#define PAGE_MASK_BITS(pgsz) (~PAGE_OFFS_BITS(pgsz))
+
+static int hwacc_register(int rw, unsigned int addr, unsigned int *data)
+{
+	int fid;
+	unsigned int pagesize, len, len_aligned;
+	unsigned int addr_aligned;
+	volatile unsigned int *pa;
+	void *vpa;
+
+	len = pagesize = sysconf(_SC_PAGESIZE);
+	if((fid = open(HWMAP_DEVICE, O_RDWR)) < 0)
+	{
+		printf("Failed to open %s\n", HWMAP_DEVICE);
+		exit(-1);
+	}
+
+	// Align the length so the mapped area is page-aligned and contains the requested area
+	addr_aligned = addr & PAGE_MASK_BITS(pagesize);
+	len_aligned =((addr + len - addr_aligned) + pagesize - 1) & PAGE_MASK_BITS(pagesize);
+
+	/* Notes on flags: MAP_PRIVATE results in copy on write; MAP_SHARED allows normal write */
+	/*   MAP_SHARED required O_RDWR in open above, otherwise mmap fails with errno=EACCES   */
+	/* Notes on prot:  PROT_WRITE allows read and write; PROT_READ allows read only         */
+	/* Notes on off: an unsigned 32-bit value, should be aligned to page size according to mmap manpage */
+	if((vpa = mmap(0, len_aligned, PROT_READ|PROT_WRITE, MAP_SHARED, fid, addr_aligned)) == MAP_FAILED)
+	{
+		printf("mmap failed (%d)\n", errno);
+	}
+	else
+	{
+		pa = (volatile unsigned int *)((unsigned char *)vpa + (addr & PAGE_OFFS_BITS(pagesize)));
+		if(rw == 0)
+		{
+			*data = *pa;
+			gpio_log("Value read from 0x%.8x via MVA=0x%p is 0x%.8x\n", addr, pa, *data);
+		}
+		else if(rw == 1)
+		{
+			*pa = *data;
+			gpio_log("Value %.8x written to 0x%.8x via MVA=0x%p\n", *data, addr, pa);
+#if defined(HWACC_DEBUG)
+			{
+				unsigned int val;
+				val = *pa;
+				printf("Value read from 0x%.8x via MVA=0x%p is 0x%.8x\n", addr, pa, val);
+			}
+#endif
+		}
+		munmap(vpa, len);
+	}
+
+	close(fid);
+	return 0;
+}
+/*
+设置GPIO 模式:第一步读GPIO22默认function
+root@OpenWrt:/# hwacc r 0xd401e134
+Option = r Addr = d401e134
+Value read from 0xd401e134 via MVA=0x0xb6fc3134 is 0x0000d040   //默认GPIO功能
+Bit0~bit2值对应上面表格各function,0代表GPIO功能
+ */
+static int gpio_register_read(int reg)
+{
+    int ret = -1;
+#if 0
+    FILE * fp;
+    // "/bin/hwacc r 0xd401e134";
+    char command[36] = {0};
+    char buffer[1024];
+    int i = 0;
+
+    sprintf(command, "/bin/hwacc r 0x%x", reg);
+    fp = popen(command, "r");
+    while(1)
+    {
+        if( fgets (buffer, sizeof(buffer), fp)!=NULL ) {
+            buffer[strlen(buffer) - 1] = 0;
+            // gpio_log("out [%d]: %s\n", strlen(buffer), buffer);
+        }else{
+            break;
+        }
+        i = strstr_n(buffer, "is");
+        if(i)
+        {
+            ret = str_to_hex(&buffer[i + 2]);
+            gpio_log("read 0x%x value:%s, %x\n", reg, &buffer[i + 2], ret);
+        }
+    }
+    pclose(fp);
+#else
+    hwacc_register(0, reg, &ret);
+#endif
+    return ret;
+}
+/*
+设置输入输出状态,设置PDR寄存器GPIO22为output
+root@OpenWrt:/# hwacc w 0xD401900c 0x00c03800
+Option = w Addr = d401900c Data=00c03800
+Value 00c03800 written to 0xd401900c via MVA=0x0xb6f9f00c
+ */
+static void gpio_register_write(int reg, int value)
+{
+#if 0
+    FILE * fp;
+    //  "/bin/hwacc w 0xD401900c 0x00c03800"
+    char command[36] = {0};
+    char buffer[1024];
+
+    sprintf(command, "/bin/hwacc w 0x%x 0x%x", reg, value);
+    gpio_log("command: %s\n", command);
+    fp = popen(command, "r");
+    while(1)
+    {
+        if( fgets (buffer, sizeof(buffer), fp)!=NULL ) {
+            gpio_log("%s\n", buffer);
+        }else{
+            break;
+        }
+    }
+    pclose(fp);
+
+#else
+    hwacc_register(1, reg, &value);
+#endif
+}
+/*
+AF SEL<p>This field is used for alternate function selection for a pin.
+It selects between the eight possible alternate functions for the pin.
+Alternate function 0 is always the reset case.
+
+<p>0x0 = Alternate function 0 (primary function at reset)
+<p>0x1 = Alternate function 1
+<p>0x2 = Alternate function 2
+<p>0x3 = Alternate function 3
+<p>0x4 = Alternate function 4
+<p>0x5 = Alternate function 5
+<p>0x6 = Alternate function 6
+<p>0x7 = Alternate function 7
+ */
+static int gpio_register_set_func_0(int port)
+{
+    int ret;
+    struct gpio_register_function *reg = NULL;
+    if(port > 128)
+        return -1;
+    reg = &gpio_func_register[port];
+    if(0 == reg->reg)
+        return -1;
+    ret = gpio_register_read(reg->reg);
+    if((ret & 0x7) != reg->func_gpio)
+    {
+        gpio_log("Gpio set func [%d] [0x%x]!\n", reg->func_gpio, (ret & 0xfffffff8) | reg->func_gpio);
+        gpio_register_write(reg->reg, (ret & 0xfffffff8) | reg->func_gpio);
+    }
+    return 0;
+}
+/*
+设置GPIO  方向
+读取输入输出状态,读PDR寄存器:0x0c
+root@OpenWrt:/# hwacc r 0xD401900c
+Option = r Addr = d401900c
+Value read from 0xd401900c via MVA=0x0xb6f3900c is 0x00803800  //bit22为0,代表Input
+ */
+static void gpio_register_set_direction(int port, int dir)
+{
+    int ret;
+    int reg = 0xD4019000;
+
+    if(port > (32 - 1))
+        reg = 0xD4019004;
+    if(port > (32 * 2 - 1))
+        reg = 0xD4019008;
+    if(port > (32 * 3 - 1))
+        reg = 0xD4019100;
+
+    reg += 0x0c;
+    port = port % 0x20;
+    ret = gpio_register_read(reg);
+    // 设置 输出 0 && 1
+    if(!(ret & (0x1 << port)) && dir)
+    {
+        gpio_register_write(reg, ret | (0x1 << port));
+    }
+    // 设置 输入 1 && 0
+    if((ret & (0x1 << port)) && !dir)
+    {
+        gpio_register_write(reg, ret | !(0x1 << port));
+    }
+}
+/*
+设置GPIO 输出电平
+
+读取电平状态,先读PLR寄存器:0x00
+root@OpenWrt:/# hwacc r 0xD4019000
+Option = r Addr = d4019000
+Value read from 0xd4019000 via MVA=0x0xb6f1c000 is 0x81e82a30
+对应下面具体BIT
+1000 0001 1110 1000 0010 1010 0011 0000   BIT22默认电平高
+
+设置输出高:设置PSR寄存器:0x18(只写寄存器)
+root@OpenWrt:/# hwacc w 0xD4019018 0x400000
+Option = w Addr = d4019018 Data=00400000
+Value 00400000 written to 0xd4019018 via MVA=0x0xb6f56018 //bit22写1,输出高
+
+设置输出低:设置PCR寄存器:0x24
+root@OpenWrt:/# hwacc w 0xD4019024 0x400000
+Option = w Addr = d4019024 Data=00400000
+Value 00400000 written to 0xd4019024 via MVA=0x0xb6faa024   //Bit22写1,GPIO22输出低
+
+ */
+static void gpio_register_set_value(int port, int value)
+{
+    int ret;
+    int reg = 0xD4019000;
+
+    if(port > (32 - 1))
+        reg = 0xD4019004;
+    if(port > (32 * 2 - 1))
+        reg = 0xD4019008;
+    if(port > (32 * 3 - 1))
+        reg = 0xD4019100;
+
+    if(value)
+    {
+        reg += 0x18;
+    }
+    else
+        reg += 0x24;
+
+    port = port % 0x20;
+    ret = gpio_register_read(reg);
+    // 设置 高电平 0 && 1
+    if(value)
+    {
+        gpio_register_write(reg, ret | (0x1 << port));
+        return;
+    }
+    // 设置 低电平 1 && 0
+    if(!(ret & (0x1 << port)) && !value)
+    {
+        gpio_register_write(reg, ret | (0x1 << port));
+    }
+}
+/*
+读取电平状态,先读PLR寄存器:0x00
+root@OpenWrt:/# hwacc r 0xD4019000
+Option = r Addr = d4019000
+Value read from 0xd4019000 via MVA=0x0xb6f1c000 is 0x81e82a30
+对应下面具体BIT
+1000 0001 1110 1000 0010 1010 0011 0000   BIT22默认电平高
+ */
+static int gpio_register_get_value(int port)
+{
+    int ret = -1;
+    int reg = 0xD4019000;
+
+    if(port > (32 - 1))
+        reg = 0xD4019004;
+    if(port > (32 * 2 - 1))
+        reg = 0xD4019008;
+    if(port > (32 * 3 - 1))
+        reg = 0xD4019100;
+    port = port % 0x20;
+    ret = gpio_register_read(reg);
+    if(ret & (0x1 << port))
+    {
+        return 1;
+    }
+    return 0;
+}
+
+void gpio_debug_set(int enable)
+{
+    gpio_debug = enable;
+}
+
+int gpio_register_test_out(int port, int value)
+{
+    int ret;
+    gpio_log("Gpio port [%d] test start!\n", port);
+    ret = gpio_register_set_func_0(port);
+    if(ret)
+    {
+        printf("gpio_port can't support!\n");
+        return -1;
+    }
+
+    gpio_register_set_direction(port, 1);
+    ret = gpio_register_get_value(port);
+    gpio_log("gpio default value is : %d.\n", ret);
+
+    gpio_register_set_value(port, 1);
+    ret = gpio_register_get_value(port);
+    gpio_log("######gpio should is high: %d.######\n", ret);
+    if(1 != ret)
+        goto exit;
+    usleep(50);
+
+    gpio_register_set_value(port, 0);
+    ret = gpio_register_get_value(port);
+    gpio_log("######gpio should is low: %d.######\n", ret);
+    if(0 != ret)
+    {
+        goto exit;
+    }
+
+exit:
+    gpio_register_set_direction(port, 0);
+
+    return ret;
+}
+
+int mbtk_at_gpio(void* arg)
+{
+    int test_gpio[] = {33, 35, 36, 34, 82, 83, 81, /*41, 40, 39, 38, 37, 42,*/
+                       21, 4, 122, 123, 20, 43, 13, 12, 14, 118, 19, 120,
+                       49, 50, 32, 31, /*51, 52, */53, 54, 119, 23, 24, 27, 28, 26, 25, 5, 9,
+                       0, 1, 2, 3, 6, 7, 15, 18, 16, 17,
+                       126, 125, 56, 55, 58, 48, 57, 59, 117, 29, 30};
+
+    int i, ret, total, n = 0;
+    int *fail_io = (int *)arg;
+    total = (sizeof(test_gpio)/sizeof(int));
+    printf("Start test gpio total: %d\n", total);
+    for(i = 0; i < total; i++)
+    {
+        ret = gpio_register_test_out(test_gpio[i], 0);
+        if(-1 == ret)
+        {
+            LOGI("######gpio [%d] test failed!!!!!!######\n", test_gpio[i]);
+            fail_io[n] = test_gpio[i];
+            n++;
+        }else{
+            LOGI("######gpio [%d] test success!!!!!!######\n", test_gpio[i]);
+        }
+    }
+    return n;
+}
diff --git a/mbtk/mbtk_lib/src/mbtk_http.c b/mbtk/mbtk_lib/src/mbtk_http.c
new file mode 100755
index 0000000..e63064e
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_http.c
@@ -0,0 +1,1361 @@
+/*************************************************************
+Description:
+    MBTK HTTP c file.
+Author:
+    LiuBin
+Date:
+    2020/4/30 13:51:42
+*************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "mbtk_http_base.h"
+#include "mbtk_http.h"
+#include "mbtk_http_chunks.h"
+
+/*************************************************************
+    Constants and Macros
+*************************************************************/
+
+/*************************************************************
+    Variables:local
+*************************************************************/
+static mbtk_http_handle_t http_handles[HTTP_HANDLE_MAX] =
+{
+    {
+        .id = -1,
+        .data_cb = NULL,
+        .session_cnt = 0,
+        .session = {NULL}
+    }
+};
+
+/*************************************************************
+    Variables:public
+*************************************************************/
+
+
+/*************************************************************
+    Local Function Declaration
+*************************************************************/
+
+
+/*************************************************************
+    Local Function Definitions
+*************************************************************/
+static void http_session_free(mbtk_http_session_t *session)
+{
+    if(session)
+    {
+        if(session->req.header_cnt > 0)
+        {
+            int index;
+            for(index = 0; index < HTTP_REQUEST_HEADER_MAX; index++)
+            {
+                if(session->req.req_h[index] != NULL)
+                {
+                    if(session->req.req_h[index]->value)
+                        free(session->req.req_h[index]->value);
+                    free(session->req.req_h[index]);
+                    session->req.req_h[index] = NULL;
+                }
+            }
+            session->req.header_cnt = 0;
+        }
+
+        if(session->req.content)
+        {
+            free(session->req.content);
+            session->req.content = NULL;
+        }
+
+        if(session->rsp.header_cnt > 0)
+        {
+            int index;
+            for(index = 0; index < HTTP_REQUEST_HEADER_MAX; index++)
+            {
+                if(session->rsp.rsp_h[index] != NULL)
+                {
+                    if(session->rsp.rsp_h[index]->value)
+                        free(session->rsp.rsp_h[index]->value);
+                    free(session->rsp.rsp_h[index]);
+                    session->rsp.rsp_h[index] = NULL;
+                }
+            }
+            session->rsp.header_cnt = 0;
+        }
+
+        free(session);
+    }
+}
+
+static int http_session_close(mbtk_http_session_t *session)
+{
+    if(session)
+    {
+        if(session->sock_fd > 0)
+        {
+            if(mbtk_http_close(session->sock_fd))
+            {
+                LOGE("mbtk_http_close() fail.");
+                return -1;
+            }
+            session->sock_fd = -1;
+        }
+
+        session->state = HTTP_SESSION_STATE_NON;
+
+        return 0;
+    }
+
+    return -1;
+}
+
+
+static bool http_handle_check(int handle_id)
+{
+    if(handle_id < 0 || handle_id >= HTTP_HANDLE_MAX
+       || http_handles[handle_id].id < 0)
+    {
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static bool http_session_check(int handle_id, int session_id)
+{
+    if(handle_id < 0 || handle_id >= HTTP_HANDLE_MAX
+       || http_handles[handle_id].id < 0
+       || http_handles[handle_id].id != handle_id)
+    {
+        return FALSE;
+    }
+
+    if(session_id < 0 || session_id >= HTTP_SESSION_MAX
+       || http_handles[handle_id].session[session_id] == NULL
+       || http_handles[handle_id].session[session_id]->id != session_id)
+    {
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static bool http_is_space_char(char ch)
+{
+    if(ch == ' ' || ch == '\r' || ch == '\t' || ch == '\n')
+        return TRUE;
+
+    return FALSE;
+}
+
+static bool http_str_empty(char *str)
+{
+    if(str == NULL || strlen(str) == 0)
+        return TRUE;
+
+    return FALSE;
+}
+
+
+static int http_url_parse
+(
+    void* url,
+    void *host,
+    void *uri,
+    int *port,
+    bool *is_ssl
+)
+{
+    if(strlen(url) == 0)
+    {
+        return -1;
+    }
+    char *url_ptr = (char*)url;
+    char *host_ptr = (char*)host;
+
+    LOGI("URL[%d]:%s",strlen(url_ptr),url_ptr);
+
+    if(!memcmp(url_ptr,"https://",8))
+    {
+        *is_ssl = TRUE;
+        url_ptr += 8;
+    }
+    else if(!memcmp(url_ptr,"http://",7))
+    {
+        *is_ssl = FALSE;
+        url_ptr += 7;
+    }
+    else
+    {
+        *is_ssl = FALSE;
+    }
+
+    // ptr point to host.
+    while(*url_ptr)
+    {
+        if(*url_ptr == ':' || *url_ptr == '/') // Host end
+            break;
+        if(http_is_space_char(*url_ptr))
+        {
+            url_ptr++;
+            continue;
+        }
+        *host_ptr++ = *url_ptr++;
+    }
+
+    // "www.baidu.com"
+    if(*url_ptr == '\0')   // No port and uri
+    {
+        if(*is_ssl)
+        {
+            *port = MBTK_HTTPS_PORT_DEF;
+        }
+        else
+        {
+            *port = MBTK_HTTP_PORT_DEF;
+        }
+        memcpy(uri,"/",1);
+
+        LOGI("HTTP parse success:'%s','%s', %d, %d",(char*)host,(char*)uri,*port,*is_ssl);
+        return 0;
+    }
+    else
+    {
+        //LOGI("Host end with:%x",*url_ptr);
+        if(*url_ptr == ':')   // Port exist.
+        {
+            *port = atoi(url_ptr + 1);
+
+            // Point to '/' or NULL
+            while(*url_ptr && *url_ptr != '/')
+            {
+                url_ptr++;
+            }
+
+            // "www.baidu.com:80"
+            if(*url_ptr == '\0')   // No uri
+            {
+                if(*port == 0)
+                {
+                    if(*is_ssl)
+                    {
+                        *port = MBTK_HTTPS_PORT_DEF;
+                    }
+                    else
+                    {
+                        *port = MBTK_HTTP_PORT_DEF;
+                    }
+                }
+                memcpy(uri,"/",1);
+
+                LOGI("HTTP parse success:'%s','%s', %d, %d",(char*)host,(char*)uri,*port,*is_ssl);
+                return 0;
+            }
+        }
+
+        // "www.baidu.com/xxx" or "www.baidu.com:80/xxx"
+        // Now,url_ptr point to '/'
+        if(*url_ptr != '/')
+        {
+            LOGE("URI must start with '/'.");
+            return -1;
+        }
+
+        //LOGI("URL3[%d]:%s",strlen(url_ptr),url_ptr);
+
+        memcpy(uri,url_ptr,strlen(url_ptr));
+
+        if(*port == 0)
+        {
+            if(*is_ssl)
+            {
+                *port = MBTK_HTTPS_PORT_DEF;
+            }
+            else
+            {
+                *port = MBTK_HTTP_PORT_DEF;
+            }
+        }
+
+        LOGI("HTTP parse success:'%s','%s', %d, %d",(char*)host,(char*)uri,*port,*is_ssl);
+        return 0;
+    }
+}
+
+static int http_session_req_head_add(mbtk_http_session_t *session,bool replace,
+                                     char *name, char *value)
+{
+    if(session == NULL || value == NULL)
+        return -1;
+
+    LOGI("Add request header - %s:%s",name,value);
+
+    int i = 0;
+    while(i < HTTP_REQUEST_HEADER_MAX)
+    {
+        if(session->req.req_h[i]
+           && !strncasecmp(session->req.req_h[i]->name,name,strlen(name)))   // Is change value
+        {
+            break;
+        }
+        i++;
+    }
+
+    if(i == HTTP_REQUEST_HEADER_MAX)   // Should add new header.
+    {
+        i = 0;
+        while(i < HTTP_REQUEST_HEADER_MAX)
+        {
+            if(session->req.req_h[i] == NULL)   // Find NULL request.
+            {
+                session->req.req_h[i] = (mbtk_http_header_t*)malloc(sizeof(mbtk_http_header_t));
+                if(session->req.req_h[i] == NULL)
+                {
+                    LOGE("malloc() fail.");
+                    return -1;
+                }
+
+                memset(session->req.req_h[i],0x0,sizeof(mbtk_http_header_t));
+                memcpy(session->req.req_h[i]->name, name, strlen(name));
+                session->req.req_h[i]->value = NULL;
+                session->req.header_cnt++;
+                break;
+            }
+            i++;
+        }
+    }
+    else     // Is change value
+    {
+        if(!replace)
+        {
+            LOGW("Found this header[%s],no replace.",name);
+            return 0;
+        }
+    }
+
+    if(i == HTTP_REQUEST_HEADER_MAX)
+    {
+        LOGE("Request header is full.");
+        return -1;
+    }
+
+    if(session->req.req_h[i]->value)
+    {
+        free(session->req.req_h[i]->value);
+    }
+    session->req.req_h[i]->value = (char*)malloc(strlen(value) + 1);
+    if(session->req.req_h[i]->value == NULL)
+    {
+        LOGE("malloc() fail.");
+        return -1;
+    }
+    memset(session->req.req_h[i]->value,0x0,strlen(value) + 1);
+    memcpy(session->req.req_h[i]->value,value,strlen(value));
+
+    return 0;
+}
+
+#if 0
+static int http_session_rsp_head_add(mbtk_http_session_t *session,
+                                     char *name, char *value)
+{
+    if(session == NULL || value == NULL)
+        return -1;
+
+    int i = 0;
+    while(i < HTTP_REQUEST_HEADER_MAX)
+    {
+        if(session->rsp.rsp_h[i] == NULL)   // Find NULL request.
+        {
+            session->rsp.rsp_h[i] = (mbtk_http_header_t*)malloc(sizeof(mbtk_http_header_t));
+            if(session->rsp.rsp_h[i] == NULL)
+            {
+                LOGE("malloc() fail.");
+                return -1;
+            }
+
+            memcpy(session->rsp.rsp_h[i]->name,name,strlen(name));
+            session->rsp.rsp_h[i]->value = (char*)malloc(strlen(value) + 1);
+            if(session->rsp.rsp_h[i]->value == NULL)
+            {
+                LOGE("malloc() fail.");
+                return -1;
+            }
+            memset(session->rsp.rsp_h[i]->value,0x0,strlen(value) + 1);
+            memcpy(session->rsp.rsp_h[i]->value,value,strlen(value));
+
+            session->rsp.header_cnt++;
+            return 0;
+        }
+        i++;
+    }
+
+    return -1;
+}
+#endif
+
+static char* http_option_str_get(mbtk_http_option_enum option)
+{
+    switch(option)
+    {
+        case HTTP_OPTION_HEAD:
+            return "HEAD";
+        case HTTP_OPTION_GET:
+            return "GET";
+        case HTTP_OPTION_POST:
+            return "POST";
+        case HTTP_OPTION_PUT:
+            return "PUT";
+        case HTTP_OPTION_DELETE:
+            return "DELETE";
+        case HTTP_OPTION_OPTIONS:
+            return "OPTIONS";
+        case HTTP_OPTION_TRACE:
+            return "TRACE";
+        case HTTP_OPTION_CONNECT:
+            return "CONNECT";
+        case HTTP_OPTION_LINK:
+            return "LINK";
+        case HTTP_OPTION_UNLINK:
+            return "UNLINK";
+        default:
+            return "";
+    }
+}
+
+static char* http_version_str_get(mbtk_http_version_enum version)
+{
+    switch(version)
+    {
+        case HTTP_VERSION_1_0:
+            return "1.0";
+        case HTTP_VERSION_1_1:
+            return "1.1";
+        case HTTP_VERSION_2:
+            return "2";
+        case HTTP_VERSION_3:
+            return "3";
+        default:
+            return "";
+    }
+}
+
+static char* http_header_find(mbtk_http_session_t *session,char* name)
+{
+    int i = 0;
+    while(i < HTTP_REQUEST_HEADER_MAX)
+    {
+        if(session->req.req_h[i] &&
+           !strncasecmp(session->req.req_h[i]->name,name,strlen(name)))
+        {
+            return session->req.req_h[i]->value;
+        }
+
+        i++;
+    }
+    return NULL;
+}
+
+static int http_header_str_get(mbtk_http_header_t *header,char *header_str,int header_str_len)
+{
+    if(header == NULL || header->value == NULL
+       || header_str == NULL)
+        return 0;
+
+    int len = 0;
+    len = snprintf(header_str,header_str_len,"%s: %s\r\n",
+                   header->name, header->value);
+
+    return len;
+}
+
+static mbtk_http_version_enum http_version_get_by_str(char *version_str)
+{
+    if(!memcmp(version_str,"1.0",3))
+        return HTTP_VERSION_1_0;
+    else if(!memcmp(version_str,"1.1",3))
+        return HTTP_VERSION_1_1;
+    else if(!memcmp(version_str,"2",1))
+        return HTTP_VERSION_2;
+    else if(!memcmp(version_str,"3",1))
+        return HTTP_VERSION_3;
+    else
+        return HTTP_VERSION_1_1;
+}
+
+static int http_header_read(mbtk_http_session_t *session)
+{
+#define BUFFER_SIZE 2048
+    char line[BUFFER_SIZE];
+    char *ptr = NULL;
+    int len = 0;
+    while((len = mbtk_http_read_line(session->sock_fd,line,BUFFER_SIZE)) > 0)
+    {
+        if(!memcmp(line,"\r\n",2))
+        {
+            LOGD("Read empty line.");
+            break;
+        }
+
+        // Delete "\r\n"
+        ptr = line + len - 1; // Point to last char.
+        while(http_is_space_char(*ptr))
+        {
+            *ptr = '\0';
+            len--;
+            ptr--;
+        }
+
+        LOGV("LINE:%s",line);
+
+        if(http_handles[session->handle_id].show_rsp_header &&
+           http_handles[session->handle_id].data_cb)
+        {
+            http_handles[session->handle_id].data_cb(session->id,
+                    MBTK_HTTP_DATA_HEADER,line,len);
+        }
+
+        if(!memcmp(line,"HTTP/",5))   // "HTTP/1.1 200 OK"
+        {
+            session->rsp.state_code = atoi(line + 9);
+            session->rsp.version = http_version_get_by_str(line + 5);
+        }
+        else     // Is response header item.
+        {
+            if(!strncasecmp(line,"Content-Length",14))
+            {
+                ptr = line + 14;
+                while(ptr && !isdigit(*ptr))
+                {
+                    ptr++;
+                }
+
+                if(ptr)
+                {
+                    session->rsp.content_length = atol(ptr);
+                }
+            }
+            else if(!strncasecmp(line,"Transfer-Encoding",17))
+            {
+                ptr = line + 17;
+                while(ptr && !isalpha(*ptr))
+                {
+                    ptr++;
+                }
+
+                if(ptr && !memcmp(ptr,"chunked",7))
+                {
+                    session->rsp.is_chunked = TRUE;
+                }
+            }
+        }
+    }
+#undef BUFFER_SIZE
+
+    LOGD("RSP:HTTP/%s %d, is_chunked:%d,Content-Length:%d",http_version_str_get(session->rsp.version),
+         session->rsp.state_code,session->rsp.is_chunked,session->rsp.content_length);
+
+    return 0;
+}
+
+static int http_session_start_write(mbtk_http_session_t *session)
+{
+    LOGI("Start HTTP write.");
+
+#define BUFFER_SIZE 1024
+    session->state = HTTP_SESSION_STATE_WRITE_HEADER;
+    char buff[BUFFER_SIZE];
+    int len = 0;
+    int index = 0;
+    len += snprintf(buff + len,BUFFER_SIZE - len,"%s %s HTTP/%s\r\n",
+                    http_option_str_get(session->option),
+                    session->uri,
+                    http_version_str_get(session->version));
+
+    // if no set "Host",should set default host.
+    char *host = http_header_find(session,"Host");
+    if(!host)
+    {
+        len += snprintf(buff + len,BUFFER_SIZE - len,"Host: %s\r\n", session->host);
+    }
+
+    if(mbtk_http_write(session->sock_fd,buff,len) != len)
+    {
+        LOGE("mbtk_http_write() fail.");
+        return -1;
+    }
+
+    char header_str[BUFFER_SIZE];
+    int header_str_len = 0;
+    while(index < HTTP_REQUEST_HEADER_MAX)
+    {
+        if(session->req.req_h[index] &&
+           (header_str_len = http_header_str_get(session->req.req_h[index], header_str, BUFFER_SIZE)) > 0)
+        {
+            if(mbtk_http_write(session->sock_fd,header_str,header_str_len) != header_str_len)
+            {
+                LOGE("mbtk_http_write() fail.");
+                return -1;
+            }
+        }
+        index++;
+    }
+
+    // Write request header success.
+    LOGI("HTTP write header complete.");
+
+#undef BUFFER_SIZE
+
+    // Write "\r\n"
+    if(mbtk_http_write(session->sock_fd,"\r\n",2) != 2)
+    {
+        LOGE("mbtk_http_write() fail.");
+        return -1;
+    }
+
+    LOGI("Start write HTTPsession->option. %d", session->option);
+    if(session->option == HTTP_OPTION_POST)
+    {
+        session->state = HTTP_SESSION_STATE_WRITE_CONTENT;
+        LOGI("Start write HTTP content data.");
+
+        if(session->req.content && session->req.content_len > 0)
+        {
+            if(mbtk_http_write(session->sock_fd,session->req.content,session->req.content_len) != session->req.content_len)
+            {
+                LOGE("mbtk_http_write() fail.");
+                return -1;
+            }
+
+            session->state = HTTP_SESSION_STATE_WRITE_END;
+        }
+    }
+    else
+    {
+        session->state = HTTP_SESSION_STATE_WRITE_END;
+
+        LOGI("HTTP write complete.");
+    }
+    return 0;
+}
+
+static int http_session_read_by_chunk(mbtk_http_session_t *session)
+{
+#undef BUFFER_SIZE
+#define BUFFER_SIZE 2048
+    http_chunk_code chunk_code;
+    http_chunker_t chunker;
+    char read_buf[BUFFER_SIZE + 1];
+    int read_len = 0;
+    char chunk_buf[BUFFER_SIZE + 1];
+    int chunk_len;
+    http_chunk_init(&chunker);
+    while(TRUE)
+    {
+        read_len = mbtk_http_read(session->sock_fd,read_buf,BUFFER_SIZE,3000);
+        //read_len = mbtk_http_read_line(session->sock_file,read_buf,BUFFER_SIZE);
+        if(read_len <= 0)
+        {
+            LOGE("Read fail.");
+            return -1;
+        }
+
+        chunk_code = http_chunk_parse(&chunker, read_buf, read_len, chunk_buf, &chunk_len);
+        if(chunk_code > CHUNKE_OK)   // Fail.
+        {
+            LOGE("http_chunk_parse() fail[err - %d].",chunk_code);
+            return -1;
+        }
+
+        LOGD("Read chunk_len:%d",chunk_len);
+        chunk_buf[chunk_len] = '\0';
+
+        if(http_handles[session->handle_id].data_cb)
+            http_handles[session->handle_id].data_cb(session->id,
+                    MBTK_HTTP_DATA_CONTENT,chunk_buf,chunk_len);
+
+        if(CHUNKE_STOP == chunk_code)
+        {
+            if(http_handles[session->handle_id].data_cb)
+                http_handles[session->handle_id].data_cb(session->id,
+                        MBTK_HTTP_DATA_COMPLETE,NULL,0);
+
+            break;
+        }
+    }
+
+    LOGV("Chunk read success.");
+
+    return 0;
+}
+
+static int http_session_read_by_length(mbtk_http_session_t *session)
+{
+#undef BUFFER_SIZE
+#define BUFFER_SIZE 2048
+    char read_buf[BUFFER_SIZE + 1];
+    int read_len = 0;
+    int64 read_count = 0;
+    while(TRUE)
+    {
+        memset(read_buf,0x0,BUFFER_SIZE + 1);
+        read_len = mbtk_http_read(session->sock_fd,read_buf,BUFFER_SIZE,3000);
+        if(read_len <= 0)
+        {
+            LOGE("Read fail.");
+            return -1;
+        }
+
+        if(read_count + read_len >= session->rsp.content_length)   // Read data complete.
+        {
+            if(http_handles[session->handle_id].data_cb)
+            {
+                http_handles[session->handle_id].data_cb(session->id,
+                        MBTK_HTTP_DATA_CONTENT,read_buf,session->rsp.content_length - read_count);
+
+                http_handles[session->handle_id].data_cb(session->id,
+                        MBTK_HTTP_DATA_COMPLETE,NULL,0);
+            }
+            break;
+        }
+
+        if(http_handles[session->handle_id].data_cb)
+            http_handles[session->handle_id].data_cb(session->id,
+                    MBTK_HTTP_DATA_CONTENT,read_buf,read_len);
+
+        read_count += read_len;
+    }
+
+    return 0;
+}
+
+static int http_session_read_by_general(mbtk_http_session_t *session)
+{
+#undef BUFFER_SIZE
+#define BUFFER_SIZE 2048
+    char read_buf[BUFFER_SIZE + 1];
+    int read_len = 0;
+    while(TRUE)
+    {
+        read_len = mbtk_http_read(session->sock_fd,read_buf,BUFFER_SIZE,1000);
+        if(read_len <= 0)
+        {
+            if(read_len == -2 || read_len == 0) // Timeout or end
+                break;
+
+            LOGW("Read end[read_len - %d].",read_len);
+            //return -1;
+            break;
+        }
+
+        read_buf[read_len] = '\0';
+
+        if(http_handles[session->handle_id].data_cb)
+            http_handles[session->handle_id].data_cb(session->id,
+                    MBTK_HTTP_DATA_CONTENT,read_buf,read_len);
+    }
+
+    if(http_handles[session->handle_id].data_cb)
+        http_handles[session->handle_id].data_cb(session->id,
+                MBTK_HTTP_DATA_COMPLETE,NULL,0);
+
+    return 0;
+}
+
+static int http_session_start_read(mbtk_http_session_t *session)
+{
+    LOGI("Start HTTP read.");
+    int result = 0;
+//    usleep(500000);
+    session->state = HTTP_SESSION_STATE_READ_HEADER;
+    if(http_header_read(session))
+    {
+        result = -1;
+        goto read_end;
+    }
+
+    if(session->option != HTTP_OPTION_HEAD)
+    {
+        session->state = HTTP_SESSION_STATE_READ_CONTENT;
+        if(session->rsp.is_chunked)
+        {
+            if(http_session_read_by_chunk(session))
+            {
+                result = -1;
+                goto read_end;
+            }
+        }
+        else if(session->rsp.content_length > 0)
+        {
+            if(http_session_read_by_length(session))
+            {
+                result = -1;
+                goto read_end;
+            }
+        }
+        else
+        {
+            if(http_session_read_by_general(session))
+            {
+                result = -1;
+                goto read_end;
+            }
+        }
+    }
+    else
+    {
+        if(http_handles[session->handle_id].data_cb)
+            http_handles[session->handle_id].data_cb(session->id,
+                    MBTK_HTTP_DATA_COMPLETE,NULL,0);
+    }
+
+read_end:
+    session->state = HTTP_SESSION_STATE_READ_END;
+
+    LOGI("HTTP request complete[result - %d].",result);
+    if(http_session_close(session))
+    {
+        return -1;
+    }
+
+#if 0
+    // Free session after HTTP request complete.
+    http_session_free(session);
+    http_handles[handle_id].session[session_id] = NULL;
+    http_handles[handle_id].session_cnt--;
+#endif
+
+    return result;
+}
+
+static bool http_session_req_check(mbtk_http_session_t *session)
+{
+    if(session == NULL || session->port == 0 ||
+       strlen(session->host) == 0)
+    {
+        LOGE("Session not set host or port.");
+        return FALSE;
+    }
+
+    if(session->option != HTTP_OPTION_HEAD &&
+       session->option != HTTP_OPTION_POST &&
+       session->option != HTTP_OPTION_GET)
+    {
+        LOGE("Only support HEAD/GET/POST");
+        return FALSE;
+    }
+
+#if 0
+    if(session->version != HTTP_VERSION_1_0 &&
+       session->version != HTTP_VERSION_1_1)
+    {
+        LOGE("Only support HTTP 1.0/1.1");
+        return FALSE;
+    }
+#endif
+
+    if(session->option == HTTP_OPTION_POST)
+    {
+        char *value = NULL;
+        value = http_header_find(session, "Content-Length");
+        if(!value)
+        {
+            LOGE("POST must set 'Content-Length'");
+            return FALSE;
+        }
+        if(session->req.content_len != atoi(value))
+        {
+            LOGE("POST 'Content-Length' error.");
+            return FALSE;
+        }
+
+        value = http_header_find(session, "Content-Type");
+        if(!value)
+        {
+            LOGE("POST must set 'Content-Type'");
+            return FALSE;
+        }
+    }
+
+    return TRUE;
+}
+
+/*************************************************************
+    Public Function Definitions
+*************************************************************/
+int mbtk_http_handle_get(bool show_rsp_header,mbtk_http_data_callback_func data_cb)
+{
+    int index = 0;
+    int i = 0;
+    for(; index < HTTP_HANDLE_MAX; index++)
+    {
+        if(http_handles[index].id < 0)   // Find free handle
+        {
+            break;
+        }
+    }
+
+    if(index == HTTP_HANDLE_MAX)
+    {
+        LOGE("HTTP Handle is full.");
+        return -1;
+    }
+
+    memset(&(http_handles[index]),0x0,sizeof(mbtk_http_handle_t));
+    http_handles[index].id = index;
+    http_handles[index].show_rsp_header = show_rsp_header;
+    http_handles[index].data_cb = data_cb;
+    http_handles[index].session_cnt = 0;
+    for(i = 0; i < HTTP_SESSION_MAX; i++)
+    {
+        http_handles[index].session[i] = NULL;
+    }
+
+    if(mbtk_http_init())
+    {
+        LOGE("mbtk_http_init() fail.");
+        return -1;
+    }
+
+    return http_handles[index].id;
+}
+
+int mbtk_http_handle_free(int handle_id)
+{
+    int i = 0;
+    if(!http_handle_check(handle_id))
+    {
+        LOGE("Handle error.");
+        return -1;
+    }
+
+    http_handles[handle_id].id = -1;
+    http_handles[handle_id].data_cb = NULL;
+    if(http_handles[handle_id].session_cnt > 0)
+    {
+        for(i = 0; i < HTTP_SESSION_MAX; i++)
+        {
+            if(http_handles[handle_id].session[i] != NULL)
+            {
+                if(http_handles[handle_id].session[i]->state != HTTP_SESSION_STATE_NON)
+                {
+                    if(http_session_close(http_handles[handle_id].session[i]))
+                    {
+                        return -1;
+                    }
+                }
+
+                http_session_free(http_handles[handle_id].session[i]);
+                http_handles[handle_id].session[i] = NULL;
+            }
+        }
+
+        http_handles[handle_id].session_cnt = 0;
+    }
+
+    if(mbtk_http_deinit())
+    {
+        LOGE("mbtk_http_deinit() fail.");
+        return -1;
+    }
+
+    return 0;
+}
+
+int mbtk_http_session_create(int handle_id, mbtk_http_option_enum option,
+                             mbtk_http_version_enum version)
+{
+    int handle_index = 0;
+    int session_index = 0;
+    if(!http_handle_check(handle_id))
+    {
+        LOGE("Handle error.");
+        return -1;
+    }
+
+    for(; handle_index < HTTP_HANDLE_MAX; handle_index++)
+    {
+        if(http_handles[handle_index].id == handle_id)   // Find handle
+        {
+            break;
+        }
+    }
+
+    if(handle_index == HTTP_HANDLE_MAX)
+    {
+        LOGE("No found handle[handle - %d].",handle_id);
+        return -1;
+    }
+
+    if(http_handles[handle_index].session_cnt >= HTTP_SESSION_MAX)
+    {
+        LOGE("Session is full.");
+        return -1;
+    }
+
+    for(; session_index < HTTP_SESSION_MAX; session_index++)
+    {
+        if(http_handles[handle_index].session[session_index] == NULL)   // Find first NULL session
+        {
+            break;
+        }
+    }
+
+    if(session_index == HTTP_SESSION_MAX)
+    {
+        LOGE("Session is full.");
+        return -1;
+    }
+
+    mbtk_http_session_t* session = (mbtk_http_session_t*)malloc(sizeof(mbtk_http_session_t));
+    if(session == NULL)
+    {
+        LOGE("malloc() fail.");
+        return -1;
+    }
+    memset(session,0x0,sizeof(mbtk_http_session_t));
+    session->sock_fd = -1;
+    session->sock_file = NULL;
+    session->handle_id = handle_id;
+    session->id = session_index;
+    session->state = HTTP_SESSION_STATE_NON;
+    session->is_ssl = FALSE;
+    session->version = version;
+    session->option = option;
+    session->req.content_len = 0;
+    session->req.content_len_send = 0;
+    session->rsp.is_chunked = FALSE;
+    session->rsp.content_length = 0;
+    session->rsp.header_cnt = 0;
+    http_handles[handle_index].session[session_index] = session;
+    http_handles[handle_index].session_cnt++;
+
+    return session->id;
+}
+
+int mbtk_http_session_option_reset(int handle_id, int session_id, mbtk_http_option_enum option)
+{
+    if(!http_session_check(handle_id,session_id))
+    {
+        LOGE("Session error.");
+        return -1;
+    }
+
+    mbtk_http_session_t *session = http_handles[handle_id].session[session_id];
+    if(session->state != HTTP_SESSION_STATE_NON)
+    {
+        LOGE("Session state error.[%d]",session->state);
+        return -1;
+    }
+
+    session->option = option;
+    return 0;
+}
+
+int mbtk_http_session_free(int handle_id,int session_id)
+{
+    if(!http_session_check(handle_id,session_id))
+    {
+        LOGE("Session error.");
+        return -1;
+    }
+
+    mbtk_http_session_t *session = http_handles[handle_id].session[session_id];
+    if(session->state != HTTP_SESSION_STATE_NON)
+    {
+        if(http_session_close(session))
+        {
+            return -1;
+        }
+    }
+
+    http_session_free(session);
+    http_handles[handle_id].session[session_id] = NULL;
+    http_handles[handle_id].session_cnt--;
+    return 0;
+}
+
+int mbtk_http_session_url_set(int handle_id,int session_id,void *url)
+{
+    if(!http_session_check(handle_id,session_id))
+    {
+        LOGE("Session error.");
+        return -1;
+    }
+
+    mbtk_http_session_t *session = http_handles[handle_id].session[session_id];
+    if(session->state == HTTP_SESSION_STATE_NON)
+        return http_url_parse(url, session->host,session->uri,&(session->port),&(session->is_ssl));
+    else
+    {
+        LOGE("Currenr session is process[state - %d].",session->state);
+        return -1;
+    }
+}
+
+int mbtk_http_session_head_add(int handle_id,int session_id,
+                               char *name, char *value)
+{
+    if(!http_session_check(handle_id,session_id))
+    {
+        LOGE("Session error.");
+        return -1;
+    }
+
+    if(http_str_empty(name) || http_str_empty(value))
+    {
+        LOGE("Param error.");
+        return -1;
+    }
+
+    mbtk_http_session_t *session = http_handles[handle_id].session[session_id];
+    if(session->state == HTTP_SESSION_STATE_NON)
+    {
+        int result = http_session_req_head_add(session,TRUE,name,value);
+        if(!result && !strncasecmp(name,"Content-Length",14))
+        {
+            session->req.content_len = atoi(value);
+        }
+        return result;
+    }
+    else
+    {
+        LOGE("Currenr session is process[state - %d].",session->state);
+        return -1;
+    }
+}
+
+int mbtk_http_session_content_set(int handle_id,int session_id,
+                                  char *content,uint32 content_len)
+{
+    if(!http_session_check(handle_id,session_id))
+    {
+        LOGE("Session error.");
+        return -1;
+    }
+
+    if(content_len <= 0 || content_len > HTTP_CONTENT_LEN_MAX)
+    {
+        LOGE("Content lenght error[%d].",content_len);
+        return -1;
+    }
+
+    mbtk_http_session_t *session = http_handles[handle_id].session[session_id];
+    if(session->state == HTTP_SESSION_STATE_NON)
+    {
+        if(session->option != HTTP_OPTION_POST)
+        {
+            LOGE("Content only for post.");
+            return -1;
+        }
+
+        if(session->req.content)
+        {
+            free(session->req.content);
+            session->req.content_len = 0;
+        }
+
+        session->req.content = (char*)malloc(content_len);
+        if(session->req.content == NULL)
+        {
+            LOGE("malloc() fail.");
+            return -1;
+        }
+
+        char *content_type = NULL;
+        if(strlen(content) == content_len)   //
+        {
+            content_type = "text/plain";
+        }
+        else
+        {
+            content_type = "application/octet-stream";
+        }
+
+        if(http_session_req_head_add(session, FALSE, "Content-Type", content_type))
+        {
+            LOGE("Set 'Content-Type' fail.");
+            return -1;
+        }
+
+        memcpy(session->req.content,content,content_len);
+        session->req.content_len = content_len;
+
+        char len_str[20] = {0};
+        snprintf(len_str,20,"%d",content_len);
+        if(http_session_req_head_add(session,FALSE,"Content-Length",len_str))
+        {
+            LOGE("Set 'Content-Length' fail.");
+            return -1;
+        }
+
+
+        return 0;
+    }
+    else
+    {
+        LOGE("Currenr session is process[state - %d].",session->state);
+        return -1;
+    }
+}
+
+int mbtk_http_session_start(int handle_id,int session_id)
+{
+    if(!http_session_check(handle_id,session_id))
+    {
+        LOGE("Session error.");
+        return -1;
+    }
+
+    mbtk_http_session_t *session = http_handles[handle_id].session[session_id];
+    if(session->state == HTTP_SESSION_STATE_NON)
+    {
+        if(!http_session_req_check(session))
+        {
+            LOGE("http_session_req_check() fail.");
+            return -1;
+        }
+
+        // Must set "Connection" for post.
+        if(session->option == HTTP_OPTION_POST)
+        {
+            if(http_session_req_head_add(session,FALSE,"Connection","KeepAlive"))
+            {
+                LOGE("Set 'Content-Length' fail.");
+                return -1;
+            }
+        }
+
+        LOGI("HTTP request start.");
+        LOGI("host:%s, port:%d, uri:%s",session->host,session->port,session->uri);
+        LOGI("is_ssl:%d, version:%d, option:%d, content_len:%d",session->is_ssl,
+             session->version,session->option,session->req.content_len);
+
+        int sock_fd = mbtk_http_open(session->is_ssl,TRUE,session->host,session->port);
+        if(sock_fd < 0)
+        {
+            return -1;
+        }
+        session->sock_fd = sock_fd;
+//        int fd = mbtk_sock_fd_get(sock_fd);
+//        if(fd < 0) {
+//            LOGE("mbtk_sock_fd_get() fail.");
+//            return -1;
+//        }
+        // session->sock_file = fdopen(sock_fd,"r");
+        session->state = HTTP_SESSION_STATE_CONN;
+
+//        if(!session->sock_file) {
+//            LOGE("fdopen() fail.");
+//            return -1;
+//        }
+
+        LOGI("HTTP connected.");
+
+        if(http_session_start_write(session))
+        {
+            return -1;
+        }
+
+        if(session->state == HTTP_SESSION_STATE_WRITE_END)
+        {
+            if(http_session_start_read(session))
+            {
+                return -1;
+            }
+        }
+        else
+        {
+            LOGI("Waitting post content data...");
+        }
+
+        return 0;
+    }
+    else
+    {
+        LOGE("Currenr session is process[state - %d].",session->state);
+        return -1;
+    }
+}
+
+int mbtk_http_session_content_send(int handle_id,int session_id,
+                                   char *data,int data_len)
+{
+    if(!http_session_check(handle_id,session_id))
+    {
+        LOGE("Session error.");
+        return -1;
+    }
+
+    if(data_len <= 0 || data_len > HTTP_CONTENT_LEN_MAX)
+    {
+        LOGE("Content lenght error[%d].",data_len);
+        return -1;
+    }
+
+    LOGV("Post send:%d - %s",data_len,data);
+
+    mbtk_http_session_t *session = http_handles[handle_id].session[session_id];
+    if(session->state == HTTP_SESSION_STATE_WRITE_CONTENT)
+    {
+        if(session->option != HTTP_OPTION_POST)
+        {
+            LOGE("Content only for post.");
+            return -1;
+        }
+
+        if(session->req.content || session->req.content_len <= 0)
+        {
+            LOGE("This post not spit package.");
+            return -1;
+        }
+
+        // Discard excess data.
+        if(session->req.content_len_send + data_len > session->req.content_len)
+            data_len = session->req.content_len - session->req.content_len_send;
+
+        if(data_len != mbtk_http_write(session->sock_fd,data,data_len))
+        {
+            return -1;
+        }
+
+        session->req.content_len_send += data_len;
+
+        LOGI("HTTP post data send: %d / %d",session->req.content_len_send,
+             session->req.content_len);
+
+        // Post data send complete.
+        if(session->req.content_len_send >= session->req.content_len)
+        {
+            session->state = HTTP_SESSION_STATE_WRITE_END;
+
+            LOGI("HTTP write complete.");
+            if(http_session_start_read(session))
+            {
+                return -1;
+            }
+        }
+
+        return 0;
+    }
+    else
+    {
+        LOGE("Currenr session state error[%d].",session->state);
+        return -1;
+    }
+}
+
+const mbtk_http_session_t* mbtk_http_session_get(int handle_id,int session_id)
+{
+    if(!http_session_check(handle_id,session_id))
+    {
+        LOGE("Session error.");
+        return NULL;
+    }
+
+    return http_handles[handle_id].session[session_id];
+}
diff --git a/mbtk/mbtk_lib/src/mbtk_http_base.c b/mbtk/mbtk_lib/src/mbtk_http_base.c
new file mode 100755
index 0000000..40a6742
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_http_base.c
@@ -0,0 +1,279 @@
+#include <sys/epoll.h>
+#include <string.h>
+
+#include "mbtk_log.h"
+#include "mbtk_http_base.h"
+
+static void http_sock_cb_func(int handle, int fd, int event);
+
+static mbtk_init_info http_init_info =
+{
+    MBTK_NET_LINUX,
+    NULL,
+    http_sock_cb_func
+};
+static bool http_sock_inited = FALSE;
+static int http_handle = -1;
+static int http_fd = -1;
+
+static void http_sock_cb_func(int handle, int fd, int event)
+{
+    if(http_handle == handle && http_fd == fd) {
+        if(event & EPOLLIN) { // READ
+
+        } else if(event & EPOLLRDHUP) { // Close
+
+        } else {
+            LOGW("Unknown event:%x",event);
+        }
+    }
+}
+
+int mbtk_http_init()
+{
+    if(http_sock_inited) {
+        LOGE("HTTP has inited.");
+        return -1;
+    }
+
+    http_handle = mbtk_sock_init(&http_init_info);
+    if(http_handle < 0) {
+        LOGE("mbtk_sock_init() fail.");
+        return -1;
+    }
+
+    http_sock_inited = TRUE;
+    return 0;
+}
+
+int mbtk_http_deinit()
+{
+    if(!http_sock_inited) {
+        LOGE("HTTP not inited.");
+        return -1;
+    }
+
+    int err = mbtk_sock_deinit(http_handle);
+    if(err != MBTK_SOCK_SUCCESS) {
+        LOGE("mbtk_sock_deinit() fail.");
+        return -1;
+    }
+
+    http_handle = -1;
+    http_sock_inited = FALSE;
+    return 0;
+}
+
+
+int mbtk_http_open
+(
+    bool is_ssl,
+    bool ingnore_cert,
+    const void *host,
+    uint16 port
+)
+{
+    int err;
+    mbtk_sock_info sock_info;
+    memset(&sock_info,0x0,sizeof(mbtk_sock_info));
+
+    sock_info.type = MBTK_SOCK_TCP;
+    sock_info.is_support_ssl = is_ssl;
+    sock_info.ingnore_cert = ingnore_cert;
+    memcpy(sock_info.address,host,strlen(host));
+    sock_info.port = port;
+
+    http_fd = mbtk_sock_open(http_handle,&sock_info, 3000, &err);
+
+    return http_fd;
+}
+
+/*=============================================
+FUNCTION
+    mbtk_http_read
+
+DESCRIPTION
+    read content from socket.
+
+DEPENDENCIES
+    None
+
+PARAMETERS
+    *buf      Store read content.
+    len       the length of Content.
+    timeout   Set timeout
+
+RETURN VALUE
+    Length of read content
+
+SIDE EFFECTS
+    None
+=============================================*/
+int mbtk_http_read
+(
+    int sock_fd,
+    void *buf,
+    uint16 len,
+    int timeout_ms
+)
+{
+    int err;
+    int read_len = mbtk_sock_read(http_handle, sock_fd, buf, len, timeout_ms, &err);
+    if(read_len < 0) {
+        if(err == MBTK_SOCK_ETIMEOUT) {
+            return -2;
+        } else {
+            return -1;
+        }
+    } else {
+        return read_len;
+    }
+}
+
+#if 0
+int mbtk_http_read_line
+(
+    FILE *file,
+    void *buf,
+    uint16 len
+)
+{
+    if(file) {
+        char *buf_ptr = (char*)buf;
+        char *line = NULL;
+read_again:
+        line = fgets(buf_ptr,len,file);
+        if(!line && errno == EWOULDBLOCK) {
+            usleep(100000);
+            goto read_again;
+        }
+        if(line && strlen(line) > 0
+            && strlen(line) <= len
+            && buf_ptr[strlen(line) - 1] == '\n') {
+            LOGV("Read-Line[%d]:%s",strlen(line),line);
+            return strlen(line);
+        }else{
+            LOGE("fgets() fail.");
+            return -1;
+        }
+    }
+
+    return -1;
+}
+#else
+int mbtk_http_read_line
+(
+    int sock_fd,
+    void *buf,
+    uint16 len
+)
+{
+#if 1
+    if(sock_fd > 0) {
+        char *buf_ptr = (char*)buf;
+        char read_buf[1];
+        int read_len = 0;
+        while(TRUE) {
+            if(mbtk_sock_read_async(http_handle, sock_fd, read_buf, 1) == 1) {
+                *buf_ptr++ = read_buf[0];
+                read_len++;
+
+                if(read_buf[0] == '\n' || read_len >= len) {
+                    return read_len;
+                }
+            } else {
+                return -1;
+            }
+        }
+    }
+#else
+    if(http_handle >= 0) {
+        char *buf_ptr = (char*)buf;
+        char read_buf[1];
+        int read_len = 0;
+        while(TRUE) {
+            if(read(http_fd, read_buf, 1) == 1) {
+                *buf_ptr++ = read_buf[0];
+                read_len++;
+
+                if(read_buf[0] == '\n' || read_len >= len) {
+                    return read_len;
+                }
+            } else {
+                if(errno == EWOULDBLOCK) {
+                    usleep(100000);
+                }
+            }
+        }
+    }
+
+#endif
+
+    return -1;
+}
+
+#endif
+
+/*=============================================
+FUNCTION
+    mbtk_http_write
+
+DESCRIPTION
+    Write content to socket.
+
+DEPENDENCIES
+    None
+
+PARAMETERS
+    *buf    Content to be transferred
+    len     the length of Content.
+
+RETURN VALUE
+    Length of written content
+
+SIDE EFFECTS
+    None
+=============================================*/
+int mbtk_http_write
+(
+    int sock_fd,
+    void *buf,
+    uint16 len
+)
+{
+    int err;
+    LOGV("Write[%d]:%s",len,(char*)buf);
+    return mbtk_sock_write(http_handle, sock_fd, buf, len, 300, &err);
+}
+
+/*=============================================
+FUNCTION
+    mbtk_http_close
+
+DESCRIPTION
+    close HTTP service.
+
+DEPENDENCIES
+    None
+
+PARAMETERS
+    *err    Error number
+
+RETURN VALUE
+    TURE or FALSE
+
+SIDE EFFECTS
+    None
+=============================================*/
+int mbtk_http_close(int sock_fd)
+{
+    int err;
+
+    if(mbtk_sock_close(http_handle, sock_fd,1000, &err)) {
+        return -1;
+    }
+
+    sock_fd = -1;
+    return 0;
+}
+
diff --git a/mbtk/mbtk_lib/src/mbtk_http_chunks.c b/mbtk/mbtk_lib/src/mbtk_http_chunks.c
new file mode 100755
index 0000000..899e0c7
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_http_chunks.c
@@ -0,0 +1,131 @@
+#include <string.h>
+
+#include "mbtk_http_chunks.h"
+#include "mbtk_http_base.h"
+
+static bool http_isxdigit(char digit)
+{
+    return (digit >= 0x30 && digit <= 0x39) /* 0-9 */
+            || (digit >= 0x41 && digit <= 0x46) /* A-F */
+            || (digit >= 0x61 && digit <= 0x66); /* a-f */
+}
+
+void http_chunk_init(http_chunker_t *chunker)
+{
+    chunker->hexindex = 0;      /* start at 0 */
+    chunker->dataleft = 0;      /* no data left yet! */
+    chunker->state = CHUNK_HEX; /* we get hex first! */
+}
+
+http_chunk_code http_chunk_parse(http_chunker_t *chunker, char *src, int src_len,
+            char *dest,int *dest_len)
+{
+    int piece;
+    char *datap = src;
+    int length = src_len;
+
+    *dest_len = 0;
+
+    while(length > 0)
+    {
+        switch(chunker->state)
+        {
+            case CHUNK_HEX:
+                if(http_isxdigit(*datap))
+                {
+                    if(chunker->hexindex < MAXNUM_SIZE)
+                    {
+                        chunker->hexbuffer[chunker->hexindex] = *datap;
+                        datap++;
+                        length--;
+                        chunker->hexindex++;
+                    }
+                    else
+                    {
+                        return CHUNKE_TOO_LONG_HEX; /* longer hex than we support */
+                    }
+                }
+                else // Not hex char. ('\r')
+                {
+                    if(0 == chunker->hexindex)
+                        return CHUNKE_ILLEGAL_HEX;
+
+                    chunker->hexbuffer[chunker->hexindex] = '\0';
+                    sscanf(chunker->hexbuffer,"%x",&chunker->datasize);
+
+                    LOGD("Chunk len:%s -> %d",chunker->hexbuffer,chunker->datasize);
+
+                    chunker->state = CHUNK_LF; /* now wait for the CRLF */
+                }
+                break;
+            case CHUNK_LF:
+                if(*datap == 0x0a) // '\n'
+                {
+                    if(0 == chunker->datasize) { // Chunk lenght is '0' (Is last chunk)
+                        chunker->state = CHUNK_TRAILER;
+                    } else {
+                        chunker->state = CHUNK_DATA;
+                    }
+                }
+
+                datap++;
+                length--;
+                break;
+            case CHUNK_DATA:
+                /* We expect 'datasize' of data. We have 'length' right now, it can be
+                   more or less than 'datasize'. Get the smallest piece.
+                */
+                piece = (chunker->datasize >= length)?length:chunker->datasize;
+
+                memcpy(dest + *dest_len,datap,piece);
+                *dest_len += piece;
+
+                chunker->datasize -= piece; /* decrease amount left to expect */
+                datap += piece;    /* move read pointer forward */
+                length -= piece;   /* decrease space left in this round */
+
+                if(0 == chunker->datasize) // Next chunk
+                    chunker->state = CHUNK_POSTLF;
+                break;
+            case CHUNK_POSTLF:
+                if(*datap == 0x0a)
+                {
+                    http_chunk_init(chunker);
+                }
+                else if(*datap != 0x0d)
+                    return CHUNKE_BAD_CHUNK;
+                datap++;
+                length--;
+                break;
+            case CHUNK_TRAILER:
+                if((*datap != 0x0d) && (*datap != 0x0a))
+                {
+                    return CHUNKE_BAD_CHUNK;
+                }
+                if(*datap == 0x0d)
+                {
+                    /* skip if CR */
+                    datap++;
+                    length--;
+                }
+                chunker->state = CHUNK_STOP;
+                break;
+            case CHUNK_STOP: // Complete
+                if(*datap == 0x0a)
+                {
+                    length--;
+
+                    /* Record the length of any data left in the end of the buffer
+                       even if there's no more chunks to read */
+                    chunker->dataleft = length;
+
+                    return CHUNKE_STOP; /* return stop */
+                }
+                else
+                    return CHUNKE_BAD_CHUNK;
+        }
+    }
+    return CHUNKE_OK;
+}
+
+
diff --git a/mbtk/mbtk_lib/src/mbtk_ifc.c b/mbtk/mbtk_lib/src/mbtk_ifc.c
new file mode 100755
index 0000000..51267f5
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_ifc.c
@@ -0,0 +1,577 @@
+/*
+* MBTK Network Interface control.
+*
+* Author : lb
+* Date   : 2021/8/20 11:44:05
+*
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+//#include <net/if.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <linux/if_ether.h>
+#include <linux/sockios.h>
+//#include <cutils/properties.h>
+#include <arpa/inet.h>
+#include <pthread.h>
+#include <linux/route.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#include "mbtk_type.h"
+#include "mbtk_log.h"
+#include "mbtk_utils.h"
+#include "mbtk_ifc.h"
+
+#define IFNAMSIZ    16
+
+static int ifc_ctl_sock = -1;
+static pthread_mutex_t ifc_sock_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static void ifc_init_ifr(const char *name, struct ifreq *ifr)
+{
+    memset(ifr, 0, sizeof(struct ifreq));
+    strncpy(ifr->ifr_name, name, IFNAMSIZ);
+    ifr->ifr_name[IFNAMSIZ - 1] = 0;
+}
+
+static int ifc_set_flags(int sock, const char *name, unsigned set, unsigned clr)
+{
+    struct ifreq ifr;
+    ifc_init_ifr(name, &ifr);
+
+    if(ioctl(sock, SIOCGIFFLAGS, &ifr) < 0)
+        return -1;
+    ifr.ifr_flags = (ifr.ifr_flags & (~clr)) | set;
+    return ioctl(sock, SIOCSIFFLAGS, &ifr);
+}
+
+static void init_sockaddr_in(struct sockaddr *sa, in_addr_t addr)
+{
+    struct sockaddr_in *sin = (struct sockaddr_in *) sa;
+    sin->sin_family = AF_INET;
+    sin->sin_port = 0;
+    sin->sin_addr.s_addr = addr;
+}
+
+#if 1
+static const char *ipaddr_to_string(in_addr_t addr)
+{
+    struct in_addr in_addr;
+
+    in_addr.s_addr = addr;
+    return inet_ntoa(in_addr);
+}
+#endif
+
+static in_addr_t prefixLengthToIpv4Netmask(int prefix_length)
+{
+    in_addr_t mask = 0;
+
+    // C99 (6.5.7): shifts of 32 bits have undefined results
+    if (prefix_length <= 0 || prefix_length > 32)
+    {
+        return 0;
+    }
+
+    mask = ~mask << (32 - prefix_length);
+    mask = htonl(mask);
+
+    return mask;
+}
+
+#if 1
+static int ifc_set_prefixLength(const char *name, int prefixLength)
+{
+    struct ifreq ifr;
+    // TODO - support ipv6
+//    if (prefixLength > 32 || prefixLength < 0) return -1;
+
+    in_addr_t mask = prefixLengthToIpv4Netmask(prefixLength);
+    ifc_init_ifr(name, &ifr);
+    init_sockaddr_in(&ifr.ifr_addr, mask);
+
+    return ioctl(ifc_ctl_sock, SIOCSIFNETMASK, &ifr);
+}
+#endif
+
+int ifc_act_on_ipv4_route(int action, const char *ifname, struct in_addr dst, int prefix_length,
+                          struct in_addr gw)
+{
+    struct rtentry rt;
+    int result;
+    in_addr_t netmask;
+
+    memset(&rt, 0, sizeof(rt));
+
+    rt.rt_dst.sa_family = AF_INET;
+    rt.rt_dev = (void*) ifname;
+
+    netmask = prefixLengthToIpv4Netmask(prefix_length);
+    init_sockaddr_in(&rt.rt_genmask, netmask);
+    init_sockaddr_in(&rt.rt_dst, dst.s_addr);
+    rt.rt_flags = RTF_UP;
+
+    if (prefix_length == 32)
+    {
+        rt.rt_flags |= RTF_HOST;
+    }
+
+    if (gw.s_addr != 0)
+    {
+        rt.rt_flags |= RTF_GATEWAY;
+        init_sockaddr_in(&rt.rt_gateway, gw.s_addr);
+    }
+
+    result = ioctl(ifc_ctl_sock, action, &rt);
+    if (result < 0)
+    {
+        if (errno == EEXIST)
+        {
+            result = 0;
+        }
+        else
+        {
+            result = -errno;
+        }
+    }
+    return result;
+}
+
+static int ifc_create_default_route1(const char *name, in_addr_t gw)
+{
+    struct in_addr in_dst, in_gw;
+
+    in_dst.s_addr = 0;
+    in_gw.s_addr = gw;
+
+    int ret = ifc_act_on_ipv4_route(SIOCADDRT, name, in_dst, 0, in_gw);
+    LOGD("ifc_create_default_route(%s, %d) = %d", name, gw, ret);
+    return ret;
+}
+
+/* deprecated - v4 only */
+static int ifc_create_default_route2(const char *name, const char *gw)
+{
+    struct in_addr in_dst, in_gw;
+
+    in_dst.s_addr = 0;
+    if(gw == NULL)
+    {
+        in_gw.s_addr = 0;
+    }
+    else
+    {
+        if(inet_aton(gw,(struct in_addr *)&(in_gw.s_addr)) < 0)
+        {
+            LOGE("inet_aton error.");
+            return -1;
+        }
+    }
+
+    int ret = ifc_act_on_ipv4_route(SIOCADDRT, name, in_dst, 0, in_gw);
+    LOGD("ifc_create_default_route(%s) = %d", name, ret);
+    return ret;
+}
+
+int mbtk_ifc_open(void)
+{
+    pthread_mutex_lock(&ifc_sock_mutex);
+    if (ifc_ctl_sock == -1)
+    {
+        ifc_ctl_sock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+        if (ifc_ctl_sock < 0)
+        {
+            LOGE("socket() failed: %s\n", strerror(errno));
+        }
+    }
+
+    return ifc_ctl_sock < 0 ? -1 : 0;
+}
+
+int mbtk_ifc_close(void)
+{
+    if (ifc_ctl_sock != -1)
+    {
+        (void)close(ifc_ctl_sock);
+        ifc_ctl_sock = -1;
+    }
+    pthread_mutex_unlock(&ifc_sock_mutex);
+
+    return 0;
+}
+
+int mbtk_ifc_set_addr(const char *name, in_addr_t addr, in_addr_t netmask)
+{
+    struct ifreq ifr, irf_mask;
+    int ret;
+
+    ifc_init_ifr(name, &ifr);
+    init_sockaddr_in(&ifr.ifr_addr, addr);
+
+    ret = ioctl(ifc_ctl_sock, SIOCSIFADDR, &ifr);
+    if(ret)
+    {
+        LOGD("set_addr(%s, %x) = %d fail.", name, addr, ret);
+    }
+
+    if(netmask)
+    {
+        ifc_init_ifr(name, &irf_mask);
+        init_sockaddr_in(&irf_mask.ifr_netmask, netmask);
+        ret = ioctl(ifc_ctl_sock, SIOCSIFNETMASK, &irf_mask);
+        if(ret)
+        {
+            LOGD("set_netmask(%s, %x) = %d fail.", name, netmask, ret);
+        }
+    }
+
+    return ret;
+}
+
+int mbtk_ifc_ip_config(const char *ifname, const char *ipv4, const char *mask, const char *gateway)
+{
+    UNUSED(gateway);
+    struct ifreq ifr;
+
+//    struct rtentry  rt;
+    // Set IPv4
+    struct sockaddr_in *sin;
+    memset(&ifr,0,sizeof(ifr));
+    strcpy(ifr.ifr_name,ifname);
+    sin = (struct sockaddr_in*)&ifr.ifr_addr;
+    sin->sin_family = AF_INET;
+    if(ipv4) {
+        if(inet_aton(ipv4,&(sin->sin_addr)) < 0)
+        {
+            LOGE("inet_aton error.");
+            return -2;
+        }
+    } else {
+        memset(&(sin->sin_addr), 0, sizeof(struct in_addr));
+    }
+
+    if(ioctl(ifc_ctl_sock,SIOCSIFADDR,&ifr) < 0)
+    {
+        LOGE("ioctl SIOCSIFADDR error.");
+        return -3;
+    }
+
+#if 1
+#if 1
+    //netmask
+    if(mask) {
+        if(inet_aton(mask,&(sin->sin_addr)) < 0)
+        {
+            LOGE("inet_pton error.");
+            return -4;
+        }
+
+        if(ioctl(ifc_ctl_sock, SIOCSIFNETMASK, &ifr) < 0)
+        {
+            LOGE("ioctl error.");
+            return -5;
+        }
+    }
+#else
+
+    //struct ifreq ifr;
+    //strcpy(ifr.ifr_name, interface_name);
+    struct sockaddr_in netmask_addr;
+    bzero(&netmask_addr, sizeof(struct sockaddr_in));
+    netmask_addr.sin_family = PF_INET;
+    inet_aton(mask, &netmask_addr.sin_addr);
+    memcpy(&ifr.ifr_ifru.ifru_netmask, &netmask_addr,
+           sizeof(struct sockaddr_in));
+    if (ioctl(ifc_ctl_sock, SIOCSIFNETMASK, &ifr) < 0)
+    {
+        LOGE("ioctl() fail.");
+        return -1;
+    }
+#endif
+#endif
+
+#if 0
+    //gateway
+    memset(&rt, 0, sizeof(struct rtentry));
+    memset(sin, 0, sizeof(struct sockaddr_in));
+    sin->sin_family = AF_INET;
+    sin->sin_port = 0;
+    if(inet_aton(gateway, &sin->sin_addr)<0)
+    {
+        LOGE( "inet_aton error." );
+    }
+    memcpy ( &rt.rt_gateway, sin, sizeof(struct sockaddr_in));
+    ((struct sockaddr_in *)&rt.rt_dst)->sin_family=AF_INET;
+    ((struct sockaddr_in *)&rt.rt_genmask)->sin_family=AF_INET;
+    rt.rt_flags = RTF_GATEWAY;
+    if (ioctl(ifc_ctl_sock, SIOCADDRT, &rt)<0)
+    {
+        LOGE("ioctl(SIOCADDRT) error in set_default_route\n");
+        return -1;
+    }
+#endif
+    return 0;
+}
+
+int mbtk_ifc_set_netmask(const char *ifname, const char *netmask)
+{
+    int s;
+    if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0)
+    {
+        LOGE("Socket");
+        return -1;
+    }
+    struct ifreq ifr;
+    strcpy(ifr.ifr_name, ifname);
+    struct sockaddr_in netmask_addr;
+    bzero(&netmask_addr, sizeof(struct sockaddr_in));
+    netmask_addr.sin_family = PF_INET;
+    inet_aton(netmask, &netmask_addr.sin_addr);
+    memcpy(&ifr.ifr_ifru.ifru_netmask, &netmask_addr,
+           sizeof(struct sockaddr_in));
+    if (ioctl(s, SIOCSIFNETMASK, &ifr) < 0)
+    {
+        LOGE("ioctl");
+        close(s);
+        return -1;
+    }
+    close(s);
+    return 0;
+}
+
+
+int mbtk_ifc_get_addr(const char *name, void *addr)
+{
+    int ret;
+    struct ifreq ifr;
+    ifc_init_ifr(name, &ifr);
+
+    ret = ioctl(ifc_ctl_sock, SIOCGIFADDR, &ifr);
+    LOGD("ifc_get_addr(%s, %x) = %d", name, ifr.ifr_addr, ret);
+    if(ret < 0) return -1;
+
+    memcpy(addr, &ifr.ifr_addr, sizeof(struct sockaddr));
+    return 0;
+}
+
+
+int mbtk_ifc_up(const char *name)
+{
+    int ret = ifc_set_flags(ifc_ctl_sock, name, IFF_UP, 0);
+//    LOGI("mbtk_ifc_up(%s) = %d", name, ret);
+    return ret;
+}
+
+int mbtk_ifc_down(const char *name)
+{
+    int ret = ifc_set_flags(ifc_ctl_sock, name, 0, IFF_UP);
+//    LOGI("mbtk_ifc_down(%s) = %d", name, ret);
+    return ret;
+}
+
+int mbtk_ifc_get_hwaddr(const char *name, void *ptr)
+{
+    int r;
+    struct ifreq ifr;
+    ifc_init_ifr(name, &ifr);
+
+    r = ioctl(ifc_ctl_sock, SIOCGIFHWADDR, &ifr);
+    if(r < 0) return -1;
+
+    memcpy(ptr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+    return 0;
+}
+
+int mbtk_ifc_get_ifindex(const char *name, int *if_indexp)
+{
+    int r;
+    struct ifreq ifr;
+    ifc_init_ifr(name, &ifr);
+
+    r = ioctl(ifc_ctl_sock, SIOCGIFINDEX, &ifr);
+    if(r < 0) return -1;
+
+    *if_indexp = ifr.ifr_ifindex;
+    return 0;
+}
+
+int mbtk_ifc_configure1(const char *ifname,
+                        in_addr_t address,
+                        uint32_t prefixLength,
+                        in_addr_t gateway,
+                        in_addr_t netmask)
+{
+    if(mbtk_ifc_open())
+    {
+        LOGE("mbtk_ifc_open() fail.", strerror(errno));
+        return -1;
+    }
+
+    if (mbtk_ifc_up(ifname))
+    {
+        LOGE("failed to turn on interface %s: %s", ifname, strerror(errno));
+        mbtk_ifc_close();
+        return -1;
+    }
+    if (mbtk_ifc_set_addr(ifname, address, netmask))
+    {
+        LOGE("failed to set ipaddr %s: %s", ipaddr_to_string(address), strerror(errno));
+        mbtk_ifc_close();
+        return -1;
+    }
+    if (ifc_set_prefixLength(ifname, prefixLength))
+    {
+        LOGE("failed to set prefixLength %d: %s", prefixLength, strerror(errno));
+        mbtk_ifc_close();
+        return -1;
+    }
+    if (ifc_create_default_route1(ifname, gateway))
+    {
+        LOGE("failed to set default route %s: %s", ipaddr_to_string(gateway), strerror(errno));
+        mbtk_ifc_close();
+        return -1;
+    }
+
+    mbtk_ifc_close();
+    return 0;
+}
+
+int mbtk_ifc_configure2(const char *ifname,
+                        const char *ipv4,
+                        uint32_t prefixLength,
+                        const char *gateway,
+                        const char *netmask)
+{
+    if(mbtk_ifc_open())
+    {
+        LOGE("mbtk_ifc_open() fail.", strerror(errno));
+        return -1;
+    }
+
+    if(ipv4 == NULL) {
+        if (mbtk_ifc_down(ifname))
+        {
+            LOGE("failed to turn off interface %s: %s", ifname, strerror(errno));
+            mbtk_ifc_close();
+            return -1;
+        }
+    } else {
+        if (mbtk_ifc_up(ifname))
+        {
+            LOGE("failed to turn on interface %s: %s", ifname, strerror(errno));
+            mbtk_ifc_close();
+            return -1;
+        }
+    }
+
+    if (mbtk_ifc_ip_config(ifname, ipv4, netmask, gateway))
+    {
+        LOGE("failed to set ipaddr: %s", strerror(errno));
+        mbtk_ifc_close();
+        return -1;
+    }
+
+//    mbtk_ifc_set_netmask(ifname, netmask);
+
+#if 0
+    if (ifc_set_prefixLength(ifname, prefixLength))
+    {
+        LOGE("failed to set prefixLength %d: %s", prefixLength, strerror(errno));
+        mbtk_ifc_close();
+        return -1;
+    }
+#endif
+
+#if 0
+    if (ifc_create_default_route2(ifname, gateway))
+    {
+        LOGE("failed to set default route: %s", strerror(errno));
+        mbtk_ifc_close();
+        return -1;
+    }
+#endif
+
+    mbtk_ifc_close();
+
+    return 0;
+}
+
+struct in6_ifreq {
+    struct in6_addr addr;
+    uint32_t        prefixlen;
+    unsigned int    ifindex;
+};
+
+int mbtk_ipv6_config(const char *ifname, const char *ipv6, uint32_t prefixLength)
+{
+    struct ifreq ifr;
+    struct in6_ifreq ifr6;
+    int sockfd;
+    int err = 0;
+
+    // Create IPv6 socket to perform the ioctl operations on
+    sockfd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP);
+    if(sockfd < 0) {
+        LOGE("socket() fail.[%d]", errno);
+        return -1;
+    }
+
+    if(ipv6) {
+        if(ifc_set_flags(sockfd, ifname, IFF_UP, 0) < 0) {
+            LOGE("if up fail[%d].", errno);
+            err = -1;
+            goto exit;
+        }
+
+        // Copy the interface name to the ifreq struct
+        strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
+        // Get the ifrindex of the interface
+        if(ioctl(sockfd, SIOGIFINDEX, &ifr) < 0)
+        {
+            LOGE("ioctl SIOGIFINDEX error.");
+            err = -1;
+            goto exit;
+        }
+
+        // Prepare the in6_ifreq struct and set the address to the interface
+        if(inet_pton(AF_INET6, ipv6, &ifr6.addr) < 0) {
+            LOGE("inet_pton() fail[%d].", errno);
+            err = -1;
+            goto exit;
+        }
+    } else {
+        if(ifc_set_flags(sockfd, ifname, 0, IFF_UP) < 0) {
+            LOGE("if down fail[%d].", errno);
+            err = -1;
+            goto exit;
+        }
+
+        // Copy the interface name to the ifreq struct
+        strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
+        // Get the ifrindex of the interface
+        if(ioctl(sockfd, SIOGIFINDEX, &ifr) < 0)
+        {
+            LOGE("ioctl SIOGIFINDEX error.");
+            err = -1;
+            goto exit;
+        }
+
+        // Set IPv6 to 0.
+        memset(&(ifr6.addr), 0, sizeof(struct in6_addr));
+    }
+    ifr6.ifindex = ifr.ifr_ifindex;
+    ifr6.prefixlen = prefixLength;
+    if(ioctl(sockfd, SIOCSIFADDR, &ifr6) < 0) {
+        LOGE("ioctl SIOCSIFADDR error.");
+        err = -1;
+        goto exit;
+    }
+
+    LOGD("Set IPv6 : %s success.", ipv6);
+exit:
+    close(socket);
+    return err;
+}
diff --git a/mbtk/mbtk_lib/src/mbtk_info.c b/mbtk/mbtk_lib/src/mbtk_info.c
new file mode 100755
index 0000000..7ff9345
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_info.c
@@ -0,0 +1,1068 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <string.h>
+
+#include "mbtk_info.h"
+#include "mbtk_list.h"
+#include "mbtk_utils.h"
+
+static int sock_read(int fd, uint8 *msg, int data_len)
+{
+    memset(msg, 0, data_len);
+    int len = 0;
+    int read_len = 0;
+    while(1)
+    {
+        len = read(fd, msg + read_len, data_len - read_len);
+        if(len > 0)
+        {
+            read_len += len;
+        }
+        else if(len == 0)
+        {
+            LOG("read() end.");
+            break;
+        }
+        else
+        {
+            if(EAGAIN == errno)
+            {
+                LOG("Read end, lenght = %d", read_len);
+            }
+            else
+            {
+                LOG("read() error[%d].", errno);
+            }
+            break;
+        }
+    }
+
+    if(read_len > 0)
+    {
+        log_hex("DATA_RECV", msg, read_len);
+        return read_len;
+    }
+    else
+    {
+        return -1;
+    }
+}
+
+static int sock_write(int fd, uint8 *msg, int data_len)
+{
+    int len = 0;
+    int write_len = 0;
+    while(write_len < data_len)
+    {
+        len = write(fd, msg + write_len, data_len - write_len);
+        if(len > 0)
+        {
+            write_len += len;
+        }
+        else if(len == 0)
+        {
+            LOG("write() end.");
+            break;
+        }
+        else
+        {
+            LOG("write() error[%d].", errno);
+            break;
+        }
+    }
+
+    if(write_len > 0)
+    {
+        log_hex("DATA_SEND", msg, write_len);
+        return write_len;
+    }
+    else
+    {
+        return -1;
+    }
+}
+
+static int pack_num_check(const void* data, int data_len)
+{
+    int count = 0;
+    int pack_len;
+    const uint8* ptr = (const uint8*)data;
+    while(ptr < (const uint8*)data + data_len)
+    {
+        if(MBTK_INFO_PACKET_FLAG != byte_2_uint32(ptr, true))
+        {
+            LOG("pack_num_check() - TAG error.");
+            break;
+        }
+        ptr += sizeof(uint32);
+
+        pack_len = byte_2_uint16(ptr, false);
+        if(pack_len < SOCK_PACK_LEN_MIN - SOCK_PACK_EXTRA_LEN)
+        {
+            LOG("pack_num_check() - Packet length error.");
+            break;
+        }
+        ptr += sizeof(uint16);
+        ptr += pack_len;
+
+        count++;
+    }
+
+    return count;
+}
+
+char* type2str(mbtk_info_type_enum type)
+{
+    switch(type)
+    {
+        case MBTK_INFO_TYPE_REQ:
+            return "REQ";
+        case MBTK_INFO_TYPE_RSP:
+            return "RSP";
+        case MBTK_INFO_TYPE_IND:
+            return "IND";
+        default:
+        {
+            return "UNKNOWN";
+        }
+    }
+}
+
+char* apn2str(mbtk_ip_type_enum type)
+{
+    switch(type)
+    {
+        case MBTK_IP_TYPE_IP:
+            return "IP";
+        case MBTK_IP_TYPE_IPV6:
+            return "IPV6";
+        case MBTK_IP_TYPE_IPV4V6:
+            return "IPV4V6";
+        case MBTK_IP_TYPE_PPP:
+            return "PPP";
+        default:
+        {
+            return "UNKNOWN";
+        }
+    }
+}
+
+/*
+IPv6 : 254.128.0.0.0.0.0.0.0.1.0.2.144.5.212.239 -> uint128
+*/
+int str_2_ipv6(const void *ip_str, void *ipv6)
+{
+    const uint8 *ptr = (const uint8*)ip_str;
+    uint8 *ipv6_ptr = (uint8*)ipv6;
+    ipv6_ptr[0] = (uint8)atoi(ptr);
+    int i = 1;
+    while(i < 16) {
+        ptr = (const uint8*)strstr(ptr, ".");
+        if(ptr == NULL)
+            return -1;
+        ptr++;
+        ipv6_ptr[i] = (uint8)atoi(ptr);
+        i++;
+    }
+
+    return 0;
+}
+
+/*
+IPv6 : uint128 -> fe80::215:1dff:fe81:484c
+*/
+int ipv6_2_str(const void *ipv6, void *ipv6_str)
+{
+    const uint8 *ptr = (const uint8*)ipv6;
+    uint8 *ipv6_ptr = (uint8*)ipv6_str;
+    int i = 0;
+    int index = 0;
+    while(i < 16) {
+        index += sprintf(ipv6_ptr + index, "%02x%02x", ptr[i], ptr[i + 1]);
+        index += sprintf(ipv6_ptr + index, ":");
+        i += 2;
+    }
+
+    ipv6_ptr[index - 1] = '\0'; // Delete last ':'
+
+    return 0;
+}
+
+
+
+char* id2str(int id)
+{
+    switch(id)
+    {
+        // <string> IMEI
+        case MBTK_INFO_ID_DEV_IMEI_REQ:
+        case MBTK_INFO_ID_DEV_IMEI_RSP:
+            return "IMEI";
+        // <string> SN
+        case MBTK_INFO_ID_DEV_SN_REQ:
+        case MBTK_INFO_ID_DEV_SN_RSP:
+            return "SN";
+        // <string> MEID
+        case MBTK_INFO_ID_DEV_MEID_REQ:
+        case MBTK_INFO_ID_DEV_MEID_RSP:
+            return "MEID";
+        // <string> VERSION
+        case MBTK_INFO_ID_DEV_VERSION_REQ:
+        case MBTK_INFO_ID_DEV_VERSION_RSP:
+            return "VERSION";
+        // <uint8> 0:Close 1:Open
+        case MBTK_INFO_ID_DEV_VOLTE_REQ:
+        case MBTK_INFO_ID_DEV_VOLTE_RSP:
+            return "VOLTE";
+        // <string> Temperature
+        case MBTK_INFO_ID_DEV_TEMP_REQ:  // Temperature
+        case MBTK_INFO_ID_DEV_TEMP_RSP:
+            return "TEMPERATURE";
+        case MBTK_INFO_ID_DEV_TIME_REQ:  // Time
+        case MBTK_INFO_ID_DEV_TIME_RSP:
+            return "Time";
+        // Sim Information
+        // <uint8> 0:NOT_EXIST 1:READY ...
+        case MBTK_INFO_ID_SIM_STATE_REQ:
+        case MBTK_INFO_ID_SIM_STATE_RSP:
+            return "SIM_STATE";
+        // <string> PIN
+        case MBTK_INFO_ID_SIM_PIN_REQ:
+        case MBTK_INFO_ID_SIM_PIN_RSP:
+            return "SIM_PIN";
+        // <string> PUK
+        case MBTK_INFO_ID_SIM_PUK_REQ:
+        case MBTK_INFO_ID_SIM_PUK_RSP:
+            return "SIM_PUK";
+        // <string> IMSI
+        case MBTK_INFO_ID_SIM_IMSI_REQ:
+        case MBTK_INFO_ID_SIM_IMSI_RSP:
+            return "IMSI";
+        // <string> ICCID
+        case MBTK_INFO_ID_SIM_ICCID_REQ:
+        case MBTK_INFO_ID_SIM_ICCID_RSP:
+            return "ICCID";
+        // <string> Phone Number
+        case MBTK_INFO_ID_SIM_PN_REQ:
+        case MBTK_INFO_ID_SIM_PN_RSP:
+            return "PHONE_NUMBER";
+        // Network Information
+        // <uint8> 0:OFF 1:ON
+        case MBTK_INFO_ID_NET_RADIO_REQ:
+        case MBTK_INFO_ID_NET_RADIO_RSP:
+            return "RADIO_STATE";
+        case MBTK_INFO_ID_NET_AVAILABLE_REQ:
+        case MBTK_INFO_ID_NET_AVAILABLE_RSP:
+            return "NET_AVAILABLE";
+        case MBTK_INFO_ID_NET_SEL_MODE_REQ:
+        case MBTK_INFO_ID_NET_SEL_MODE_RSP:
+            return "NET_SEL_MODE";
+        case MBTK_INFO_ID_NET_BAND_REQ:
+        case MBTK_INFO_ID_NET_BAND_RSP:
+            return "NET_BNAD";
+        // <uint16>[4]  rssi,rscp,rsrp,snr
+        case MBTK_INFO_ID_NET_SIGNAL_REQ:
+        case MBTK_INFO_ID_NET_SIGNAL_RSP:
+            return "SIGNAL";
+        case MBTK_INFO_ID_NET_REG_REQ:
+        case MBTK_INFO_ID_NET_REG_RSP:
+            return "NET_REG";
+        // <string> cmnet/ctnet/3gnet/...
+        case MBTK_INFO_ID_NET_APN_REQ:
+        case MBTK_INFO_ID_NET_APN_RSP:
+            return "APN";
+        // Lock net/cell/frequency
+        case MBTK_INFO_ID_NET_CELL_REQ:
+        case MBTK_INFO_ID_NET_CELL_RSP:
+            return "NET_CELL";
+        case MBTK_INFO_ID_NET_DATA_CALL_REQ:
+        case MBTK_INFO_ID_NET_DATA_CALL_RSP:
+            return "DATA_CALL";
+        // Call Information
+        case MBTK_INFO_ID_CALL_STATE_REQ:
+        case MBTK_INFO_ID_CALL_STATE_RSP:
+            return "CALL_STATE";
+        // SMS Information
+        case MBTK_INFO_ID_SMS_STATE_REQ:
+        case MBTK_INFO_ID_SMS_STATE_RSP:
+            return "SMS_STATE";
+        // PhoneBook Information
+        case MBTK_INFO_ID_PB_STATE_REQ:
+        case MBTK_INFO_ID_PB_STATE_RSP:
+            return "PB_STATE";
+        // IND Information
+        // <uint8>  State
+        case MBTK_INFO_ID_IND_NET_STATE_CHANGE:
+            return "IND_NET_STATE";
+        // <uint8>  State
+        case MBTK_INFO_ID_IND_CALL_STATE_CHANGE:
+            return "IND_CALL_STATE";
+        // <uint8>  State
+        case MBTK_INFO_ID_IND_SMS_STATE_CHANGE:
+            return "IND_SMS_STATE";
+        // <uint8>  State
+        case MBTK_INFO_ID_IND_RADIO_STATE_CHANGE:
+            return "IND_RADIO_STATE";
+        // <uint8>  State
+        case MBTK_INFO_ID_IND_SIM_STATE_CHANGE:
+            return "IND_SIM_STATE";
+        // <uint8>  State
+        case MBTK_INFO_ID_IND_PDP_STATE_CHANGE:
+            return "IND_PDP_STATE";
+        // <uint8>  State
+        case MBTK_INFO_ID_IND_SERVER_STATE_CHANGE:
+            return "IND_SERVER_STATE";
+        default:
+        {
+            return "UNKNOWN";
+        }
+    }
+}
+
+char* err2str(mbtk_info_err_enum err)
+{
+    switch(err)
+    {
+        case MBTK_INFO_ERR_SUCCESS:
+            return "SUCCESS";
+        case MBTK_INFO_ERR_FORMAT:
+            return "ERR_FORMAT";
+        case MBTK_INFO_ERR_REQ_UNKNOWN:
+            return "ERR_REQ_UNKNOWN";
+        case MBTK_INFO_ERR_REQ_PARAMETER:
+            return "ERR_REQ_PARAMETER";
+        case MBTK_INFO_ERR_UNSUPPORTED:
+            return "ERR_UNSUPPORTED";
+        case MBTK_INFO_ERR_MEMORY:
+            return "ERR_MEMORY";
+        case MBTK_INFO_ERR_IND_FULL:
+            return "ERR_IND_FULL";
+        case MBTK_INFO_ERR_IND_UNKNOWN:
+            return "ERR_IND_UNKNOWN";
+        case MBTK_INFO_ERR_CID_EXIST:
+            return "ERR_CID_EXIS";
+        case MBTK_INFO_ERR_CID_NO_EXIST:
+            return "ERR_CID_NO_EXIST";
+        case MBTK_INFO_ERR_NET_NO_INIT:
+            return "ERR_CID_NO_EXIST";
+        default:
+        {
+            if(err >= MBTK_INFO_ERR_CME) {
+                return "CME ERROR";
+            }
+
+            return "UNKNOWN";
+        }
+    }
+}
+
+/*
+0   GSM
+1   GSM_COMPACT
+2   UTRAN
+3   GSM_EGPRS
+4   UTRAN_HSDPA
+5   UTRAN_HSUPA
+6   UTRAN_HSDPA_HSUPA
+7   EUTRAN
+8   ECGSM
+*/
+mbtk_net_type_enum mbtk_net_type_get(mbtk_radio_technology_enum radio_tech)
+{
+    switch(radio_tech)
+    {
+        case MBTK_RADIO_TECH_GSM:
+        case MBTK_RADIO_TECH_GSM_COMPACT:
+        case MBTK_RADIO_TECH_GSM_EGPRS:
+        case MBTK_RADIO_TECH_UTRAN_HSPA:
+        {
+            return MBTK_NET_TYPE_GSM;
+        }
+        case MBTK_RADIO_TECH_UTRAN:
+        case MBTK_RADIO_TECH_UTRAN_HSDPA:
+        case MBTK_RADIO_TECH_UTRAN_HSUPA:
+        case MBTK_RADIO_TECH_UTRAN_HSDPA_HSUPA:
+        {
+            return MBTK_NET_TYPE_UMTS;
+        }
+        case MBTK_RADIO_TECH_E_UTRAN:
+        {
+            return MBTK_NET_TYPE_LTE;
+        }
+        default:
+        {
+            return MBTK_NET_TYPE_UNKNOWN;
+        }
+    }
+}
+
+
+#if 0
+void net_list_free(void *data)
+{
+    if (data)
+    {
+        mbtk_net_info_t *info = (mbtk_net_info_t*) data;
+        LOG("Free net [%s].", info->plmn);
+        free(info);
+    }
+}
+#endif
+
+mbtk_info_type_enum mbtk_info_type_get(int info_id)
+{
+    if(info_id > MBTK_INFO_ID_IND_BEGIN && info_id < MBTK_INFO_ID_IND_END)
+    {
+        return MBTK_INFO_TYPE_IND;
+    }
+    else if(info_id == MBTK_INFO_ID_DEV_BEGIN ||
+            info_id == MBTK_INFO_ID_DEV_END ||
+            info_id == MBTK_INFO_ID_SIM_BEGIN ||
+            info_id == MBTK_INFO_ID_SIM_END ||
+            info_id == MBTK_INFO_ID_NET_BEGIN ||
+            info_id == MBTK_INFO_ID_NET_END ||
+            info_id == MBTK_INFO_ID_CALL_BEGIN ||
+            info_id == MBTK_INFO_ID_CALL_END ||
+            info_id == MBTK_INFO_ID_SMS_BEGIN ||
+            info_id == MBTK_INFO_ID_SMS_END ||
+            info_id == MBTK_INFO_ID_PB_BEGIN ||
+            info_id == MBTK_INFO_ID_PB_END ||
+            info_id == MBTK_INFO_ID_REQ_UNKNOWN)
+    {
+        return MBTK_INFO_TYPE_UNKNOWN;
+    }
+    else if(info_id % 2 == 1)
+    {
+        return MBTK_INFO_TYPE_REQ;
+    }
+    else
+    {
+        return MBTK_INFO_TYPE_RSP;
+    }
+}
+
+mbtk_info_pack_t* mbtk_info_pack_creat(int info_id)
+{
+    mbtk_info_pack_t *pack = (mbtk_info_pack_t *)malloc(sizeof(mbtk_info_pack_t));
+    if(!pack)
+    {
+        LOG("malloc() error[%d]", errno);
+        return NULL;
+    }
+
+    pack->info_id = (uint16)info_id;
+    pack->info_err = (uint16)0;
+    pack->data_len = (uint16)0;
+    pack->data = NULL;
+
+    return pack;
+}
+
+#if 0
+int mbtk_info_pack_data_set(mbtk_info_pack_t *pack, const void *data, int data_len)
+{
+    if(!pack)
+    {
+        LOG("Packet is NULL.");
+        return -1;
+    }
+
+    mbtk_info_type_enum info_type = mbtk_info_type_get(pack->info_id);
+    // IND
+    if(info_type == MBTK_INFO_TYPE_IND)
+    {
+        switch(pack->info_id)
+        {
+            // <uint8>  State
+            case MBTK_INFO_ID_IND_NET_STATE_CHANGE:
+            case MBTK_INFO_ID_IND_CALL_STATE_CHANGE:
+            case MBTK_INFO_ID_IND_SMS_STATE_CHANGE:
+            case MBTK_INFO_ID_IND_RADIO_STATE_CHANGE:
+            case MBTK_INFO_ID_IND_SIM_STATE_CHANGE:
+            {
+                pack->data_len = (uint16)data_len;
+                pack->data = (uint8*)memdup(data, pack->data_len);
+                break;
+            }
+            default:
+            {
+                LOG("Unknown IND : %s", id2str(pack->info_id));
+                break;
+            }
+        }
+    }
+    //else if(pack->info_id % 2 == 1)     // REQ (Set Data)
+    else if(info_type == MBTK_INFO_TYPE_REQ || info_type == MBTK_INFO_TYPE_RSP) // REQ or RSP
+    {
+        switch(pack->info_id)
+        {
+            case MBTK_INFO_ID_DEV_VOLTE_REQ:    // <uint8> 0:Close 1:Open
+            case MBTK_INFO_ID_DEV_VOLTE_RSP:
+            case MBTK_INFO_ID_SIM_STATE_REQ:    // <uint8> 0:NOT_EXIST 1:READY ...
+            case MBTK_INFO_ID_SIM_STATE_RSP:
+            case MBTK_INFO_ID_NET_RADIO_REQ:    // <uint8> 0:OFF 1:ON
+            case MBTK_INFO_ID_NET_RADIO_RSP:
+            case MBTK_INFO_ID_NET_BAND_REQ:     // mbtk_band_info_t
+            case MBTK_INFO_ID_NET_BAND_RSP:
+            case MBTK_INFO_ID_NET_CELL_REQ:     // Lock net/cell/frequency
+            case MBTK_INFO_ID_NET_CELL_RSP:
+            case MBTK_INFO_ID_DEV_IMEI_REQ:     // <string> SN
+            case MBTK_INFO_ID_DEV_IMEI_RSP:
+            case MBTK_INFO_ID_DEV_SN_REQ:       // <string> SN
+            case MBTK_INFO_ID_DEV_SN_RSP:
+            case MBTK_INFO_ID_DEV_MEID_REQ:     // <string> MEID
+            case MBTK_INFO_ID_DEV_MEID_RSP:
+            case MBTK_INFO_ID_DEV_VERSION_REQ:  // <string> VERSION
+            case MBTK_INFO_ID_DEV_VERSION_RSP:
+            case MBTK_INFO_ID_DEV_TEMP_REQ:     // <string> Temperature
+            case MBTK_INFO_ID_DEV_TEMP_RSP:
+            case MBTK_INFO_ID_SIM_PIN_REQ:      // <string> PIN
+            case MBTK_INFO_ID_SIM_PIN_RSP:
+            case MBTK_INFO_ID_SIM_PUK_REQ:      // <string> PUK
+            case MBTK_INFO_ID_SIM_PUK_RSP:
+            case MBTK_INFO_ID_SIM_IMSI_REQ:     // <string> IMSI
+            case MBTK_INFO_ID_SIM_IMSI_RSP:
+            case MBTK_INFO_ID_SIM_ICCID_REQ:    // <string> ICCID
+            case MBTK_INFO_ID_SIM_ICCID_RSP:
+            case MBTK_INFO_ID_NET_APN_REQ:      // <string> cmnet/ctnet/3gnet/...
+            case MBTK_INFO_ID_NET_APN_RSP:
+            case MBTK_INFO_ID_NET_SEL_MODE_REQ: // mbtk_net_info_t
+            case MBTK_INFO_ID_NET_SEL_MODE_RSP:
+            case MBTK_INFO_ID_NET_AVAILABLE_REQ: // mbtk_net_info_t[]
+            case MBTK_INFO_ID_NET_AVAILABLE_RSP:
+            {
+                pack->data_len = (uint16)data_len;
+#if 1
+                pack->data = (uint8*)memdup(data, pack->data_len);
+#else
+                LOG("%d -> %d", data_len, pack->data_len);
+                log_hex("pack1", pack, sizeof(mbtk_info_pack_t));
+                #if 0
+                pack->data = (uint8*)memdup(data, pack->data_len);
+                #else
+
+                LOG("1 pack->%p,data->%p", pack, pack->data);
+                pack->data = (uint8*)calloc(pack->data_len, sizeof(uint8));
+                LOG("2 pack->%p,data->%p", pack, pack->data);
+
+                memcpy(pack->data, data, data_len);
+                #endif
+
+                LOG("data_len - %d", pack->data_len);
+                log_hex("pack2", pack, sizeof(mbtk_info_pack_t));
+#endif
+                break;
+            }
+            case MBTK_INFO_ID_NET_SIGNAL_REQ:   // <sint16>[4]  rssi,rscp,rsrp,snr
+            case MBTK_INFO_ID_NET_SIGNAL_RSP:
+            {
+                // const mbtk_net_signal_t* signal = (const mbtk_net_signal_t*)data;
+                pack->data_len = (uint16)sizeof(mbtk_net_signal_t);
+                pack->data = (uint8*)memdup(data, pack->data_len);
+                break;
+            }
+            case MBTK_INFO_ID_NET_IPV4_DNS_REQ: // <uint32>[2]  Preferred DNS,Alternate DNS
+            case MBTK_INFO_ID_NET_IPV4_DNS_RSP:
+            {
+                // const mbtk_net_dns_ipv4_t* dns_ipv4 = (const mbtk_net_dns_ipv4_t*)data;
+                pack->data_len = (uint16)sizeof(mbtk_net_dns_ipv4_t);
+                pack->data = (uint8*)memdup(data, pack->data_len);
+                break;
+            }
+            case MBTK_INFO_ID_NET_IPV6_DNS_REQ: // <uint32>[8]  Preferred DNS,Alternate DNS
+            case MBTK_INFO_ID_NET_IPV6_DNS_RSP:
+            {
+                // const mbtk_net_dns_ipv6_t* dns_ipv6 = (const mbtk_net_dns_ipv6_t*)data;
+                pack->data_len = (uint16)sizeof(mbtk_net_dns_ipv6_t);
+                pack->data = (uint8*)memdup(data, pack->data_len);
+                break;
+            }
+            case MBTK_INFO_ID_NET_IPV4_REQ:     // <uint32> IPv4
+            case MBTK_INFO_ID_NET_IPV4_RSP:
+            {
+                pack->data_len = (uint16)sizeof(uint32);
+                pack->data = (uint8*)memdup(data, pack->data_len);
+                break;
+            }
+            case MBTK_INFO_ID_NET_IPV6_REQ:     // <uint32>[4] IPv6
+            case MBTK_INFO_ID_NET_IPV6_RSP:
+            {
+                pack->data_len = (uint16)(sizeof(uint32) * 4);
+                pack->data = (uint8*)memdup(data, pack->data_len);
+                break;
+            }
+            case MBTK_INFO_ID_NET_LOCAL_REQ:    // <uint16>[2] tag,earfcn
+            case MBTK_INFO_ID_NET_LOCAL_RSP:
+            {
+                pack->data_len = (uint16)sizeof(mbtk_local_info_t);
+                pack->data = (uint8*)memdup(data, pack->data_len);
+                break;
+            }
+#if 0
+            case MBTK_INFO_ID_NET_SEL_MODE_REQ:
+            case MBTK_INFO_ID_NET_SEL_MODE_RSP: // sel_mode(uint8)type(uint8)plmn(uint32)
+            {
+                const mbtk_net_info_t* net = (const mbtk_net_info_t*)data;
+                pack->data_len = sizeof(uint8) + sizeof(uint8) + sizeof(uint32);
+                pack->data = (uint8*)malloc(pack->data_len);
+                if(pack->data == NULL) {
+                    LOG("malloc() fail.");
+                    return -1;
+                }
+
+                pack->data[0] = net->net_sel_mode;
+                pack->data[1] = net->net_type;
+                uint32_2_byte((uint32)atoi((char*)net->plmn), pack->data + 2,false);
+                break;
+            }
+#endif
+#if 0
+            case MBTK_INFO_ID_NET_AVAILABLE_REQ:
+            case MBTK_INFO_ID_NET_AVAILABLE_RSP: // sel_mode(uint8)type(uint8)plmn(uint32)...sel_mode(uint8)type(uint8)plmn(uint32)
+            {
+                const mbtk_net_array_info_t* nets = (const mbtk_net_array_info_t*)data;
+                mbtk_net_info_t *net = NULL;
+                //LOG("MBTK_INFO_ID_NET_AVAILABLE_RSP set");
+                //sleep(1);
+                list_first(nets->net_list);
+                pack->data_len = nets->count * sizeof(mbtk_net_info_t);
+                if(pack->data_len > 0) {
+                    int i = 0;
+                    pack->data = (uint8*)malloc(pack->data_len);
+                    if(pack->data == NULL) {
+                        LOG("malloc() fail.");
+                        return -1;
+                    }
+                    memset(pack->data, 0, pack->data_len);
+
+                    while ((net = (mbtk_net_info_t*) list_next(nets->net_list)))
+                    {
+                        #if 0
+                        memcpy(pack->data + i, net, sizeof(mbtk_net_info_t));
+                        i += sizeof(mbtk_net_info_t);
+                        #else
+                        pack->data[i++] = net->net_sel_mode;
+                        pack->data[i++] = net->net_type;
+                        //uint32_2_byte((uint32)atoi((char*)net->plmn), pack->data + i,false);
+                        uint32_2_byte(net->plmn, pack->data + i,false);
+                        i += sizeof(uint32);
+                        #endif
+                    }
+                }
+                break;
+            }
+#endif
+            default:
+            {
+                LOG("Unknown REQ/RSP : %s", id2str(pack->info_id));
+                break;
+            }
+        }
+    }
+    else
+    {
+        LOG("Unknown info : %s", id2str(pack->info_id));
+        return -1;
+    }
+    return 0;
+}
+
+void* mbtk_info_pack_data_get(mbtk_info_pack_t *pack, int *data_len)
+{
+    if(pack == NULL || pack->data_len == 0 || pack->data == NULL)
+    {
+        LOG("Packet is NULL.");
+        return NULL;
+    }
+
+    mbtk_info_type_enum info_type = mbtk_info_type_get(pack->info_id);
+    // IND
+    if(info_type == MBTK_INFO_TYPE_IND)
+    {
+        switch(pack->info_id)
+        {
+            // <uint8>  State
+            case MBTK_INFO_ID_IND_NET_STATE_CHANGE:
+            case MBTK_INFO_ID_IND_CALL_STATE_CHANGE:
+            case MBTK_INFO_ID_IND_SMS_STATE_CHANGE:
+            case MBTK_INFO_ID_IND_RADIO_STATE_CHANGE:
+            case MBTK_INFO_ID_IND_SIM_STATE_CHANGE:
+            {
+                return pack->data;
+            }
+            default:
+            {
+                LOG("Unknown IND : %s", id2str(pack->info_id));
+                break;
+            }
+        }
+    }
+    //else if(pack->info_id % 2 == 1)     // REQ (Set Data)
+    else if(info_type == MBTK_INFO_TYPE_REQ || info_type == MBTK_INFO_TYPE_RSP) // REQ or RSP
+    {
+        switch(pack->info_id)
+        {
+            case MBTK_INFO_ID_DEV_VOLTE_REQ:    // <uint8> 0:Close 1:Open
+            case MBTK_INFO_ID_DEV_VOLTE_RSP:
+            case MBTK_INFO_ID_SIM_STATE_REQ:    // <uint8> 0:NOT_EXIST 1:READY ...
+            case MBTK_INFO_ID_SIM_STATE_RSP:
+            case MBTK_INFO_ID_NET_RADIO_REQ:    // <uint8> 0:OFF 1:ON
+            case MBTK_INFO_ID_NET_RADIO_RSP:
+            case MBTK_INFO_ID_DEV_IMEI_REQ:     // <string> SN
+            case MBTK_INFO_ID_DEV_IMEI_RSP:
+            case MBTK_INFO_ID_DEV_SN_REQ:       // <string> SN
+            case MBTK_INFO_ID_DEV_SN_RSP:
+            case MBTK_INFO_ID_DEV_MEID_REQ:     // <string> MEID
+            case MBTK_INFO_ID_DEV_MEID_RSP:
+            case MBTK_INFO_ID_DEV_VERSION_REQ:  // <string> VERSION
+            case MBTK_INFO_ID_DEV_VERSION_RSP:
+            case MBTK_INFO_ID_DEV_TEMP_REQ:     // <string> Temperature
+            case MBTK_INFO_ID_DEV_TEMP_RSP:
+            case MBTK_INFO_ID_SIM_PIN_REQ:      // <string> PIN
+            case MBTK_INFO_ID_SIM_PIN_RSP:
+            case MBTK_INFO_ID_SIM_PUK_REQ:      // <string> PUK
+            case MBTK_INFO_ID_SIM_PUK_RSP:
+            case MBTK_INFO_ID_SIM_IMSI_REQ:     // <string> IMSI
+            case MBTK_INFO_ID_SIM_IMSI_RSP:
+            case MBTK_INFO_ID_SIM_ICCID_REQ:    // <string> ICCID
+            case MBTK_INFO_ID_SIM_ICCID_RSP:
+            case MBTK_INFO_ID_NET_APN_REQ:      // <string> cmnet/ctnet/3gnet/...
+            case MBTK_INFO_ID_NET_APN_RSP:
+            case MBTK_INFO_ID_NET_BAND_REQ:     // mbtk_band_info_t
+            case MBTK_INFO_ID_NET_BAND_RSP:
+            case MBTK_INFO_ID_NET_CELL_REQ:     // Lock net/cell/frequency
+            case MBTK_INFO_ID_NET_CELL_RSP:
+            case MBTK_INFO_ID_NET_SEL_MODE_REQ:  // mbtk_net_info_t
+            case MBTK_INFO_ID_NET_SEL_MODE_RSP:
+            case MBTK_INFO_ID_NET_AVAILABLE_REQ: // mbtk_net_info_t[]
+            case MBTK_INFO_ID_NET_AVAILABLE_RSP:
+            {
+                return pack->data;
+            }
+            case MBTK_INFO_ID_NET_SIGNAL_REQ:   // <sint16>[4]  rssi,rscp,rsrp,snr
+            case MBTK_INFO_ID_NET_SIGNAL_RSP:
+            {
+                mbtk_net_signal_t* signal = (mbtk_net_signal_t*)malloc(sizeof(mbtk_net_signal_t));
+                if(!signal)
+                {
+                    LOG("malloc() error[%d]", errno);
+                    return NULL;
+                }
+
+                signal->rssi = (sint16)byte_2_uint16(pack->data, false);
+                signal->rscp = (sint16)byte_2_uint16(pack->data + sizeof(uint16), false);
+                signal->rsrp = (sint16)byte_2_uint16(pack->data + sizeof(uint16) * 2, false);
+                signal->snr = (sint16)byte_2_uint16(pack->data + sizeof(uint16) * 3, false);
+                return signal;
+            }
+            case MBTK_INFO_ID_NET_IPV4_DNS_REQ: // <uint32>[2]  Preferred DNS,Alternate DNS
+            case MBTK_INFO_ID_NET_IPV4_DNS_RSP:
+            {
+                mbtk_net_dns_ipv4_t* dns_ipv4 = (mbtk_net_dns_ipv4_t*)malloc(sizeof(mbtk_net_dns_ipv4_t));
+                if(!dns_ipv4)
+                {
+                    LOG("malloc() error[%d]", errno);
+                    return NULL;
+                }
+
+                dns_ipv4->preferred_dns = byte_2_uint32(pack->data, false);
+                dns_ipv4->alternate_dns = byte_2_uint32(pack->data + sizeof(uint32), false);
+                return dns_ipv4;
+            }
+            case MBTK_INFO_ID_NET_IPV6_DNS_REQ: // <uint32>[8]  Preferred DNS,Alternate DNS
+            case MBTK_INFO_ID_NET_IPV6_DNS_RSP:
+            {
+                return memdup(pack->data, sizeof(mbtk_net_dns_ipv6_t));
+            }
+            case MBTK_INFO_ID_NET_IPV4_REQ:     // <uint32> IPv4
+            case MBTK_INFO_ID_NET_IPV4_RSP:
+            {
+                return memdup(pack->data, sizeof(uint32));
+                break;
+            }
+            case MBTK_INFO_ID_NET_IPV6_REQ:     // <uint32>[4] IPv6
+            case MBTK_INFO_ID_NET_IPV6_RSP:
+            {
+                return memdup(pack->data, sizeof(uint32) * 4);
+                break;
+            }
+            case MBTK_INFO_ID_NET_LOCAL_REQ:    // <uint16>[2] tag,earfcn
+            case MBTK_INFO_ID_NET_LOCAL_RSP:
+            {
+                mbtk_local_info_t* local = (mbtk_local_info_t*)malloc(sizeof(mbtk_local_info_t));
+                if(!local)
+                {
+                    LOG("malloc() error[%d]", errno);
+                    return NULL;
+                }
+
+                local->tag = (sint16)byte_2_uint16(pack->data, false);
+                local->earfcn = (sint16)byte_2_uint16(pack->data + sizeof(uint16), false);
+                return local;
+            }
+#if 0
+            case MBTK_INFO_ID_NET_SEL_MODE_REQ:
+            case MBTK_INFO_ID_NET_SEL_MODE_RSP: // sel_mode(uint8)type(uint8)plmn(uint32)
+            {
+                mbtk_net_info_t* net = (mbtk_net_info_t*)malloc(sizeof(mbtk_net_info_t));
+                if(!net)
+                {
+                    LOG("malloc() error[%d]", errno);
+                    return NULL;
+                }
+                memset(net, 0, sizeof(mbtk_net_info_t));
+                net->net_sel_mode = pack->data[0];
+                net->net_type = pack->data[1];
+                //itoa(byte_2_uint32(pack->data + 2, false), net->plmn, 10);
+                sprintf(net->plmn, "%d", byte_2_uint32(pack->data + 2, false));
+
+                return net;
+            }
+#endif
+#if 0
+            case MBTK_INFO_ID_NET_AVAILABLE_REQ:
+            case MBTK_INFO_ID_NET_AVAILABLE_RSP: // sel_mode(uint8)type(uint8)plmn(uint32)...sel_mode(uint8)type(uint8)plmn(uint32)
+            {
+                mbtk_net_array_info_t* nets = (mbtk_net_array_info_t*)malloc(sizeof(mbtk_net_array_info_t));
+                if(!nets)
+                {
+                    LOG("malloc() error[%d]", errno);
+                    return NULL;
+                }
+                nets->count = 0;
+                nets->net_list = list_create(NULL);
+                if(nets->net_list == NULL)
+                {
+                    LOG("list_create() fail.");
+                    free(nets);
+                    return NULL;
+                }
+
+                int i = 0;
+                while(i < pack->data_len) {
+                    LOG("MBTK_INFO_ID_NET_AVAILABLE_RSP get 1");
+                    sleep(1);
+                    mbtk_net_info_t* net = (mbtk_net_info_t*)malloc(sizeof(mbtk_net_info_t));
+                    LOG("MBTK_INFO_ID_NET_AVAILABLE_RSP get 2");
+                    sleep(1);
+                    if(!net)
+                    {
+                        LOG("malloc() error[%d]", errno);
+                        sleep(3);
+                        //list_free(nets->net_list);
+                        //free(nets);
+                        return NULL;
+                    }
+                    memset(net, 0, sizeof(mbtk_net_info_t));
+
+                    LOG("MBTK_INFO_ID_NET_AVAILABLE_RSP get 3");
+                    sleep(1);
+#if 1
+                    #if 1
+                    #if 0
+                    memcpy(net, pack->data + i, sizeof(mbtk_net_info_t));
+                    i += sizeof(mbtk_net_info_t);
+                    #else
+                    net->net_sel_mode = pack->data[i++];
+                    net->net_type = pack->data[i++];
+                    //sprintf(net->plmn, "%d", byte_2_uint32(pack->data + i, false));
+                    net->plmn = byte_2_uint32(pack->data + i, false);
+                    i += sizeof(uint32);
+                    #endif
+                    #endif
+
+                    LOG("MBTK_INFO_ID_NET_AVAILABLE_RSP get 5");
+                    log_hex("NET 2", net, sizeof(mbtk_net_info_t));
+                    sleep(1);
+
+                    list_add(nets->net_list, net);
+
+#endif
+                    LOG("list_add");
+                    sleep(1);
+                }
+
+                sleep(10);
+
+                // Data lenght changed.
+                *data_len = sizeof(mbtk_net_array_info_t);
+                return nets;
+            }
+#endif
+            default:
+            {
+                LOG("Unknown REQ/RSP : %s", id2str(pack->info_id));
+                break;
+            }
+        }
+    }
+    else
+    {
+        LOG("Unknown info : %s", id2str(pack->info_id));
+    }
+
+    return NULL;
+}
+#endif
+
+int mbtk_info_pack_send(int fd, mbtk_info_pack_t *pack)
+{
+    if(!pack)
+    {
+        LOG("Packet is NULL.");
+        return -1;
+    }
+    uint8 data[SOCK_MSG_LEN_MAX] = {0};
+    uint8* data_ptr = data + SOCK_PACK_EXTRA_LEN;
+
+    data_ptr += uint16_2_byte(pack->info_id, data_ptr, false);
+    data_ptr += uint16_2_byte(pack->info_err, data_ptr, false);
+    data_ptr += uint16_2_byte(pack->data_len, data_ptr, false);
+    //log_hex("DATA1", data, 40);
+    if(pack->data_len > 0)
+    {
+        memcpy(data_ptr, pack->data, pack->data_len);
+        data_ptr += pack->data_len;
+        //log_hex("DATA2", data, 40);
+    }
+
+    // Set flag and packet length.
+    uint32_2_byte(MBTK_INFO_PACKET_FLAG, data, true);
+    uint16_2_byte(data_ptr - data - SOCK_PACK_EXTRA_LEN, data + sizeof(uint32), false);
+
+    //log_hex("DATA3", data, 40);
+    return sock_write(fd, data, data_ptr - data);
+}
+
+mbtk_info_pack_t** mbtk_info_pack_recv(int fd, bool is_server, mbtk_info_err_enum *err)
+{
+    uint8 msg[SOCK_MSG_LEN_MAX + 1];
+    *err = MBTK_INFO_ERR_SUCCESS;
+    int len = sock_read(fd, msg, SOCK_MSG_LEN_MAX + 1);
+    if(len < SOCK_PACK_LEN_MIN)
+    {
+        if(len > 0)
+        {
+            *err = MBTK_INFO_ERR_FORMAT;
+            LOG("Insufficient packet data.");
+        }
+        return NULL;
+    }
+
+    int pack_count = pack_num_check(msg, len);
+    LOG("Packet number : %d", pack_count);
+    if(pack_count < 1)
+    {
+        *err = MBTK_INFO_ERR_FORMAT;
+        LOG("Packet not found.");
+        return NULL;
+    }
+    uint8 *ptr = msg;
+    mbtk_info_pack_t** packs = (mbtk_info_pack_t**)malloc(sizeof(mbtk_info_pack_t*) * (pack_count + 1));
+    int i = 0;
+    while(i < pack_count)
+    {
+        // TAG
+        if(MBTK_INFO_PACKET_FLAG != byte_2_uint32(ptr, true))
+        {
+            *err = MBTK_INFO_ERR_FORMAT;
+            LOG("Packet TAG error.");
+            goto error;
+        }
+        ptr += sizeof(uint32);
+
+        // Jump packet length.
+        ptr += sizeof(uint16);
+
+        mbtk_info_id_enum info_id = (mbtk_info_id_enum)byte_2_uint16(ptr, false);
+        mbtk_info_type_enum info_type = mbtk_info_type_get(info_id);
+        if(is_server)
+        {
+            // For server,"info_type" must by REQ or IND(Register IND).
+            if(info_type != MBTK_INFO_TYPE_REQ && info_type != MBTK_INFO_TYPE_IND)
+            {
+                *err = MBTK_INFO_ERR_FORMAT;
+                LOG("Packet Type error.");
+                goto error;
+            }
+        }
+        else
+        {
+            // For client,"info_type" must by RSP or IND.
+            if(info_type != MBTK_INFO_TYPE_RSP && info_type != MBTK_INFO_TYPE_IND)
+            {
+                *err = MBTK_INFO_ERR_FORMAT;
+                LOG("Packet Type error.");
+                goto error;
+            }
+        }
+        ptr += sizeof(uint16);
+
+        mbtk_info_pack_t* pack = mbtk_info_pack_creat(info_id);
+        if(pack == NULL)
+        {
+            *err = MBTK_INFO_ERR_MEMORY;
+            LOG("Packet malloc() fail.");
+            goto error;
+        }
+
+        // "info_err"
+        pack->info_err = byte_2_uint16(ptr, false);
+        ptr += sizeof(uint16);
+
+        // "data_len"
+        pack->data_len = byte_2_uint16(ptr, false);
+        ptr += sizeof(uint16);
+
+        if(pack->data_len > 0)
+        {
+            pack->data = (uint8*)memdup(ptr, pack->data_len);
+            ptr += pack->data_len;
+        }
+
+        packs[i++] = pack;
+    }
+    packs[i] = NULL;
+
+    return packs;
+
+error:
+    LOG("mbtk_info_pack_recv error, will free().");
+    if(packs)
+    {
+        mbtk_info_pack_t** pack_ptr = packs;
+        while(*pack_ptr)
+        {
+            mbtk_info_pack_free(pack_ptr);
+            pack_ptr++;
+        }
+
+        free(packs);
+    }
+    return NULL;
+}
+
+int mbtk_info_pack_free(mbtk_info_pack_t **pack)
+{
+    if(pack == NULL || *pack == NULL)
+    {
+        LOG("Packet is NULL.");
+        return -1;
+    }
+
+    // LOG("Free packet : %s", id2str((*pack)->info_id));
+#if 0
+    if((*pack)->data)
+    {
+        free((*pack)->data);
+    }
+#endif
+    free(*pack);
+    *pack = NULL;
+    return 0;
+}
+
+
diff --git a/mbtk/mbtk_lib/src/mbtk_info_api.c b/mbtk/mbtk_lib/src/mbtk_info_api.c
new file mode 100755
index 0000000..6472ea0
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_info_api.c
@@ -0,0 +1,2232 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <pthread.h>
+#include <sys/epoll.h>
+#include <string.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#include "mbtk_info.h"
+#include "mbtk_list.h"
+#include "mbtk_utils.h"
+
+#include "time.h"
+
+#define EPOLL_LISTEN_MAX 100
+#define EPOLL_LISTEN_MAX 100
+
+#if 0
+struct
+{
+    uint8 operator[128];
+    uint8 operator[128];
+    uint8 mcc_mnc[10];
+} operator_mcc_mnc =
+{
+    {"China Mobile","CMCC","46000"},
+    {"China Unicom","CU","46001"},
+    {"China Mobile","CMCC","46002"},
+    {"China Telecom","CT","46003"},
+    {"China Mobile","CMCC","46004"},
+    {"China Telecom","CT","46005"},
+    {"China Unicom","CU","46006"},
+    {"China Mobile","CMCC","46007"},
+    {"China Mobile","CMCC","46008"},
+    {"China Unicom","CU","46009"},
+    {"China Telecom","CT","46011"},
+    {NULL, NULL, NULL}
+};
+#endif
+
+static int pack_process(mbtk_info_handle_t* handle, mbtk_info_pack_t* pack)
+{
+    mbtk_info_type_enum info_type = mbtk_info_type_get(pack->info_id);
+    LOG("Type : %s, ID : %s, Result : %s ,Length : %d", type2str(info_type),
+                                                        id2str(pack->info_id),
+                                                        err2str(pack->info_err),
+                                                        pack->data_len);
+    if(0 && pack->data_len > 0)
+    {
+        log_hex("DATA", pack->data, pack->data_len);
+    }
+    // IND Message.
+    if(info_type == MBTK_INFO_TYPE_IND)
+    {
+        if(pack->data_len > 0 && pack->data != NULL) // IND message.
+        {
+            log_hex(id2str(pack->info_id), pack->data, pack->data_len);
+            switch(pack->info_id)
+            {
+                case MBTK_INFO_ID_IND_NET_STATE_CHANGE:
+                {
+                    if(handle->net_state_cb)
+                        handle->net_state_cb(pack->data, pack->data_len);
+                    break;
+                }
+                case MBTK_INFO_ID_IND_CALL_STATE_CHANGE:
+                {
+                    if(handle->call_state_cb)
+                        handle->call_state_cb(pack->data, pack->data_len);
+                    break;
+                }
+                case MBTK_INFO_ID_IND_SMS_STATE_CHANGE:
+                {
+                    if(handle->sms_state_cb)
+                        handle->sms_state_cb(pack->data, pack->data_len);
+                    break;
+                }
+                case MBTK_INFO_ID_IND_RADIO_STATE_CHANGE:
+                {
+                    if(handle->radio_state_cb)
+                        handle->radio_state_cb(pack->data, pack->data_len);
+                    break;
+                }
+                case MBTK_INFO_ID_IND_SIM_STATE_CHANGE:
+                {
+                    if(handle->sim_state_cb)
+                        handle->sim_state_cb(pack->data, pack->data_len);
+                    break;
+                }
+                case MBTK_INFO_ID_IND_PDP_STATE_CHANGE:
+                {
+                    if(handle->pdp_state_cb)
+                        handle->pdp_state_cb(pack->data, pack->data_len);
+                    break;
+                }
+                //mbtk wyq for server_ready_status add start
+                case MBTK_INFO_ID_IND_SERVER_STATE_CHANGE:
+                {
+                    handle->server_ready_status = 1;
+                    LOG("handshake message recv ok.");
+                    break;
+                }
+                //mbtk wyq for server_ready_status add end
+                default:
+                {
+                    LOG("Unknown IND : %d", pack->info_id);
+                    break;
+                }
+            }
+        }
+        else // Register IND response.
+        {
+            handle->info_err = pack->info_err;
+            if(pack->info_err == MBTK_INFO_ERR_SUCCESS)
+            {
+                LOG("IND %s register success.", id2str(pack->info_id));
+            }
+            else
+            {
+                LOG("IND %s register fail : %s", id2str(pack->info_id), err2str(pack->info_err));
+            }
+
+            if(handle->is_waitting) {
+                pthread_mutex_lock(&handle->mutex);
+                pthread_cond_signal(&handle->cond);
+                pthread_mutex_unlock(&handle->mutex);
+            }
+        }
+    }
+    else     // Response Information.
+    {
+        handle->info_err = pack->info_err;
+
+        // Set data length.
+        // If data change,will change this lenght in mbtk_info_pack_data_get().
+        handle->data_len = pack->data_len;
+        // Copy data buffer,because it will be released.
+        if(handle->data && pack->data && pack->data_len > 0) {
+            memcpy(handle->data, pack->data, handle->data_len);
+        }
+
+        if(handle->is_waitting) {
+            pthread_mutex_lock(&handle->mutex);
+            pthread_cond_signal(&handle->cond);
+            pthread_mutex_unlock(&handle->mutex);
+        }
+
+
+#if 0
+        if(pack->info_err == MBTK_INFO_ERR_SUCCESS)
+        {
+            LOG("REQ %s success.", id2str(pack->info_id));
+#if 0
+            if(pack->data_len > 0)
+            {
+                log_hex("DATA", pack->data, pack->data_len);
+            }
+#endif
+            switch(pack->info_id)
+            {
+                case MBTK_INFO_ID_NET_AVAILABLE_RSP:
+                {
+                    mbtk_net_array_info_t* nets = (mbtk_net_array_info_t*)mbtk_info_pack_data_get(pack);
+                    if(nets)
+                    {
+                        mbtk_net_info_t *net = NULL;
+                        list_first(nets->net_list);
+                        while ((net = (mbtk_net_info_t*) list_next(nets->net_list)))
+                        {
+                            LOG("NET : %d, %d, %s", net->net_sel_mode, net->net_type, net->plmn);
+                        }
+                        list_free(nets->net_list);
+                        free(nets);
+                    }
+                    else
+                    {
+                        LOG("mbtk_info_pack_data_get() fail.");
+                    }
+
+                    break;
+                }
+                case MBTK_INFO_ID_NET_SEL_MODE_RSP:
+                {
+                    mbtk_net_info_t* net = (mbtk_net_info_t*)mbtk_info_pack_data_get(pack);
+                    if(net)
+                    {
+                        LOG("NET : %d, %d, %d", net->net_sel_mode, net->net_type, net->plmn);
+                        free(net);
+                    }
+                    else
+                    {
+                        LOG("mbtk_info_pack_data_get() fail.");
+                    }
+
+                    break;
+                }
+                case MBTK_INFO_ID_NET_BAND_RSP:
+                {
+                    mbtk_band_info_t* band = (mbtk_band_info_t*)mbtk_info_pack_data_get(pack);
+                    if(band) {
+                        LOG("BAND : %d, %d, %d, %d, %d", band->net_pref,
+                                                         band->gsm_band,
+                                                         band->umts_band,
+                                                         band->tdlte_band,
+                                                         band->fddlte_band);
+                    } else {
+                        LOG("mbtk_info_pack_data_get() fail.");
+                    }
+
+                    break;
+                }
+                default:
+                {
+                    break;
+                }
+            }
+        }
+        else
+        {
+            LOG("REQ %s fail : %s", id2str(pack->info_id), err2str(pack->info_err));
+        }
+#endif
+    }
+    return 0;
+}
+
+
+static void* info_read_run(void* arg)
+{
+    int epoll_fd = epoll_create(5);
+    if(epoll_fd < 0)
+    {
+        LOG("epoll_create() fail[%d].", errno);
+        return NULL;
+    }
+    mbtk_info_handle_t* handle = (mbtk_info_handle_t*)arg;
+
+    uint32 event = EPOLLIN | EPOLLET;
+    struct epoll_event ev_cli, ev_exit;
+    ev_cli.data.fd = handle->client_fd;
+    ev_cli.events = event; //EPOLLIN | EPOLLERR | EPOLLET;
+    epoll_ctl(epoll_fd,EPOLL_CTL_ADD,handle->client_fd,&ev_cli);
+
+    ev_exit.data.fd = handle->exit_fd[0];
+    ev_exit.events = event; //EPOLLIN | EPOLLERR | EPOLLET;
+    epoll_ctl(epoll_fd,EPOLL_CTL_ADD,handle->exit_fd[0],&ev_exit);
+
+    int nready = -1;
+    struct epoll_event epoll_events[EPOLL_LISTEN_MAX];
+    while(1)
+    {
+        nready = epoll_wait(epoll_fd, epoll_events, EPOLL_LISTEN_MAX, -1);
+        if(nready > 0)
+        {
+            int i;
+            for(i = 0; i < nready; i++)
+            {
+                LOG("fd[%d] event = %x",epoll_events[i].data.fd, epoll_events[i].events);
+                if(epoll_events[i].events & EPOLLHUP)   // Closed by server.
+                {
+
+                }
+                else if(epoll_events[i].events & EPOLLIN)
+                {
+                    if(handle->client_fd == epoll_events[i].data.fd)  // Server data arrive.
+                    {
+                        // Read and process every message.
+                        mbtk_info_err_enum err = MBTK_INFO_ERR_SUCCESS;
+                        mbtk_info_pack_t **pack = mbtk_info_pack_recv(handle->client_fd, false, &err);
+
+                        // Parse packet error,send error response to client.
+                        if(pack == NULL)
+                        {
+                            if(err != MBTK_INFO_ERR_SUCCESS)
+                            {
+                                LOG("RSP packet error[%s].", err2str(err));
+                            }
+                        }
+                        else
+                        {
+                            mbtk_info_pack_t** pack_ptr = pack;
+                            while(*pack_ptr)
+                            {
+                                pack_process(handle, *pack_ptr);
+                                mbtk_info_pack_free(pack_ptr);
+                                pack_ptr++;
+                            }
+                            free(pack);
+                        }
+                    }
+                    else if(handle->exit_fd[0] == epoll_events[i].data.fd) //
+                    {
+                        char buff[100] = {0};
+                        int len = read(handle->exit_fd[0], buff, 100);
+                        if(len > 0) {
+                            LOGI("CMD : %s", buff);
+                            if(strcmp(buff, "EXIT") == 0) {
+                                goto read_thread_exit;
+                            } else {
+                                LOGD("Unkonw cmd : %s", buff);
+                            }
+                        } else {
+                            LOGE("sock_read() fail.");
+                        }
+                    }
+                    else
+                    {
+                        LOG("Unknown socket : %d", epoll_events[i].data.fd);
+                    }
+                }
+                else
+                {
+                    LOG("Unknown event : %x", epoll_events[i].events);
+                }
+            }
+        }
+        else
+        {
+            LOG("epoll_wait() fail[%d].", errno);
+        }
+    }
+
+read_thread_exit:
+    LOGD("info_read thread exit.");
+    return NULL;
+}
+
+#if 0
+static int info_item_get(mbtk_info_handle_t* handle, mbtk_info_id_enum id, void* data)
+{
+    int data_len = 0;
+    if(data == NULL) {
+        LOG("data is null.");
+        return -1;
+    }
+    mbtk_info_pack_t* pack = mbtk_info_pack_creat(id);
+    if(pack == NULL) {
+        LOG("mbtk_info_item_get() fail.");
+        return -1;
+    }
+
+    mbtk_info_pack_send(handle->client_fd, pack);
+    mbtk_info_pack_free(&pack);
+    handle->data = data;
+
+    // Wait for server response.
+    pthread_mutex_lock(&handle->mutex);
+    handle->is_waitting = true;
+    pthread_cond_wait(&handle->cond, &handle->mutex);
+    handle->is_waitting = false;
+    pthread_mutex_unlock(&handle->mutex);
+
+    if(handle->info_err == MBTK_INFO_ERR_SUCCESS)
+    {
+        LOG("REQ %s success.", id2str(id));
+        if(data && handle->data_len > 0) {
+            data_len = handle->data_len;
+            handle->data_len = 0;
+            handle->data = NULL;
+        }
+        return data_len;
+    } else {
+        LOG("REQ %s fail : %s", id2str(id), err2str(handle->info_err));
+        return -1;
+    }
+}
+#endif
+
+/*
+* Return recv data length.
+* -1 : fail.
+*/
+static int info_item_process(mbtk_info_handle_t *handle,
+                             mbtk_info_id_enum  id,
+                             const void         *send_buff,
+                             int                send_buff_len,
+                             void               *recv_buff)
+{
+    if(handle == NULL/* || ((send_buff == NULL || send_buff_len == 0) && recv_buff == NULL)*/) {
+        LOG("ARG error.");
+        return -1;
+    }
+
+    mbtk_info_pack_t* pack = mbtk_info_pack_creat(id);
+    if(pack == NULL) {
+        return -1;
+    }
+    if(send_buff && send_buff_len > 0) { // Set the data to be sent.
+        // log_hex("data", send_buff, send_buff_len);
+        // mbtk_info_pack_data_set(pack, data, data_len);
+        pack->data_len = (uint16)send_buff_len;
+        pack->data = (const uint8*)send_buff;
+    }
+    mbtk_info_pack_send(handle->client_fd, pack);
+    mbtk_info_pack_free(&pack);
+
+    if(recv_buff != NULL)
+        handle->data = recv_buff;
+
+    // Wait for server response.
+    pthread_mutex_lock(&handle->mutex);
+    handle->is_waitting = true;
+    pthread_cond_wait(&handle->cond, &handle->mutex);
+    handle->is_waitting = false;
+    pthread_mutex_unlock(&handle->mutex);
+
+    if(handle->info_err == MBTK_INFO_ERR_SUCCESS)
+    {
+        LOG("REQ %s success.", id2str(id));
+        int recv_len = 0;
+        if(recv_buff && handle->data_len > 0) {
+            recv_len = handle->data_len;
+            handle->data_len = 0;
+            handle->data = NULL;
+        }
+        return recv_len;
+    } else {
+        LOG("REQ %s fail : %s", id2str(id), err2str(handle->info_err));
+        return -1;
+    }
+}
+
+
+
+mbtk_info_handle_t* mbtk_info_handle_get()
+{
+    mbtk_info_handle_t* handle = (mbtk_info_handle_t*)malloc(sizeof(mbtk_info_handle_t));
+    if(!handle)
+    {
+        LOG("malloc() error[%d].", errno);
+        return NULL;
+    }
+    memset(handle, 0, sizeof(mbtk_info_handle_t));
+    handle->client_fd = socket(AF_LOCAL, SOCK_STREAM, 0);
+    if(handle->client_fd < 0)
+    {
+        LOG("socket() fail[%d].", errno);
+        goto error;
+    }
+
+    // Set O_NONBLOCK
+    int flags = fcntl(handle->client_fd, F_GETFL, 0);
+    if (flags < 0)
+    {
+        LOG("Get flags error:%d", errno);
+        goto error;
+    }
+    flags |= O_NONBLOCK;
+    if (fcntl(handle->client_fd, F_SETFL, flags) < 0)
+    {
+        LOG("Set flags error:%d", errno);
+        goto error;
+    }
+
+    struct sockaddr_un cli_addr;
+    memset(&cli_addr, 0, sizeof(cli_addr));
+    cli_addr.sun_family = AF_LOCAL;
+    strcpy(cli_addr.sun_path, SOCK_INFO_PATH);
+    if(connect(handle->client_fd, (struct sockaddr *)&cli_addr, sizeof(cli_addr)))
+    {
+        LOG("connect() fail[%d].", errno);
+        goto error;
+    }
+
+    if(pipe(handle->exit_fd)) {
+        LOG("pipe() fail[%d].", errno);
+        goto error;
+    }
+#if 0
+    pthread_attr_t thread_attr;
+    pthread_attr_init(&thread_attr);
+    if(pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED))
+    {
+        LOG("pthread_attr_setdetachstate() fail.");
+        goto error;
+    }
+
+    if(pthread_create(&(handle->read_thread_id), &thread_attr, info_read_run, handle))
+    {
+        LOG("pthread_create() fail.");
+        goto error;
+    }
+    pthread_attr_destroy(&thread_attr);
+#else
+    if(pthread_create(&(handle->read_thread_id), NULL, info_read_run, handle))
+    {
+        LOG("pthread_create() fail.");
+        goto error;
+    }
+#endif
+
+    pthread_mutex_init(&handle->mutex, NULL);
+    pthread_cond_init(&handle->cond, NULL);
+    handle->is_waitting = false;
+
+    //mbtk wyq for server_ready_status add start
+    int timeout = 5;//The wait server timeout by default
+    LOG("wait server handshake message--->.");
+    while(timeout)
+    {
+        if(handle->server_ready_status)
+        {
+            break;
+        }
+        else
+        {
+            sleep(1);
+            timeout--;
+        }
+    }
+
+    if(timeout <= 0)
+    {
+        if(handle->exit_fd[1] > 0)
+        {
+            write(handle->exit_fd[1], "EXIT", 4);
+        }
+        pthread_join(handle->read_thread_id,NULL);
+        LOG("mbtk_info_handle_get() server not ready.");
+        goto error;
+    }
+    else
+    {
+        LOG("mbtk_info_handle_get() server ready ok.");
+    }
+    //mbtk wyq for server_ready_status add end
+    return handle;
+error:
+    if(handle)
+    {
+        if(handle->client_fd > 0)
+        {
+            close(handle->client_fd);
+        }
+        if(handle->exit_fd[0] > 0) {
+            close(handle->exit_fd[0]);
+        }
+        if(handle->exit_fd[1] > 0) {
+            close(handle->exit_fd[1]);
+        }
+        free(handle);
+        handle = NULL;
+    }
+
+    return NULL;
+}
+
+int mbtk_info_handle_free(mbtk_info_handle_t** handle)
+{
+    if(handle == NULL || *handle == NULL)
+    {
+        LOG("Handle is NULL.");
+        return -1;
+    }
+
+    if((*handle)->exit_fd[1] > 0) {
+        write((*handle)->exit_fd[1], "EXIT", 4);
+    }
+
+    // Wait read_thread exit.
+    pthread_join((*handle)->read_thread_id,NULL);
+
+    if((*handle)->exit_fd[0] > 0) {
+        close((*handle)->exit_fd[0]);
+        (*handle)->exit_fd[0] = -1;
+    }
+
+    if((*handle)->exit_fd[1] > 0) {
+        close((*handle)->exit_fd[1]);
+        (*handle)->exit_fd[1] = -1;
+    }
+
+    if((*handle)->client_fd > 0)
+    {
+        close((*handle)->client_fd);
+        (*handle)->client_fd = -1;
+    }
+    free(*handle);
+    *handle = NULL;
+    return 0;
+}
+
+/*
+* Get platform version.
+*/
+int mbtk_version_get(mbtk_info_handle_t* handle, void *version)
+{
+    if(handle == NULL || version == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+
+    if(info_item_process(handle, MBTK_INFO_ID_DEV_VERSION_REQ, NULL, 0, version) > 0) {
+        LOG("Version : %s", version);
+        return 0;
+    } else {
+        return handle->info_err;
+    }
+}
+
+/*
+* Get platform version.
+*/
+int mbtk_model_get(mbtk_info_handle_t* handle, void *model)
+{
+    if(handle == NULL || model == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+
+    if(info_item_process(handle, MBTK_INFO_ID_DEV_MODEL_REQ, NULL, 0, model) > 0) {
+        LOG("Version : %s", model);
+        return 0;
+    } else {
+        return handle->info_err;
+    }
+}
+
+/*
+* Get platform IMEI.
+*/
+int mbtk_imei_get(mbtk_info_handle_t* handle, void *imei)
+{
+    if(handle == NULL || imei == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    if(info_item_process(handle, MBTK_INFO_ID_DEV_IMEI_REQ, NULL, 0, imei) > 0) {
+        LOG("IMEI : %s", imei);
+        return 0;
+    } else {
+        return handle->info_err;
+    }
+}
+
+/*
+* Get platform SN.
+*/
+int mbtk_sn_get(mbtk_info_handle_t* handle, void *sn)
+{
+    if(handle == NULL || sn == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    if(info_item_process(handle, MBTK_INFO_ID_DEV_SN_REQ, NULL, 0, sn) > 0) {
+        LOG("SN : %s", sn);
+        return 0;
+    } else {
+        return handle->info_err;
+    }
+}
+
+/*
+* Get platform MEID.
+*/
+int mbtk_meid_get(mbtk_info_handle_t* handle, void *meid)
+{
+    if(handle == NULL || meid == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    if(info_item_process(handle, MBTK_INFO_ID_DEV_MEID_REQ, NULL, 0, meid) > 0) {
+        LOG("MEID : %s", meid);
+        return 0;
+    } else {
+        return handle->info_err;
+    }
+}
+
+/*
+* Return VoLTE state.
+*/
+int mbtk_volte_state_get(mbtk_info_handle_t* handle, int *volte_state)
+{
+    uint8 state;
+    if(handle == NULL || volte_state == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    if(info_item_process(handle, MBTK_INFO_ID_DEV_VOLTE_REQ, NULL, 0, &state) > 0) {
+        LOG("VoLTE State : %d", state);
+        *volte_state = state;
+        return 0;
+    } else {
+        return handle->info_err;
+    }
+}
+
+/*
+* Set VoLTE state.
+*
+* volte_state:
+* 0 : Close VoLTE.
+* 1 : Open VoLTE.
+*
+* Restarting takes effect after execution.
+*/
+int mbtk_volte_state_set(mbtk_info_handle_t* handle, int volte_state)
+{
+    if(handle == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    return info_item_process(handle, MBTK_INFO_ID_DEV_VOLTE_REQ, (uint8*)&volte_state, sizeof(uint8), NULL) ? handle->info_err : 0;
+}
+
+/*
+* Get platform IMSI.
+*/
+int mbtk_imsi_get(mbtk_info_handle_t* handle, void *imsi)
+{
+    if(handle == NULL || imsi == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    if(info_item_process(handle, MBTK_INFO_ID_SIM_IMSI_REQ, NULL, 0, imsi) > 0) {
+        LOG("IMSI : %s", imsi);
+        return 0;
+    } else {
+        return handle->info_err;
+    }
+}
+
+/*
+* Get platform ICCID.
+*/
+int mbtk_iccid_get(mbtk_info_handle_t* handle, void *iccid)
+{
+    if(handle == NULL || iccid == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    if(info_item_process(handle, MBTK_INFO_ID_SIM_ICCID_REQ, NULL, 0, iccid) > 0) {
+        LOG("ICCID : %s", iccid);
+        return 0;
+    } else {
+        return handle->info_err;
+    }
+}
+
+/*
+* Get current phone number.
+*/
+int mbtk_phone_number_get(mbtk_info_handle_t* handle, void *phone_number)
+{
+    if(handle == NULL || phone_number == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    if(info_item_process(handle, MBTK_INFO_ID_SIM_PN_REQ, NULL, 0, phone_number) > 0) {
+        LOG("Phone Number : %s", phone_number);
+        return 0;
+    } else {
+        return handle->info_err;
+    }
+}
+
+/*
+* Get PIN’s number of remaining retry
+*/
+int mbtk_pin_last_num_get(mbtk_info_handle_t* handle, mbtk_pin_puk_last_times *last_times)
+{
+    if(handle == NULL || last_times == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    if(info_item_process(handle, MBTK_INFO_ID_SIM_PINPUK_TIMES_REQ, NULL, 0, last_times) > 0) {
+        LOG("Sim sim_pin_puk_last_times : %d, %d, %d, %d", last_times->p1_retry,last_times->p2_retry,last_times->puk1_retry,last_times->puk2_retry);
+        return 0;
+    } else {
+        return handle->info_err;
+    }
+}
+
+/*
+* emable PIN
+*/
+int mbtk_enable_pin(mbtk_info_handle_t* handle, mbtk_enable_pin_info *pin)
+{
+    if(handle == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    if(info_item_process(handle, MBTK_INFO_ID_SIM_ENABLE_PIN_REQ, pin, sizeof(mbtk_enable_pin_info), NULL) >= 0) {
+        LOG("pin Number : %s", pin->pin_value);
+        return 0;
+    } else {
+        return handle->info_err;
+    }
+
+}
+
+/*
+* Verify PIN
+*/
+int mbtk_verify_pin(mbtk_info_handle_t* handle, char *pin)
+{
+    if(handle == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    if(info_item_process(handle, MBTK_INFO_ID_SIM_PIN_REQ, pin, strlen(pin), NULL) >= 0) {
+        LOG("pin Number : %s", pin);
+        return 0;
+    } else {
+        return handle->info_err;
+    }
+
+}
+
+/*
+* Verify PIN
+*/
+int mbtk_change_pin(mbtk_info_handle_t* handle, mbtk_change_pin_info *pin)
+{
+    if(handle == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    if(info_item_process(handle, MBTK_INFO_ID_SIM_CHANGE_PIN_REQ, pin, sizeof(mbtk_change_pin_info), NULL) >= 0) {
+        LOG("Change PIN : %s -> %s", pin->old_pin_value, pin->new_pin_value);
+        return 0;
+    } else {
+        return handle->info_err;
+    }
+}
+
+/*
+* unblock_pin
+*/
+int mbtk_unlock_pin(mbtk_info_handle_t* handle, mbtk_unlock_pin_info *pin)
+{
+    if(handle == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    if(info_item_process(handle, MBTK_INFO_ID_SIM_PUK_REQ, pin, sizeof(mbtk_unlock_pin_info), NULL) >= 0) {
+        LOG("Unlock : %s , %s", pin->pin_value , pin->puk_value);
+        return 0;
+    } else {
+        return handle->info_err;
+    }
+}
+
+/*
+* Get plmn list
+*/
+int mbtk_get_plmn_list(mbtk_info_handle_t* handle, mbtk_plmn_info *pin)
+{
+    if(handle == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    if(info_item_process(handle, MBTK_INFO_ID_SIM_PLMN_REQ, NULL, 0, pin) >= 0) {
+        //LOG("pin Number : %s", pin);
+        return 0;
+    } else {
+        return handle->info_err;
+    }
+}
+
+
+/*
+* Get available network.
+*/
+int mbtk_available_net_get(mbtk_info_handle_t* handle, list_node_t **net_list)
+{
+    if(handle == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    *net_list = list_create(NULL);
+    if(*net_list == NULL)
+    {
+        LOG("list_create() fail.");
+        return MBTK_INFO_ERR_MEMORY;
+    }
+
+    uint8 buff[SOCK_MSG_LEN_MAX] = {0};
+    int buff_len;
+    if((buff_len = info_item_process(handle, MBTK_INFO_ID_NET_AVAILABLE_REQ, NULL, 0, buff)) > 0) {
+        int i = 0;
+        while (i < buff_len / sizeof(mbtk_net_info_t))
+        {
+            mbtk_net_info_t* net = (mbtk_net_info_t*)malloc(sizeof(mbtk_net_info_t));
+            if(net == NULL)
+            {
+                LOG("malloc() fail.");
+                list_free(*net_list);
+                return MBTK_INFO_ERR_MEMORY;
+            }
+            memcpy(net, buff + i * sizeof(mbtk_net_info_t), sizeof(mbtk_net_info_t));
+            list_add(*net_list, net);
+
+            LOG("NET-%d: %d, %d, %d, %d", i + 1, net->net_sel_mode, net->net_type, net->net_state, net->plmn);
+            i++;
+        }
+
+        return 0;
+    } else {
+        list_free(*net_list);
+        return handle->info_err;
+    }
+}
+
+/*
+* Set network select mode. (+COPS=...)
+*/
+int mbtk_net_sel_mode_set(mbtk_info_handle_t* handle, const mbtk_net_info_t *net)
+{
+    if(handle == NULL || net == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    return info_item_process(handle, MBTK_INFO_ID_NET_SEL_MODE_REQ, net, sizeof(mbtk_net_info_t), NULL) ? handle->info_err : 0;
+}
+
+/*
+* Get network select mode. (+COPS?)
+*/
+int mbtk_net_sel_mode_get(mbtk_info_handle_t* handle, mbtk_net_info_t *net)
+{
+    if(handle == NULL || net == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    if(info_item_process(handle, MBTK_INFO_ID_NET_SEL_MODE_REQ, NULL, 0, net) > 0) {
+        LOG("NET : %d, %d, %d, %d", net->net_sel_mode, net->net_type, net->net_state, net->plmn);
+        return 0;
+    } else {
+        return handle->info_err;
+    }
+}
+
+/*
+* Get platform support bands.
+*/
+int mbtk_support_band_get(mbtk_info_handle_t* handle, mbtk_band_info_t *band)
+{
+    uint8 type = 0; // Get support bands.
+    if(handle == NULL || band == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    if(info_item_process(handle, MBTK_INFO_ID_NET_BAND_REQ, &type, sizeof(uint8), band) > 0) {
+        LOG("BAND : %d, %d, %d, %d, %d", band->net_pref, band->gsm_band, band->umts_band, band->tdlte_band, band->fddlte_band);
+        return 0;
+    } else {
+        return handle->info_err;
+    }
+}
+
+/*
+* Get platform current bands.
+*/
+int mbtk_current_band_get(mbtk_info_handle_t* handle, mbtk_band_info_t *band)
+{
+    uint8 type = 1; // Get current bands.
+    if(handle == NULL || band == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    if(info_item_process(handle, MBTK_INFO_ID_NET_BAND_REQ, &type, sizeof(uint8), band) > 0) {
+        LOG("BAND : %d, %d, %d, %d, %d", band->net_pref, band->gsm_band, band->umts_band, band->tdlte_band, band->fddlte_band);
+        return 0;
+    } else {
+        return handle->info_err;
+    }
+}
+
+/*
+* Set platform current bands.
+*/
+int mbtk_current_band_set(mbtk_info_handle_t* handle, const mbtk_band_info_t *band)
+{
+    if(handle == NULL || band == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    return info_item_process(handle, MBTK_INFO_ID_NET_BAND_REQ, band, sizeof(mbtk_band_info_t), NULL) ? handle->info_err : 0;
+}
+
+/*
+* Get current cell infomation.
+*/
+int mbtk_cell_get(mbtk_info_handle_t* handle, mbtk_cell_type_enum *type, list_node_t **cell_list)
+{
+    if(handle == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    *cell_list = list_create(NULL);
+    if(*cell_list == NULL)
+    {
+        LOG("list_create() fail.");
+        return MBTK_INFO_ERR_MEMORY;
+    }
+
+    uint8 buff[SOCK_MSG_LEN_MAX] = {0};
+    int buff_len;
+    if((buff_len = info_item_process(handle, MBTK_INFO_ID_NET_CELL_REQ, NULL, 0, buff)) > 0) {
+        int i = 0;
+        *type = buff[0]; // Set network type.
+        while (i < (buff_len - sizeof(uint8)) / sizeof(mbtk_cell_info_t))
+        {
+            mbtk_cell_info_t* cell = (mbtk_cell_info_t*)malloc(sizeof(mbtk_cell_info_t));
+            if(cell == NULL)
+            {
+                LOG("malloc() fail.");
+                list_free(*cell_list);
+                return MBTK_INFO_ERR_MEMORY;
+            }
+            memcpy(cell, buff + sizeof(uint8) + i * sizeof(mbtk_cell_info_t), sizeof(mbtk_cell_info_t));
+            list_add(*cell_list, cell);
+
+            // LOG("Cell-%d: %d, %d, %d, %d, %d", i + 1, cell->value1, cell->value2, cell->value3, cell->value4, cell->value5);
+            i++;
+        }
+
+        return 0;
+    } else {
+        list_free(*cell_list);
+        return handle->info_err;
+    }
+}
+/*
+* Set cell info.
+*
+* at*CELL=<mode>,<act>,< band>,<freq>,<cellId>
+* at*cell=2,3,,40936,429   //
+* at*cell=0  //
+*
+* Restarting takes effect after execution.
+*/
+int mbtk_cell_set(mbtk_info_handle_t* handle, char * info, char* response)
+{
+    printf("mbtk_cell_set() info:%s, len:%d",info, strlen(info));
+    char req[128] = {0};
+    if( info_item_process(handle, MBTK_INFO_ID_NET_CELL_REQ, info, strlen(info), req) > 0){
+        memcpy(response, req, strlen(req));
+        return 0;
+    }
+    else{
+        return 0;
+    //    return handle->info_err;
+    }
+}
+
+/*
+* Get all APN informations.
+*/
+int mbtk_apn_get(mbtk_info_handle_t* handle, int *apn_num, mbtk_apn_info_t apns[])
+{
+    int len;
+    if(handle == NULL || apn_num == NULL || apns == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    uint8 data[SOCK_MSG_LEN_MAX];
+    if((len = info_item_process(handle, MBTK_INFO_ID_NET_APN_REQ, NULL, 0, data)) > 0) {
+        /*
+        <apn_num[1]><cid[1]><ip_type[1]><apn_len[2]><apn><user_len[2]><user><pass_len[2]><pass><auth_len[2]><auth>...
+                    <cid[1]><ip_type[1]><apn_len[2]><apn><user_len[2]><user><pass_len[2]><pass><auth_len[2]><auth>
+        */
+        uint8* ptr = data;
+        if(apn_num == NULL || apns == NULL || *apn_num < *ptr) {
+            *apn_num = 0;
+            LOGE("APN array size to not enough.");
+            return -1;
+        }
+        *apn_num = *ptr++;
+        LOG("APN Number : %d", *apn_num);
+        int i = 0;
+        while(i < *apn_num) {
+            memset(&(apns[i]), 0 ,sizeof(mbtk_apn_info_t));
+            apns[i].cid = *ptr++;
+            apns[i].ip_type = (mbtk_ip_type_enum)(*ptr++);
+
+            // apn
+            len = byte_2_uint16(ptr, false);
+            ptr += sizeof(uint16);
+            if(len > 0) { // Has APN
+                memcpy(apns[i].apn, ptr, len);
+                ptr += len;
+            }
+            // user
+            len = byte_2_uint16(ptr, false);
+            ptr += sizeof(uint16);
+            if(len > 0) { // Has APN
+                memcpy(apns[i].user, ptr, len);
+                ptr += len;
+            }
+
+            // pass
+            len = byte_2_uint16(ptr, false);
+            ptr += sizeof(uint16);
+            if(len > 0) { // Has APN
+                memcpy(apns[i].pass, ptr, len);
+                ptr += len;
+            }
+            // auth
+            len = byte_2_uint16(ptr, false);
+            ptr += sizeof(uint16);
+            if(len > 0) { // Has APN
+                memcpy(apns[i].auth, ptr, len);
+                ptr += len;
+            }
+
+            i++;
+        }
+        return 0;
+    } else {
+        return handle->info_err;
+    }
+}
+
+/*
+* Set current APN informations.
+*/
+int mbtk_apn_set(mbtk_info_handle_t* handle, int cid, mbtk_ip_type_enum ip_type, const void* apn_name,
+                    const void *user_name, const void *user_pass, const void *auth)
+{
+    if(handle == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    uint8 data[SOCK_MSG_LEN_MAX];
+    memset(data, 0, SOCK_MSG_LEN_MAX);
+    // cid : 2 - 7
+    if(cid < MBTK_APN_CID_MIN || cid > MBTK_APN_CID_MAX) {
+        LOGE("CID error.");
+        return -1;
+    }
+    uint8* ptr = data;
+    // <cid[1]><ip_type[1]><apn_len[2]><apn><user_len[2]><user><pass_len[2]><pass><auth_len[2]><auth>
+    *ptr++ = (uint8)cid;
+    *ptr++ = (uint8)ip_type;
+    if(str_empty(apn_name)) {
+        uint16_2_byte((uint16)0, ptr, false);
+        ptr += sizeof(uint16);
+    } else {
+        uint16_2_byte((uint16)strlen(apn_name), ptr, false);
+        ptr += sizeof(uint16);
+        memcpy(ptr, apn_name, strlen(apn_name));
+        ptr += strlen(apn_name);
+    }
+    if(str_empty(user_name)) {
+        uint16_2_byte((uint16)0, ptr, false);
+        ptr += sizeof(uint16);
+    } else {
+        uint16_2_byte((uint16)strlen(user_name), ptr, false);
+        ptr += sizeof(uint16);
+        memcpy(ptr, user_name, strlen(user_name));
+        ptr += strlen(user_name);
+    }
+
+    if(str_empty(user_pass)) {
+        uint16_2_byte((uint16)0, ptr, false);
+        ptr += sizeof(uint16);
+    } else {
+        uint16_2_byte((uint16)strlen(user_pass), ptr, false);
+        ptr += sizeof(uint16);
+        memcpy(ptr, user_pass, strlen(user_pass));
+        ptr += strlen(user_pass);
+    }
+
+    if(str_empty(auth)) {
+        uint16_2_byte((uint16)0, ptr, false);
+        ptr += sizeof(uint16);
+    } else {
+        uint16_2_byte((uint16)strlen(auth), ptr, false);
+        ptr += sizeof(uint16);
+        memcpy(ptr, auth, strlen(auth));
+        ptr += strlen(auth);
+    }
+
+    return info_item_process(handle, MBTK_INFO_ID_NET_APN_REQ, data, ptr - data, NULL) ? handle->info_err : 0;
+}
+
+/*
+* Start data call.
+*/
+int mbtk_data_call_start(mbtk_info_handle_t* handle, int cid, int auto_conn_interval, bool boot_conn, int timeout)
+{
+    uint8 data[10];
+    if(handle == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    memset(data, 0, 10);
+    if(cid < MBTK_APN_CID_MIN || cid > MBTK_APN_CID_MAX) {
+        LOGE("CID error.");
+        return -1;
+    }
+    uint8* ptr = data;
+    /* <call_type[1]><cid[1]><auto_conn_interval[1]><boot_conn[1]><timeout[1]>
+     call_type : mbtk_data_call_type_enum
+     cid : 2 - 7
+     timeout : second
+    */
+    *ptr++ = (uint8)MBTK_DATA_CALL_START;
+    *ptr++ = (uint8)cid;
+    *ptr++ = (uint8)(auto_conn_interval > 0 ? auto_conn_interval : 0); // 拨号失败后重拨间隔(s)
+    *ptr++ = (uint8)(boot_conn ? 1 : 0); // 开机自动拨号
+    if(timeout <= 0) {
+        *ptr++ = (uint8)MBTK_DATA_CALL_TIMEOUT_DEFAULT;
+    } else {
+        *ptr++ = (uint8)timeout;
+    }
+
+    return info_item_process(handle, MBTK_INFO_ID_NET_DATA_CALL_REQ, data, 5, NULL) ? handle->info_err : 0;
+}
+
+/*
+* Stop data call.
+*/
+int mbtk_data_call_stop(mbtk_info_handle_t* handle, int cid, int timeout)
+{
+    uint8 data[10];
+    if(handle == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    memset(data, 0, 10);
+    if(cid < MBTK_APN_CID_MIN || cid > MBTK_APN_CID_MAX) {
+        LOGE("CID error.");
+        return -1;
+    }
+    uint8* ptr = data;
+    /* <call_type[1]><cid[1]><timeout[1]>
+     call_type : mbtk_data_call_type_enum
+     cid : 2 - 7
+     timeout : second
+    */
+    *ptr++ = (uint8)MBTK_DATA_CALL_STOP;
+    *ptr++ = (uint8)cid;
+    *ptr++ = (uint8)timeout;
+
+    return info_item_process(handle, MBTK_INFO_ID_NET_DATA_CALL_REQ, data, 3, NULL) ? handle->info_err : 0;
+}
+
+/*
+* Get data call state.
+*/
+int mbtk_data_call_state_get(mbtk_info_handle_t* handle, int cid, mbtk_ipv4_info_t *ipv4, mbtk_ipv6_info_t *ipv6)
+{
+    uint8 data[10];
+    if(handle == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    uint8 recv_buff[SOCK_MSG_LEN_MAX]={0};
+    memset(data, 0, 10);
+    if(cid < MBTK_APN_CID_MIN || cid > MBTK_APN_CID_MAX) {
+        LOGE("CID error.");
+        return -1;
+    }
+    uint8* ptr = data;
+    /* <call_type[1]><cid[1]><timeout[1]>
+     call_type : mbtk_data_call_type_enum
+     cid : 2 - 7
+     timeout : second
+    */
+    *ptr++ = (uint8)MBTK_DATA_CALL_STATE;
+    *ptr++ = (uint8)cid;
+    *ptr++ = (uint8)0;
+
+    if(ipv4) {
+        memset(ipv4, 0, sizeof(mbtk_ipv4_info_t));
+    }
+
+    if(ipv6) {
+        memset(ipv6, 0, sizeof(mbtk_ipv6_info_t));
+    }
+
+    if(info_item_process(handle, MBTK_INFO_ID_NET_DATA_CALL_REQ, data, 3, recv_buff) > 0) {
+        if(recv_buff[0] == 0) { // IPv4 Only.
+            if(ipv4) {
+                memcpy(ipv4, recv_buff + sizeof(uint8), sizeof(mbtk_ipv4_info_t));
+            }
+        } else if(recv_buff[0] == 1) { // IPv6 Only.
+            if(ipv6) {
+                memcpy(ipv6, recv_buff + sizeof(uint8), sizeof(mbtk_ipv6_info_t));
+            }
+        } else if(recv_buff[0] == 2) { // IPv4 and IPv6.
+            if(ipv4) {
+                memcpy(ipv4, recv_buff + sizeof(uint8), sizeof(mbtk_ipv4_info_t));
+            }
+
+            if(ipv6) {
+                memcpy(ipv6, recv_buff + sizeof(uint8) + sizeof(mbtk_ipv4_info_t), sizeof(mbtk_ipv6_info_t));
+            }
+        } else {
+            LOGE("Unknown IP type : %d", recv_buff[0]);
+            return -1;
+        }
+        return 0;
+    } else {
+        return handle->info_err;
+    }
+}
+
+
+
+/*
+* Get current network signal.
+*/
+int mbtk_net_signal_get(mbtk_info_handle_t* handle, mbtk_signal_info_t *signal)
+{
+    if(handle == NULL || signal == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    if(info_item_process(handle, MBTK_INFO_ID_NET_SIGNAL_REQ, NULL, 0, signal) > 0) {
+        LOG("Signal : %d, %d, %d, %d, %d, %d, %d", signal->rssi, signal->rxlev, signal->ber, signal->rscp, signal->ecno,
+            signal->rsrq, signal->rsrp);
+        return 0;
+    } else {
+        return handle->info_err;
+    }
+}
+
+/*
+* Get current network register information.
+*/
+int mbtk_net_reg_get(mbtk_info_handle_t* handle, mbtk_net_reg_info_t *reg)
+{
+    if(handle == NULL || reg == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    if(info_item_process(handle, MBTK_INFO_ID_NET_REG_REQ, NULL, 0, reg) > 0) {
+        if(reg->call_state || reg->data_state || reg->ims_state) {
+            LOGD("REG : call_state=%d, data_state=%d, ims_state=%d, net_type=%d, %04x, %08x", reg->call_state, reg->data_state, reg->ims_state, reg->type, reg->lac, reg->ci);
+        } else {
+            LOGE("Net not reg.");
+        }
+        return 0;
+    } else {
+        return handle->info_err;
+    }
+}
+
+
+/*
+* Get radio state.
+*/
+int mbtk_radio_state_get(mbtk_info_handle_t* handle, int *radio_state)
+{
+    uint8 state;
+    if(handle == NULL || radio_state == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    if(info_item_process(handle, MBTK_INFO_ID_NET_RADIO_REQ, NULL, 0, &state) > 0) {
+        LOG("Radio state : %d", state);
+        *radio_state = state;
+        return 0;
+    } else {
+        return handle->info_err;
+    }
+}
+
+/*
+* Set radio state.
+*/
+int mbtk_radio_state_set(mbtk_info_handle_t* handle, int radio_state)
+{
+    if(handle == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    return info_item_process(handle, MBTK_INFO_ID_NET_RADIO_REQ, (uint8*)&radio_state, sizeof(uint8), NULL) ? handle->info_err : 0;
+}
+
+/*
+* Get time type.
+*/
+int mbtk_time_get(mbtk_info_handle_t* handle, int *time_type)
+{
+    uint8 state;
+    if(handle == NULL || time_type == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    if(info_item_process(handle, MBTK_INFO_ID_DEV_TIME_REQ, NULL, 0, &state) > 0) {
+        LOG("Time type : %d", state);
+        *time_type = state;
+        return 0;
+    } else {
+        return handle->info_err;
+    }
+}
+
+/*
+*Get Absolute time
+*"23/05/24 06:09:32 +32 00"
+*/
+int mbtk_get_abs_time(char *time_str, time_t *time_out)
+{
+    struct tm tm_;
+
+    char *ptr = strstr(time_str + 10, " ");
+    *ptr = '\0';
+
+    LOGD("time : \"%s\"", time_str);
+#if 1
+    if(strptime(time_str, "%y/%m/%d %T", &tm_) == NULL) {
+        LOGE("strptime() fail.");
+        return -1;
+    }
+#else
+    int year, month, day, hour, minute,second,time_zone;
+    if(strstr(time_str, "+")) {
+        sscanf(time_str, "%d/%d/%d %d:%d:%d +%d",&year,&month,&day,&hour,&minute,&second,&time_zone);
+    } else if(strstr(time_str, "-")) {
+        sscanf(time_str, "%d/%d/%d %d:%d:%d -%d",&year,&month,&day,&hour,&minute,&second,&time_zone);
+    } else {
+        LOGE("Time format error:%s", time_str);
+        return -1;
+    }
+
+    // 1970+
+    if(year < 70) { // 20xx
+        tm_.tm_year  = 2000 + year;
+    } else { // 19xx
+        tm_.tm_year  = 1900 + year;
+    }
+    tm_.tm_mon   = month - 1;
+    tm_.tm_mday  = day;
+    tm_.tm_hour  = hour;
+    tm_.tm_min   = minute;
+    tm_.tm_sec   = second;
+    tm_.tm_isdst = 0;
+#endif
+
+    time_t timeStamp = mktime(&tm_);
+    LOGD("tm_.tm_year = %d,tm_.tm_mon = %d,tm_.tm_mday = %d,tm_.tm_hour = %d,tm_.tm_min = %d,tm_.tm_sec = %d,tm_.tm_isdst = %d",tm_.tm_year,tm_.tm_mon,tm_.tm_mday,tm_.tm_hour,tm_.tm_min,tm_.tm_sec,tm_.tm_isdst);
+    LOGD("time = %ld,%x", timeStamp,timeStamp);
+    *time_out = timeStamp;
+
+    return 0;
+}
+
+/*
+* Get time type.
+* "23/05/24,06:09:32+32" -> "23/05/24 06:09:32 +32 00"
+*/
+int mbtk_net_time_get(mbtk_info_handle_t* handle, char* time_str)
+{
+    uint8 buff[SOCK_MSG_LEN_MAX] = {0};
+    if(handle == NULL || time_str == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    //printf("mbtk_net_time_get begin info_item_process\n");
+    if(info_item_process(handle, MBTK_INFO_ID_NET_TIME_REQ, NULL, 0, buff) > 0) {
+        memcpy(time_str,buff,strlen(buff));
+
+        uint8 *temp = strstr(time_str, ",");
+        if(temp) {
+            *temp = ' '; // ',' -> ' '
+
+            temp = strstr(time_str, "+");
+            if(temp == NULL) {
+                temp = strstr(time_str, "-");
+            }
+
+            if(temp) {
+                // Copy +XX or -XX
+                uint8 *last_ptr = temp + strlen(temp) + 1;
+                while(last_ptr > temp) {
+                    *last_ptr = *(last_ptr - 1);
+                    last_ptr--;
+                }
+
+                *last_ptr = ' ';
+
+                memcpy(temp + strlen(temp), "00", 2);
+
+                LOGD("%s -> %s", buff, time_str);
+                return 0;
+            } else {
+                LOGE("Time error:%s",buff);
+                return MBTK_INFO_ERR_TIME_FORMAT;
+            }
+        } else {
+            LOGE("Time error:%s",buff);
+            return MBTK_INFO_ERR_TIME_FORMAT;
+        }
+    } else {
+        return handle->info_err;
+    }
+}
+
+/*
+* Set time.
+*
+* time_type:
+* 0: Cell time
+* 1: NTP time
+* 2: User time
+* time_str: "YYYY-MM-DD HH:MM:SS"
+*/
+int mbtk_time_set(mbtk_info_handle_t* handle, mbtk_time_type_enum time_type, char* time_str)
+{
+    if(handle == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    uint8 buffer[100] = {0};
+    buffer[0] = (uint8)time_type;
+    if(time_type == MBTK_TIME_TYPE_USER) {
+        if(!str_empty(time_str)) {
+            memcpy(buffer + sizeof(uint8), time_str, strlen(time_str));
+            return info_item_process(handle, MBTK_INFO_ID_DEV_TIME_REQ,
+                    buffer, sizeof(uint8) + strlen(time_str), NULL) ? handle->info_err : 0;
+        } else {
+            return -1;
+        }
+    } else {
+        return info_item_process(handle, MBTK_INFO_ID_DEV_TIME_REQ,
+                buffer, sizeof(uint8), NULL) ? handle->info_err : 0;
+    }
+}
+
+/*
+* Return sms cmgf.
+*/
+int mbtk_sms_cmgf_get(mbtk_info_handle_t* handle, int *volte_state)
+{
+    uint8 state;
+    if(info_item_process(handle, MBTK_INFO_ID_SMS_CMGF_REQ, NULL, 0, &state) > 0) {
+        LOG("mbtk_sms_cmgf_get()-----------sms cmgf : %d", state);
+        *volte_state = state;
+        return 0;
+    } else {
+        return handle->info_err;
+    }
+}
+
+/*
+* Set sms cmgf.
+*
+* volte_state:
+* 0 : PDU mode.
+* 1 :  text mode.
+*
+* Restarting takes effect after execution.
+*/
+int mbtk_sms_cmgf_set(mbtk_info_handle_t* handle, int mode)
+{
+    printf("mbtk_sms_cmgf_set()--------mode=:%d, len:%d", mode, sizeof(uint8));
+    return info_item_process(handle, MBTK_INFO_ID_SMS_CMGF_REQ, (uint8*)&mode, sizeof(uint8), NULL) ? handle->info_err : 0;
+}
+
+/*
+* Set sms cmgs.
+*
+if PDU mode (+CMGF=0):
++CMGS=<length><CR>
+PDU is given<ctrl-Z/ESC>
+
+if text mode (+CMGF=1):
++CMGS=<da>[,<toda>]<CR>
+text is entered<ctrl-Z/ESC>
+
+* Restarting takes effect after execution.
+*/
+int mbtk_sms_cmgs_set(mbtk_info_handle_t* handle, char * cmgs, char *resp)
+{
+    printf("mbtk_sms_cmgs_set(1)--------cmgs=:%s, len:%d", cmgs, strlen(cmgs));
+//    char req[20] = {0}
+    if(info_item_process(handle, MBTK_INFO_ID_SMS_CMGS_REQ, cmgs, strlen(cmgs), resp)  > 0){
+        printf("resp:%s\n", resp);
+        return 0;
+    }else{
+        return handle->info_err;
+    }
+}
+
+/*
+* Set sms cmgw.
+*
+if text mode (+CMGF=1):
++CMGW=<oa/da>[,<tooa/toda>[,<stat>]]
+<CR>
+text is entered<ctrl-Z/ESC>
+if PDU mode (+CMGF=0):
++CMGW=<length>[,<stat>]<CR>PDU is
+given<ctrl-Z/ESC>
+
+*/
+
+int mbtk_sms_cmgw_set(mbtk_info_handle_t* handle, char * cmgw, char *resp)
+{
+    printf("mbtk_sms_cmgw_set() ----------cmgw:%s, len:%d", cmgw, strlen(cmgw));
+    if(info_item_process(handle, MBTK_INFO_ID_SMS_CMGW_REQ, cmgw, strlen(cmgw), resp)  > 0){
+        printf("resp:%s\n", resp);
+        return 0;
+    }else{
+        return handle->info_err;
+    }
+}
+
+/*
+* Set sms cmgd.
+*
+* +CMGD=<index>[,<delflag>]
+*
+* Restarting takes effect after execution.
+*/
+int mbtk_sms_cmgd_set(mbtk_info_handle_t* handle, char * cmdg)
+{
+    printf("mbtk_sms_cmgd_set() cmdg:%s, len:%d",cmdg, strlen(cmdg));
+    return info_item_process(handle, MBTK_INFO_ID_SMS_CMGD_REQ, cmdg, strlen(cmdg), NULL) ? handle->info_err : 0;
+}
+
+/*
+* Set sms cmgl.
+*
+* AT+CMGL[=<stat>]
+*
+* Restarting takes effect after execution.
+*/
+int mbtk_sms_cmgl_set(mbtk_info_handle_t* handle, char * cmgl, char *resp)
+{
+    printf("0mbtk_sms_cmgl_set() cmgl:%s, len:%d\n",cmgl, strlen(cmgl));
+    char reg[5*1024] ={0};
+    if( info_item_process(handle, MBTK_INFO_ID_SMS_CMGL_REQ, cmgl, strlen(cmgl), reg) > 0){
+        printf("len:%d , reg:%s\n", strlen(reg), reg);
+    //    memcpy(resp, reg, strlen(reg));
+        return 0;
+    }else {
+        return handle->info_err;
+    }
+}
+
+/*
+* Return sms csca.
+*/
+int mbtk_sms_csca_get(mbtk_info_handle_t* handle, char *buf)
+{
+  //  char  state;
+    if(info_item_process(handle, MBTK_INFO_ID_SMS_CSCA_REQ, NULL, 0, buf) > 0) {
+        LOG("mbtk_sms_csca_get()-----------sms csca : %s", buf);
+    //    *volte_state = state;
+        return 0;
+    } else {
+        return handle->info_err;
+    }
+}
+
+/*
+* Set sms csca.
+*
+* AT+CSCA=<number> [,<type>]
+*
+* Restarting takes effect after execution.
+*/
+int mbtk_sms_csca_set(mbtk_info_handle_t* handle, char * csca)
+{
+    printf("mbtk_sms_csca_set() csca:%s, len:%d",csca, strlen(csca));
+    return info_item_process(handle, MBTK_INFO_ID_SMS_CSCA_REQ, csca, strlen(csca), NULL) ? handle->info_err : 0;
+}
+
+/*
+* Set sms csmp.
+*
+* AT+CSMP=[<fo>[,<vp>[,<pid>[,<dcs>]]]]
+*
+* Restarting takes effect after execution.
+*/
+int mbtk_sms_csmp_set(mbtk_info_handle_t* handle, char * csmp)
+{
+    printf("mbtk_sms_csmp_set() csmp:%s, len:%d",csmp, strlen(csmp));
+    return info_item_process(handle, MBTK_INFO_ID_SMS_CSMP_REQ, csmp, strlen(csmp), NULL) ? handle->info_err : 0;
+}
+
+/*
+* Set sms cscb.
+*
+* AT+CSCB=<[<mode>[,<mids>[,<dcss>]]]>
+*
+* Restarting takes effect after execution.
+*/
+int mbtk_sms_cscb_set(mbtk_info_handle_t* handle, char * cscb)
+{
+    printf("mbtk_sms_cscb_set() cscb:%s, len:%d",cscb, strlen(cscb));
+    return info_item_process(handle, MBTK_INFO_ID_SMS_CSCB_REQ, cscb, strlen(cscb), NULL) ? handle->info_err : 0;
+}
+
+/*
+* Set sms cnmi.
+*
+at+cnmi=1,2
+
+OK
+if sending fails:
++CMS ERROR: <err>
+*/
+int mbtk_sms_cnmi_set(mbtk_info_handle_t* handle)
+{
+    printf("mbtk_sms_cnmi_set()------------start\n");
+
+    return info_item_process(handle, MBTK_INFO_ID_SMS_CNMI_REQ, NULL, 0, NULL)? handle->info_err : 0;
+}
+
+/*
+* Set sms cmss.
+*
++CMSS=<index>[,<da>[,<toda>]]
+
+if sending successful:
++CMSS: <mr>
+OK
+if sending fails:
++CMS ERROR: <err>
+*/
+int mbtk_sms_cmss_set(mbtk_info_handle_t* handle, char * cmss, char *resp)
+{
+    printf("mbtk_sms_cmss_set()------------cmss:%s, len:%d", cmss, strlen(cmss));
+    if( info_item_process(handle, MBTK_INFO_ID_SMS_CMSS_REQ, cmss, strlen(cmss), resp) > 0){
+        printf("resp:%s\n", resp);
+        return 0;
+    }else{
+        return handle->info_err;
+    }
+}
+
+/*
+* Return sms cmgf.
+*/
+int mbtk_sms_cpms_get(mbtk_info_handle_t* handle, char * mem)
+{
+    char req[128] = {0};
+    if(info_item_process(handle, MBTK_INFO_ID_SMS_CPMS_REQ, NULL, 0, &req) > 0) {
+        LOG("mbtk_sms_cpms_get()  req : %s, strlen(mem_ptr):%d\n", req, strlen(req));
+        memcpy(mem, req, strlen(req));
+        return 0;
+    } else {
+        return handle->info_err;
+    }
+}
+
+
+/*
+* Set sms cpms.
+*
+* AT+CPMS=<mem1>[,<mem2>[,<mem3>]]
+*
+* Restarting takes effect after execution.
+*/
+int mbtk_sms_cpms_set(mbtk_info_handle_t* handle, char * mem, char* response)
+{
+    printf("mbtk_sms_cpms_set() mem:%s, len:%d",mem, strlen(mem));
+    char req[128] = {0};
+    if( info_item_process(handle, MBTK_INFO_ID_SMS_CPMS_REQ, mem, strlen(mem), req) > 0){
+        memcpy(response, req, strlen(req));
+        return 0;
+    }
+    else{
+        return handle->info_err;
+    }
+}
+
+/*
+* Set sms cm.
+*
+* +CMGR=<index>
+
+if PDU mode (+CMGF=0) ��command successful:
++CMGR: <stat>,[<alpha>],<length><CR><LF><pdu>
+OK
+if text mode (+CMGF=1), command successful and SMS-DELIVER:
++CMGR:<stat>,<oa>,[<alpha>],<scts>[,<tooa>,<fo>,<pid>,<dcs
+>, <sca>,<tosca>,<length>]<CR><LF><data>
+OK
+if text mode (+CMGF=1), command successful and SMS-SUBMIT:
++CMGR:
+<stat>,<da>,[<alpha>][,<toda>,<fo>,<pid>,<dcs>,[<vp>],
+<sca>,<tosca>,<length>]<CR><LF><data>
+OK
+otherwise:
++CMS ERROR: <err>
+*
+* Restarting takes effect after execution.
+*/
+int mbtk_sms_cmgr_set(mbtk_info_handle_t* handle, int index, char *resp)
+{
+    printf("mbtk_sms_cmgr_set() --------------index:%d",index);
+    if( info_item_process(handle, MBTK_INFO_ID_SMS_CMGR_REQ, (uint8*)&index, sizeof(uint8), resp) > 0){
+        printf("resp:%s\n", resp);
+        return 0;
+    }else{
+        return handle->info_err;
+    }
+}
+
+
+/*
+* Get sim state.
+*/
+int mbtk_sim_state_get(mbtk_info_handle_t* handle, mbtk_sim_state_enum *sim_state)
+{
+    uint8 state;
+    if(handle == NULL || sim_state == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    if(info_item_process(handle, MBTK_INFO_ID_SIM_STATE_REQ, NULL, 0, &state) > 0) {
+        *sim_state = (mbtk_sim_state_enum)state;
+        LOG("Sim state : %d", *sim_state);
+        return 0;
+    } else {
+        return handle->info_err;
+    }
+}
+
+/*
+* Get sim card type.
+*/
+int mbtk_sim_card_type_get(mbtk_info_handle_t* handle, mbtk_sim_card_type_enum *sim_card_type)
+{
+    uint8 state;
+    if(handle == NULL || sim_card_type == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    if(info_item_process(handle, MBTK_INFO_ID_SIM_STYPE_REQ, NULL, 0, &state) > 0) {
+        *sim_card_type = (mbtk_sim_card_type_enum)state;
+        LOG("Sim sim_card_type : %d", *sim_card_type);
+        return 0;
+    } else {
+        return handle->info_err;
+    }
+}
+
+/*
+* Get system temperature.
+*
+* type[IN]:
+*   0: Soc temperature.
+*   1: RF temperature.
+* temp[OUT]:
+*   temperature in celsius.
+*/
+int mbtk_temp_get(mbtk_info_handle_t* handle, int type, int* temp)
+{
+    if(handle == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    if(type != 0 && type != 1 || temp == NULL)
+    {
+        return -1;
+    }
+
+    uint8 temp_type = (uint8)type;
+    uint8 temp_ptr;
+    if(info_item_process(handle, MBTK_INFO_ID_DEV_TEMP_REQ, &temp_type, sizeof(uint8), &temp_ptr) > 0) {
+        *temp = temp_ptr;
+        LOG("Temperature : %d", *temp);
+        return 0;
+    } else {
+        return handle->info_err;
+    }
+
+    return 0;
+}
+
+
+/*
+* Set sim power state.
+* power:
+* 0: Sim power off.
+* 1: Sim power on.
+*/
+int mbtk_sim_power_set(int power)
+{
+    if(power != 0 && power != 1)
+    {
+        return -1;
+    }
+
+    //  /sys/devices/virtual/usim_event/usim0/send_event
+    char cmd[100] = {0};
+    sprintf(cmd, "echo %d > /sys/devices/virtual/usim_event/usim0/send_event", power ? 0 : 1);
+    system(cmd);
+
+    return 0;
+}
+
+/*
+* System power.
+* type:
+* 0: Reboot system.
+* 1: Poweroff system.
+* 2: Halt system.
+*/
+int mbtk_system_reboot(int type)
+{
+    if(type != 0 && type != 1 && type != 2)
+    {
+        return -1;
+    }
+
+    switch(type) {
+        case 0: {
+            system("reboot");
+            break;
+        }
+        case 1: {
+            system("poweroff");
+            break;
+        }
+        case 2: {
+            system("halt");
+            break;
+        }
+        default: {
+            break;
+        }
+    }
+
+    return 0;
+}
+
+/*
+*   set modem fun
+*
+*/
+int mbtk_set_modem_fun(mbtk_info_handle_t* handle, mbtk_modem_info_t *info)
+{
+    if(handle == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    return info_item_process(handle, MBTK_INFO_ID_DEV_MODEM_REQ, info, sizeof(mbtk_modem_info_t), NULL) ? handle->info_err : 0;
+}
+
+/*
+*   get modem fun
+*
+*/
+int mbtk_get_modem_fun(mbtk_info_handle_t* handle, int* fun)
+{
+    uint8 state;
+    if(handle == NULL || fun == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    if(info_item_process(handle, MBTK_INFO_ID_DEV_MODEM_REQ, NULL, 0, &state) > 0) {
+        LOG("modem type : %d", state);
+        *fun = state;
+        return 0;
+    } else {
+        return handle->info_err;
+    }
+}
+
+/*
+* call_start
+*
+*/
+int mbtk_call_start(mbtk_info_handle_t* handle, char* phone_number)
+{
+    if(handle == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    if(str_empty(phone_number))
+        return -1;
+
+    return info_item_process(handle, MBTK_INFO_ID_CALL_START_REQ,
+            phone_number, strlen(phone_number), NULL) ? handle->info_err : 0;
+}
+/*
+* Answer the phone call.
+*
+*/
+int mbtk_call_answer(mbtk_info_handle_t* handle)
+{
+    return info_item_process(handle, MBTK_INFO_ID_CALL_ANSWER_REQ, NULL, 0, NULL) ? handle->info_err : 0;
+}
+
+/*
+* Hang up all call.
+*
+*/
+int mbtk_call_hang(mbtk_info_handle_t* handle)
+{
+    return info_item_process(handle, MBTK_INFO_ID_CALL_HANGUP_REQ, NULL, 0, NULL) ? handle->info_err : 0;
+}
+
+/*
+* Hang up a call.
+*
+*/
+int mbtk_a_call_hang(mbtk_info_handle_t* handle, int phone_id)
+{
+    return info_item_process(handle, MBTK_INFO_ID_CALL_HANGUP_A_REQ, (uint8*)&phone_id, sizeof(uint8), NULL) ? handle->info_err : 0;
+}
+
+/*
+* Hang up waiting or background call.
+*
+*/
+int mbtk_waiting_or_background_call_hang(mbtk_info_handle_t* handle)
+{
+    return info_item_process(handle, MBTK_INFO_ID_CALL_HANGUP_B_REQ, NULL, 0, NULL) ? handle->info_err : 0;
+}
+
+/*
+* Hang up foreground resume background call.
+*
+*/
+int mbtk_foreground_resume_background_call_hang(mbtk_info_handle_t* handle)
+{
+    return info_item_process(handle, MBTK_INFO_ID_CALL_HANGUP_C_REQ, NULL, 0, NULL) ? handle->info_err : 0;
+}
+
+/*
+* Get current call phone number.
+*/
+int mbtk_call_reg_get(mbtk_info_handle_t* handle, mbtk_call_info_t *reg)
+{
+    if(info_item_process(handle, MBTK_INFO_ID_CALL_WAITIN_REQ, NULL, 0, reg) > 0) {
+        LOG("CLCC : %d, %d, %d, %d, %d, %s, %d", reg->dir1, reg->dir, reg->state, reg->mode, reg->mpty, reg->phone_number, reg->type);
+        return 0;
+    } else {
+        return handle->info_err;
+    }
+}
+
+/*
+* Return mute state.
+*/
+int mbtk_mute_state_get(mbtk_info_handle_t* handle, int *mute_state)
+{
+    uint8 state;
+    if(info_item_process(handle, MBTK_INFO_ID_CALL_MUTE_REQ, NULL, 0, &state) > 0) {
+        LOG("Mute State : %d", state);
+        *mute_state = state;
+        return 0;
+    } else {
+        return handle->info_err;
+    }
+}
+
+/*
+* Set mute state.
+*
+* mute_state:
+* 0 : of mute.
+* 1 : on mute.
+*
+* Restarting takes effect after execution.
+*/
+int mbtk_mute_state_set(mbtk_info_handle_t* handle, int mute_state)
+{
+    return info_item_process(handle, MBTK_INFO_ID_CALL_MUTE_REQ, (uint8*)&mute_state, sizeof(uint8), NULL) ? handle->info_err : 0;
+}
+
+/*
+* Set DTMF character.
+*
+*/
+int mbtk_dtmf_send(mbtk_info_handle_t* handle, mbtk_call_dtmf_info_t *dtmf_character)
+{
+    if(handle == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    return info_item_process(handle, MBTK_INFO_ID_CALL_DTMF_REQ,
+            dtmf_character, sizeof(mbtk_call_dtmf_info_t), NULL) ? handle->info_err : 0;
+}
+
+/*
+* Set pdp state change callback function.
+*/
+int mbtk_pdp_state_change_cb_reg(mbtk_info_handle_t* handle, mbtk_info_callback_func cb)
+{
+    if(handle == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    if(info_item_process(handle, MBTK_INFO_ID_IND_PDP_STATE_CHANGE, NULL, 0, NULL) < 0) {
+        return handle->info_err;
+    } else {
+        handle->pdp_state_cb = cb;
+        return 0;
+    }
+}
+
+/*
+* Set network state change callback function.
+*/
+int mbtk_net_state_change_cb_reg(mbtk_info_handle_t* handle, mbtk_info_callback_func cb)
+{
+    if(handle == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    if(info_item_process(handle, MBTK_INFO_ID_IND_NET_STATE_CHANGE, NULL, 0, NULL) > 0) {
+        handle->net_state_cb = cb;
+        return 0;
+    } else {
+        return handle->info_err;
+    }
+}
+
+/*
+* Set call state change callback function.
+*/
+int mbtk_call_state_change_cb_reg(mbtk_info_handle_t* handle, mbtk_info_callback_func cb)
+{
+    if(handle == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    if(info_item_process(handle, MBTK_INFO_ID_IND_CALL_STATE_CHANGE, NULL, 0, NULL) < 0) {
+        return handle->info_err;
+    } else {
+        handle->call_state_cb = cb;
+        return 0;
+    }
+}
+
+/*
+* Set sms state change callback function.
+*/
+int mbtk_sms_state_change_cb_reg(mbtk_info_handle_t* handle, mbtk_info_callback_func cb)
+{
+    if(handle == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    if(info_item_process(handle, MBTK_INFO_ID_IND_SMS_STATE_CHANGE, NULL, 0, NULL) < 0) {
+        return handle->info_err;
+    } else {
+        handle->sms_state_cb = cb;
+        return 0;
+    }
+}
+
+/*
+* Set radio state change callback function.
+*/
+int mbtk_radio_state_change_cb_reg(mbtk_info_handle_t* handle, mbtk_info_callback_func cb)
+{
+    if(handle == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    if(info_item_process(handle, MBTK_INFO_ID_IND_RADIO_STATE_CHANGE, NULL, 0, NULL) < 0) {
+        return handle->info_err;
+    } else {
+        handle->radio_state_cb = cb;
+        return 0;
+    }
+}
+
+/*
+* Set sim state change callback function.
+*/
+int mbtk_sim_state_change_cb_reg(mbtk_info_handle_t* handle, mbtk_info_callback_func cb)
+{
+    if(handle == NULL)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+    if(info_item_process(handle, MBTK_INFO_ID_IND_SIM_STATE_CHANGE, NULL, 0, NULL) < 0) {
+        return handle->info_err;
+    } else {
+        handle->sim_state_cb = cb;
+        return 0;
+    }
+}
diff --git a/mbtk/mbtk_lib/src/mbtk_list.c b/mbtk/mbtk_lib/src/mbtk_list.c
new file mode 100755
index 0000000..d31cedf
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_list.c
@@ -0,0 +1,332 @@
+/*
+ * mbtk_list.c
+ *
+ *  Created on: Aug 18, 2020
+ *      Author: lb
+ */
+#include <string.h>
+#include <stdlib.h>
+#include "mbtk_list.h"
+
+list_node_t* list_create(list_free_func free_func)
+{
+    list_node_t *list = (list_node_t*) malloc(sizeof(list_node_t));
+    if (list) {
+        memset(list, 0x0, sizeof(list_node_t));
+        list->size = 0;
+        list->cur_index = 0;
+        list->cur_array_data = NULL;
+        list->sort_func = NULL;
+        list->free_func = free_func;
+        return list;
+    }
+
+    return NULL;
+}
+
+uint32 list_size(list_node_t *list)
+{
+    if (list) {
+        return list->size;
+    } else {
+        return 0;
+    }
+}
+
+static list_treenode_t* list_treeadd(list_node_t *list, list_treenode_t *node,
+        list_arraynode_t *data)
+{
+    if (node && node->data) {
+        int result = list->sort_func(data->data, node->data->data);
+        if (result == 0) { // Same node
+            node->count++;
+            // printf("Same[%d]:%s\n", node->count, data->data);
+
+            // Add the same node to last.
+            list_arraynode_t *array_node = node->data;
+            while (array_node->next) {
+                array_node = array_node->next;
+            }
+            array_node->next = data;
+            array_node->next->next = NULL;
+
+        } else if (result < 0) {
+            node->left = list_treeadd(list, node->left, data);
+        } else {
+            node->right = list_treeadd(list, node->right, data);
+        }
+    } else {
+        if (!node) {
+            node = (list_treenode_t*) malloc(sizeof(list_treenode_t));
+        }
+        node->left = NULL;
+        node->right = NULL;
+        node->data = data;
+        node->count = 1;
+    }
+
+    return node;
+}
+
+void list_add(list_node_t *list, void *data)
+{
+    if (list && data) {
+        list_arraynode_t *node = &(list->array_data);
+        while (node->next) {
+            node = node->next;
+        }
+
+        node->next = (list_arraynode_t*) malloc(sizeof(list_arraynode_t));
+        if(node->next) {
+            node->next->data = data;
+            node->next->next = NULL;
+            list->size++;
+        }
+    }
+}
+
+void list_add_unique(list_node_t *list, void *data, uint32 len)
+{
+    if (list && data && len > 0) {
+        list_arraynode_t *node = &(list->array_data);
+        while (node->next) {
+            if (!memcmp(node->next->data, data, len)) {
+                return;
+            }
+            node = node->next;
+        }
+
+        node->next = (list_arraynode_t*) malloc(sizeof(list_arraynode_t));
+        node->next->data = data;
+        node->next->next = NULL;
+
+        list->size++;
+    }
+}
+
+void* list_remove(list_node_t *list, void *data)
+{
+    if (list) {
+        list_arraynode_t *node = list->array_data.next;
+        if (node) {
+            if (data == node->data) { // Find node
+                list_arraynode_t *result = node;
+                void *reault_data = result->data;
+                list->array_data.next = node->next;
+                free(result);
+                list->size--;
+                return reault_data;
+            }
+        } else {
+            return NULL;
+        }
+
+        while (node->next) {
+            if (data == node->next->data) { // Find node
+                list_arraynode_t *result = node->next;
+                void *reault_data = result->data;
+                node->next = node->next->next;
+                free(result);
+                list->size--;
+                return reault_data;
+            }
+            node = node->next;
+        }
+    }
+
+    return NULL;
+}
+
+void* list_remove_by_content(list_node_t *list, void *data, uint32 data_len)
+{
+    if (list) {
+        list_arraynode_t *node = list->array_data.next;
+        if (node) {
+            if (!memcmp(data, node->data, data_len)) { // Find node
+                list_arraynode_t *result = node;
+                void *reault_data = result->data;
+                list->array_data.next = node->next;
+                free(result);
+                list->size--;
+                return reault_data;
+            }
+        } else {
+            return NULL;
+        }
+
+        while (node->next) {
+            if (!memcmp(data, node->next->data, data_len)) { // Find node
+                list_arraynode_t *result = node->next;
+                void *reault_data = result->data;
+                node->next = node->next->next;
+                free(result);
+                list->size--;
+                return reault_data;
+            }
+            node = node->next;
+        }
+    }
+
+    return NULL;
+}
+
+void list_first(list_node_t *list)
+{
+    if (list) {
+        list->cur_index = 0;
+        list->cur_array_data = list->array_data.next;
+    }
+}
+
+void* list_next(list_node_t *list)
+{
+    if (list) {
+        list_arraynode_t *node = list->cur_array_data;
+        if (node) {
+            list->cur_array_data = list->cur_array_data->next;
+            list->cur_index++;
+
+            return node->data;
+        } else {
+            return NULL;
+        }
+    } else {
+        return NULL;
+    }
+}
+
+void* list_get(list_node_t *list, uint32 index)
+{
+    if (list) {
+        if (index >= list->size) {
+            return NULL;
+        }
+
+        list_arraynode_t *node = list->array_data.next;
+        if (node) {
+            uint32 i = 0;
+            while (node) {
+                if (i == index) {
+                    return node->data;
+                }
+                node = node->next;
+                i++;
+            }
+        }
+
+        return NULL;
+    } else {
+        return NULL;
+    }
+}
+
+static void list_treenext(list_node_t *list, list_treenode_t *tree_node)
+{
+    if (list && tree_node) {
+        list_treenode_t *left = tree_node->left;
+        list_treenode_t *right = tree_node->right;
+        list_treenext(list, left);
+
+        list_arraynode_t *array_node = tree_node->data;
+        while (array_node) {
+            list->cur_array_data->next = array_node;
+            list->cur_array_data = array_node;
+            array_node = array_node->next;
+        }
+        list_treenext(list, right);
+    }
+}
+
+static void list_treefree(list_node_t *list, list_treenode_t *tree_node)
+{
+    if (list && tree_node) {
+        list_treefree(list, tree_node->left);
+        list_treefree(list, tree_node->right);
+
+        if (&(list->tree_data) != tree_node) {
+            free(tree_node);
+            tree_node = NULL;
+        }
+    }
+}
+
+void list_sort(list_node_t *list, list_sort_func sort_func)
+{
+    if (list && sort_func) {
+        list->sort_func = sort_func;
+        list_arraynode_t *node = list->array_data.next; // First node
+        if (node) {
+            uint32 i = 0;
+            list_arraynode_t **temp = (list_arraynode_t **) malloc(
+                    sizeof(list_arraynode_t*) * list->size);
+            list_arraynode_t *temp_node = node;
+            while (node) {
+                temp[i] = node;
+                temp_node = node;
+                node = node->next;
+                temp_node->next = NULL;
+                i++;
+            }
+
+            for (i = 0; i < list->size; i++) {
+                list_treeadd(list, &(list->tree_data), temp[i]);
+            }
+
+            /*while (node) {
+             list_treeadd(list, &(list->tree_data), node);
+             node = node->next;
+             }*/
+
+            // Sort complete.
+            list->cur_array_data = &(list->array_data);
+            list_treenext(list, &(list->tree_data));
+
+            list_treefree(list, &(list->tree_data));
+            free(temp);
+        }
+    }
+}
+
+void list_free(list_node_t *list)
+{
+    if (list) {
+        list_arraynode_t *node = &(list->array_data); // Head node
+        list_arraynode_t *node_temp = NULL;
+        while (node->next) {
+            node_temp = node->next;
+            node->next = node->next->next;
+
+            if (list->free_func) {
+                list->free_func(node_temp->data);
+            } else {
+                free(node_temp->data);
+            }
+            free(node_temp);
+        }
+        free(list);
+    }
+}
+
+void list_clear(list_node_t *list)
+{
+    if (list) {
+        list_arraynode_t *node = &(list->array_data); // Head node
+        list_arraynode_t *node_temp = NULL;
+        while (node->next) {
+            node_temp = node->next;
+            node->next = node->next->next;
+
+            if (list->free_func) {
+                list->free_func(node_temp->data);
+            }
+            free(node_temp);
+        }
+        list_free_func free_func = list->free_func;
+        memset(list, 0x0, sizeof(list_node_t));
+        list->size = 0;
+        list->cur_index = 0;
+        list->cur_array_data = NULL;
+        list->sort_func = NULL;
+        list->free_func = free_func;
+    }
+}
diff --git a/mbtk/mbtk_lib/src/mbtk_log.c b/mbtk/mbtk_lib/src/mbtk_log.c
new file mode 100755
index 0000000..54c107b
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_log.c
@@ -0,0 +1,182 @@
+#include <stdio.h>
+#include <include/log.h>
+#include <sys/un.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <sys/prctl.h>
+#include <time.h>
+#include <string.h>
+#include <sys/time.h>
+#include <syslog.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "mbtk_type.h"
+#include "mbtk_log.h"
+
+#define LOG_VERBOSE 8
+
+static int tlog_fd = -1;
+static int syslog_radio_enable = 0;
+static FILE* logfile = NULL;
+static int signal_fd = -1;
+
+/**
+ * @brief      mbtk_log_init
+ *
+ * @details    设置Log输出方式
+ * @param
+ *             path:
+ *                   不填参数(NULL) stdout : 命令行输
+ *                   "syslog":输出到syslog
+ *                   "radio":CatStudio
+ *                   文件路径:输出到自定义文件路径
+ *             tag : 自定义tag
+ *
+ *   example:
+ *       mbtk_log_init(NULL, "MBTK_RIL");
+ *       mbtk_log_init("syslog", "MBTK_RIL");
+ *       mbtk_log_init("radio", "MBTK_RIL");
+ *       mbtk_log_init("/tmp/log/test.log", "MBTK_RIL");
+ */
+void mbtk_log_init(char* path, char* tag)
+{
+    if (str_empty(path)) {
+        tlog_fd = STDOUT_FILENO;
+    } else if (0 == memcmp(path, "syslog", 6)) {
+        openlog(tag, LOG_PID, LOG_USER);
+        syslog_radio_enable = 1;
+    } else if (0 == memcmp(path, "radio", 5)) {
+        if (tag && strlen(tag) > 0) {
+            set_service_log_tag(tag);
+        } else {
+            set_service_log_tag("MBTK");
+        }
+        syslog_radio_enable = 2;
+    } else if (path) {
+        tlog_fd = open(path, O_CREAT | O_WRONLY | O_APPEND, 0600);
+        if (tlog_fd < 0) {
+            fprintf(stderr, "failed to open %s: %s\n", path, strerror(errno));
+            exit(-1);
+        }
+    }
+}
+
+/* Control the log output */
+void mbtk_log(int level, const char* format, ...)
+{
+    char buf[1024] = {0};
+    va_list ap;
+    struct timeval log_time;
+    int length = 0;
+
+    va_start(ap, format);
+    length = vsnprintf(buf, sizeof(buf), format, ap);
+    if (length < 0 || 0 == length) {
+        return -1;
+    }
+
+    if (1 == syslog_radio_enable) {
+        syslog(level, "%s", buf);
+    } else if (2 == syslog_radio_enable) {
+        __android_log_printf(LOG_ID_RADIO, level, "%s", buf);
+    } else if (-1 != tlog_fd) {
+        char tmp[50] = {0};
+        gettimeofday(&log_time, NULL);
+        struct tm* tm_t = localtime(&(log_time.tv_sec));
+        strftime(tmp, 50, "%F %T", tm_t);
+        snprintf(tmp + strlen(tmp), sizeof(tmp) - strlen(tmp), " %d<%d>:", (int)(log_time.tv_usec / 1000), level);
+        write(tlog_fd, tmp, strlen(tmp));
+        write(tlog_fd, buf, length);
+        if (buf[length - 1] != '\n') {
+            write(tlog_fd, "\n", 1);
+        }
+        if (tlog_fd > 2) {
+            fsync(tlog_fd);
+        }
+    }
+
+    va_end(ap);
+}
+
+void log_hex(const char* tag, const void* data, int data_len)
+{
+    char buffer[60];
+    char str[17];
+    int size = 0;
+    uint8* ptr = (uint8*)data;
+    int i, j;
+    memset(buffer, 0x0, 60);
+    memset(str, 0x0, 17);
+    LOGI("%s,Length-%d:", tag, data_len);
+    LOGI("       0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f");
+    size += snprintf(buffer, 60, "%04x| ", 0);
+    for (i = 0; i < data_len; i++) {
+        size += snprintf(buffer + size, 60 - size, "%02x ", ptr[i]);
+        if (isprint(ptr[i])) {
+            str[i % 16] = ptr[i];
+        } else {
+            str[i % 16] = '.';
+        }
+        if ((i + 1) % 16 == 0 || i == data_len - 1) {
+            for (j = size; j < 54; j++) {
+                buffer[j] = ' ';
+            }
+            LOGI("%s| %s", buffer, str);
+
+            memset(buffer, 0x0, 60);
+            memset(str, 0x0, 17);
+            size = 0;
+            size += snprintf(buffer, 60, "%04x| ", (i + 1) / 16);
+        }
+    }
+}
+
+#define _MOPEN_RILD_SOCKET "/tmp/logd_socket"
+
+int mbtk_signal_log(char *data)
+{
+    char buff[256];
+    int size = 0;
+    int ret = 0;
+    int i = 0;
+    static struct sockaddr_un srv_addr;
+
+    if(signal_fd < 0) {
+        if (access(_MOPEN_RILD_SOCKET, F_OK) == -1) {
+            LOGW("Service not running...");
+            return -1;
+        }
+
+        signal_fd = socket(PF_UNIX, SOCK_STREAM, 0);
+        if (signal_fd < 0) {
+            LOGE("cannot creat socket");
+            return -1;
+        }
+
+        srv_addr.sun_family = AF_UNIX;
+        strcpy(srv_addr.sun_path, _MOPEN_RILD_SOCKET);
+        ret = connect(signal_fd, (struct sockaddr*)&srv_addr, sizeof(srv_addr));
+        if (ret < 0) {
+            LOGE("cannot connect server, ret=%d, errno=%d", ret, errno);
+            close(signal_fd);
+            signal_fd = -1;
+            return -1;
+        }
+    }
+
+    memset(buff, 0, sizeof(buff));
+    snprintf(buff, sizeof(buff), "%s\n", data);
+    size = write(signal_fd, buff, sizeof(buff));
+    if (size < 0 || size == 0) {
+        LOGE("cannot write , ret=%d, errno=%d\n", ret, errno);
+        return 1;
+    }
+
+    // close(signal_fd);
+
+    return 0;
+}
diff --git a/mbtk/mbtk_lib/src/mbtk_map.c b/mbtk/mbtk_lib/src/mbtk_map.c
new file mode 100755
index 0000000..061a538
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_map.c
@@ -0,0 +1,242 @@
+/*
+ * mbtk_map.c
+ *
+ *  Created on: Aug 18, 2020
+ *      Author: lb
+ */
+#include <stdlib.h>
+#include <string.h>
+#include "mbtk_map.h"
+
+map_node_t* map_create(uint32 capacity, map_free_func free_func)
+{
+    if (capacity > 0) {
+        map_node_t *map = (map_node_t*) malloc(sizeof(map_node_t));
+        if (map) {
+            memset(map, 0x0, sizeof(map_node_t));
+            map->size = 0;
+            map->cur_index = 0;
+            map->cur_data = NULL;
+            map->capacity = (uint32) (capacity * 0.75f); // Default 0.75
+            if(map->capacity < 1) {
+                free(map);
+                return NULL;
+            }
+            map->free_func = free_func;
+            map->map_array = (map_data_t**) malloc(
+                    sizeof(map_data_t*) * map->capacity);
+            uint32 i = 0;
+            while (i < map->capacity) {
+                map->map_array[i] = NULL;
+                i++;
+            }
+            return map;
+        }
+    }
+
+    return NULL;
+}
+
+uint32 map_size(map_node_t* map)
+{
+    if (map) {
+        return map->size;
+    }
+
+    return 0;
+}
+
+uint32 map_hash(const char* key, uint32 capacity)
+{
+    uint32 hash = 0;
+    const uint8 *ptr = (const uint8*) key;
+    while (*ptr) {
+        hash = 31 * hash + *ptr;
+        ptr++;
+    }
+
+    return hash % capacity;
+}
+
+void map_put(map_node_t* map, const char* key, void* value)
+{
+    if (map && key && strlen(key) > 0 && value) {
+        uint32 index = map_hash(key, map->capacity);
+
+        map_data_t *ptr = map->map_array[index];
+        if (!ptr) { // Add to first position.
+            ptr = (map_data_t*) malloc(sizeof(map_data_t));
+            ptr->key = strdup(key);
+            ptr->value = value;
+            ptr->next = NULL;
+
+            map->size++;
+            map->map_array[index] = ptr;
+        } else { // This position has one item at least.
+            if (!memcmp(ptr->key, key, strlen(key))) { // Has this item,will change.
+                if (map->free_func) {
+                    map->free_func(ptr->value);
+                }
+
+                ptr->value = value;
+            } else {
+                while (ptr->next) {
+                    if (!memcmp(ptr->next->key, key, strlen(key))) // Has this item,will change.
+                        break;
+                    ptr = ptr->next;
+                }
+
+                if (!ptr->next) { // Add new item.
+                    ptr->next = (map_data_t*) malloc(sizeof(map_data_t));
+
+                    ptr->next->key = strdup(key);
+                    ptr->next->value = value;
+                    ptr->next->next = NULL;
+                    map->size++;
+                } else { // Change item.
+                    if (map->free_func) {
+                        map->free_func(ptr->next->value);
+                    }
+
+                    ptr->next->value = value;
+                }
+            }
+        }
+    }
+}
+
+void* map_get(map_node_t* map, char* key)
+{
+    if (map && key && strlen(key) > 0) {
+        uint32 index = map_hash(key, map->capacity);
+        map_data_t *ptr = map->map_array[index];
+        while (ptr) {
+            if (ptr->key && !memcmp(ptr->key, key, strlen(key))) {
+                return ptr->value;
+            }
+            ptr = ptr->next;
+        }
+    }
+    return NULL;
+}
+
+void* map_remove(map_node_t* map, char* key)
+{
+    if (map && key && strlen(key) > 0) {
+        uint32 index = map_hash(key, map->capacity);
+        map_data_t *ptr = map->map_array[index];
+        if (!ptr) { // No items.
+            return NULL;
+        }
+
+        if (!memcmp(ptr->key, key, strlen(key))) { // Is first item
+            map_data_t *temp = ptr;
+            void *result = temp->value;
+            map->map_array[index] = ptr->next;
+            free(temp);
+            map->size--;
+
+            return result;
+        } else {
+            while (ptr->next) {
+                if (!memcmp(ptr->next->key, key, strlen(key))) {
+                    map_data_t *temp = ptr->next;
+                    void *result = temp->value;
+                    ptr->next = temp->next;
+                    free(temp);
+                    map->size--;
+
+                    return result;
+                }
+                ptr = ptr->next;
+            }
+        }
+    }
+    return NULL;
+}
+
+void map_first(map_node_t *map)
+{
+    if (map) {
+        map->cur_index = 0;
+        map->cur_data = map->map_array[0];
+    }
+}
+
+void* map_next(map_node_t *map)
+{
+    if (map) {
+        while (1) {
+            if (map->cur_data) {
+                void *result = map->cur_data;
+                map->cur_data = map->cur_data->next;
+                return result;
+            } else {
+                map->cur_index++;
+                if (map->cur_index < map->capacity) {
+                    map->cur_data = map->map_array[map->cur_index];
+                } else { // Finish
+                    return NULL;
+                }
+            }
+        }
+    } else {
+        return NULL;
+    }
+}
+
+void map_clear(map_node_t* map)
+{
+    if (map) {
+        uint32 i = 0;
+        map_data_t *ptr = NULL;
+        while (i < map->capacity) {
+            ptr = map->map_array[i];
+            while (ptr) {
+                map->map_array[i] = ptr->next;
+
+                free(ptr->key);
+                if (map->free_func) {
+                    map->free_func(ptr->value);
+                }
+                free(ptr);
+
+                ptr = map->map_array[i];
+            }
+            i++;
+        }
+
+        map->size = 0;
+        map->cur_index = 0;
+        map->cur_data = NULL;
+        memset(map->map_array, 0x0,
+                sizeof(sizeof(map_data_t*) * map->capacity));
+    }
+}
+
+void map_free(map_node_t* map)
+{
+    if (map) {
+        uint32 i = 0;
+        map_data_t *ptr = NULL;
+        while (i < map->capacity) {
+            ptr = map->map_array[i];
+            while (ptr) {
+                map->map_array[i] = ptr->next;
+
+                free(ptr->key);
+                if (map->free_func) {
+                    map->free_func(ptr->value);
+                }
+                free(ptr);
+
+                ptr = map->map_array[i];
+            }
+            i++;
+        }
+
+        free(map->map_array);
+        free(map);
+    }
+}
+
diff --git a/mbtk/mbtk_lib/src/mbtk_mp3_to_wav.c b/mbtk/mbtk_lib/src/mbtk_mp3_to_wav.c
new file mode 100755
index 0000000..fe13e38
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_mp3_to_wav.c
@@ -0,0 +1,372 @@
+#include "audio_if_types.h"
+#include "audio_if_ubus.h"
+#include "audio_if_parameter.h"
+#include "audio_if.h"
+#include "audio_if_api.h"
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <libubox/blobmsg_json.h>
+#include "libubus.h"
+#include <string.h>
+//#include "audio_if_audio_hw_mrvl.h"
+#include "utlEventHandler.h"
+#include "udev_monitor.h"
+#include "audio_hw_mrvl.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <cutils/str_parms.h>
+#include "vcm.h"
+#include <libavutil/avutil.h>
+#include <libavutil/attributes.h>
+#include <libavutil/opt.h>
+#include <libavutil/mathematics.h>
+#include <libavutil/imgutils.h>
+#include <libavutil/samplefmt.h>
+#include <libavutil/timestamp.h>
+#include <libavformat/avformat.h>
+#include <libavcodec/avcodec.h>
+#include <libswscale/swscale.h>
+#include <libavutil/mathematics.h>
+#include <libswresample/swresample.h>
+//#include <libavutil/channel_layout.h>
+#include <libavutil/common.h>
+#include <libavformat/avio.h>
+#include <libavutil/file.h>
+#include <libswresample/swresample.h>
+#include <libavfilter/avfilter.h>
+
+
+extern int mbtk_audio_play_stream_old(void *dev_hdl, const void *pData, int len);
+
+#define AVCODEC_MAX_AUDIO_FRAME_SIZE 192000
+
+/* the size may be modified because of different decoded sample number. */
+#define MGR_BUFFER_SIZE 1024
+
+//#define DEBUG_FFMPEG_PCM_STREAM
+
+/* needed by PCM stream */
+extern void* audio_hal_install(void);
+extern void audio_hal_uninstall(void);
+extern void configure_vcm(unsigned int data[]);
+extern void set_pcm_config(int role, int rate);
+extern int mrvl_hw_dev_config_pcm(struct audio_hw_device *dev, unsigned int pcm);
+
+
+typedef struct {
+    int videoindex;
+    int sndindex;
+    AVFormatContext* pFormatCtx;
+    AVCodecContext* sndCodecCtx;
+    AVCodec* sndCodec;
+    SwrContext *swr_ctx;
+    DECLARE_ALIGNED(16,uint8_t,audio_buf) [AVCODEC_MAX_AUDIO_FRAME_SIZE * 2];
+    //DECLARE_ALIGNED(16,uint8_t,audio_buf) [AVCODEC_MAX_AUDIO_FRAME_SIZE];
+}AudioState;
+
+/******************************************************************************
+ *  Globals
+ ******************************************************************************/
+/* needed by FFMPEG */
+extern int64_t av_get_default_channel_layout(int nb_channels);
+extern AVFrame *avcodec_alloc_frame(void);
+
+
+static int my_init_ffmpeg1(AudioState* is, char* filepath)
+{
+    is->sndindex = -1;
+
+    if(NULL == filepath)
+    {
+        printf("input file is NULL");
+        return -1;
+    }
+
+    avcodec_register_all();
+    avfilter_register_all();
+    av_register_all();
+
+    is->pFormatCtx = avformat_alloc_context();
+
+    if(avformat_open_input(&is->pFormatCtx, filepath, NULL, NULL)!=0)
+        return -1;
+
+    if(avformat_find_stream_info(is->pFormatCtx, NULL)<0)
+        return -1;
+
+    av_dump_format(is->pFormatCtx,0, 0, 0);
+
+    is->videoindex = av_find_best_stream(is->pFormatCtx, AVMEDIA_TYPE_VIDEO, is->videoindex, -1, NULL, 0); 
+    is->sndindex = av_find_best_stream(is->pFormatCtx, AVMEDIA_TYPE_AUDIO,is->sndindex, is->videoindex, NULL, 0);
+
+    printf("videoindex=%d, sndindex=%d", is->videoindex, is->sndindex);
+
+    if(is->sndindex != -1)
+    {
+        is->sndCodecCtx = is->pFormatCtx->streams[is->sndindex]->codec;
+        is->sndCodec = avcodec_find_decoder(is->sndCodecCtx->codec_id);
+
+        if(is->sndCodec == NULL)
+        {
+            printf("Codec not found");
+            return -1;
+        }
+
+        if(avcodec_open2(is->sndCodecCtx, is->sndCodec, NULL) < 0)
+            return -1;
+    }
+
+    return 0;
+} 
+
+
+int mbtk_audio_mp3_to_wav(const char *wavpath, char *mp3path)
+{
+    int in_channel_nb = 0;
+    int ret, rc;
+    int fd;
+    int size;
+    int len;
+    int file_data_size = 0;
+    int converted_nb_samples = 0;
+    int got_frame;
+    int index =0;
+    int i =0;
+    AVPacket *packet = av_mallocz(sizeof(AVPacket));
+    //AVFrame *frame = av_frame_alloc();
+    AVFrame *frame = avcodec_alloc_frame();
+    AudioState* is = (AudioState*) av_mallocz(sizeof(AudioState));
+    uint8_t *out[] = { is->audio_buf };
+    //char buf[MGR_BUFFER_SIZE];
+
+    enum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16; //输出采样格式16bit PCM
+
+    int out_channel_nb = 0;
+    struct wav_header header;
+
+
+#if 1
+    int sample_rate = 8000;
+    //int sample_rate = 16000;
+    //int sample_rate = 44100;
+
+    int out_sample_rate = sample_rate; //输出采样率
+    uint64_t out_ch_layout = AV_CH_LAYOUT_MONO; //输出的声道布局
+    //uint64_t out_ch_layout = AV_CH_LAYOUT_STEREO; //输出的声道布局
+
+#endif
+
+    fd = open(wavpath, O_WRONLY | O_CREAT);  //wavpath
+
+    //leave enough room for header
+    lseek(fd, sizeof(struct wav_header), SEEK_SET);
+
+
+    //第1步初始化ffmpeg,并用ffmpeg解码,最后转为pcm格式
+    if( (ret = my_init_ffmpeg1(is, mp3path)) != 0)            //1.1 初始化ffmpeg
+    {
+        printf("my_init_ffmpeg error");
+        return -1;
+    }
+
+    while(av_read_frame(is->pFormatCtx, packet) >= 0)    //1.2 循环读取mp3文件中的数据帧
+    {
+        if(packet->stream_index != is->sndindex)
+            continue;
+
+        if((ret = avcodec_decode_audio4(is->sndCodecCtx, frame, &got_frame, packet)) < 0) //1.3 解码数据帧
+        {
+            printf("file eof");
+            break;
+        }
+
+        if(got_frame <= 0) /* No data yet, get more frames */
+            continue;
+
+        //1.4下面将ffmpeg解码后的数据帧转为我们需要的数据(关于"需要的数据"下面有解释)
+        if(NULL==is->swr_ctx)
+        {
+            if(is->swr_ctx != NULL)
+                swr_free(&is->swr_ctx);
+
+            //输入的声道个数
+            in_channel_nb = av_get_channel_layout_nb_channels(frame->channel_layout);
+
+            //输出的声道个数
+            out_channel_nb = av_get_channel_layout_nb_channels(out_ch_layout);
+
+            printf("frame: input channel: channnels=%d, format=%d, sample_rate=%d, nb_samples=%d.\n",
+                    in_channel_nb, frame->format, frame->sample_rate, frame->nb_samples);
+            printf("frame: out channel: channels=%d, format=%d, sample_rate=%d.\n", out_channel_nb, out_sample_fmt, out_sample_rate);
+
+            is->swr_ctx = swr_alloc_set_opts(NULL, out_ch_layout, out_sample_fmt, out_sample_rate, av_get_default_channel_layout(in_channel_nb), frame->format, frame->sample_rate, 0, NULL);
+
+
+            if(is->swr_ctx == NULL)
+            {
+                printf("swr_ctx == NULL");
+            }
+
+            swr_init(is->swr_ctx);
+        }
+
+        converted_nb_samples = swr_convert(is->swr_ctx, out, AVCODEC_MAX_AUDIO_FRAME_SIZE * 2, (const uint8_t **)frame->data, frame->nb_samples);
+
+        file_data_size += converted_nb_samples;
+
+        //1.5 数据格式转换完成后就写到文件中
+        //sample bit is 16bits = 2Bytes
+        size = converted_nb_samples*out_channel_nb*2;
+        write(fd, (char *)(is->audio_buf), size);
+
+    }
+
+    //sample bit is 16bits = 2Bytes
+    file_data_size *= (out_channel_nb*2);//(numSamples * wavFMT->numChannels * wavFMT->uiBitsPerSample) / 8
+
+    printf("file_data_size=%d, nb_samples=%d, converted_nb_samples=%d.\n", file_data_size, frame->nb_samples, converted_nb_samples);
+
+    //第2步添加上wav的头
+    //write header now all information is known
+    header.riff_id = ID_RIFF;
+    header.riff_sz = 0;
+    header.riff_fmt = ID_WAVE;
+    header.fmt_id = ID_FMT;
+    header.fmt_sz = 16;
+    header.audio_format = 1;        //FORMAT_PCM;
+    header.num_channels = out_channel_nb;        //Modem ONLY support mono recording
+    header.sample_rate = sample_rate;     //44100, 8000, 16000
+    header.bits_per_sample = 16;    //PCM_SAMPLEBITS_S16_LE;
+    header.byte_rate = (header.bits_per_sample / 8) * header.num_channels * header.sample_rate;
+    header.block_align = header.num_channels * (header.bits_per_sample / 8);
+    header.data_id = ID_DATA;
+
+    header.data_sz = file_data_size;
+    header.riff_sz = header.data_sz + sizeof(header) - 8;
+
+    lseek(fd, 0, SEEK_SET);
+    write(fd, &header, sizeof(struct wav_header));
+    printf("%s: sample_rate = %d, num_channels = %d!\n", __FUNCTION__,  header.sample_rate, header.num_channels);
+
+    av_free_packet(packet);
+    av_free(frame);
+    avcodec_close(is->sndCodecCtx);
+    avformat_close_input(&is->pFormatCtx);
+
+    close(fd);
+
+    return 0;
+}
+
+
+int mbtk_audio_mp3_to_play(char *mp3path, int hdl, int sample_rate)
+{
+    int in_channel_nb = 0;
+    int ret, rc;
+    int size;
+    int len;
+    int file_data_size = 0;
+    int converted_nb_samples = 0;
+    int got_frame;
+    int index =0;
+    int i =0;
+    AVPacket *packet = av_mallocz(sizeof(AVPacket));
+    //AVFrame *frame = av_frame_alloc();
+    AVFrame *frame = avcodec_alloc_frame();
+    AudioState* is = (AudioState*) av_mallocz(sizeof(AudioState));
+    uint8_t *out[] = { is->audio_buf };
+    //char buf[MGR_BUFFER_SIZE];
+
+    enum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16; //输出采样格式16bit PCM
+
+    int out_channel_nb = 0;
+
+
+    if(sample_rate)
+    {
+        sample_rate = 16000;
+    }
+    else
+    {
+        sample_rate = 8000;
+    }
+//    int sample_rate = 8000;
+    //int sample_rate = 16000;
+    //int sample_rate = 44100;
+
+    int out_sample_rate = sample_rate; //输出采样率
+    uint64_t out_ch_layout = AV_CH_LAYOUT_MONO; //输出的声道布局
+    //uint64_t out_ch_layout = AV_CH_LAYOUT_STEREO; //输出的声道布局
+
+    //第1步初始化ffmpeg,并用ffmpeg解码,最后转为pcm格式
+    if( (ret = my_init_ffmpeg1(is, mp3path)) != 0)            //1.1 初始化ffmpeg
+    {
+        printf("my_init_ffmpeg error");
+        return -1;
+    }
+
+    while(av_read_frame(is->pFormatCtx, packet) >= 0)    //1.2 循环读取mp3文件中的数据帧
+    {
+        if(packet->stream_index != is->sndindex)
+            continue;
+
+        if((ret = avcodec_decode_audio4(is->sndCodecCtx, frame, &got_frame, packet)) < 0) //1.3 解码数据帧
+        {
+            printf("file eof");
+            break;
+        }
+
+        if(got_frame <= 0) /* No data yet, get more frames */
+            continue;
+
+        //1.4下面将ffmpeg解码后的数据帧转为我们需要的数据(关于"需要的数据"下面有解释)
+        if(NULL==is->swr_ctx)
+        {
+            if(is->swr_ctx != NULL)
+                swr_free(&is->swr_ctx);
+
+            //输入的声道个数
+            in_channel_nb = av_get_channel_layout_nb_channels(frame->channel_layout);
+
+            //输出的声道个数
+            out_channel_nb = av_get_channel_layout_nb_channels(out_ch_layout);
+
+            printf("frame: input channel: channnels=%d, format=%d, sample_rate=%d, nb_samples=%d.\n",
+                    in_channel_nb, frame->format, frame->sample_rate, frame->nb_samples);
+            printf("frame: out channel: channels=%d, format=%d, sample_rate=%d.\n", out_channel_nb, out_sample_fmt, out_sample_rate);
+
+            is->swr_ctx = swr_alloc_set_opts(NULL, out_ch_layout, out_sample_fmt, out_sample_rate, av_get_default_channel_layout(in_channel_nb), frame->format, frame->sample_rate, 0, NULL);
+
+
+            if(is->swr_ctx == NULL)
+            {
+                printf("swr_ctx == NULL");
+            }
+
+            swr_init(is->swr_ctx);
+        }
+
+        converted_nb_samples = swr_convert(is->swr_ctx, out, AVCODEC_MAX_AUDIO_FRAME_SIZE * 2, (const uint8_t **)frame->data, frame->nb_samples);
+
+        file_data_size += converted_nb_samples;
+
+        //1.5 数据格式转换完成后就写到文件中
+        //sample bit is 16bits = 2Bytes
+        size = converted_nb_samples*out_channel_nb*2;
+
+        mbtk_audio_play_stream_old((void *)hdl, (char *)(is->audio_buf), size);
+
+    }
+
+
+    av_free_packet(packet);
+    av_free(frame);
+    avcodec_close(is->sndCodecCtx);
+    avformat_close_input(&is->pFormatCtx);
+
+    return 0;
+}
+
+
diff --git a/mbtk/mbtk_lib/src/mbtk_net_control.c b/mbtk/mbtk_lib/src/mbtk_net_control.c
new file mode 100755
index 0000000..ff3af08
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_net_control.c
@@ -0,0 +1,535 @@
+/*************************************************************
+Description:
+    C file for network control.
+Author:
+    LiuBin
+Date:
+    2019/7/24 17:13:06
+*************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/un.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/route.h>
+#include <cutils/properties.h>
+#include <telephony/ril.h>
+
+#include "mbtk_type.h"
+#include "mbtk_net_control.h"
+#include "mbtk_task.h"
+#include "mbtk_utils.h"
+#include "mbtk_str.h"
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+#define LOG_TAG "mbtk_net_control"
+#include "mbtk_log.h"
+
+/*************************************************************
+    Constants and Macros
+*************************************************************/
+#define NET_CONTROL_BUF_SIZE 1024
+#ifndef INFTIM
+#define INFTIM          (-1)    /* infinite poll timeout */
+#endif
+#define MBTK_NET_PING_HOST "223.5.5.5"
+#define MBTK_NET_PING_IP "180.97.33.107" // IP for www.baidu.com
+
+// #define MBTK_NET_MONITOR_SUPPORT
+
+/*************************************************************
+    Variables:local
+*************************************************************/
+static char net_interface[20];
+static char net_ip[20];
+static mbtk_net_state_t net_state = MBTK_NET_STATE_OFF;
+static bool net_control_thread_running = FALSE;
+
+#ifdef MBTK_NET_MONITOR_SUPPORT
+static pthread_t net_control_thread_id = -1;
+static int net_control_fd = -1;
+#endif
+
+/*************************************************************
+    Variables:public
+*************************************************************/
+
+
+/*************************************************************
+    Local Function Declaration
+*************************************************************/
+
+
+/*************************************************************
+    Local Function Definitions
+*************************************************************/
+// Test network connected?
+// ping www.baidu.com
+static bool net_connected(const char *inf)
+{
+    char cmd[100];
+    char cmd_rsp[100];
+
+    // IP get now, ping www.baidu.com
+    memset(cmd,0,100);
+    snprintf(cmd,100,
+             "ping -I %s -c1 -s0 -w1000 %s | grep \"8 bytes from \"",
+             inf,
+             MBTK_NET_PING_HOST);
+    if(!mbtk_cmd_line(cmd,cmd_rsp,100))
+    {
+        LOGE("ping www.baidu.com cmd error.");
+        return FALSE;
+    }
+
+    LOGI("cmd_rsp:%s",cmd_rsp);
+    // ping www.baidu.com success.
+    if(str_startwith(cmd_rsp, "8 bytes from "))
+    {
+        return TRUE;
+    }
+#if 0
+    else if(str_contains(cmd_rsp, "unknown host"))
+    {
+        // DNS error,ping IP angin.
+        memset(cmd,0,100);
+        snprintf(cmd,100,
+                 "ping -I %s -c1 -s0 -w1000 %s | grep \"8 bytes from \"",
+                 inf,
+                 MBTK_NET_PING_IP);
+        if(!mbtk_cmd_line(cmd,cmd_rsp,100))
+        {
+            LOGW("ping www.baidu.com IP cmd error.");
+            return FALSE;
+        }
+
+        if(str_startwith(cmd_rsp, "8 bytes from "))
+        {
+            return TRUE;
+        }
+        else
+        {
+            LOGW("Network unconnected.(ping baidu IP fail)");
+            return FALSE;
+        }
+    }
+#endif
+    else
+    {
+        LOGW("Network unconnected.(ping baidu host fail)");
+        return FALSE;
+    }
+
+    LOGW("ifconfig cmd fail.");
+    return FALSE;
+}
+
+#ifdef MBTK_NET_MONITOR_SUPPORT
+static int net_control_netlink_init()
+{
+    struct sockaddr_nl sa;
+    int len = 2048;
+
+    net_control_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+    if(net_control_fd < 0)
+    {
+        LOGE("socket() fail.[%d]",errno);
+        return -1;
+    }
+
+    if(setsockopt(net_control_fd,
+                  SOL_SOCKET, SO_RCVBUF, &len, sizeof(len)) < 0)
+    {
+        LOGE("setsockopt() fail.[%d]",errno);
+        return -1;
+    }
+
+    bzero(&sa, sizeof(sa));
+    sa.nl_family = AF_NETLINK;
+    sa.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE /*| RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE*/;
+    if(bind(net_control_fd,
+            (struct sockaddr *) &sa, sizeof(sa)) < 0)
+    {
+        LOGE("bind() fail.[%d]",errno);
+        return -1;
+    }
+
+    return 0;
+}
+
+static void net_control_if_change(struct nlmsghdr *nh)
+{
+    struct rtattr *tb[IFLA_MAX + 1];
+    struct ifinfomsg *ifinfo;
+    bzero(tb, sizeof(tb));
+    if(nh == NULL)
+    {
+        LOGE("mbtk_net_if_change() nh == NULL");
+        return;
+    }
+
+    ifinfo = NLMSG_DATA(nh);
+    if(ifinfo == NULL)
+    {
+        LOGE("mbtk_net_if_change() ifinfo == NULL");
+        return;
+    }
+
+    LOGD("nlmsghdr:%d,%d,%d,%d,%d\n",nh->nlmsg_len,
+         nh->nlmsg_type,
+         nh->nlmsg_flags,
+         nh->nlmsg_seq,
+         nh->nlmsg_pid);
+
+    LOGD("ifinfomsg:%d,%d,%d,%d,%d,%d\n",ifinfo->ifi_family,
+         ifinfo->__ifi_pad,
+         ifinfo->ifi_type,
+         ifinfo->ifi_index,
+         ifinfo->ifi_flags,
+         ifinfo->ifi_change);
+
+    if((ifinfo->ifi_flags & IFF_RUNNING)
+       && (ifinfo->ifi_flags & IFF_LOWER_UP))
+    {
+        LOGD("Wired inserted.");
+    }
+    else
+    {
+        LOGD("Wired not insert.");
+    }
+}
+
+static void net_control_addr_change(struct nlmsghdr *nh)
+{
+    if(nh == NULL)
+    {
+        LOGE("mbtk_net_if_change() nh == NULL");
+        return;
+    }
+
+    if(nh->nlmsg_type==RTM_NEWADDR)
+    {
+        LOGD("New addr...");
+    }
+    else
+    {
+        LOGD("Del addr...");
+    }
+}
+
+static void* net_control_monitor_run(void *arg)
+{
+    LOGI("net_control_monitor_run start.");
+    if(net_control_netlink_init() < 0)
+    {
+        LOGE("mbtk_net_monitor_run() fail.");
+        return ((void*)0);
+    }
+
+    fd_set rd_set;
+    struct timeval timeout;
+    int select_r;
+    int read_r;
+    struct sockaddr_nl sa;
+    struct nlmsghdr *nh;
+    char buff[NET_CONTROL_BUF_SIZE];
+
+    while (net_control_thread_running)
+    {
+        FD_ZERO(&rd_set);
+        FD_SET(net_control_fd, &rd_set);
+        timeout.tv_sec = 5;
+        timeout.tv_usec = 0;
+        select_r = select(net_control_fd + 1, &rd_set, NULL, NULL, &timeout);
+        if (select_r < 0)
+        {
+            perror("select");
+        }
+        else if (select_r > 0)
+        {
+            if (FD_ISSET(net_control_fd, &rd_set))
+            {
+                read_r = read(net_control_fd, buff, NET_CONTROL_BUF_SIZE);
+                LOGI("Net change:read len:%d",read_r);
+
+                int i;
+                for(i = 0; i < 32 && i < read_r; i++)
+                    LOGI("data:%x",buff[i]);
+
+                for (nh = (struct nlmsghdr *) buff; NLMSG_OK(nh, read_r); nh = NLMSG_NEXT(nh, read_r))
+                {
+                    LOGI("msg_type:%d",nh->nlmsg_type);
+                    switch (nh->nlmsg_type)
+                    {
+                        default:
+                            LOGI("nh->nlmsg_type = %d\n", nh->nlmsg_type);
+                            break;
+                        case NLMSG_DONE:
+                        case NLMSG_ERROR:
+                            break;
+                        case RTM_NEWLINK:
+                        case RTM_DELLINK:
+                            net_control_if_change(nh);
+                            break;
+                        case RTM_NEWADDR:
+                        case RTM_DELADDR:
+                            net_control_addr_change(nh);
+                            break;
+                        case RTM_NEWROUTE:
+                        case RTM_DELROUTE:
+                            //print_rtmsg(nh);
+                            break;
+                    }
+
+                }
+            }
+        }
+    }
+
+    LOGD("mbtk_net_monitor_run exist ...");
+
+    return ((void*)0);
+}
+
+static mbtk_task_info net_control_thread =
+{
+    .task_id = &net_control_thread_id,
+    .thread_run = net_control_monitor_run,
+    .args = NULL
+};
+#endif
+
+static int net_control_interface_init()
+{
+    // seth_ltex
+    int i = 0;
+    int size = 0;
+    char result[NET_CONTROL_BUF_SIZE];
+    char cmd[100];
+    int index = 0;
+    while(i <= 7)
+    {
+        size = snprintf(cmd, 100,"ifconfig ccinet%d", i);
+        cmd[size] = '\0';
+        memset(result,0x0,NET_CONTROL_BUF_SIZE);
+        if(mbtk_cmd_line(cmd, result, NET_CONTROL_BUF_SIZE))
+        {
+            index = str_indexof(result,"inet addr:");
+            if(index > 0)
+            {
+                size = snprintf(net_interface, 20,"ccinet%d", i);
+                net_interface[size] = '\0';
+
+                memcpy(net_ip,result + index + 10,20);
+
+                char *ptr = net_ip;
+                while(*ptr && *ptr != ' ')
+                {
+                    ptr++;
+                }
+                *ptr = '\0';
+                break;
+            }
+        }
+        i++;
+    }
+
+    LOGI("Interface : %s, IP : %s",net_interface,net_ip);
+    if(index )
+    {
+        return 0;
+    }
+    else{
+        return -1;
+    }
+}
+
+static int net_control_init()
+{
+//    if(net_control_thread_running)
+//    {
+//        LOGD("Network control has inited.");
+//        return 0;
+//    }
+
+    memset(net_ip,0x0,20);
+    memset(net_interface,0x0,20);
+    if( net_control_interface_init())
+        return -1;
+    net_control_thread_running = TRUE;
+
+#ifdef MBTK_NET_MONITOR_SUPPORT
+    if(mbtk_task_start(&net_control_thread))
+    {
+        LOGE("Create thread fail.");
+        net_control_thread_id = -1;
+        net_control_thread_running = FALSE;
+        return -1;
+    }
+#endif
+    LOGI("net_control_init() success.");
+    return 0;
+}
+
+static int net_control_state_change(bool enable)
+{
+    int size;
+    char result[NET_CONTROL_BUF_SIZE];
+    char cmd[100];
+    if(enable)
+    {
+        // ifconfig seth_lte1 up
+        // ip route add default via 10.94.251.205 dev seth_lte1
+        size = snprintf(cmd,100,"ifconfig %s up",net_interface);
+        cmd[size] = '\0';
+
+        if(mbtk_cmd_line(cmd, result, NET_CONTROL_BUF_SIZE))
+        {
+            size = snprintf(cmd,100,"ip route add default via %s dev %s",net_ip,net_interface);
+            cmd[size] = '\0';
+
+            if(mbtk_cmd_line(cmd, result, NET_CONTROL_BUF_SIZE))
+            {
+                net_state = MBTK_NET_STATE_CONN;
+                return 0;
+            }
+        }
+    }
+    else
+    {
+        // ifconfig seth_lte1 down
+        size = snprintf(cmd,100,"ifconfig %s down",net_interface);
+        cmd[size] = '\0';
+
+        if(mbtk_cmd_line(cmd, result, NET_CONTROL_BUF_SIZE))
+        {
+            net_state = MBTK_NET_STATE_OFF;
+            return 0;
+        }
+    }
+
+    return -1;
+}
+
+/*************************************************************
+    Public Function Definitions
+*************************************************************/
+/*=============================================
+FUNCTION
+    mbtk_net_state_get()
+
+DESCRIPTION
+    Get network state.
+
+DEPENDENCIES
+    None
+
+PARAMETERS
+    None
+
+RETURN VALUE
+    Current network state.
+
+SIDE EFFECTS
+    None
+=============================================*/
+mbtk_net_state_t mbtk_net_state_get()
+{
+    net_control_init();
+
+    net_state = MBTK_NET_STATE_OFF;
+    if(strlen(net_ip) > 0)
+    {
+        if(net_connected(net_interface))
+            net_state = MBTK_NET_STATE_CONN;
+    }
+
+    LOGI("[GET]Net state:%d",net_state);
+
+    if(net_state == MBTK_NET_STATE_CONN)
+    {
+        char value[PROPERTY_VALUE_MAX] = {0};
+        if (property_get("persist.mbtk.netstate", value, "0,0") > 0 && strcmp(value,"0,0")) {
+            int regStatus = 0, gprsState = 0;
+            char *ptr = value;
+            regStatus = atoi(ptr);
+            if((ptr = strstr(ptr, ",")))
+            {
+                gprsState = atoi(ptr + 1);
+            }
+
+            LOGI("regStatus : %d, gprsState : %d", regStatus, gprsState);
+            if(regStatus != 1 && regStatus != 5)   // Not Home/Roaming Network.
+            {
+                net_state = MBTK_NET_STATE_CONN_UNKNOWN;
+            }
+            else
+            {
+                if (gprsState == RADIO_TECH_LTE || gprsState == RADIO_TECH_LTEP)
+                {
+                    net_state = MBTK_NET_STATE_CONN_4G;
+                }
+                else if ((gprsState == RADIO_TECH_GPRS) || (gprsState == RADIO_TECH_EDGE) || (gprsState == RADIO_TECH_GSM))
+                {
+                    net_state = MBTK_NET_STATE_CONN_2G;
+                } else if((gprsState == RADIO_TECH_UMTS) || (gprsState == RADIO_TECH_HSDPA)
+                         || (gprsState == RADIO_TECH_HSUPA) || (gprsState == RADIO_TECH_HSPA)) {
+                    net_state = MBTK_NET_STATE_CONN_3G;
+                } else {
+                    net_state = MBTK_NET_STATE_CONN_UNKNOWN;
+                }
+            }
+        }
+        else
+        {
+            LOGE("property_get persist.mbtk.netstate fail.");
+            net_state = MBTK_NET_STATE_CONN_UNKNOWN;
+            goto end;
+        }
+    }
+
+end:
+    return net_state;
+}
+
+/*=============================================
+FUNCTION
+    mbtk_net_enable()
+
+DESCRIPTION
+    Set network state.
+
+DEPENDENCIES
+    None
+
+PARAMETERS
+    enable
+        TRUE : Enable network.
+        FALSE: Diable network.
+
+RETURN VALUE
+    0 : Success
+    -1: Fail
+
+SIDE EFFECTS
+    None
+=============================================*/
+int mbtk_net_enable(bool enable)
+{
+    if( net_control_init())
+        return -1;
+
+    int result = net_control_state_change(enable);
+
+    LOGI("[SET]Net state:%d",net_state);
+
+    return result;
+}
+
diff --git a/mbtk/mbtk_lib/src/mbtk_ntp.c b/mbtk/mbtk_lib/src/mbtk_ntp.c
new file mode 100755
index 0000000..de92991
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_ntp.c
@@ -0,0 +1,289 @@
+/*
+* FILE: ntp.c
+* NOTE: socket网络编程学习,NTP时间获取程序
+*
+* TIME: 2021年11月13日00:05:39
+*/
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdio.h>
+#include <errno.h>
+#include <netdb.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <netinet/in.h>
+
+#include "mbtk_log.h"
+
+#define   NTP_PORT   123
+#define   TIME_PORT  37
+#define   NTP_SERVER_IP  "cn.pool.ntp.org"
+
+#define   NTP_PORT_STR   "123"
+#define   NTPV1       "NTP/V1"
+#define NTPV2 "NTP/V2"
+
+#define NTPV3 "NTP/V3"
+#define NTPV4 "NTP/V4"
+#define TIME "TIME/UDP"
+
+#define   NTP_PCK_LEN   48
+
+#define LI 0
+#define VN 3
+#define MODE 3
+#define STRATUM 0
+#define POLL 4
+#define PREC -6
+
+#define JAN_1970 0x83aa7e80 /* 1900 年~1970 年之间的时间秒数 */
+#define NTPFRAC(x) (4294 * (x) + ((1981 * (x)) >> 11))
+#define USEC(x) (((x) >> 12) - 759 * ((((x) >> 10) + 32768) >> 16))
+
+
+time_t sys_time;
+typedef struct _ntp_time
+{
+    unsigned int coarse;
+    unsigned int fine;
+} ntp_time;
+
+/* NTP时钟同步报文 */
+struct ntp_packet
+{
+    unsigned char leap_ver_mode;
+    unsigned char startum;
+    char poll;
+    char precision;
+    int root_delay;
+    int root_dispersion;
+    int reference_identifier;
+    ntp_time reference_timestamp;
+    ntp_time originage_timestamp;
+    ntp_time receive_timestamp;
+    ntp_time transmit_timestamp;
+};
+
+char protocol[32];
+
+int construct_packet(char *packet)
+{
+    char version = 1;
+    long tmp_wrd;
+    int port;
+    time_t timer;
+    strcpy(protocol, NTPV4);
+    /*判断协议版本*/
+    if(!strcmp(protocol, NTPV1)||!strcmp(protocol, NTPV2)||!strcmp(protocol, NTPV3)||!strcmp(protocol, NTPV4))
+    {
+        memset(packet, 0, NTP_PCK_LEN);
+        port = NTP_PORT;
+        /*设置 16 字节的包头*/
+        version = protocol[5] - 0x30;
+        tmp_wrd = htonl((LI << 30)|(version << 27) \
+            |(MODE << 24)|(STRATUM << 16)|(POLL << 8)|(PREC & 0xff));
+        memcpy(packet, &tmp_wrd, sizeof(tmp_wrd));
+
+        /*设置 Root Delay、 Root Dispersion 和 Reference Indentifier */
+        tmp_wrd = htonl(1<<16);
+        memcpy(&packet[4], &tmp_wrd, sizeof(tmp_wrd));
+        memcpy(&packet[8], &tmp_wrd, sizeof(tmp_wrd));
+        /*设置 Timestamp 部分*/
+        time(&timer);
+        /*设置 Transmit Timestamp coarse*/
+        tmp_wrd = htonl(JAN_1970 + (long)timer);
+        memcpy(&packet[40], &tmp_wrd, sizeof(tmp_wrd));
+        /*设置 Transmit Timestamp fine*/
+        tmp_wrd = htonl((long)NTPFRAC(timer));
+        memcpy(&packet[44], &tmp_wrd, sizeof(tmp_wrd));
+        return NTP_PCK_LEN;
+    }
+    else if (!strcmp(protocol, TIME))/* "TIME/UDP" */
+    {
+        port = TIME_PORT;
+        memset(packet, 0, 4);
+        return 4;
+    }
+
+    return 0;
+}
+
+/*获取 NTP 时间*/
+int get_ntp_time(int sk, struct addrinfo *addr, struct ntp_packet *ret_time)
+{
+    fd_set pending_data;
+    struct timeval block_time;
+    char data[NTP_PCK_LEN * 8];
+    int packet_len, data_len = addr->ai_addrlen, count = 0, result, i,re;
+
+    /* 组织请求报文 */
+    if (!(packet_len = construct_packet(data)))
+    {
+        return 0;
+    }
+    /*客户端给服务器端发送 NTP 协议数据包*/
+    if ((result = sendto(sk, data, packet_len, 0, addr->ai_addr, data_len)) < 0)
+    {
+        LOGE("sendto");
+        return 0;
+    }
+    /*调用select()函数,并设定超时时间为10s*/
+    FD_ZERO(&pending_data);
+    FD_SET(sk, &pending_data);
+    block_time.tv_sec=10;
+    block_time.tv_usec=0;
+    if (select(sk + 1, &pending_data, NULL, NULL, &block_time) > 0)
+    {
+        /*接收服务器端的信息*/
+        if ((count = recvfrom(sk, data, NTP_PCK_LEN * 8, 0, addr->ai_addr, &data_len)) < 0)
+        {
+            LOGE("recvfrom");
+            return 0;
+        }
+
+       // if (protocol == TIME)
+        if(!strcmp(protocol,TIME))
+        {
+            memcpy(&ret_time->transmit_timestamp, data, 4);
+            return 1;
+        }
+        else if (count < NTP_PCK_LEN)
+        {
+            return 0;
+        }
+
+        /* 设置接收 NTP 包的数据结构 */
+        ret_time->leap_ver_mode = ntohl(data[0]);
+        ret_time->startum = ntohl(data[1]);
+        ret_time->poll = ntohl(data[2]);
+        ret_time->precision = ntohl(data[3]);
+        ret_time->root_delay = ntohl(*(int*)&(data[4]));
+        ret_time->root_dispersion = ntohl(*(int*)&(data[8]));
+        ret_time->reference_identifier = ntohl(*(int*)&(data[12]));
+        ret_time->reference_timestamp.coarse = ntohl(*(int*)&(data[16]));
+        ret_time->reference_timestamp.fine = ntohl(*(int*)&(data[20]));
+        ret_time->originage_timestamp.coarse = ntohl(*(int*)&(data[24]));
+        ret_time->originage_timestamp.fine = ntohl(*(int*)&(data[28]));
+        ret_time->receive_timestamp.coarse = ntohl(*(int*)&(data[32]));
+        ret_time->receive_timestamp.fine = ntohl(*(int*)&(data[36]));
+        ret_time->transmit_timestamp.coarse = ntohl(*(int*)&(data[40]));
+        ret_time->transmit_timestamp.fine = ntohl(*(int*)&(data[44]));
+
+        /* 将NTP时间戳转换为日期 */
+        time_t currentTime = ret_time->transmit_timestamp.coarse - JAN_1970;
+        sys_time = ret_time->transmit_timestamp.coarse - JAN_1970;
+        struct tm CurlocalTime;
+        localtime_r(&currentTime, &CurlocalTime);
+        char dateTime[30];
+        strftime(dateTime, 30, "%Y-%m-%d %H:%M:%S %A", &CurlocalTime);
+
+        LOGI("%s\n", dateTime);
+
+        return 1;
+    } /* end of if select */
+
+
+    return 0;
+}
+
+/* 修改本地时间 */
+int set_local_time(struct ntp_packet * pnew_time_packet)
+{
+    struct timeval tv;
+    tv.tv_sec = pnew_time_packet->transmit_timestamp.coarse - JAN_1970;
+    tv.tv_usec = USEC(pnew_time_packet->transmit_timestamp.fine);
+    return settimeofday(&tv, NULL);
+}
+
+int ntp_main()
+{
+    int sockfd, rc;
+    struct addrinfo hints, *res = NULL;
+    struct ntp_packet new_time_packet;
+
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = AF_INET;
+    hints.ai_socktype = SOCK_DGRAM;
+    hints.ai_protocol = IPPROTO_UDP;
+
+    /*调用 getaddrinfo()函数, 获取地址信息*/
+    rc = getaddrinfo(NTP_SERVER_IP, NTP_PORT_STR, &hints, &res);
+    if (rc != 0)
+    {
+        LOGE("getaddrinfo");
+        return 0;
+    }
+
+    /* 创建套接字 */
+    sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); //IPv4, 数据报套接字, UDP
+    if (sockfd <0 )
+    {
+        LOGE("socket");
+        return 0;
+    }
+
+    /*调用取得 NTP 时间的函数*/
+    if (get_ntp_time(sockfd, res, &new_time_packet))
+    {
+        LOGI("NTP client success!\n");
+    }
+
+    close(sockfd);
+
+    return sys_time;
+}
+
+time_t ntp_server_set(const char* server_ip, const char * port)
+{
+    int sockfd, rc;
+    struct addrinfo hints, *res = NULL;
+    struct ntp_packet new_time_packet;
+
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = AF_INET;
+    hints.ai_socktype = SOCK_DGRAM;
+    hints.ai_protocol = IPPROTO_UDP;
+
+    printf("server_ip:%s,port:%s\n", server_ip, port);
+
+    /*调用 getaddrinfo()函数, 获取地址信息*/
+    rc = getaddrinfo(NTP_SERVER_IP, NTP_PORT_STR, &hints, &res);
+    if (rc != 0)
+    {
+        printf("getaddrinfo");
+        return 0;
+    }
+
+    /* 创建套接字 */
+    sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); //IPv4, 数据报套接字, UDP
+    if (sockfd <0 )
+    {
+        printf("socket");
+        return 0;
+    }
+
+    /*调用取得 NTP 时间的函数*/
+    if (get_ntp_time(sockfd, res, &new_time_packet))
+    {
+        printf("NTP client success!\n");
+    }
+
+    close(sockfd);
+
+    return sys_time;
+}
+
+
+time_t mbtk_ntp_server_set(const char* server_ip, const char * port)
+{
+    return ntp_server_set(server_ip, port);
+}
+
+
+int mbtk_at_systime(void)
+{
+    return ntp_main();
+}
+
+
diff --git a/mbtk/mbtk_lib/src/mbtk_pdu_sms.c b/mbtk/mbtk_lib/src/mbtk_pdu_sms.c
new file mode 100755
index 0000000..5825cb6
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_pdu_sms.c
@@ -0,0 +1,1086 @@
+//

+// Created by hitmoon on 15-12-9.

+//

+

+#include <stddef.h>

+#include <stdlib.h>

+#include <stdio.h>

+#include "mbtk_pdu_sms.h"

+#include "mbtk_alphabet.h"

+

+#define SUB_STR_SIZE 512

+char temp[SUB_STR_SIZE];

+

+// some constant

+

+//长短信信息元素参考号

+enum EnumCSMIEI mCSMIEI;

+// 服务中心地址

+char *mSCA;

+// 请求状态报告

+bool mSRR;

+// 拒绝副本

+bool mRD;

+// 短信有效期

+char *mVP;

+// 长短信信息元素消息参考号

+int mCSMMR;

+

+// initialize PDU constants

+void sms_init()

+{

+    mCSMMR = 0;

+    mRD = false;

+    mSRR = false;

+    mSCA = "";

+    mVP = "";

+    mCSMIEI = BIT8MIEI;

+}

+

+

+char *sub_str(const char *str, int start, int size) {

+    memset(temp, '\0', SUB_STR_SIZE);

+    if (size > 0)

+        strncpy(temp, str + start, size);

+    else if (size < 0)

+        strcpy(temp, str + start);

+

+    return temp;

+}

+

+struct SMS_Struct PDUDecoding(const char *data) {

+

+    struct SMS_Struct sms;

+    int end_index;

+    int PDUType;

+    // 短信中心

+    sms.SCA = SCADecoding(data, &end_index);

+

+    // 协议数据单元类型

+    PDUType = strtol(sub_str(data, end_index, 2), NULL, 16);

+    end_index += 2;

+

+    sms.RP = PDUType & (1 << 7) ? true : false;   // 应答路径

+    sms.UDHI = PDUType & (1 << 6) ? true : false;  // 用户数据头标识

+    sms.SRI = PDUType & (1 << 5) ? true : false;  // 状态报告指示

+    sms.MMS = PDUType & (1 << 2) ? false : true;  // 更多信息发送

+    sms.MTI = PDUType & 3;                        // 信息类型指示

+

+    // 发送方SME的地址

+    sms.OA = OADecoding(data, end_index, &end_index);

+

+    // 协议标识

+    sms.PID = strtol(sub_str(data, end_index, 2), NULL, 16);

+    end_index += 2;

+

+    // 数据编码方案

+    int DCSType = strtol(sub_str(data, end_index, 2), NULL, 16);

+    end_index += 2;

+

+    // 文本压缩指示

+    sms.TC = DCSType & (1 << 5);

+    // 编码字符集

+    sms.DCS = (enum EnumDCS) ((DCSType >> 2) & 3);

+

+    if (DCSType & (1 << 4)) {

+        // 信息类型信息 0:立即显示 1:移动设备特定类型 2:SIM特定类型 3:终端设备特定类型

+        sms.MC = DCSType & 3;

+    }

+    else {

+        // 不含信息类型信息

+        sms.MC = -1;

+    }

+    // 服务中心时间戳(BCD编码)

+    sms.SCTS = SCTSDecoding(data, end_index);

+    end_index += 14;

+

+    // 用户数据头

+    if (sms.UDHI) {

+        sms.UDH = UDHDecoding(data, end_index + 2);

+    }

+    else {

+        sms.UDH = NULL;

+    }

+

+    // 用户数据

+    sms.UD = UserDataDecoding(data, end_index, sms.UDHI, sms.DCS);

+

+    return sms;

+}

+

+

+char *SCADecoding(const char *data, int *EndIndex) {

+    int len;

+

+    char *result;

+    char *buf;

+	int i = 0;

+

+    len = strtol(sub_str(data, 0, 2), NULL, 16);

+    if (len == 0) {

+        *EndIndex = 2;

+        return NULL;

+    }

+

+    *EndIndex = (len + 1) * 2;

+

+    result = (char *) malloc(sizeof(char) * len * 2);

+    //wmemset(result, '0', sizeof(char) * (len * 2 + 1));

+

+    buf = result;

+    len *= 2;

+

+    // 服务中心地址类型

+    if (strncmp(data + 2, "91", 2) == 0) {

+        sprintf(buf++, "+");

+    }

+

+    // 服务中心地

+

+	for (i = 4; i < *EndIndex; i += 2) {

+        sprintf(buf++, "%c", data[i + 1]);

+        sprintf(buf++, "%c", data[i]);

+    }

+

+    //  去掉填充的 'F'

+    if (result[strlen(result) - 1] == L'F') {

+        result[strlen(result) - 1] = L'\0';

+    }

+

+    return result;

+}

+

+char *OADecoding(const char *data, int index, int *EndIndex) {

+    int len;

+    char *result, *buf;

+

+    len = strtol(sub_str(data, index, 2), NULL, 16);

+

+    if (len == 0) {

+        *EndIndex = index + 2;

+        return NULL;

+    }

+

+    *EndIndex = index + 4 + len;

+

+    result = (char *) malloc(sizeof(char) * (len + 2));

+    //wmemset(result, 0, sizeof(char) * (len + 1));

+    buf = result;

+    

+    if (strncmp(data + index + 2, "91", 2) == 0) {

+        sprintf(buf++, "+");

+    }

+

+    // 电话号码

+	int i = 0;

+    for (i = 0; i < len; i += 2) {

+        sprintf(buf++, "%c", data[index + i + 5]);

+        sprintf(buf++, "%c", data[index + i + 4]);

+

+    }

+

+    if (len % 2 != 0) {

+        result[strlen(result) - 1] = '\0';

+        (*EndIndex)++;

+    }

+    return result;

+}

+

+char *SCTSDecoding(const char *data, int index) {

+

+    char *result;

+

+    result = (char *) malloc(sizeof(char) * 32);

+    sprintf(result, "20%02d-%02d-%02d %02d:%02d:%02d",

+             BCDDecoding(data, index, 0),                // 年

+             BCDDecoding(data, index + 2, 0),            // 月

+             BCDDecoding(data, index + 4, 0),            // 日

+             BCDDecoding(data, index + 6, 0),            // 时

+             BCDDecoding(data, index + 8, 0),            // 分

+             BCDDecoding(data, index + 10, 0)            // 秒

+

+    );

+    return result;

+}

+

+int BCDDecoding(const char *data, int index, bool isMSB) {

+

+    int n1, n10;

+

+    n1 = strtol(sub_str(data, index, 1), NULL, 10);

+    n10 = strtol(sub_str(data, index + 1, 1), NULL, 10);

+

+    if (isMSB) {

+        if (n10 >= 8)

+            return -((n10 - 8) * 10 + n1); // 负值

+        else

+            return n10 * 10 + n1;

+    }

+    else {

+        return n10 * 10 + n1;

+    }

+}

+

+struct UDHS *UDHDecoding(const char *data, int index) {

+

+    int len;

+    struct UDHS *result;

+

+    len = strtol(sub_str(data, index, 2), NULL, 16);

+    index += 2;

+    int i = 0;

+

+    result = (struct UDHS *) malloc(sizeof(struct UDHS));

+    result->UDH = (struct PDUUDH *) malloc(sizeof(struct PDUUDH) * len);

+    result->count = 0;

+    memset(result->UDH, 0, sizeof(struct PDUUDH) * len);

+

+    while (i < len) {

+        // 信息元素标识(Information Element Identifier

+        char IEI = strtol(sub_str(data, index, 2), NULL, 16);

+        index += 2;

+        // 信息元素数据长度(Length of Information Element)

+        int IEDL = strtol(sub_str(data, index, 2), NULL, 16);

+        index += 2;

+        // 信息元素数据(Information Element Data)

+        char *IED = (char *) malloc(sizeof(char) * (IEDL + 1));

+        int j = 0;

+		for (j = 0; j < IEDL; j++) {

+            IED[j] = strtol(sub_str(data, index, 2), NULL, 16);

+            index += 2;

+        }

+        result->UDH[result->count].IEI = IEI;

+        result->UDH[result->count].IED = IED;

+        result->count++;

+        i += IEDL + 2;

+    }

+

+    return result;

+}

+

+char *UserDataDecoding(const char *data, int index, bool UDHI, enum EnumDCS dcs) {

+    char *result = NULL;

+    char *buf;

+

+    // 用户数据区长度

+    int UDL = strtol(sub_str(data, index, 2), NULL, 16);

+    index += 2;

+

+    // 跳过用户数据头

+    int UDHL = 0;

+    if (UDHI) {

+        // 用户数据头长度

+        UDHL = strtol(sub_str(data, index, 2), NULL, 16);

+        UDHL++;

+        index += UDHL << 1;

+

+    }

+

+    // 获取用户数据

+    if (dcs == UCS2) {

+        int len = (UDL - UDHL) >> 1;

+        int utf8_len;

+

+        result = (char *) malloc(sizeof(char) * (len * 3));

+        buf = result;

+        u_int32_t code[2];

+

+		int i = 0;

+        for (i = 0; i < len; i++) {

+            code[0] = strtol(sub_str(data, (i << 2) + index, 4), NULL, 16);

+            code[1] = 0;

+            utf32toutf8((wchar_t*)code, (unsigned char*)buf, len * 3, &utf8_len);

+            buf += utf8_len;

+        }

+

+        buf[0] = '\0';

+        return result;

+    }

+    else if (dcs == BIT7) {

+        int Septets = UDL - (UDHL * 8 + 6) / 7;           // 7-Bit编码字符数

+

+        int FillBits = (UDHL * 8 + 6) / 7 * 7 - UDHL * 8; //  填充位数

+        return BIT7Decoding(BIT7Unpack(data, index, Septets, FillBits), Septets);

+    }

+    else {// 8Bit编码

+        // 获取数据长度

+        UDL -= UDHL;

+        result = (char *) malloc(sizeof(char) * (UDL + 1));

+        int i = 0;

+		for (i = 0; i < UDL; i++) {

+            result[i] = strtol(sub_str(data, (i << 1) + index, 2), NULL, 16);

+        }

+        return result;

+    }

+

+    return "Error!";

+}

+

+char *BIT7Unpack(const char *data, int index, int Septets, int FillBits) {

+    char *result;

+

+    result = (char *) malloc(sizeof(char) * (Septets + 1));

+    // 每8个7-Bit编码字符存放到7个字节

+    int PackLen = (Septets * 7 + FillBits + 7) / 8;

+    int n = 0;

+    int left = 0;

+

+//    printf("Unapck data = %s\n", data + index);

+//    printf("Septets = %d\n", Septets);

+//    printf("pack len = %d\n", PackLen);

+//    printf("fillbits = %d\n", FillBits);

+

+	int i = 0;

+    for (i = 0; i < PackLen; i++) {

+

+        int Order = (i + (7 - FillBits)) % 7;

+        int Value = strtol(sub_str(data, (i << 1) + index, 2), NULL, 16);

+        if (i != 0 || FillBits == 0) {

+            result[n++] = ((Value << Order) + left) & 0x7F;

+        }

+//        printf("left = %d, i = %d, order = %d, value = %d\n", left, i, Order, Value);

+//        printf("result[%d] = %d\n", n - 1, result[n - 1]);

+        left = Value >> (7 - Order);

+        if (Order == 6) {

+            if (n == Septets)

+                break;

+            result[n++] = left;

+            //printf("result[%d] = %d\n", n - 1, result[n - 1]);

+            left = 0;

+        }

+    }

+

+    return result;

+}

+

+char *BIT7Decoding(char *BIT7Data, unsigned int size)

+{

+    char *result, *buf;

+

+    result = (char *) malloc(sizeof(char) * (size + 1));

+    buf = result;

+

+	int i = 0;

+    for (i = 0; i < size; i++) {

+        u_int16_t key = BIT7Data[i];

+        if (isBIT7Same(key)) {

+            sprintf(buf++, "%c", key);

+        }

+        else if (map_get_value(BIT7ToUCS2, map_size(BIT7ToUCS2), key) >= 0) {

+            u_int16_t value;

+            if (key == 0x1B) { // 转义字符

+                value = map_get_value(BIT7EToUCS2, map_size(BIT7EToUCS2), BIT7Data[i + 1]);

+                if (i < size - 1 && value > 0) {

+                    sprintf(buf++, "%c", value);

+                    i++;

+                }

+                else {

+                    value = map_get_value(BIT7ToUCS2, map_size(BIT7ToUCS2), key);

+                    sprintf(buf++, "%c", value);

+                }

+            }

+            else {

+                //printf("go b\n");

+                value = map_get_value(BIT7ToUCS2, map_size(BIT7ToUCS2), key);

+                //printf("value = %u\n", value);

+                sprintf(buf++, "%c", value);

+

+            }

+        }

+        else {// 异常数据

+            sprintf(buf++, "?");

+        }

+

+    }

+    return result;

+

+}

+

+int isBIT7Same(u_int16_t UCS2) {

+	if ((UCS2 >= 0x61 && UCS2 <= 0x7A) ||

+	    (UCS2 >= 0x41 && UCS2 <= 0x5A) ||

+	    (UCS2 >= 0x25 && UCS2 <= 0x3F) ||

+	    (UCS2 >= 0x20 && UCS2 <= 0x23) ||

+	    UCS2 == 0x0A || UCS2 == 0x0D) {

+        return 1;

+    }

+    return 0;

+}

+

+struct PDUS *PDUEncoding(char *SCA, char *DA, char *UDC, struct UDHS *udhs) {

+    enum EnumDCS DCS;

+

+    sms_init();

+

+//    if (isGSMString(UDC))

+//        DCS = BIT7;

+//    else

+        DCS = UCS2;

+

+    return PDUDoEncoding(SCA, DA, UDC, udhs, DCS);

+}

+

+struct PDUS *PDUDoEncoding(char *SCA, char *DA, char *UDC, struct UDHS *udhs, enum EnumDCS DCS) {

+    // 短信拆分

+    struct UDS *uds = UDCSplit(UDC, udhs, DCS);

+    struct PDUS *pdus;

+

+    if (uds == NULL)

+        return NULL;

+    pdus = (struct PDUS *) malloc(sizeof(struct PDUS));

+    pdus->count = 0;

+    pdus->PDU = (char **) malloc(sizeof(char *) * uds->total);

+

+    if (uds->total > 1) {

+        // 长短信

+        int CSMMR = mCSMMR;

+        if (++mCSMMR > 0xFFFF)

+            mCSMMR = 0;

+        // 生成短信编码序列

+		int i = 0;

+        for (i = 0; i < uds->total; i++) {

+            // 更新用户数据头

+            struct UDHS *CSMUDH = UpdateUDH(udhs, CSMMR, uds->total, i);

+            pdus->PDU[i] = SoloPDUEncoding(SCA, DA, uds->Data[i], CSMUDH, DCS);

+            pdus->count++;

+        }

+

+    }

+    else {  // 单条短信

+        pdus->PDU[0] = SoloPDUEncoding(SCA, DA, uds->Data[0], udhs, DCS);

+        pdus->count = 1;

+    }

+

+    return pdus;

+}

+

+int isGSMString(char *Data) {

+

+    if (Data == NULL || strcmp(Data, "") == 0)

+        return 1;

+

+    if (is_acsii((unsigned char*)Data) == 0) {

+        int len;

+        len = utf8len((unsigned char *) Data); 

+

+        u_int16_t *code = (u_int16_t *) malloc(sizeof(u_int16_t) * len);

+        utf8toutf16((unsigned char *) Data, code, len, &len);

+

+        while (*code) {

+            if (!(isBIT7Same(*code) || map_get_value(UCS2ToBIT7, map_size(UCS2ToBIT7), *code) >= 0))

+                return 0;

+            code++;

+        }

+

+        return 1;

+    }

+    else

+        return 1;

+

+}

+

+struct UDS *UDCSplit(char *UDC, struct UDHS *udhs, enum EnumDCS DCS) {

+    int UDHL = getUDHL(udhs);

+    struct UDS *result;

+

+    if (DCS == BIT7) {

+        // 7-Bit编码

+        // 计算剩余房间数

+        int room = BIT7UDL - (UDHL * 8 + 6) / 7;

+        if (room < 1) {

+            if (UDC == NULL || strcmp(UDC, "") == 0) {

+                result = (struct UDS *) malloc(sizeof(struct UDS));

+                result->Data = (char **)malloc(sizeof(char *));

+                result->total = 1;

+                result->Data[0] = UDC;

+                return result;

+            }

+            else

+                return NULL;

+        }

+

+        // 不需要拆分

+        if (SeptetsLength(UDC) <= room) {

+            result = (struct UDS *) malloc(sizeof(struct UDS));

+            result->Data = (char **)malloc(sizeof(char *));

+            result->total = 1;

+            result->Data[0] = UDC;

+            return result;

+        }

+        else // 拆分短信

+        {

+            if (UDHL == 0)

+                UDHL++;

+            if (mCSMIEI == BIT8MIEI)

+                UDHL += 5;  // 1字节消息参考号

+            else

+                UDHL += 6;  // 2字节消息参考号

+            // 更新剩余房间数

+            room = BIT7UDL - (UDHL * 8 + 6) / 7;

+            if (room < 1)

+                return NULL;

+

+            int i = 0;

+            int len = strlen(UDC);

+

+            result = (struct UDS *) malloc(sizeof(struct UDS));

+            result->total = 0;

+            result->Data = (char **) malloc(MAX_SMS_NR * sizeof(char *));

+

+            while (i < len) {

+                int step = SeptetsToChars(UDC, i, room);

+                if (i + step < len) {

+                    result->Data[result->total] = (char *) malloc(sizeof(char) * (step + 1));

+                    strcpy(result->Data[result->total++], sub_str(UDC, i, step));

+                }

+                else {

+                    result->Data[result->total] = (char *) malloc(sizeof(char) * (len - i + 1));

+                    strcpy(result->Data[result->total++], sub_str(UDC, i, -1));

+                }

+

+                i += step;

+

+            }

+            return result;

+

+        }

+    }

+    else { // UCS2编码

+        // 计算剩余房间数

+        int room = (BIT8UDL - UDHL) >> 1;

+        if (room < 1) {

+            if (UDC == NULL || strcmp(UDC, "") == 0) {

+                result = (struct UDS *) malloc(sizeof(struct UDS));

+                result->Data = (char **)malloc(sizeof(char *));

+                result->total = 1;

+                result->Data[0] = UDC;

+                return result;

+            }

+            else

+                return NULL;

+        }

+        if (UDC == NULL || utf8len(UDC) <= room) {

+            result = (struct UDS *) malloc(sizeof(struct UDS));

+            result->Data = (char **)malloc(sizeof(char *));

+            result->total = 1;

+            result->Data[0] = UDC;

+            return result;

+        }

+        else // 需要拆分成多条短信

+        {

+            if (UDHL == 0)

+                UDHL++;

+            if (mCSMIEI == BIT8MIEI)

+                UDHL += 5;  // 1字节消息参考号

+            else

+                UDHL += 6;  // 2字节消息参考号

+

+            // 更新剩余房间数

+            room = (BIT8UDL - UDHL) >> 1;

+            if (room < 1)

+                return NULL;

+

+            int len = utf8len(UDC);

+

+            result = (struct UDS *) malloc(sizeof(struct UDS));

+            result->total = 0;

+            result->Data = (char **) malloc(MAX_SMS_NR * sizeof(char *));

+	    int index = 0;

+		int i = 0;

+            for (i = 0; i < len; i += room) {

+		    int real_size;

+                if (i + room < len) {

+		    real_size = utf8_get_size(UDC + index, room);

+                    result->Data[result->total] = (char*)malloc(sizeof(char) * (real_size + 1));

+                    strcpy(result->Data[result->total++],sub_str(UDC, index, real_size));

+                }

+                else {

+		    real_size = utf8_get_size(UDC + index, len - i);

+                    result->Data[result->total] = (char*)malloc(sizeof(char) * (real_size + 1));

+                    strcpy(result->Data[result->total++], sub_str(UDC, index, -1));

+                }

+		index += real_size;

+            }

+            return result;

+        }

+

+    }

+}

+

+int getUDHL(struct UDHS *udhs) {

+    if (udhs == NULL)

+        return 0;

+

+    // 加上1字节的用户数据头长度

+    int UDHL = 1;

+	int i = 0;

+    for (i = 0; i < udhs->count; i++) {

+        UDHL += strlen(udhs->UDH[i].IED) + 2;

+    }

+    return UDHL;

+}

+

+int SeptetsLength(char *source) {

+    if (source == NULL || strcmp(source, "") == 0) {

+        return 0;

+    }

+    int len = strlen(source);

+    while (*source) {

+        u_int16_t code = (u_int16_t) *source;

+        if (map_get_value(UCS2ToBIT7, map_size(UCS2ToBIT7), code) > 0xFF) {

+            len++;

+        }

+        source++;

+    }

+    return len;

+}

+

+int SeptetsToChars(char *source, int index, int septets) {

+    if (source == NULL || strcmp(source, "") == 0)

+        return 0;

+    int count = 0;

+    int i;

+

+    for (i = index; i < strlen(source); i++) {

+        u_int16_t code = (u_int16_t) source[i];

+        if (map_get_value(UCS2ToBIT7, map_size(UCS2ToBIT7), code) > 0xFF)

+            count++;

+

+        if (++count >= septets) {

+            if (count == septets)

+                i++;

+            break;

+        }

+    }

+    return i - index;

+}

+

+struct UDHS *UpdateUDH(struct UDHS *udhs, int CSMMR, int total, int index) {

+    struct UDHS *result;

+

+    result = (struct UDHS *) malloc(sizeof(struct UDHS));

+

+    if (udhs == NULL || udhs->count == 0) {

+        result->UDH = (struct PDUUDH *) malloc(sizeof(struct PDUUDH));

+        result->count = 1;

+

+    }

+    else {

+        result->UDH = (struct PDUUDH *) malloc(sizeof(struct PDUUDH) * (udhs->count + 1));

+        result->count = udhs->count + 1;

+        // 复制 UDH

+        memcpy(&result->UDH[1], udhs->UDH, sizeof(struct PDUUDH) * udhs->count);

+    }

+    // 插入第一个位置

+    if (mCSMIEI == BIT8MIEI) {

+        result->UDH[0].IED = (char *) malloc(sizeof(char) * 3);

+        result->UDH[0].count = 3;

+        result->UDH[0].IED[0] = CSMMR & 0xFF;

+        result->UDH[0].IED[1] = total;

+        result->UDH[0].IED[2] = index + 1;

+        result->UDH[0].IEI = 0;

+    }

+    else {

+        result->UDH[0].IED = (char *) malloc(sizeof(char) * 4);

+        result->UDH[0].count = 4;

+        result->UDH[0].IED[0] = (CSMMR >> 8) & 0xFF;

+        result->UDH[0].IED[1] = CSMMR & 0xFF;

+        result->UDH[0].IED[2] = total;

+        result->UDH[0].IED[3] = index + 1;

+        result->UDH[0].IEI = 8;

+    }

+

+    return result;

+}

+

+char *SoloPDUEncoding(char *SCA, char *DA, char *UC, struct UDHS *udhs, enum EnumDCS DCS) {

+    char *result;

+    char *buf, *ret;

+    int index;

+

+    result = (char *) malloc(sizeof(char) * 400);

+    buf = result;

+

+/*    //  短信中心

+	ret = SCAEncoding(SCA);

+    index = strlen(ret);

+    sprintf(buf, "%s", ret);

+    printf("buf:%s\n",buf);

+    buf += index;

+

+*/

+    // 协议数据单元类型

+    if (udhs == NULL || udhs->count == 0) {

+        ret = PDUTypeEncoding(false);

+        sprintf(buf, "%s", ret);

+        buf += strlen(ret);

+    }

+    else {

+        ret = PDUTypeEncoding(true);

+        sprintf(buf, "%s", ret);

+        buf += strlen(ret);

+    }

+    // 消息参考值

+    ret = MREncoding();

+    sprintf(buf, "%s", ret);

+    buf += strlen(ret);

+    // 接收方SME地址

+    ret = DAEncoding(DA);

+    sprintf(buf, "%s", ret);

+    buf += strlen(ret);

+    // 协议标识

+    ret = PIDEncoding();

+    sprintf(buf, "%s", ret);

+    buf += strlen(ret);

+    // 编码方案

+    ret = DCSEncoding(UC, DCS);

+    sprintf(buf, "%s", ret);

+    buf += strlen(ret);

+    // 有效期

+    sprintf(buf, "%s", mVP);

+    buf += strlen(mVP);

+

+    // 用户数据长度及内容

+    ret = UDEncoding(UC, udhs, DCS);

+    sprintf(buf, "%s", ret);

+

+    return result;

+}

+

+char *SCAEncoding(char *SCA) {

+

+    if (SCA == NULL || strcmp(SCA, "") == 0) {

+        // 表示使用SIM卡内部的设置值,该值通过AT+CSCA指令设置

+        return "00";

+    }

+

+    char *result;

+    char *buf;

+    int len;

+    len = strlen(SCA);

+    result = (char *) malloc((len + 5) * sizeof(char));

+    buf = result;

+

+    int index = 0;

+    if (SCA[0] == '+') {

+        // 国际号码

+        sprintf(buf, "%02X", len / 2 + 1);

+        buf += 2;

+        sprintf(buf, "91");

+        buf += 2;

+        index = 1;

+    }

+    else {

+        // 国内号码

+        sprintf(buf, "%02X", len / 2 + 1);

+        buf += 2;

+        sprintf(buf, "81");

+        buf += 2;

+    }

+    // SCA地址编码

+    for (; index < len; index += 2) {

+        if (index == len - 1) {

+            // 补“F”凑成偶数个

+            sprintf(buf++, "F");

+            sprintf(buf++, "%c", SCA[index]);

+

+        }

+        else {

+            sprintf(buf++, "%c", SCA[index + 1]);

+            sprintf(buf++, "%c", SCA[index]);

+        }

+    }

+

+    return result;

+}

+

+char *PDUTypeEncoding(bool UDH) {

+    // 信息类型指示(Message Type Indicator)

+    // 01 SMS-SUBMIT(MS -> SMSC)

+    int PDUType = 0x11;    // 508TLC change

+//	int PDUType = 0x01;

+    char *result;

+    result = (char *) malloc(3 * sizeof(char));

+

+    // 用户数据头标识(User Data Header Indicator)

+    if (UDH) {

+        PDUType |= 0x40;

+    }

+    // 有效期格式(Validity Period Format)

+    if (strlen(mVP) == 2) {

+        // VP段以整型形式提供(相对的)

+        PDUType |= 0x10;

+    }

+    else if (strlen(mVP) == 14) {

+        // VP段以8位组的一半(semi-octet)形式提供(绝对的)

+        PDUType |= 0x18;

+    }

+

+    // 请求状态报告(Status Report Request)

+    if (mSRR) {

+        // 请求状态报告

+        PDUType |= 0x20;

+    }

+

+    // 拒绝复本(Reject Duplicate)

+    if (mRD) {

+        PDUType |= 0x04;

+    }

+    sprintf(result, "%02X", PDUType);

+    return result;

+}

+

+char *MREncoding() {

+    // 由手机设置

+    return "00";

+}

+

+char *DAEncoding(char *DA) {

+    if (DA == NULL || strcmp(DA, "") == 0) {

+        // 地址长度0,地址类型未知

+        return "0080";

+    }

+    char *result, *buf;

+    int len = strlen(DA);

+    int index;

+

+    result = (char *) malloc(sizeof(char) * (len + 5));

+    buf = result;

+

+    if (DA[0] == '+') {

+        // 国际号码

+        // 地址长度编码

+        sprintf(buf, "%02X", len - 1);

+        buf += 2;

+        // 地址类型

+        sprintf(buf, "91");

+        buf += 2;

+        index = 1;

+    }

+    else {

+        // 国内号码

+        // 地址长度编码

+        sprintf(buf, "%02X", len);

+        buf += 2;

+        // 地址类型

+        sprintf(buf, "81");

+        buf += 2;

+    }

+

+    for (; index < len; index += 2) {

+        // 号码部分奇偶位对调

+        if (index == len - 1) {

+            sprintf(buf++, "F");

+            sprintf(buf++, "%c", DA[index]);

+        }

+        else {

+            sprintf(buf++, "%c", DA[index + 1]);

+            sprintf(buf++, "%c", DA[index]);

+        }

+    }

+    return result;

+

+}

+

+char *PIDEncoding() {

+    return "00";

+}

+

+char *DCSEncoding(char *UD, enum EnumDCS DCS) {

+    if (DCS == BIT7) {

+        // 7-Bit编码

+        return "00";

+    }

+    else {

+        // UCS2编码

+        return "0800";

+    }

+}

+

+char *UDEncoding(char *UD, struct UDHS *udhs, enum EnumDCS DCS) {

+    int UDHL;

+

+    char *result;

+

+    // 用户数据头编码

+    char *header = UDHEncoding(udhs, &UDHL);

+

+    // 用户数据内容编码

+    int UDCL;

+    char *body;

+

+    body = UDCEncoding(UD, &UDCL, UDHL, DCS);

+

+    // 用户数据区长度

+    int UDL;

+    if (DCS == BIT7) {

+        // 7-Bit编码

+        UDL = (UDHL * 8 + 6) / 7 + UDCL;

+    }

+    else {

+        // UCS2编码或者8-Bit编码

+        UDL = UDHL + UDCL;

+    }

+

+    int len = strlen(header) + strlen(body) + 2;

+    result = (char *) malloc(sizeof(char) * (len + 1));

+    sprintf(result, "%02X%s%s", UDL, header, body);

+

+    return result;

+

+}

+

+char *UDHEncoding(struct UDHS *udhs, int *UDHL) {

+

+    *UDHL = 0;

+

+    if (udhs == NULL || udhs->count == 0)

+        return "";

+	int i = 0;

+    for (i = 0; i < udhs->count; i++) {

+        *UDHL += udhs->UDH[i].count + 2;

+    }

+

+    char *result;

+    char *buf;

+    result = (char *) malloc(sizeof(char) * ((*UDHL + 1) * 2 + 1));

+    buf = result;

+

+    sprintf(buf, "%02X", *UDHL);

+    buf += 2;

+	

+    for (i = 0; i < udhs->count; i++) {

+        // 信息元素标识1字节

+        sprintf(buf, "%02X", udhs->UDH[i].IEI);

+        buf += 2;

+        // 信息元素长度1字节

+        sprintf(buf, "%02X", udhs->UDH[i].count);

+        buf += 2;

+        // 信息元素数据

+		int j = 0;

+        for (j = 0; j < udhs->UDH[i].count; j++) {

+            sprintf(buf, "%02X", udhs->UDH[i].IED[j]);

+            buf += 2;

+        }

+

+    }

+    // 加上1字节的用户数据头长度

+    (*UDHL)++;

+    return result;

+

+}

+

+char *UDCEncoding(char *UDC, int *UDCL, int UDHL, enum EnumDCS DCS) {

+    if (UDC == NULL || strcmp(UDC, "") == 0) {

+        *UDCL = 0;

+        return "";

+    }

+

+    if (DCS == BIT7) {

+        // 7-Bit编码,需要参考用户数据头长度,已保证7-Bit边界对齐

+        return BIT7Pack(BIT7Encoding(UDC, UDCL), UDHL);

+    }

+    else {

+        // UCS2编码

+

+        int len = utf8len((unsigned char*)UDC);

+        int len2;

+        unsigned short *code;

+

+        code = (unsigned short*)malloc(sizeof(unsigned short) * len);

+        utf8toutf16((unsigned char*)UDC, code, len, &len2);

+        *UDCL = len * 2;

+        char *result = (char *) malloc(sizeof(char) * (*UDCL * 2 + 1));

+        char *buf = result;

+

+		int i = 0;

+        for (i = 0; i < len; i++) {

+            sprintf(buf, "%04X", code[i]);

+            buf += 4;

+        }

+        free(code);

+        return result;

+    }

+}

+

+struct ByteArray *BIT7Encoding(char *UDC, int *Septets) {

+    struct ByteArray *result;

+

+    int len = strlen(UDC);

+

+    result = (struct ByteArray *) malloc(sizeof(struct ByteArray));

+    result->len = 0;

+    result->array = (char *) malloc(sizeof(char) * (len * 2 + 1));

+    *Septets = 0;

+

+	int i = 0;

+    for (i = 0; i < len; i++) {

+        u_int16_t code = (u_int16_t) UDC[i];

+        if (isBIT7Same(code)) {

+            //  编码不变

+	    result->array[(*Septets)++] = code;

+        }

+        else {

+            u_int16_t value = map_get_value(UCS2ToBIT7, map_size(UCS2ToBIT7), code);

+            if (value >= 0) {

+                if (value > 0xFF) {

+                    // 转义序列

+			result->array[(*Septets)++] = value >> 8;

+			result->array[(*Septets)++] = value & 0xFF;

+                }

+                else {

+			result->array[(*Septets)++] = value;

+                }

+            }

+            else {

+                // 未知字符

+		    result->array[(*Septets)++] = (u_int16_t) '?';

+            }

+        }

+    }

+    // 重新调整大小

+    result->len = *Septets;

+

+    return result;

+}

+

+char *BIT7Pack(struct ByteArray *Bit7Array, int UDHL) {

+    // 7Bit对齐需要的填充位

+    int fillBits = (UDHL * 8 + 6) / 7 * 7 - (UDHL * 8);

+

+    // 压缩字节数

+    int len = Bit7Array->len;

+    int packLen = (len * 7 + fillBits + 7) / 8;

+    char *result;

+    char *buf;

+

+    result = (char *) malloc(sizeof(char) * (packLen * 2 + 1));

+    buf = result;

+

+    int left = 0;

+	int i = 0;

+    for (i = 0; i < len; i++) {

+        // 每8个字节压缩成7个字节

+        int32_t Value = Bit7Array->array[i];

+        int32_t index = (i + 8 - fillBits) % 8;

+        if (index == 0) {

+            left = Value;

+        }

+        else {

+            int32_t n = ((Value << (8 - index)) | left) & 0xFF;

+            sprintf(buf, "%02X", n);

+            buf += 2;

+            left = Value >> index;

+        }

+    }

+

+

+    if ((len * 7 + fillBits) % 8 != 0) {

+        // 写入剩余数据

+        sprintf(buf, "%02X", left);

+        buf += 2;

+    }

+    buf[0] = '\0';

+    return result;

+}

+

diff --git a/mbtk/mbtk_lib/src/mbtk_queue.c b/mbtk/mbtk_lib/src/mbtk_queue.c
new file mode 100755
index 0000000..8abc4d1
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_queue.c
@@ -0,0 +1,218 @@
+/*************************************************************
+Description:
+    mbtk_queue.c
+    Used to implement mobiletek standard queue interfaces
+Author:
+    YangDagang
+Date:
+    2019-7-13
+*************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "mbtk_type.h"
+#include "mbtk_queue.h"
+
+/*************************************************************
+    Constants and Macros
+*************************************************************/
+
+/*************************************************************
+    Definitions:enum,struct,union
+*************************************************************/
+
+
+/*************************************************************
+    Variables:local,extern
+*************************************************************/
+
+/*************************************************************
+    Local Function Declaration
+*************************************************************/
+
+/*************************************************************
+    Extern Function Declaration
+*************************************************************/
+
+/*************************************************************
+    Function Definitions
+*************************************************************/
+/*=============================================
+FUNCTION
+    mbtk_tcpip_ptr
+
+DESCRIPTION
+    get tcpip information pointer
+
+DEPENDENCIES
+    None
+
+PARAMETERS
+    None
+
+RETURN VALUE
+    tcpip information pointer
+
+SIDE EFFECTS
+    None
+=============================================*/
+void mbtk_queue_init(mbtk_queue_node_t *queue)
+{
+    if(queue == NULL)
+    {
+        return;
+    }
+    queue->count = 0;
+    queue->payload = NULL;
+    queue->front = NULL;
+    queue->rear = NULL;
+    mbtk_mutex_init(&queue->mutex);
+}
+
+int mbtk_queue_put(mbtk_queue_node_t *queue,void *payload)
+{
+    mbtk_queue_node_t *curr = NULL;
+    mbtk_queue_node_t *p = NULL;
+
+    if(payload == NULL || queue == NULL)
+    {
+        return -1;
+    }
+    mbtk_mutex_lock(&queue->mutex);
+    curr = (mbtk_queue_node_t*)malloc(sizeof(mbtk_queue_node_t));
+    if(curr == NULL)
+    {
+        mbtk_mutex_unlock(&queue->mutex);
+        return -1;
+    }
+    curr->count = 0;
+    curr->rear = NULL;
+    curr->payload = payload;
+    if(queue->front != NULL)
+    {
+        p = queue->front;
+        p->rear = curr;
+        curr->front = p;
+        queue->front = curr;
+    }
+    else
+    {
+        //fisrt add node
+        queue->front = curr;
+        queue->rear = curr;
+        curr->front = queue;
+    }
+    queue->count++;
+    mbtk_mutex_unlock(&queue->mutex);
+    return 0;
+}
+
+int mbtk_queue_insert(mbtk_queue_node_t *queue,void *payload)
+{
+    mbtk_queue_node_t *curr = NULL;
+    mbtk_queue_node_t *p = NULL;
+
+    if(payload == NULL || queue == NULL)
+    {
+        return -1;
+    }
+    mbtk_mutex_lock(&queue->mutex);
+    curr = (mbtk_queue_node_t*)malloc(sizeof(mbtk_queue_node_t));
+    if(curr == NULL)
+    {
+        mbtk_mutex_unlock(&queue->mutex);
+        return -1;
+    }
+    curr->rear = NULL;
+    curr->count = 0;
+    curr->payload = payload;
+    if(queue->front != NULL)
+    {
+        p = queue->rear;
+        queue->rear = curr;
+        curr->front = queue;
+        curr->rear = p;
+        p->front = curr;
+    }
+    else
+    {
+        //fisrt add node
+        queue->front = curr;
+        queue->rear = curr;
+        curr->front = queue;
+    }
+    queue->count++;
+    mbtk_mutex_unlock(&queue->mutex);
+    return 0;
+}
+
+void* mbtk_queue_get(mbtk_queue_node_t *queue)
+{
+    mbtk_queue_node_t *curr = NULL;
+    void *payload = NULL;
+
+    if((queue == NULL) || mbtk_queue_empty(queue))
+    {
+        return NULL;
+    }
+    mbtk_mutex_lock(&queue->mutex);
+    curr = queue->rear;
+    payload = curr->payload;
+    if(curr->rear != NULL)
+    {
+        queue->rear = curr->rear;
+        curr->rear->front = queue;
+    }
+    else
+    {
+        queue->front = NULL;
+        queue->rear = NULL;
+    }
+    free(curr);
+    curr = NULL;
+    queue->count--;
+    mbtk_mutex_unlock(&queue->mutex);
+    return payload;
+}
+
+bool mbtk_queue_empty(mbtk_queue_node_t *queue)
+{
+    bool is_empty = FALSE;
+
+    mbtk_mutex_lock(&queue->mutex);
+    if(queue != NULL)
+    {
+        if((queue->front == NULL) && (queue->rear == NULL))
+        {
+            is_empty = TRUE;
+        }
+    }
+    mbtk_mutex_unlock(&queue->mutex);
+    return is_empty;
+}
+
+bool mbtk_queue_is_full(mbtk_queue_node_t *queue)
+{
+    bool is_full = FALSE;
+
+    mbtk_mutex_lock(&queue->mutex);
+    if((queue != NULL) && (queue->count == MBTK_QUEUE_MAX_NUM))
+    {
+        is_full = TRUE;
+    }
+    mbtk_mutex_unlock(&queue->mutex);
+    return is_full;
+}
+
+void mbtk_queue_deinit(mbtk_queue_node_t *queue)
+{
+    if(queue == NULL)
+    {
+        return;
+    }
+    queue->count = 0;
+    queue->payload = NULL;
+    queue->front = NULL;
+    queue->rear = NULL;
+    mbtk_mutex_deinit(&queue->mutex);
+}
diff --git a/mbtk/mbtk_lib/src/mbtk_sock.c b/mbtk/mbtk_lib/src/mbtk_sock.c
new file mode 100755
index 0000000..d52abdb
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_sock.c
@@ -0,0 +1,659 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/select.h>
+#include <signal.h>
+#include <sys/time.h>
+
+#include "mbtk_sock.h"
+#include "mbtk_log.h"
+
+int sock_net_open(mbtk_net_info_s *net_info, mbtk_addr_family_e addr_family)
+{
+    return 0;
+}
+
+int sock_open(mbtk_net_info_s *net_info, mbtk_sock_info_s *sock_info, uint32 timeout, int *err)
+{
+    if(net_info == NULL || sock_info == NULL) {
+        LOGE("ARG error.");
+        return -1;
+    }
+    int family = AF_INET;
+    if(sock_info->addr_family == MBTK_ADDR_IPV6) { // Only IPv6
+        family = AF_INET6;
+    }
+    if(sock_info->sock_type == MBTK_SOCK_UDP) { // UDP
+        // socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+        if((sock_info->fd = socket(family, SOCK_DGRAM, IPPROTO_UDP)) < 0){
+            LOGE("socket() fail.[%d]",errno);
+            goto result_fail;
+        }
+    } else if(sock_info->sock_type == MBTK_SOCK_TCP){ // TCP
+        if((sock_info->fd = socket(family, SOCK_STREAM, IPPROTO_TCP)) < 0){
+            LOGE("socket() fail.[%d]",errno);
+            goto result_fail;
+        }
+    } else {
+        LOGE("Unknown socket type:%d", sock_info->sock_type);
+        return -1;
+    }
+#if 1
+    // Set O_NONBLOCK
+    int flags = fcntl(sock_info->fd, F_GETFL, 0);
+    if (flags < 0) {
+        LOGE("Get flags error:%d", errno);
+        goto result_fail_with_close;
+    }
+    flags |= O_NONBLOCK;
+    if (fcntl(sock_info->fd, F_SETFL, flags) < 0) {
+        LOGE("Set flags error:%d", errno);
+        goto result_fail_with_close;
+    }
+#endif
+    // Connect
+    LOGD("Start conn:%s:%d",sock_info->host,sock_info->port);
+    if (strlen(sock_info->host) > 0 && sock_info->port > 0)
+    {
+        if(family == AF_INET6)
+        {
+            struct addrinfo hints;
+            struct addrinfo *answer, *curr;
+            memset(&hints, 0, sizeof(struct addrinfo));
+            hints.ai_family = AF_INET6;    /* Allow IPv4 or IPv6 */
+            hints.ai_socktype = sock_info->sock_type; /* Datagram socket */
+            hints.ai_flags = 0;
+            hints.ai_protocol = 0;          /* Any protocol */
+
+            int ret = getaddrinfo(sock_info->host, NULL, &hints, &answer);
+            if(ret < 0)
+            {
+                printf("\ngetaddrinfo error\n");
+                goto result_fail_with_close;
+            }
+
+            struct sockaddr_in6 *addr_in6=NULL;
+            struct sockaddr_in6 serverSockAddr;
+            bzero(&serverSockAddr, sizeof(struct sockaddr_in6));
+            serverSockAddr.sin6_family = AF_INET6;
+            serverSockAddr.sin6_port = htons(sock_info->port);
+            //memcpy(&serverSockAddr.sin6_addr, he->h_addr_list[0], sizeof(struct in6_addr));
+            //memcpy(&sock_info->addr.addr.dst_sock_addr6.sin6_addr, he->h_addr_list[0], sizeof(struct in_addr));
+            sock_info->addr.addr_len = sizeof(struct sockaddr_in6);
+
+            for (curr = answer; curr != NULL; curr = curr->ai_next)
+            {
+                addr_in6 = (struct sockaddr_in6 *)curr->ai_addr;
+				memcpy(&(serverSockAddr.sin6_addr), &(addr_in6->sin6_addr), sizeof(struct in6_addr));
+            }
+            if (connect(sock_info->fd, &serverSockAddr, sizeof(struct sockaddr_in6)) < 0)
+            {
+                if (EINPROGRESS != errno)
+                {
+                    LOGE("connect() fail.[%d]", errno);
+                    goto result_fail_with_close;
+                }
+            }
+        }
+        else
+        {
+            struct hostent *he = gethostbyname(sock_info->host);
+            if (he == NULL)
+            {
+                LOGE("gethostbyname() fail.[%d]", errno);
+                goto result_fail_with_close;
+            }
+            struct sockaddr_in serverSockAddr;
+            bzero(&serverSockAddr, sizeof(struct sockaddr_in));
+            serverSockAddr.sin_family = AF_INET;
+            serverSockAddr.sin_port = htons(sock_info->port);
+            memcpy(&serverSockAddr.sin_addr, he->h_addr_list[0], sizeof(struct in_addr));
+
+            log_hex("IPv4", he->h_addr_list[0], sizeof(struct in_addr));
+
+            memcpy(&sock_info->addr.addr.serverSockAddr.sin_addr, he->h_addr_list[0], sizeof(struct in_addr));
+            sock_info->addr.addr_len = sizeof(struct sockaddr_in);
+            if (connect(sock_info->fd, (struct sockaddr *) &serverSockAddr, sizeof(serverSockAddr)) < 0)
+            {
+                if (EINPROGRESS != errno)
+                {
+                    LOGE("connect() fail.[%d]", errno);
+                    goto result_fail_with_close;
+                }
+            }
+        }
+
+        fd_set rset, wset;
+        FD_ZERO(&rset);
+        FD_ZERO(&wset);
+        FD_SET(sock_info->fd, &rset);
+        FD_SET(sock_info->fd, &wset);
+        struct timeval time_out;
+        /*
+        time_out.tv_sec = timeout / 1000;
+        time_out.tv_usec = timeout % 1000 * 1000;
+        */
+        time_out.tv_sec = 100;
+        time_out.tv_usec = 0;
+        int nready = select(sock_info->fd + 1, &rset, &wset, NULL, &time_out);
+        LOGD("nready = %d", nready);
+        if (nready == 0)   // Timeout
+        {
+            LOGE("Timeout.");
+            goto result_fail_with_close;
+        }
+        else
+        {
+            if (FD_ISSET(sock_info->fd, &rset) && FD_ISSET(sock_info->fd, &wset))
+            {
+                int error = -1;
+                socklen_t len = sizeof(int);
+                LOGE("Can read and write.");
+                if (getsockopt(sock_info->fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
+                {
+                    LOGE("getsockopt fail.[%d]", errno);
+                    goto result_fail_with_close;
+                }
+                else
+                {
+                    LOGE("error = %d", error);
+                    if (error != 0)   // Fail
+                    {
+                        goto result_fail_with_close;
+                    }
+                }
+            }
+            else if (FD_ISSET(sock_info->fd, &wset))
+            {
+                LOGI("Can write.");
+            }
+            else
+            {
+                LOGE("Can read(Impossible).");
+                goto result_fail_with_close;
+            }
+        }
+    }
+    else
+    {
+        LOGE("Can not conn.");
+        goto result_fail_with_close;
+    }
+
+    if (sock_info->is_ssl)
+    {
+
+    }
+
+    sock_info->read_buff.buffer = (char*)malloc(MBTK_BUFF_SIZE);
+    if(sock_info->read_buff.buffer) {
+        memset(sock_info->read_buff.buffer, 0x0, MBTK_BUFF_SIZE);
+        sock_info->read_buff.size = 0;
+        sock_info->read_buff.size_max = MBTK_BUFF_SIZE;
+    } else {
+        LOGE("malloc() fail.");
+        goto result_fail_with_close;
+    }
+
+    return sock_info->fd;
+result_fail_with_close:
+    close(sock_info->fd);
+    sock_info->fd = -1;
+result_fail:
+    LOGE("mbtk_sock_open() end:fail");
+    return -1;
+}
+
+int sock_addr_set(mbtk_net_info_s *net_info,mbtk_sock_info_s *sock_info,void *host, uint32 port)
+{
+    if(sock_info->fd <= 0) {
+        LOGE("Socket not open.");
+        return -1;
+    }
+
+    sock_info->addr.addr.serverSockAddr.sin_family = AF_INET;
+    sock_info->addr.addr.serverSockAddr.sin_port = htons(port);
+    struct hostent *he = gethostbyname(host);
+    if (he == NULL)
+    {
+        LOGE("gethostbyname() fail.[%d]", errno);
+        return -1;
+    }
+    else
+    {
+        LOGD("Ip(len-%d)", he->h_length);
+        int i = 0;
+        for(;i < he->h_length;i++){
+            LOGD("Ip Addr[%d]:%.2x", i, 0x0FF & he->h_addr_list[0][i]);
+        }
+    }
+    memcpy(&sock_info->addr.addr.serverSockAddr.sin_addr, he->h_addr_list[0], sizeof(struct in_addr));
+    sock_info->addr.addr_len = sizeof(struct sockaddr_in);
+
+    return 0;
+}
+
+int sock_bind(int sockfd, bool is_ipv6, uint16 port)
+{
+    int ret_val;
+    struct sockaddr *ds_sockaddr = NULL;
+    struct sockaddr_in localaddr;
+    uint16 addr_legth = 0;
+
+    memset((char *) &localaddr, 0, sizeof(struct sockaddr_in));
+    localaddr.sin_family = AF_INET;
+    localaddr.sin_addr.s_addr = INADDR_ANY;
+    localaddr.sin_port = htons(port);
+    addr_legth = sizeof(struct sockaddr_in);
+    ds_sockaddr = (struct sockaddr *)&localaddr;
+    ret_val = bind(sockfd,ds_sockaddr,addr_legth);
+    if (ret_val < 0){
+         LOGE("bind() sockfd= %d fail", sockfd);
+         return -1;
+    }
+
+    return 0;
+}
+
+int sock_read(mbtk_net_info_s *net_info, mbtk_sock_info_s *sock_info, void *buffer, uint32 buf_len, uint32 timeout, int *err)
+{
+    unsigned int count = 0;
+    int len = 0;
+    int try_count = 0;
+    int times = timeout / 50;
+    memset(buffer, 0x0, buf_len);
+    while (count < buf_len)
+    {
+        try_count++;
+        if (sock_info->is_ssl)
+        {
+
+        }
+        else
+        {
+            len = read(sock_info->fd, (char*) buffer + count, buf_len - count);
+        }
+        if (len <= 0)
+        {
+            if (errno == EWOULDBLOCK || errno == EAGAIN)
+            {
+                if (count > 0) // Read data
+                    break; // Read data end.
+
+                if (try_count >= times)   // Timeout
+                {
+                    count = -1;
+                    if (times != 0)
+                    {
+                        *err = 10; // FTP_ERR_NET_TIMEOUT
+                    }
+                    LOGE("Not read enough data,return.[%d/%d]", count, buf_len);
+                    break;
+                }
+                else
+                {
+                    usleep(50000);
+                    continue;
+                }
+            }
+            else
+            {
+                LOGD("read error.[%d]", errno);
+                if (errno == EINPROGRESS)
+                {
+                    if (close(sock_info->fd) == 0)   // Success
+                    {
+                        LOGD("Socket disconnected.Close it.");
+                        sock_info->fd = -1;
+                    }
+                    if (count <= 0)
+                        count = -1;
+                }
+                else
+                {
+                    if (count <= 0)
+                        count = 0;
+                }
+                break;
+            }
+        }
+        else
+        {
+            count += len;
+        }
+    }
+
+    LOGV("Read data[%d/%d].", count, buf_len);
+
+    return count;
+}
+
+int sock_read_async(mbtk_net_info_s *net_info, mbtk_sock_info_s *sock_info, void *buffer, uint32 buf_len, uint32 timeout, int *err)
+{
+    int len = -1;
+    int try_count = 0;
+    int times = timeout / 50;
+
+TCP_READ_AGAIN:
+    memset(buffer, 0x0, buf_len);
+    try_count++;
+    if (sock_info->is_ssl)
+    {
+
+    }
+    else
+    {
+        len = read(sock_info->fd, (char*) buffer, buf_len);
+    }
+    if (len < 0)
+    {
+        if (errno == EWOULDBLOCK)
+        {
+            if(try_count == times)
+            {
+                LOGD("read timeout");
+                return -1;
+            }
+            usleep(50000);
+            goto TCP_READ_AGAIN;
+        }
+        else
+        {
+            LOGE("read error.[%d]", errno);
+            return -1;
+        }
+    }
+
+    LOGV("Read data[%d/%d].", len, buf_len);
+
+    return len;
+}
+
+#if 0
+int sock_readline(mbtk_net_info_s *net_info, mbtk_sock_info_s *sock_info, void *buffer, uint32 buf_len, uint32 timeout, int *err)
+{
+    if (sock_info->fd > 0)
+    {
+        char *buf_ptr = (char*)buffer;
+        char read_buf[1];
+        int read_len = 0;
+        while (TRUE)
+        {
+            if (sock_read_async(net_info, sock_info, read_buf, 1, timeout, err) == 1)
+            {
+                *buf_ptr++ = read_buf[0];
+                read_len++;
+
+                if (read_buf[0] == '\n' || read_len >= buf_len)
+                {
+                    return read_len;
+                }
+            }
+            else
+            {
+                return -1;
+            }
+        }
+    }
+    return -1;
+}
+#else
+int sock_readline(mbtk_net_info_s *net_info, mbtk_sock_info_s *sock_info, void *buffer, uint32 buf_len, uint32 timeout, int *err)
+{
+    if (sock_info->fd > 0)
+    {
+        memset(buffer,0,buf_len);
+        int read_len = 0;
+        char *buf_ptr = (char*)buffer;
+        char *temp_ptr = sock_info->read_buff.buffer;
+
+        LOGV("TEMP buffer[fd - %d, len-%d]:%s", sock_info->fd, sock_info->read_buff.size, sock_info->read_buff.buffer);
+copy_angin:
+        while(sock_info->read_buff.size > 0 && *temp_ptr != '\n') {
+            *buf_ptr++ = *temp_ptr++;
+            sock_info->read_buff.size--;
+            read_len++;
+        }
+
+        LOGV("SIZE : %d,TEMP is \\n : %d", sock_info->read_buff.size, *temp_ptr == '\n');
+
+        if(sock_info->read_buff.size == 0) {
+            sock_info->read_buff.size = sock_read(net_info, sock_info, sock_info->read_buff.buffer,
+                                            sock_info->read_buff.size_max, timeout, err);
+            if(sock_info->read_buff.size <= 0) {
+                if(read_len == 0) { // No data.
+                    LOGE("sock_read() fail.");
+                    return -1;
+                } else {
+                    return read_len;
+                }
+            }
+
+            temp_ptr = sock_info->read_buff.buffer;
+            goto copy_angin;
+        } else if(*temp_ptr == '\n') { // Read line.
+            *buf_ptr++ = '\n';
+            sock_info->read_buff.size--;
+            read_len++;
+
+            if(sock_info->read_buff.size > 0)
+                memcpy(sock_info->read_buff.buffer, temp_ptr + 1, sock_info->read_buff.size);
+
+            return read_len;
+        }
+
+    }
+    return -1;
+}
+#endif
+
+int sock_write(mbtk_net_info_s *net_info, mbtk_sock_info_s *sock_info, const void *buffer, uint32 buf_len, uint32 timeout, int *err)
+{
+    int len = 0;
+    uint32 try_count = 0;
+    uint32 times = timeout * 100;
+    unsigned int count = 0;
+    while (count < buf_len)
+    {
+        try_count++;
+        if (sock_info->is_ssl)
+        {
+            //printf("  try_count = %d   timeout = %d        sock_info->is_ssl = 1\n",try_count,timeout);
+            if(try_count >= times)
+            {
+                printf("over time \n");
+                break;
+            }
+        }
+        else
+        {
+            len = write(sock_info->fd, (const char*)buffer + count, buf_len - count);
+        }
+        if (len < 0)
+        {
+            if (errno == EWOULDBLOCK)
+            {
+                usleep(50000);
+                continue;
+            }
+            else
+            {
+                LOGE("write error.[%d]", errno);
+                if (count <= 0)
+                    count = -1;
+                break;
+            }
+        }
+        else if (len == 0)
+        {
+            LOGE("write error(len == 0).[%d]", errno);
+        }
+        else
+        {
+            count += len;
+        }
+    }
+
+    if (count > 0)
+    {
+        LOGV("Write data[%d/%d] success.", count, buf_len);
+    }
+    else     // Open session fail
+    {
+        LOGV("Write data[%d/%d] fail.", count, buf_len);
+    }
+
+    return count;
+}
+
+int sock_recv(mbtk_net_info_s *net_info, mbtk_sock_info_s *sock_info, void *buffer, uint32 buf_len, uint32 timeout)
+{
+    unsigned int count = 0;
+    int len = 0;
+    int try_count = 0;
+    int times = timeout / 50; // ms
+    memset(buffer, 0x0, buf_len);
+    while (count < buf_len)
+    {
+        try_count++;
+        len = recvfrom(sock_info->fd, (char*) buffer + count, buf_len - count, 0,
+                    (struct sockaddr *)&sock_info->addr.addr.serverSockAddr, (socklen_t *)&sock_info->addr.addr_len);
+        if (len <= 0)
+        {
+            if (errno == EWOULDBLOCK || errno == EAGAIN)
+            {
+                if (count > 0) // Read data
+                    break; // Read data end.
+
+                if (try_count >= times)   // Timeout
+                {
+                    count = -1;
+                    break;
+                }
+                else
+                {
+                    usleep(50000);
+                    continue;
+                }
+            }
+            else if (errno == EINPROGRESS)
+            {
+                if (close(sock_info->fd) == 0)   // Success
+                {
+                    LOGD("Socket disconnected.Close it.");
+                }
+                if (count <= 0)
+                    count = -1;
+                break;
+            }
+            else
+            {
+                LOGE("recv error.[%d]", errno);
+                if (count <= 0)
+                    count = -1;
+                break;
+            }
+        }
+        else
+        {
+            count += len;
+        }
+    }
+
+    LOGV("Read data[%d/%d].", count, buf_len);
+
+    return count;
+}
+
+int sock_sendto(mbtk_net_info_s *net_info, mbtk_sock_info_s *sock_info, const void *buffer, uint32 buf_len, uint32 timeout)
+{
+    int len = 0;
+    unsigned int count = 0;
+    int try_count = 0;
+    int times = timeout / 50; // ms
+    while (count < buf_len)
+    {
+        try_count++;
+        len = sendto(sock_info->fd,(char*) buffer + count, buf_len - count, 0,
+                (struct sockaddr *)&sock_info->addr.addr.serverSockAddr, sock_info->addr.addr_len);
+        if (len < 0)
+        {
+            if (errno == EWOULDBLOCK)
+            {
+                if (count > 0) // Send part data
+                    break;
+
+                if (try_count >= times)   // Timeout
+                {
+                    count = -1;
+                    break;
+                }
+                else
+                {
+                    usleep(50000);
+                    continue;
+                }
+            }
+            else
+            {
+                LOGE("sendto error.[%d]", errno);
+                if (count <= 0)
+                    count = -1;
+                break;
+            }
+        }
+        else if (len == 0)
+        {
+            LOGE("write error(len == 0).[%d]", errno);
+        }
+        else
+        {
+            count += len;
+        }
+    }
+
+    if (count == buf_len)
+    {
+        LOGV("Sendto data[%d/%d] success.", count, buf_len);
+    }
+    else
+    {
+        LOGV("Sendto data[%d/%d] fail.", count, buf_len);
+    }
+
+    return count;
+}
+
+int sock_close(mbtk_net_info_s *net_info, mbtk_sock_info_s *sock_info, uint32 timeout, int *err)
+{
+    if(sock_info->fd > 0) {
+        if(close(sock_info->fd) < 0)
+        {
+            LOGE("Close socket fail[%d].", errno);
+            return -1;
+        }
+        sock_info->fd = -1;
+    }
+
+    if(sock_info->read_buff.buffer) {
+        free(sock_info->read_buff.buffer);
+        sock_info->read_buff.buffer = NULL;
+    }
+    return 0;
+}
+
+int sock_net_close(mbtk_net_info_s *net_info, uint32 timeout, int *err)
+{
+    return 0;
+}
+
diff --git a/mbtk/mbtk_lib/src/mbtk_sock2.c b/mbtk/mbtk_lib/src/mbtk_sock2.c
new file mode 100755
index 0000000..461c68c
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_sock2.c
@@ -0,0 +1,1344 @@
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <polarssl/net.h>
+#include <polarssl/ssl.h>
+#include <polarssl/entropy.h>
+#include <polarssl/ctr_drbg.h>
+#include <polarssl/certs.h>
+#include <polarssl/x509.h>
+#include <polarssl/error.h>
+#include <polarssl/debug.h>
+#include <polarssl/config.h>
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+#define LOG_TAG "mbtk_sock"
+#include <mbtk_log.h>
+
+#include "mbtk_sock2.h"
+#include "mbtk_sock_internal.h"
+//#include "mbtk_openssl.h"
+
+#define SA struct sockaddr
+
+// Must define LOG_TAG in the first.
+//#include "mbtk_log.h"
+static int epoll_fd = -1;
+static int pipe_fds[2];
+static struct epoll_event epoll_events[20];
+static pthread_t sock_thread_id = -1;
+static bool sock_thread_running = FALSE;
+static mbtk_sock_s *mbtk_sock[MBTK_HANDLE_MAX_NUM] = {NULL};
+
+static int sock_find_first_free(const mbtk_sock_inter_info_s *inter_info)
+{
+    if(inter_info == NULL) {
+        LOGE("inter_info is NULL.");
+        return -1;
+    }
+
+    int index = 0;
+    //while((inter_info + index)->fd > 0) {
+    while(inter_info[index].fd > 0) {
+        index++;
+    }
+
+    if(index == MBTK_SOCK_MAX_NUM) {
+        LOGE("sock_infos too more.");
+        return -1;
+    }
+
+    return index;
+}
+
+static bool sock_info_check(int handle,mbtk_sock_inter_info_s *inter_info)
+{
+    if(inter_info == NULL || mbtk_sock[handle] == NULL) {
+        LOGE("internal_info is NULL.");
+        return FALSE;
+    }
+
+    int index = 0;
+    while(index < MBTK_SOCK_MAX_NUM) {
+        if(inter_info->fd ==
+            mbtk_sock[handle]->inter_infos[index].fd) {
+            return TRUE;
+        }
+        index++;
+    }
+
+    return FALSE;
+}
+
+static bool sock_is_close(int sockfd)
+{
+    char buff[32];
+    int recvBytes = recv(sockfd, buff, sizeof(buff), MSG_PEEK);
+
+    int err = errno;
+    //cout << "In close function, recv " << recvBytes << " bytes, err " << sockErr << endl;
+
+    if(recvBytes > 0) //Get data
+        return FALSE;
+
+    if((recvBytes == -1) && (err == EWOULDBLOCK)) //No receive data
+        return FALSE;
+
+    return TRUE;
+}
+
+static int sock_info_find_by_fd(int handle,int fd)
+{
+    int index = 0;
+    while(index < MBTK_SOCK_MAX_NUM) {
+        if(fd == mbtk_sock[handle]->inter_infos[index].fd) {
+            return index;
+        }
+        index++;
+    }
+
+    return -1;
+}
+
+static void* sock_thread_run(void *data)
+{
+    LOGD("socket thread is running...");
+    if(data != NULL)
+        LOGD("sock_thread_run has arg.");
+
+    int nready;
+    if (socketpair( AF_UNIX, SOCK_STREAM, 0, pipe_fds) < 0) {
+        LOGE("socketpair() error.");
+        return NULL;
+    } else {
+        struct epoll_event ev;
+        ev.data.fd = pipe_fds[1];
+        ev.events = EPOLLIN | EPOLLET;
+        epoll_ctl(epoll_fd,EPOLL_CTL_ADD,pipe_fds[1],&ev);
+    }
+
+    while(sock_thread_running) {
+        nready = epoll_wait(epoll_fd,epoll_events,20,-1);
+        int i;
+        for(i=0;i<nready;++i) {
+            LOGD("fd[%d] event = %x",epoll_events[i].data.fd,epoll_events[i].events);
+            if(pipe_fds[1] == epoll_events[i].data.fd) {
+                LOGD("Get exist sig.");
+                sock_thread_running = FALSE;
+                break;
+            }
+
+            int handle = 0;
+            while(handle < MBTK_HANDLE_MAX_NUM) {
+                if(mbtk_sock[handle] != NULL) {
+                    int index = sock_info_find_by_fd(handle,epoll_events[i].data.fd);
+                    if(index >= 0 && mbtk_sock[handle]->init_info.sock_cb != NULL) {
+                        mbtk_sock_inter_info_s *inter_info = &(mbtk_sock[handle]->inter_infos[index]);
+
+                        //if(sock_is_close(epoll_events[i].data.fd)) {
+                        //    LOGE("Socket %d is closed.",epoll_events[i].data.fd);
+                        //    break;
+                        //}
+
+                        mbtk_sock[handle]->init_info.sock_cb(handle,inter_info->fd,epoll_events[i].events);
+
+                        if(epoll_events[i].events & EPOLLERR){ // error[ UDP server can't use.]
+                            LOGD("%d EPOLLERR.",epoll_events[i].data.fd);
+                        }
+
+                        if ((epoll_events[i].events & EPOLLIN)
+                            && (epoll_events[i].events & EPOLLOUT)) {
+                            LOGD("%d can read and write.",epoll_events[i].data.fd);
+                            int error = -1;
+                            int len = sizeof(int);
+                            if(getsockopt(epoll_events[i].data.fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0){
+                                LOGE("getsockopt fail.[%d]",errno);
+                            }else{
+                                LOGD("error = %d",error);
+                            }
+                        }
+
+                        if (epoll_events[i].events & EPOLLOUT) { // Can write.
+                            LOGD("%d can write.",epoll_events[i].data.fd);
+                        }
+
+                        if (epoll_events[i].events & EPOLLIN) { // Can read.
+                            LOGD("%d can read.",epoll_events[i].data.fd);
+                        }
+                    }
+                }
+
+                handle++;
+            }
+        }
+    }
+
+    LOGD("socket thread exit.");
+    return ((void*)0);
+}
+
+static int sock_thread_start()
+{
+    sock_thread_running = TRUE;
+    if (0 != pthread_create(&sock_thread_id, NULL, sock_thread_run, NULL))
+    {
+        LOGE("error when create pthread,%d\n", errno);
+        return -1;
+    }
+
+    return 0;
+}
+
+extern mbtk_sock_handle mbtk_sock_init(mbtk_init_info *info)
+{
+    mbtk_sock_handle handle = 0;
+    while(handle < MBTK_HANDLE_MAX_NUM) {
+        if(mbtk_sock[handle] == NULL)
+            break;
+
+        handle++;
+    }
+
+    if(handle == MBTK_HANDLE_MAX_NUM) {
+        LOGE("Socket handle is full.");
+        return -1;
+    }
+
+    mbtk_sock[handle] = (mbtk_sock_s*)malloc(sizeof(mbtk_sock_s));
+    memset(mbtk_sock[handle],0x0,sizeof(mbtk_sock_s));
+    if(info != NULL) {
+        mbtk_sock[handle]->init_info.net_type = info->net_type;
+        mbtk_sock[handle]->init_info.net_cb = info->net_cb;
+        mbtk_sock[handle]->init_info.sock_cb = info->sock_cb;
+    } else {
+        mbtk_sock[handle]->init_info.net_type = MBTK_NET_LINUX;
+        mbtk_sock[handle]->init_info.net_cb = NULL;
+        mbtk_sock[handle]->init_info.sock_cb = NULL;
+    }
+
+    if(!sock_thread_running) {
+        epoll_fd = epoll_create(256);
+        if(sock_thread_start()) {
+            LOGE("Start thread fail.");
+            return -1;
+        }
+    }
+
+    return handle;
+}
+
+static int mbtk_ssl_init(int fd ,bool ingnore_cert,mbtk_sock_inter_info_s* inter_info)
+{
+    LOGE("8\n");
+    int ret = 0, len, tail_len, i, written, frags;
+    unsigned char buf[SSL_MAX_CONTENT_LEN + 1];
+    const char *pers = "ssl_client";
+    opt.server_name         = DFL_SERVER_NAME;
+    opt.server_addr         = DFL_SERVER_ADDR;
+    opt.server_port         = DFL_SERVER_PORT;
+    opt.debug_level         = DFL_DEBUG_LEVEL;
+    opt.nbio                = DFL_NBIO;
+    opt.request_page        = DFL_REQUEST_PAGE;
+    opt.request_size        = DFL_REQUEST_SIZE;
+    opt.ca_file             = DFL_CA_FILE;
+    opt.ca_path             = DFL_CA_PATH;
+    opt.crt_file            = DFL_CRT_FILE;
+    opt.key_file            = DFL_KEY_FILE;
+    opt.psk                 = DFL_PSK;
+    opt.psk_identity        = DFL_PSK_IDENTITY;
+    opt.force_ciphersuite[0]= DFL_FORCE_CIPHER;
+    opt.renegotiation       = DFL_RENEGOTIATION;
+    opt.allow_legacy        = DFL_ALLOW_LEGACY;
+    opt.renegotiate         = DFL_RENEGOTIATE;
+    opt.exchanges           = DFL_EXCHANGES;
+    opt.min_version         = DFL_MIN_VERSION;
+    opt.max_version         = DFL_MAX_VERSION;
+    opt.auth_mode           = DFL_AUTH_MODE;
+    opt.mfl_code            = DFL_MFL_CODE;
+    opt.trunc_hmac          = DFL_TRUNC_HMAC;
+    opt.reconnect           = DFL_RECONNECT;
+    opt.reco_delay          = DFL_RECO_DELAY;
+    opt.tickets             = DFL_TICKETS;
+    opt.alpn_string         = DFL_ALPN_STRING;
+
+    entropy_context entropy;
+    ctr_drbg_context ctr_drbg;
+    ssl_context ssl;
+    ssl_session saved_session;
+    x509_crt cacert;
+    x509_crt clicert;
+    pk_context pkey;
+
+    memset( &ssl, 0, sizeof( ssl_context ) );
+    memset( &saved_session, 0, sizeof( ssl_session ) );
+    x509_crt_init( &cacert );
+    x509_crt_init( &clicert );
+    pk_init( &pkey );
+    LOGE("9\n");
+     /*
+     * 0. Initialize the RNG and the session data
+     */
+
+    entropy_init( &entropy );
+    if( ( ret = ctr_drbg_init( &ctr_drbg, entropy_func, &entropy,
+                               (const unsigned char *) pers,
+                               strlen( pers ) ) ) != 0 )
+    {
+        LOGE( " failed\n  ! ctr_drbg_init returned -0x%x\n", -ret );
+        return -1;
+    }
+    if(!ingnore_cert)
+    {
+        LOGE("10\n");
+     /*
+     * 1.1. Load the trusted CA
+     */
+    //ret = x509_crt_parse(&cacert,ca1_cert,strlen(ca1_cert));
+        ret = x509_crt_parse_file( &cacert, opt.ca_path );
+        if( ret < 0 )
+        {
+            LOGE( " failed\n  !  ca x509_crt_parse returned -0x%x\n\n", -ret );
+            return -1;
+        }
+
+         /*
+         * 1.2. Load own certificate and private key
+         *
+         * (can be skipped if client authentication is not required)
+         */
+
+        ret = x509_crt_parse_file( &clicert, opt.crt_file );
+        if( ret != 0 )
+        {
+            LOGE( " failed\n  !  crt x509_crt_parse returned -0x%x\n\n", -ret );
+            return -1;
+        }
+
+        ret = pk_parse_keyfile( &pkey, opt.key_file, NULL);
+        if( ret != 0 )
+        {
+            LOGE( " failed\n  !  key x509_crt_parse returned -0x%x\n\n", -ret );
+            return -1;
+        }
+    }
+     /*
+     * 2. Setup stuff
+     */
+    LOGE( "  . Setting up the SSL/TLS structure..." );
+
+    if( ( ret = ssl_init( &ssl ) ) != 0 )
+    {
+        LOGE( " failed\n  ! ssl_init returned -0x%x\n\n", -ret );
+        return -1;
+    }
+
+    ssl_set_endpoint( &ssl, SSL_IS_CLIENT );
+    if(ingnore_cert)
+    {
+        opt.auth_mode = SSL_VERIFY_OPTIONAL;
+    }
+    else
+    {
+        opt.auth_mode = SSL_VERIFY_REQUIRED;
+    }
+
+    ssl_set_authmode( &ssl, opt.auth_mode );
+
+    ssl_set_rng( &ssl, ctr_drbg_random, &ctr_drbg );
+
+    ssl_set_bio( &ssl, net_recv, &fd, net_send, &fd );
+
+    ssl_set_renegotiation( &ssl, opt.renegotiation );
+    ssl_legacy_renegotiation( &ssl, opt.allow_legacy );
+
+    ssl_set_ca_chain( &ssl, &cacert, NULL, NULL );
+    if(!ingnore_cert)
+    {
+        if( ( ret = ssl_set_own_cert( &ssl, &clicert, &pkey ) ) != 0 )
+            {
+                LOGE( " failed\n  ! ssl_set_own_cert returned %d\n\n", ret );
+                    return -1;
+            }
+    }
+    if( opt.min_version != -1 )
+        ssl_set_min_version( &ssl, SSL_MAJOR_VERSION_3, opt.min_version );
+    if( opt.max_version != -1 )
+        ssl_set_max_version( &ssl, SSL_MAJOR_VERSION_3, opt.max_version );
+    /*
+     * 3. Handshake
+     */
+    LOGE( "  . Performing the SSL/TLS handshake..." );
+
+    while( ( ret = ssl_handshake( &ssl ) ) != 0 )
+    {
+        if( ret != POLARSSL_ERR_NET_WANT_READ && ret != POLARSSL_ERR_NET_WANT_WRITE )
+        {
+            LOGE( " failed\n  ! ssl_handshake returned -0x%x\n", -ret );
+            if( ret == POLARSSL_ERR_X509_CERT_VERIFY_FAILED )
+                LOGE(
+                    "    Unable to verify the server's certificate. "
+                        "Either it is invalid,\n"
+                    "    or you didn't set ca_file or ca_path "
+                        "to an appropriate value.\n"
+                    "    Alternatively, you may want to use "
+                        "auth_mode=optional for testing purposes.\n" );
+            LOGE( "\n" );
+            return -1;;
+        }
+    }
+
+    LOGE( " ok\n    [ Protocol is %s ]\n    [ Ciphersuite is %s ]\n",ssl_get_version( &ssl ), ssl_get_ciphersuite( &ssl ) );
+    printf( " ok\n    [ Protocol is %s ]\n    [ Ciphersuite is %s ]\n",ssl_get_version( &ssl ), ssl_get_ciphersuite( &ssl ) );
+
+    /*
+     * 4. Verify the server certificate
+     */
+    LOGE( "  . Verifying peer X.509 certificate..." );
+
+    if( ( ret = ssl_get_verify_result( &ssl ) ) != 0 )
+    {
+        LOGE( " failed\n" );
+
+        if( ( ret & BADCERT_EXPIRED ) != 0 )
+            LOGE( "  ! server certificate has expired\n" );
+
+        if( ( ret & BADCERT_REVOKED ) != 0 )
+            LOGE( "  ! server certificate has been revoked\n" );
+
+        if( ( ret & BADCERT_CN_MISMATCH ) != 0 )
+            LOGE( "  ! CN mismatch (expected CN=%s)\n", opt.server_name );
+
+        if( ( ret & BADCERT_NOT_TRUSTED ) != 0 )
+            LOGE( "  ! self-signed or not signed by a trusted CA\n" );
+
+    }
+
+    if( ssl_get_peer_cert( &ssl ) != NULL )
+    {
+        LOGE( "  . Peer certificate information    ...\n" );
+        x509_crt_info( (char *) buf, sizeof( buf ) - 1, "      ",
+                       ssl_get_peer_cert( &ssl ) );
+        LOGE( "%s\n", buf );
+    }
+
+    inter_info->cacert = &cacert;
+    inter_info->clicert = &clicert;
+    inter_info->ctr_drbg = &ctr_drbg;
+    inter_info->entropy = &entropy;
+    inter_info->pkey = &pkey;
+    inter_info->saved_session = &saved_session;
+    inter_info->ssl  = &ssl;
+
+    return 0;
+}
+
+int mbtk_ssl_close(mbtk_sock_inter_info_s *inter_info)
+{
+    if (inter_info == NULL)
+    {
+        return -1;
+    }
+
+    int ret = -1;
+    while( ( ret = ssl_close_notify( inter_info->ssl ) ) < 0 )
+    {
+        if( ret == POLARSSL_ERR_NET_CONN_RESET )
+        {
+            LOGE( " ok (already closed by peer)\n" );
+            ret = 0;
+            return -1;
+        }
+
+        if( ret != POLARSSL_ERR_NET_WANT_READ &&
+            ret != POLARSSL_ERR_NET_WANT_WRITE )
+        {
+            LOGE( " failed\n  ! ssl_close_notify returned %d\n\n", ret );
+            return -1;
+        }
+    }
+
+    x509_crt_free( inter_info->clicert );
+    x509_crt_free( inter_info->cacert );
+    pk_free( inter_info->pkey );
+    ssl_session_free( inter_info->saved_session );
+    ssl_free( inter_info->ssl );
+    ctr_drbg_free( inter_info->ctr_drbg );
+    entropy_free( inter_info->entropy );
+    return 0;
+}
+
+extern mbtk_sock_session mbtk_sock_open(mbtk_sock_handle handle,mbtk_sock_info *info,
+                unsigned int timeout,
+                int *mbtk_errno)
+{
+    if(handle < 0 || handle >= MBTK_HANDLE_MAX_NUM
+        || mbtk_sock[handle] == NULL) {
+        LOGE("Socket not inited.");
+        return -1;
+    }
+
+    *mbtk_errno = MBTK_SOCK_ERROR;
+    if(info == NULL) {
+        LOGE("mbtk_sock_info not be NULL.");
+        return -1;
+    }
+
+    int index_free = sock_find_first_free(mbtk_sock[handle]->inter_infos);
+    if(index_free < 0) {
+        LOGE("sock_find_first_free() fail.");
+        return -1;
+    }
+
+    memcpy(&(mbtk_sock[handle]->infos[index_free]),info,sizeof(mbtk_sock_info));
+    if(info->type == MBTK_SOCK_UDP) { // UDP
+        if((mbtk_sock[handle]->inter_infos[index_free].fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0){
+            LOGE("socket() fail.[%d]",errno);
+            goto result_fail;
+        }
+    } else { // TCP
+        if((mbtk_sock[handle]->inter_infos[index_free].fd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
+            LOGE("socket() fail.[%d]",errno);
+            goto result_fail;
+        }
+    }
+    // Set O_NONBLOCK
+    int flags = fcntl(mbtk_sock[handle]->inter_infos[index_free].fd, F_GETFL, 0);
+    if (flags < 0) {
+        LOGE("Get flags error:%s\n", strerror(errno));
+        goto result_fail_with_close;
+    }
+    flags |= O_NONBLOCK;
+    if (fcntl(mbtk_sock[handle]->inter_infos[index_free].fd, F_SETFL, flags) < 0) {
+        LOGE("Set flags error:%s\n", strerror(errno));
+        goto result_fail_with_close;
+    }
+
+    // Connect
+    LOGD("Start conn:%s:%d",info->address,info->port);
+    if(strlen(info->address) > 0 && info->port > 0) {
+        struct sockaddr_in servaddr;
+        bzero(&servaddr, sizeof(servaddr));
+        servaddr.sin_family = AF_INET;
+        servaddr.sin_port = htons(info->port);
+
+        struct hostent *he = gethostbyname(info->address);
+        if (he == NULL){
+            LOGE("gethostbyname() fail.[%d]",errno);
+            goto result_fail_with_close;
+        } else {
+            LOGD("Ip : %s",he->h_addr_list[0]);
+        }
+        memcpy(&servaddr.sin_addr, he->h_addr_list[0], sizeof(struct in_addr));
+
+        if(connect(mbtk_sock[handle]->inter_infos[index_free].fd, (SA *) &servaddr, sizeof(servaddr)) < 0){
+            if(EINPROGRESS != errno){
+                LOGE("connect() fail.[%d]",errno);
+                goto result_fail_with_close;
+            }
+        }
+
+        fd_set rset, wset;
+        FD_ZERO(&rset);
+        FD_ZERO(&wset);
+        FD_SET(mbtk_sock[handle]->inter_infos[index_free].fd, &rset);
+        FD_SET(mbtk_sock[handle]->inter_infos[index_free].fd, &wset);
+        struct timeval time_out;
+        time_out.tv_sec = timeout/1000;
+        time_out.tv_usec = timeout%1000*1000;
+        int nready = select(mbtk_sock[handle]->inter_infos[index_free].fd + 1,
+                        &rset, &wset, NULL, &time_out);
+        LOGD("nready = %d",nready);
+        if(nready == 0){// Timeout
+            LOGE("Timeout.");
+            printf("Timeout.\n");
+            goto result_fail_with_close;
+        }else{
+            if (FD_ISSET(mbtk_sock[handle]->inter_infos[index_free].fd, &rset)
+                && FD_ISSET(mbtk_sock[handle]->inter_infos[index_free].fd, &wset)) {
+                int error = -1;
+                int len = sizeof(int);
+                LOGE("Can read and write.");
+                if(getsockopt(mbtk_sock[handle]->inter_infos[index_free].fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0){
+                    LOGE("getsockopt fail.[%d]",errno);
+                    goto result_fail_with_close;
+                }else{
+                    LOGE("error = %d",error);
+                    if(error != 0){ // Fail
+                        goto result_fail_with_close;
+                    }
+                }
+            }else if(FD_ISSET(mbtk_sock[handle]->inter_infos[index_free].fd, &wset)){
+                LOGI("Can write.");
+                printf("Can write.\n");
+            }else{
+                LOGE("Can read(Impossible).");
+                goto result_fail_with_close;
+            }
+        }
+    } else {
+        LOGE("Can not conn.");
+        goto result_fail_with_close;
+    }
+
+    if(mbtk_sock[handle]->init_info.sock_cb) {
+        struct epoll_event ev;
+        ev.data.fd = mbtk_sock[handle]->inter_infos[index_free].fd;
+        ev.events = EPOLLIN | EPOLLET;
+        epoll_ctl(epoll_fd,EPOLL_CTL_ADD,mbtk_sock[handle]->inter_infos[index_free].fd,&ev);
+    }
+#if 1
+    if(info->ftp_ssl_support)
+    {
+        if(info->is_support_ssl){
+            mbtk_sock[handle]->infos[index_free].is_support_ssl = 0;
+            unsigned char mbtk_ftp_ssl_read_buf_s[256];
+            int err_rw;
+                memset(mbtk_ftp_ssl_read_buf_s,0,sizeof(mbtk_ftp_ssl_read_buf_s));
+                mbtk_sock_read(handle,mbtk_sock[handle]->inter_infos[index_free].fd,
+                        mbtk_ftp_ssl_read_buf_s,
+                        sizeof(mbtk_ftp_ssl_read_buf_s),
+                        60000,
+                        &err_rw);
+                printf("\nmbtk_sock_read:\n%s\n",mbtk_ftp_ssl_read_buf_s);
+
+            char cmd_buff[50];
+            int len=0,code;
+            memset(cmd_buff,0,sizeof(cmd_buff));
+
+            len = snprintf(cmd_buff, 50, "AUTH TLS\r\n");
+            cmd_buff[len] = '\0';
+            //printf("\n cmd_buff = %s\n", cmd_buff);
+
+            mbtk_sock_write(handle,mbtk_sock[handle]->inter_infos[index_free].fd,
+                                                cmd_buff,
+                                                strlen(cmd_buff),
+                                                60000,
+                                                &err_rw);
+
+            memset(mbtk_ftp_ssl_read_buf_s,0,sizeof(mbtk_ftp_ssl_read_buf_s));
+                mbtk_sock_read(handle,mbtk_sock[handle]->inter_infos[index_free].fd,
+                        mbtk_ftp_ssl_read_buf_s,
+                        sizeof(mbtk_ftp_ssl_read_buf_s),
+                        60000,
+                        &err_rw);
+                printf("\nmbtk_sock_read:\n%s\n",mbtk_ftp_ssl_read_buf_s);
+                
+            mbtk_sock[handle]->infos[index_free].is_support_ssl=1;
+        }else{
+            mbtk_sock[handle]->infos[index_free].is_support_ssl=1;
+        }
+    }
+#endif
+    if(info->is_support_ssl){
+        if(mbtk_ssl_init(mbtk_sock[handle]->inter_infos[index_free].fd,info->ingnore_cert,&mbtk_sock[handle]->inter_infos[index_free]) == -1){
+            LOGE("mbtk_openssl_init fail");
+            goto result_fail_with_close;
+        }
+
+    }
+
+    *mbtk_errno = MBTK_SOCK_SUCCESS;
+
+    mbtk_sock[handle]->sock_num++;
+    return mbtk_sock[handle]->inter_infos[index_free].fd;
+result_fail_with_close:
+    close(mbtk_sock[handle]->inter_infos[index_free].fd);
+    mbtk_sock[handle]->inter_infos[index_free].fd = -1;
+result_fail:
+    memset(&(mbtk_sock[handle]->inter_infos[index_free]),0x0,sizeof(mbtk_sock_inter_info_s));
+    memset(&(mbtk_sock[handle]->infos[index_free]),0x0,sizeof(mbtk_sock_info));
+    LOGE("mbtk_sock_open() end:fail");
+    return -1;
+}
+extern int mbtk_ssl_init_func(mbtk_sock_handle handle ,bool ingnore_cert,mbtk_sock_session fd)
+{   
+    int i=0;
+    int index_free=0;
+
+    for (i=0;i<10;i++)
+    {
+        if(mbtk_sock[handle]->inter_infos[i].fd == fd)
+        {
+            index_free = i;
+            break;
+        }
+    }
+    return mbtk_ssl_init(mbtk_sock[handle]->inter_infos[index_free].fd,ingnore_cert,&mbtk_sock[handle]->inter_infos[index_free]);
+}
+extern int mbtk_ssl_close_func(mbtk_sock_handle handle ,bool ingnore_cert,mbtk_sock_session fd)
+{   
+    int i=0;
+    int index_free=0;
+
+    for (i=0;i<10;i++)
+    {
+        if(mbtk_sock[handle]->inter_infos[i].fd == fd)
+        {
+            index_free = i;
+            break;
+        }
+    }
+    if(mbtk_sock[handle]->inter_infos[index_free].ssl!=NULL);
+        printf("\nmbtk_sock[handle]->inter_infos[index_free].ssl not empty\n");
+    return mbtk_ssl_close(&mbtk_sock[handle]->inter_infos[index_free]);
+}
+
+extern int mbtk_sock_write(mbtk_sock_handle handle,mbtk_sock_session session,
+                void *buffer,
+                unsigned int buf_len,
+                unsigned int timeout,
+                int *mbtk_errno)
+{
+    if(handle < 0 || handle >= MBTK_HANDLE_MAX_NUM
+        || session < 0 || mbtk_sock[handle] == NULL) {
+        LOGE("Socket not inited.");
+        return -1;
+    }
+
+    *mbtk_errno = MBTK_SOCK_ERROR;
+    if(buffer == NULL) {
+        LOGE("mbtk_sock_write() args error.");
+        return -1;
+    }
+
+    mbtk_sock_inter_info_s *inter_info = NULL;
+    int index = 0;
+    while(index < MBTK_SOCK_MAX_NUM) {
+        if(session ==
+            mbtk_sock[handle]->inter_infos[index].fd) {
+            inter_info = &(mbtk_sock[handle]->inter_infos[index]);
+            break;
+        }
+        index++;
+    }
+
+    if(!sock_info_check(handle,inter_info)) {
+        LOGE("sock_info_check() fail.");
+        return -1;
+    }
+
+    index = sock_info_find_by_fd(handle,inter_info->fd);
+    if(index < 0) {
+        LOGE("No such socket in session list.");
+        return -1;
+    }
+
+    int len = 0;
+    unsigned int count = 0;
+    if(mbtk_sock[handle]->infos[index].type == MBTK_SOCK_TCP) {
+        while(count < buf_len){
+            if(mbtk_sock[handle]->infos[index].is_support_ssl)
+                len = ssl_write(inter_info->ssl,(char*)buffer + count,buf_len - count);
+            else
+                len = write(inter_info->fd,(char*)buffer + count,buf_len - count);
+            if(len < 0){
+                if(errno == EWOULDBLOCK){
+                    usleep(50000);
+                    continue;
+                } else {
+                    LOGE("write error.[%d]",errno);
+                    if(count <= 0)
+                        count = -1;
+                    break;
+                }
+            } else if(len == 0) {
+                LOGE("write error(len == 0).[%d]",errno);
+            } else {
+                count += len;
+            }
+        }
+    } else if(mbtk_sock[handle]->infos[index].type == MBTK_SOCK_UDP){
+        // Start send data
+        while(count < buf_len){
+            len = sendto(inter_info->fd,(char*)buffer + count,buf_len - count,0,NULL,0);
+            if(len < 0){
+                if(errno == EWOULDBLOCK){
+                    usleep(50000);
+                    continue;
+                } else {
+                    LOGE("sendto error.[%d]",errno);
+                    if(ECONNREFUSED == errno) { // Disconnected.
+                        LOGD("Socket Disconnected.");
+                    }
+                    break;
+                }
+            } else if(len == 0) {
+                LOGD("write error(len == 0).[%d]",errno);
+            } else {
+                count += len;
+            }
+        }
+    } else {
+        LOGE("Socket type error.");
+        return -1;
+    }
+
+    if(count == buf_len){
+        LOGD("Write data[%d/%d] success.",count,buf_len);
+    } else { // Open session fail
+        LOGD("Write data[%d/%d] fail.",count,buf_len);
+    }
+
+    *mbtk_errno = MBTK_SOCK_SUCCESS;
+    return count;
+}
+
+extern int mbtk_sock_read(mbtk_sock_handle handle,mbtk_sock_session session,
+            void *buffer,
+            unsigned int buf_len,
+            unsigned int timeout,
+            int *mbtk_errno)
+{
+    if(handle < 0 || handle >= MBTK_HANDLE_MAX_NUM
+        || session < 0 || mbtk_sock[handle] == NULL) {
+        LOGE("Socket not inited.");
+        return -1;
+    }
+
+    *mbtk_errno = MBTK_SOCK_ERROR;
+    if(buffer == NULL) {
+        LOGE("mbtk_sock_write() args error.");
+        return -1;
+    }
+
+    mbtk_sock_inter_info_s *inter_info = NULL;
+    int index = 0;
+    while(index < MBTK_SOCK_MAX_NUM) {
+        if(session ==
+            mbtk_sock[handle]->inter_infos[index].fd) {
+            inter_info = &(mbtk_sock[handle]->inter_infos[index]);
+            break;
+        }
+        index++;
+    }
+
+    if(!sock_info_check(handle,inter_info)) {
+        LOGE("sock_info_check() fail.");
+        return -1;
+    }
+
+    index = sock_info_find_by_fd(handle,inter_info->fd);
+    if(index < 0) {
+        LOGE("No such socket in session list.");
+        return -1;
+    }
+
+    unsigned int count = 0;
+    if(mbtk_sock[handle]->infos[index].type == MBTK_SOCK_TCP) {
+        int len = 0;
+        int try_count = 0;
+        int times = timeout / 50;
+        memset(buffer,0x0,buf_len);
+        while(count < buf_len){
+            try_count++;
+            if(mbtk_sock[handle]->infos[index].is_support_ssl)
+                len = ssl_read(inter_info->ssl,(char*)buffer + count,buf_len - count);
+            else
+                len = read(inter_info->fd,(char*)buffer + count,buf_len - count);
+            if(len < 0){
+                if(errno == EWOULDBLOCK){
+                    if(count > 0) // Read data
+                        break; // Read data end.
+
+                    if(try_count >= times){ // Timeout
+                        count = -1;
+                        if(times != 0) {
+                            *mbtk_errno = MBTK_SOCK_ETIMEOUT;
+                        }
+                        LOGE("Not read enough data,return.[%d/%d]",count,buf_len);
+                        break;
+                    } else {
+                        usleep(50000);
+                        continue;
+                    }
+                } else {
+                    LOGE("read error.[%d]",errno);
+                    if(count <= 0)
+                        count = -1;
+                    break;
+                }
+            } else if(len == 0) {
+                LOGE("read error(len == 0).[%d]",errno);
+                if(errno == EINPROGRESS) {
+                    if(close(inter_info->fd) == 0) {// Success
+                        LOGD("Socket disconnected.Close it.");
+                    }
+                    if(count <= 0)
+                        count = -1;
+                } else {
+                    if(count <= 0)
+                        count = 0;
+                }
+                break;
+            } else {
+                count += len;
+            }
+        }
+    } else if(mbtk_sock[handle]->infos[index].type == MBTK_SOCK_UDP) {
+        // Start recv data
+        struct sockaddr_in seraddr;
+        socklen_t seraddr_len;
+        int try_count = 0;
+        int times = timeout / 50;
+        int len = 0;
+        memset(buffer,0x0,buf_len);
+        while(TRUE){
+            try_count++;
+            seraddr_len = sizeof(struct sockaddr_in);
+            len = recvfrom(inter_info->fd,buffer,buf_len,0,&seraddr,&seraddr_len);
+            if(len < 0){
+                if(errno == EWOULDBLOCK){// No data can read.
+                    if(count > 0) // Read data
+                        break; // Read data end.
+
+                    if(try_count >= times){ // Timeout
+                        if(times == 0) {
+                            LOGE("Can not read.");
+                        } else {
+                            LOGE("Timeout");
+                            *mbtk_errno = MBTK_SOCK_ETIMEOUT;
+                        }
+                        count = -1;
+                        LOGE("Not read enough data,return.[%d/%d]",count,buf_len);
+                        break;
+                    } else {
+                        usleep(50000);
+                        continue;
+                    }
+                } else {
+                    LOGE("recvfrom error.[%d]",errno);
+                    if(count <= 0)
+                        count = -1;
+                    break;
+                }
+            } else if(len == 0) {
+                LOGE("write error(len == 0).[%d]",errno);
+                if(count <= 0)
+                    count = 0;
+                break;
+            } else {
+                count += len;
+            }
+        }
+    } else {
+        LOGE("Socket type error.");
+        return -1;
+    }
+
+//    if(count == buf_len){
+//        LOGD("Read data[%d/%d] success.",count,buf_len);
+//    } else { // Open session fail
+//        LOGD("Read data[%d/%d] fail.",count,buf_len);
+//    }
+
+    LOGV("Read data[%d/%d].",count,buf_len);
+
+    *mbtk_errno = MBTK_SOCK_SUCCESS;
+    return count;
+}
+extern int mbtk_sock_readline(mbtk_sock_handle handle,mbtk_sock_session session,
+            void *buffer,
+            unsigned int buf_len,
+            unsigned int timeout,
+            int *mbtk_errno,
+            int *read_line_count,
+            char *buf_ptr)
+{
+    if(handle < 0 || handle >= MBTK_HANDLE_MAX_NUM
+        || session < 0 || mbtk_sock[handle] == NULL) {
+        LOGE("Socket not inited.");
+        return -1;
+    }
+
+    *mbtk_errno = MBTK_SOCK_ERROR;
+    if(buffer == NULL) {
+        LOGE("mbtk_sock_write() args error.");
+        return -1;
+    }
+
+    mbtk_sock_inter_info_s *inter_info = NULL;
+    int index = 0;
+    while(index < MBTK_SOCK_MAX_NUM) {
+        if(session ==
+            mbtk_sock[handle]->inter_infos[index].fd) {
+            inter_info = &(mbtk_sock[handle]->inter_infos[index]);
+            break;
+        }
+        index++;
+    }
+
+    if(!sock_info_check(handle,inter_info)) {
+        LOGE("sock_info_check() fail.");
+        return -1;
+    }
+
+    index = sock_info_find_by_fd(handle,inter_info->fd);
+    if(index < 0) {
+        LOGE("No such socket in session list.");
+        return -1;
+    }
+
+    unsigned int count = 0;
+    unsigned int read_count = 0;
+    memset(buf_ptr, 0, buf_len);
+    char *temp_ptr = (char *)buffer;
+copy_angin_ssl:
+    while(*read_line_count > 0 && *temp_ptr != '\n') {
+        if(*temp_ptr == NULL)
+        {
+            printf("\n*temp_ptr is null\n");
+            goto read_end;
+        }
+        *buf_ptr++ = *temp_ptr++;
+        (*read_line_count)--;
+        count++;
+    }
+    if(*read_line_count == 0)
+    {
+        if(mbtk_sock[handle]->infos[index].type == MBTK_SOCK_TCP) {
+            int len = 0;
+            int try_count = 0;
+            int times = timeout / 50;
+            memset(buffer,0x0,buf_len);
+            while(count < buf_len){
+                try_count++;
+                if( inter_info->ssl == NULL)
+                    printf("\ninter_info->ssl == NULL\n");
+                if(mbtk_sock[handle]->infos[index].is_support_ssl)
+                    len = ssl_read(inter_info->ssl,(char*)buffer + count,buf_len - count);
+                else
+                    len = read(inter_info->fd,(char*)buffer + count,buf_len - count);
+                *read_line_count = len;
+                if(len < 0){
+                    if(errno == EWOULDBLOCK){
+                        if(count > 0) // Read data
+                        {
+                            *read_line_count = count;
+                            count = 0;
+                            goto copy_angin_ssl;
+                            break; // Read data end.
+                        }
+                        else
+                        {
+                            //printf("\nread_end\n");
+                            goto read_end;
+                        }
+                        if(try_count >= times){ // Timeout
+                            count = -1;
+                            if(times != 0) {
+                                *mbtk_errno = MBTK_SOCK_ETIMEOUT;
+                            }
+                            LOGE("Not read enough data,return.[%d/%d]",count,buf_len);
+                            goto read_end;
+                            break;
+                        } else {
+                            usleep(50000);
+                            continue;
+                        }
+                    } else {
+                        LOGE("read error.[%d]",errno);
+                        if(count <= 0)
+                            count = -1;
+                        else { 
+                            *read_line_count = count;
+                        }
+                        break;
+                    }
+                } else if(len == 0) {
+                    LOGE("read error(len == 0).[%d]",errno);
+                    if(errno == EINPROGRESS) {
+                        if(close(inter_info->fd) == 0) {// Success
+                            LOGD("Socket disconnected.Close it.");
+                        }
+                        if(count <= 0)
+                            count = -1;
+                    } else {
+                        if(count <= 0)
+                            count = 0;
+                        else
+                            count = -1;
+                    }
+                    goto read_end;
+                    break;
+                } else {
+                    count += len;
+                }
+            }
+        } else if(mbtk_sock[handle]->infos[index].type == MBTK_SOCK_UDP) {
+            // Start recv data
+            struct sockaddr_in seraddr;
+            socklen_t seraddr_len;
+            int try_count = 0;
+            int times = timeout / 50;
+            int len = 0;
+            memset(buffer,0x0,buf_len);
+            while(TRUE){
+                try_count++;
+                seraddr_len = sizeof(struct sockaddr_in);
+                len = recvfrom(inter_info->fd,buffer,buf_len,0,&seraddr,&seraddr_len);
+                if(len < 0){
+                    if(errno == EWOULDBLOCK){// No data can read.
+                        if(count > 0) // Read data
+                            break; // Read data end.
+
+                        if(try_count >= times){ // Timeout
+                            if(times == 0) {
+                                LOGE("Can not read.");
+                                //printf("Can not read.\n");
+                            } else {
+                                LOGE("Timeout");
+                                //printf("Timeout\n");
+                                *mbtk_errno = MBTK_SOCK_ETIMEOUT;
+                            }
+                            count = -1;
+                            LOGE("Not read enough data,return.[%d/%d]",count,buf_len);
+                            //printf("Not read enough data,return.[%d/%d]\n",count,buf_len);
+                            break;
+                        } else {
+                            usleep(50000);
+                            continue;
+                        }
+                    } else {
+                        LOGE("recvfrom error.[%d]",errno);
+                        if(count <= 0)
+                            count = -1;
+                        break;
+                    }
+                } else if(len == 0) {
+                    LOGE("write error(len == 0).[%d]",errno);
+                    if(count <= 0)
+                        count = 0;
+                    break;
+                } else {
+                    count += len;
+                }
+            }
+        } else {
+            LOGE("Socket type error.");
+            //printf("Socket type error.\n");
+            return -1;
+        }
+        count = 0;
+        goto copy_angin_ssl;
+    } else if(*temp_ptr == '\n') { // Read line.
+        *buf_ptr++ = '\n';
+        (*read_line_count)--;
+        count++;
+
+        if(*read_line_count > 0)
+            memcpy(buffer, temp_ptr + 1, *read_line_count);
+        return count;
+    }
+    LOGV("Read data[%d/%d].",count,buf_len);
+read_end:
+    *mbtk_errno = MBTK_SOCK_SUCCESS;
+    return count;
+}
+
+extern int mbtk_sock_read_async(mbtk_sock_handle handle,mbtk_sock_session session,
+            void *buffer,
+            unsigned int buf_len)
+{
+    if(handle < 0 || handle >= MBTK_HANDLE_MAX_NUM
+        || session < 0 || mbtk_sock[handle] == NULL) {
+        LOGE("Socket not inited.");
+        return -1;
+    }
+
+    if(buffer == NULL) {
+        LOGE("mbtk_sock_write() args error.");
+        return -1;
+    }
+
+    mbtk_sock_inter_info_s *inter_info = NULL;
+    int index = 0;
+    while(index < MBTK_SOCK_MAX_NUM) {
+        if(session ==
+            mbtk_sock[handle]->inter_infos[index].fd) {
+            inter_info = &(mbtk_sock[handle]->inter_infos[index]);
+            break;
+        }
+        index++;
+    }
+    if(!sock_info_check(handle,inter_info)) {
+        LOGE("sock_info_check() fail.");
+        return -1;
+    }
+
+    index = sock_info_find_by_fd(handle,inter_info->fd);
+    if(index < 0) {
+        LOGE("No such socket in session list.");
+        return -1;
+    }
+
+    int len;
+    if(mbtk_sock[handle]->infos[index].type == MBTK_SOCK_TCP) {
+TCP_READ_AGAIN:
+        memset(buffer,0x0,buf_len);
+        if(mbtk_sock[handle]->infos[index].is_support_ssl)
+            len = ssl_read(inter_info->ssl,(char*)buffer,buf_len);
+        else
+            len = read(inter_info->fd,(char*)buffer,buf_len);
+        if(len < 0){
+            if(errno == EWOULDBLOCK){
+                usleep(100000);
+                goto TCP_READ_AGAIN;
+            } else {
+                LOGE("read error.[%d]",errno);
+                return -1;
+            }
+        }
+    } else if(mbtk_sock[handle]->infos[index].type == MBTK_SOCK_UDP) {
+        // Start recv data
+        struct sockaddr_in seraddr;
+        socklen_t seraddr_len;
+UDP_READ_AGAIN:
+        memset(buffer,0x0,buf_len);
+        seraddr_len = sizeof(struct sockaddr_in);
+        len = recvfrom(inter_info->fd,buffer,buf_len,0,&seraddr,&seraddr_len);
+        if(len < 0){
+            if(errno == EWOULDBLOCK){
+                usleep(100000);
+                goto UDP_READ_AGAIN;
+            } else {
+                LOGE("read error.[%d]",errno);
+                return -1;
+            }
+        }
+    } else {
+        LOGE("Socket type error.");
+        return -1;
+    }
+
+    LOGV("Read data[%d/%d].",len,buf_len);
+
+    return len;
+}
+
+extern int mbtk_sock_close(mbtk_sock_handle handle,mbtk_sock_session session,
+            unsigned int timeout,
+            int *mbtk_errno)
+{
+    if(handle < 0 || handle >= MBTK_HANDLE_MAX_NUM
+        || session < 0 || mbtk_sock[handle] == NULL) {
+        LOGE("Socket not inited.");
+        return -1;
+    }
+
+    *mbtk_errno = MBTK_SOCK_ERROR;
+    mbtk_sock_inter_info_s *inter_info = NULL;
+    int index = 0;
+    while(index < MBTK_SOCK_MAX_NUM) {
+        if(session == mbtk_sock[handle]->inter_infos[index].fd) {
+            inter_info = &(mbtk_sock[handle]->inter_infos[index]);
+            break;
+        }
+        index++;
+    }
+    if(!sock_info_check(handle,inter_info)) {
+        LOGE("sock_info_check() fail.");
+        return -1;
+    }
+
+    index = sock_info_find_by_fd(handle,inter_info->fd);
+    if(index < 0) {
+        LOGE("No such socket in session list.");
+        return -1;
+    }
+
+    int i;
+    for(i = 0;i < MBTK_SOCK_MAX_NUM;i++) {
+        if(mbtk_sock[handle]->inter_infos[i].fd == inter_info->fd){
+            if(mbtk_sock[handle]->init_info.sock_cb) {
+                struct epoll_event ev;
+                ev.data.fd = inter_info->fd;
+                ev.events = EPOLLIN | EPOLLET;
+                epoll_ctl(epoll_fd,EPOLL_CTL_DEL,inter_info->fd,&ev);
+            }
+
+            if(close(inter_info->fd) < 0) {// Success
+                LOGE("Close socket fail[%d].",errno);
+                //break;
+            }
+            mbtk_sock[handle]->inter_infos[i].fd = -1;
+            memset(&(mbtk_sock[handle]->infos[i]),0x0,sizeof(mbtk_sock_info));
+            mbtk_sock[handle]->sock_num--;
+            break;
+        }
+    }
+
+    if(mbtk_sock[handle]->infos[index].is_support_ssl){
+        if(mbtk_ssl_close(inter_info)== -1)
+        {
+            LOGE("close ssl fail");
+            return -1;
+        }
+    }
+
+    *mbtk_errno = MBTK_SOCK_SUCCESS;
+    return 0;
+}
+
+extern int mbtk_sock_deinit(mbtk_sock_handle handle)
+{
+    if(handle < 0 || handle >= MBTK_HANDLE_MAX_NUM
+        || mbtk_sock[handle] == NULL) {
+        LOGE("Socket not inited.");
+        return -1;
+    }
+
+    if(mbtk_sock[handle]->sock_num > 0) {
+        LOGE("There are socket not close.");
+        return MBTK_SOCK_ERROR;
+    }
+
+    LOGD("mbtk_sock_deinit() start.");
+#if 0
+    sock_thread_running = FALSE;
+    write(pipe_fds[0],"0",1);
+
+    // Wait for thread exist.
+    while(sock_inited) {
+        usleep(100);
+    }
+#endif
+
+    int i;
+    for(i = 0;i < MBTK_SOCK_MAX_NUM;i++) {
+        if(mbtk_sock[handle]->inter_infos[i].fd > 0){
+            if(mbtk_sock[handle]->init_info.sock_cb) {
+                struct epoll_event ev;
+                ev.data.fd = mbtk_sock[handle]->inter_infos[i].fd;
+                ev.events = EPOLLIN | EPOLLET;
+                epoll_ctl(epoll_fd,EPOLL_CTL_DEL,mbtk_sock[handle]->inter_infos[i].fd,&ev);
+            }
+
+            if(close(mbtk_sock[handle]->inter_infos[i].fd) < 0) {// Success
+                LOGE("Close socket fail[%d].",errno);
+                //break;
+            }
+            mbtk_sock[handle]->inter_infos[i].fd = -1;
+            memset(&(mbtk_sock[handle]->infos[i]),0x0,sizeof(mbtk_sock_info));
+            break;
+        }
+    }
+
+    //memset(&mbtk_sock,0x0,sizeof(mbtk_sock_s));
+    free(mbtk_sock[handle]);
+    mbtk_sock[handle] = NULL;
+    LOGD("mbtk_sock_deinit() end.");
+    return MBTK_SOCK_SUCCESS;
+}
+
+
diff --git a/mbtk/mbtk_lib/src/mbtk_str.c b/mbtk/mbtk_lib/src/mbtk_str.c
new file mode 100755
index 0000000..ff2db9e
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_str.c
@@ -0,0 +1,250 @@
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <mbtk_str.h>
+
+/*
+* Converts all of the characters in this String to lower.
+*
+* Parameters:
+*   src - The string should be converted.
+*   dest  - The lowercase string.
+*   len - The length of result string.Must be "strlen(src) + 1"
+* Returns:
+*   The string, converted to lowercase,or NULL for fail.
+*/
+void*
+str_tolower
+(
+    const void *src,
+    void *dest,
+    size_t len
+)
+{
+    const char *s = (char*)src;
+    char *d = (char*)dest;
+    if(!s
+        || !d
+        || (strlen(d) + 1 > len)){
+        return NULL;
+    }
+
+    char* temp = d;
+    while(*s){
+        *temp++ = tolower(*s++);
+    }
+    *temp = '\0';
+
+    return dest;
+}
+
+/*
+* Converts all of the characters in this String to upper case.
+*
+* Parameters:
+*   src - The string should be converted.
+*   dest  - The uppercase string.
+*   len - The length of result string.Must be "strlen(str_ptr) + 1"
+* Returns:
+*   The string, converted to uppercase.or NULL for fail.
+*/
+void*
+str_toupper
+(
+    const void *src,
+    void *dest,
+    size_t len
+)
+{
+    const char *s = (char*)src;
+    char *d = (char*)dest;
+    if(!s
+        || !d
+        || (strlen(d) + 1 > len)){
+        return NULL;
+    }
+    char* temp = d;
+    while(*s){
+        *temp++ = toupper(*s++);
+    }
+    *temp = '\0';
+    return dest;
+}
+
+/*
+* Remove the head and tail spaces.
+*/
+void*
+str_trim
+(
+    const void* str,
+    void *result,
+    size_t len
+)
+{
+    if(str == NULL || result == NULL
+        || len <= strlen((char*)str))
+    {
+        return NULL;
+    }
+
+    char* str_ptr = (char*)str;
+    while(*str_ptr && (*str_ptr == ' ' || *str_ptr == '\t' || *str_ptr == '\n' || *str_ptr == '\r')){
+        str_ptr++;
+    }
+
+    memset(result,0x0,len);
+    if(*str_ptr && strlen(str_ptr) > 0)
+    {
+        memset(result,0x0,len);
+        memcpy(result,str_ptr,strlen(str_ptr));
+        str_ptr = (char*)result + strlen((char*)result) - 1;
+        while(*str_ptr && (*str_ptr == ' ' || *str_ptr == '\t' || *str_ptr == '\n' || *str_ptr == '\r')){
+            *str_ptr = '\0';
+            str_ptr--;
+        }
+    }
+
+    return result;
+}
+
+/*
+* Returns true if and only if this string contains the specified sequence of char values.
+*
+* Parameters:
+*   str   -   The substring to search from.
+*   sub_str - The substring to search for.
+* Returns:
+*   True if str contains sub_str, false otherwise.
+*/
+bool
+str_contains
+(
+    const void* str,
+    const void* sub_str
+)
+{
+    const char *s = (char*)str;
+    const char *s_sub = (char*)sub_str;
+
+    if(!s || !s_sub){
+        return FALSE;
+    }
+
+    return str_indexof(s,s_sub)==-1?FALSE:TRUE;
+}
+
+/*
+* Returns the index within this string of the first occurrence of the specified substring.
+* If no such substring, then -1 is returned.
+*
+* Parameters:
+*   str   -   The substring to search from.
+*   sub_str - The substring to search for.
+* Returns:
+*   The index of the first occurrence of the specified substring,
+*   or -1 if there is no such occurrence.
+*/
+ssize_t
+str_indexof
+(
+    const void* str,
+    const void* sub_str
+)
+{
+    const char *s = (char*)str;
+    const char *s_sub = (char*)sub_str;
+
+    if(!s || !s_sub){
+            return -1;
+    }
+    char* position = strstr(s,s_sub);
+    if(!position){
+        return -1;
+    }else{
+        return position - s;
+    }
+}
+
+/*
+* Returns a new string that is a substring of this string. The substring begins
+* at the specified beginIndex and extends to the character at index endIndex - 1.
+* Thus the length of the substring is endIndex-beginIndex.
+*
+* Examples:
+*   "hamburger".substring(4, 8) returns "urge"
+*   "smiles".substring(1, 5) returns "mile"
+*
+* Parameters:
+*   begin_index     The beginning index, inclusive.
+*   end_index       The ending index, exclusive.
+* Returns:
+*   The specified substring or NULL.
+*/
+void*
+str_substring
+(
+    const void* str,
+    size_t begin_index,
+    size_t end_index,
+    void *sub_str,
+    size_t len
+)
+{
+    const char* s = (char*)str;
+    char *result = (char*)sub_str;
+    if(!s
+        || !result
+        || begin_index >= end_index
+        || begin_index >= strlen(s)
+        || end_index - begin_index + 1 > len){
+        return NULL;
+    }
+
+    if(end_index > strlen(s)){
+        end_index = strlen(s);
+    }
+
+    memcpy(result,s + begin_index,end_index - begin_index);
+    result[end_index - begin_index] = '\0';
+
+    return result;
+}
+
+bool str_startwith(const char* str, const void* prefix)
+{
+    if (!str || !(char*) prefix) {
+        return FALSE;
+    }
+    if (strlen((char*) str) < strlen((char*) prefix)) {
+        return FALSE;
+    }
+    return str_indexof(str, (char*) prefix) ? FALSE : TRUE;
+}
+
+void* strstr_hex(char *haystack,int haystack_len,
+                const char *needle,int needle_len)
+{
+    int index = 0;
+    if(haystack_len < needle_len)
+        return NULL;
+
+    while(index <= haystack_len - needle_len) {
+        if(!memcmp(haystack + index,needle,needle_len)){
+            return haystack + index;
+        }
+        index++;
+    }
+
+    return NULL;
+}
+
+bool str_empty(const void *str)
+{
+    if (str && strlen((char*)str) > 0)
+        return false;
+
+    return true;
+}
+
diff --git a/mbtk/mbtk_lib/src/mbtk_task.c b/mbtk/mbtk_lib/src/mbtk_task.c
new file mode 100755
index 0000000..935369a
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_task.c
@@ -0,0 +1,139 @@
+/*************************************************************
+Description:
+    mbtk_task.c
+    Used to implement mobiletek standard task or thread interfaces
+Author:
+    YangDagang
+Date:
+    2019-7-13
+*************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include "mbtk_type.h"
+#include "mbtk_queue.h"
+#include "mbtk_task.h"
+#include "mbtk_log.h"
+
+/*************************************************************
+    Constants and Macros
+*************************************************************/
+
+/*************************************************************
+    Definitions:enum,struct,union
+*************************************************************/
+
+
+/*************************************************************
+    Variables:local,extern
+*************************************************************/
+
+/*************************************************************
+    Local Function Declaration
+*************************************************************/
+
+/*************************************************************
+    Extern Function Declaration
+*************************************************************/
+
+/*************************************************************
+    Function Definitions
+*************************************************************/
+void mbtk_mutex_init(mbtk_mutex *mutex)
+{
+    pthread_mutex_init(&mutex->crit_sect, NULL);
+}
+
+void mbtk_mutex_deinit(mbtk_mutex *mutex)
+{
+    pthread_mutex_destroy(&mutex->crit_sect);
+}
+
+void mbtk_mutex_lock(mbtk_mutex *mutex)
+{
+    pthread_mutex_lock(&mutex->crit_sect);
+}
+
+void mbtk_mutex_unlock(mbtk_mutex *mutex)
+{
+    pthread_mutex_unlock(&mutex->crit_sect);
+}
+
+int mbtk_task_start(mbtk_task_info *task)
+{
+    static pthread_attr_t thread_attr;
+    pthread_attr_init(&thread_attr);
+    if(pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED))
+    {
+        LOGE("pthread_attr_setdetachstate() fail.");
+        return -1;
+    }
+
+    if (pthread_create((pthread_t*)task->task_id,
+        &thread_attr, task->thread_run, task->args) != 0)
+    {
+        LOGE("%s errno: %d (%s)",__func__, errno, strerror(errno));
+        return -1;
+    }
+    pthread_attr_destroy(&thread_attr);
+
+    return 0;
+}
+
+int mbtk_task_queue_start(void *param,mbtk_task_cb_handle cb)
+{
+    int res = 0;
+    mbtk_task_queue_info *ts = (mbtk_task_queue_info *)param;
+
+    if(cb == NULL)
+    {
+        return MBTK_FAILE;
+    }
+    mbtk_queue_init(&ts->queue);
+    mbtk_mutex_init(&ts->mutex);
+    pthread_cond_init(&ts->cond, NULL);
+    res = pthread_create(&ts->thread_id,NULL,cb,NULL);
+    usleep(500000);
+    return res;
+}
+
+void mbtk_task_queue_stop(void *param)
+{
+    mbtk_task_queue_info *ts = (mbtk_task_queue_info *)param;
+    pthread_cancel(ts->thread_id);
+}
+
+int mbtk_signal_send(void *param,mbtk_signal_info* info)
+{
+    int res = 0;
+    mbtk_task_queue_info *ts = (mbtk_task_queue_info *)param;
+
+    mbtk_mutex_lock(&ts->mutex);
+    res = mbtk_queue_put(&ts->queue,(void*)info);
+    pthread_cond_signal(&ts->cond);
+    mbtk_mutex_unlock(&ts->mutex);
+    return res;
+}
+
+mbtk_signal_info *mbtk_signal_get(void *param)
+{
+    void *res = NULL;
+    mbtk_task_queue_info *ts = (mbtk_task_queue_info *)param;
+
+    mbtk_mutex_lock(&ts->mutex);
+    res = mbtk_queue_get(&ts->queue);
+    if(res == NULL){
+        // extern int pthread_cond_wait (pthread_cond_t *__restrict __cond,
+        //      pthread_mutex_t *__restrict __mutex)
+        pthread_cond_wait(&ts->cond, (pthread_mutex_t*)(&ts->mutex));
+        res = mbtk_queue_get(&ts->queue);
+    }
+    mbtk_mutex_unlock(&ts->mutex);
+    return res;
+}
+
diff --git a/mbtk/mbtk_lib/src/mbtk_utf.c b/mbtk/mbtk_lib/src/mbtk_utf.c
new file mode 100755
index 0000000..295ff1d
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_utf.c
@@ -0,0 +1,703 @@
+//
+// Created by hitmoon on 15-12-17.
+//
+#include "mbtk_utf.h"
+#include <stdio.h>
+#include <wchar.h>
+#include <string.h>
+
+static const int halfShift = 10;
+/* used for shifting by 10 bits */
+
+static const UTF32 halfBase = 0x0010000UL;
+static const UTF32 halfMask = 0x3FFUL;
+
+#define UNI_SUR_HIGH_START  (UTF32)0xD800
+#define UNI_SUR_HIGH_END    (UTF32)0xDBFF
+#define UNI_SUR_LOW_START   (UTF32)0xDC00
+#define UNI_SUR_LOW_END     (UTF32)0xDFFF
+#define false       0
+#define true        1
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF32toUTF16(
+        const UTF32 **sourceStart, const UTF32 *sourceEnd,
+        UTF16 **targetStart, UTF16 *targetEnd, ConversionFlags flags) {
+    ConversionResult result = conversionOK;
+    const UTF32 *source = *sourceStart;
+    UTF16 *target = *targetStart;
+    while (source < sourceEnd) {
+        UTF32 ch;
+        if (target >= targetEnd) {
+            result = targetExhausted;
+            break;
+        }
+        ch = *source++;
+        if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
+            /* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */
+            if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
+                if (flags == strictConversion) {
+                    --source; /* return to the illegal value itself */
+                    result = sourceIllegal;
+                    break;
+                } else {
+                    *target++ = UNI_REPLACEMENT_CHAR;
+                }
+            } else {
+                *target++ = (UTF16) ch; /* normal case */
+            }
+        } else if (ch > UNI_MAX_LEGAL_UTF32) {
+            if (flags == strictConversion) {
+                result = sourceIllegal;
+            } else {
+                *target++ = UNI_REPLACEMENT_CHAR;
+            }
+        } else {
+            /* target is a character in range 0xFFFF - 0x10FFFF. */
+            if (target + 1 >= targetEnd) {
+                --source; /* Back up source pointer! */
+                result = targetExhausted;
+                break;
+            }
+            ch -= halfBase;
+            *target++ = (UTF16) ((ch >> halfShift) + UNI_SUR_HIGH_START);
+            *target++ = (UTF16) ((ch & halfMask) + UNI_SUR_LOW_START);
+        }
+    }
+    *sourceStart = source;
+    *targetStart = target;
+    return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF16toUTF32(
+        const UTF16 **sourceStart, const UTF16 *sourceEnd,
+        UTF32 **targetStart, UTF32 *targetEnd, ConversionFlags flags) {
+    ConversionResult result = conversionOK;
+    const UTF16 *source = *sourceStart;
+    UTF32 *target = *targetStart;
+    UTF32 ch, ch2;
+    while (source < sourceEnd) {
+        const UTF16 *oldSource = source; /*  In case we have to back up because of target overflow. */
+        ch = *source++;
+        /* If we have a surrogate pair, convert to UTF32 first. */
+        if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) {
+            /* If the 16 bits following the high surrogate are in the source buffer... */
+            if (source < sourceEnd) {
+                ch2 = *source;
+                /* If it's a low surrogate, convert to UTF32. */
+                if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
+                    ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
+                         + (ch2 - UNI_SUR_LOW_START) + halfBase;
+                    ++source;
+                } else if (flags == strictConversion) { /* it's an unpaired high surrogate */
+                    --source; /* return to the illegal value itself */
+                    result = sourceIllegal;
+                    break;
+                }
+            } else { /* We don't have the 16 bits following the high surrogate. */
+                --source; /* return to the high surrogate */
+                result = sourceExhausted;
+                break;
+            }
+        } else if (flags == strictConversion) {
+            /* UTF-16 surrogate values are illegal in UTF-32 */
+            if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
+                --source; /* return to the illegal value itself */
+                result = sourceIllegal;
+                break;
+            }
+        }
+        if (target >= targetEnd) {
+            source = oldSource; /* Back up source pointer! */
+            result = targetExhausted;
+            break;
+        }
+        *target++ = ch;
+    }
+    *sourceStart = source;
+    *targetStart = target;
+#ifdef CVTUTF_DEBUG
+    if (result == sourceIllegal) {
+    fprintf(stderr, "ConvertUTF16toUTF32 illegal seq 0x%04x,%04x/n", ch, ch2);
+    fflush(stderr);
+}
+#endif
+    return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Index into the table below with the first byte of a UTF-8 sequence to
+ * get the number of trailing bytes that are supposed to follow it.
+ * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
+ * left as-is for anyone who may want to do such conversion, which was
+ * allowed in earlier algorithms.
+ */
+static const char trailingBytesForUTF8[256] = {
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5
+};
+
+/*
+ * Magic values subtracted from a buffer value during UTF8 conversion.
+ * This table contains as many values as there might be trailing bytes
+ * in a UTF-8 sequence.
+ */
+static const UTF32 offsetsFromUTF8[6] = {0x00000000UL, 0x00003080UL, 0x000E2080UL,
+                                         0x03C82080UL, 0xFA082080UL, 0x82082080UL};
+
+/*
+ * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
+ * into the first byte, depending on how many bytes follow.  There are
+ * as many entries in this table as there are UTF-8 sequence types.
+ * (I.e., one byte sequence, two byte... etc.). Remember that sequencs
+ * for *legal* UTF-8 will be 4 or fewer bytes total.
+ */
+static const UTF8 firstByteMark[7] = {0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC};
+
+/* --------------------------------------------------------------------- */
+
+/* The interface converts a whole buffer to avoid function-call overhead.
+ * Constants have been gathered. Loops & conditionals have been removed as
+ * much as possible for efficiency, in favor of drop-through switches.
+ * (See "Note A" at the bottom of the file for equivalent code.)
+ * If your compiler supports it, the "isLegalUTF8" call can be turned
+ * into an inline function.
+ */
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF16toUTF8(
+        const UTF16 **sourceStart, const UTF16 *sourceEnd,
+        UTF8 **targetStart, UTF8 *targetEnd, ConversionFlags flags) {
+    ConversionResult result = conversionOK;
+    const UTF16 *source = *sourceStart;
+    UTF8 *target = *targetStart;
+    while (source < sourceEnd) {
+        UTF32 ch;
+        unsigned short bytesToWrite = 0;
+        const UTF32 byteMask = 0xBF;
+        const UTF32 byteMark = 0x80;
+        const UTF16 *oldSource = source; /* In case we have to back up because of target overflow. */
+        ch = *source++;
+        /* If we have a surrogate pair, convert to UTF32 first. */
+        if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) {
+            /* If the 16 bits following the high surrogate are in the source buffer... */
+            if (source < sourceEnd) {
+                UTF32 ch2 = *source;
+                /* If it's a low surrogate, convert to UTF32. */
+                if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
+                    ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
+                         + (ch2 - UNI_SUR_LOW_START) + halfBase;
+                    ++source;
+                } else if (flags == strictConversion) { /* it's an unpaired high surrogate */
+                    --source; /* return to the illegal value itself */
+                    result = sourceIllegal;
+                    break;
+                }
+            } else { /* We don't have the 16 bits following the high surrogate. */
+                --source; /* return to the high surrogate */
+                result = sourceExhausted;
+                break;
+            }
+        } else if (flags == strictConversion) {
+            /* UTF-16 surrogate values are illegal in UTF-32 */
+            if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
+                --source; /* return to the illegal value itself */
+                result = sourceIllegal;
+                break;
+            }
+        }
+        /* Figure out how many bytes the result will require */
+        if (ch < (UTF32) 0x80) {
+            bytesToWrite = 1;
+        } else if (ch < (UTF32) 0x800) {
+            bytesToWrite = 2;
+        } else if (ch < (UTF32) 0x10000) {
+            bytesToWrite = 3;
+        } else if (ch < (UTF32) 0x110000) {
+            bytesToWrite = 4;
+        } else {
+            bytesToWrite = 3;
+            ch = UNI_REPLACEMENT_CHAR;
+        }
+
+        target += bytesToWrite;
+        if (target > targetEnd) {
+            source = oldSource; /* Back up source pointer! */
+            target -= bytesToWrite;
+            result = targetExhausted;
+            break;
+        }
+        switch (bytesToWrite) { /* note: everything falls through. */
+            case 4:
+                *--target = (UTF8) ((ch | byteMark) & byteMask);
+                ch >>= 6;
+            case 3:
+                *--target = (UTF8) ((ch | byteMark) & byteMask);
+                ch >>= 6;
+            case 2:
+                *--target = (UTF8) ((ch | byteMark) & byteMask);
+                ch >>= 6;
+            case 1:
+                *--target = (UTF8) (ch | firstByteMark[bytesToWrite]);
+        }
+        target += bytesToWrite;
+    }
+    *sourceStart = source;
+    *targetStart = target;
+    return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Utility routine to tell whether a sequence of bytes is legal UTF-8.
+ * This must be called with the length pre-determined by the first byte.
+ * If not calling this from ConvertUTF8to*, then the length can be set by:
+ *  length = trailingBytesForUTF8[*source]+1;
+ * and the sequence is illegal right away if there aren't that many bytes
+ * available.
+ * If presented with a length > 4, this returns false.  The Unicode
+ * definition of UTF-8 goes up to 4-byte sequences.
+ */
+
+static Boolean isLegalUTF8(const UTF8 *source, int length) {
+    UTF8 a;
+    const UTF8 *srcptr = source + length;
+    switch (length) {
+        default:
+            return false;
+            /* Everything else falls through when "true"... */
+        case 4:
+            if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
+        case 3:
+            if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
+        case 2:
+            if ((a = (*--srcptr)) > 0xBF) return false;
+
+            switch (*source) {
+                /* no fall-through in this inner switch */
+                case 0xE0:
+                    if (a < 0xA0) return false;
+                    break;
+                case 0xED:
+                    if (a > 0x9F) return false;
+                    break;
+                case 0xF0:
+                    if (a < 0x90) return false;
+                    break;
+                case 0xF4:
+                    if (a > 0x8F) return false;
+                    break;
+                default:
+                    if (a < 0x80) return false;
+            }
+
+        case 1:
+            if (*source >= 0x80 && *source < 0xC2) return false;
+    }
+    if (*source > 0xF4) return false;
+    return true;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Exported function to return whether a UTF-8 sequence is legal or not.
+ * This is not used here; it's just exported.
+ */
+Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) {
+    int length = trailingBytesForUTF8[*source] + 1;
+    if (source + length > sourceEnd) {
+        return false;
+    }
+    return isLegalUTF8(source, length);
+}
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF8toUTF16(
+        const UTF8 **sourceStart, const UTF8 *sourceEnd,
+        UTF16 **targetStart, UTF16 *targetEnd, ConversionFlags flags) {
+    ConversionResult result = conversionOK;
+    const UTF8 *source = *sourceStart;
+    UTF16 *target = *targetStart;
+    while (source < sourceEnd) {
+        UTF32 ch = 0;
+        unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
+        if (source + extraBytesToRead >= sourceEnd) {
+            result = sourceExhausted;
+            break;
+        }
+        /* Do this check whether lenient or strict */
+        if (!isLegalUTF8(source, extraBytesToRead + 1)) {
+            result = sourceIllegal;
+            break;
+        }
+        /*
+         * The cases all fall through. See "Note A" below.
+         */
+        switch (extraBytesToRead) {
+            case 5:
+                ch += *source++;
+                ch <<= 6; /* remember, illegal UTF-8 */
+            case 4:
+                ch += *source++;
+                ch <<= 6; /* remember, illegal UTF-8 */
+            case 3:
+                ch += *source++;
+                ch <<= 6;
+            case 2:
+                ch += *source++;
+                ch <<= 6;
+            case 1:
+                ch += *source++;
+                ch <<= 6;
+            case 0:
+                ch += *source++;
+        }
+        ch -= offsetsFromUTF8[extraBytesToRead];
+
+        if (target >= targetEnd) {
+            source -= (extraBytesToRead + 1); /* Back up source pointer! */
+            result = targetExhausted;
+            break;
+        }
+        if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
+            /* UTF-16 surrogate values are illegal in UTF-32 */
+            if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
+                if (flags == strictConversion) {
+                    source -= (extraBytesToRead + 1); /* return to the illegal value itself */
+                    result = sourceIllegal;
+                    break;
+                } else {
+                    *target++ = UNI_REPLACEMENT_CHAR;
+                }
+            } else {
+                *target++ = (UTF16) ch; /* normal case */
+            }
+        } else if (ch > UNI_MAX_UTF16) {
+            if (flags == strictConversion) {
+                result = sourceIllegal;
+                source -= (extraBytesToRead + 1); /* return to the start */
+                break; /* Bail out; shouldn't continue */
+            } else {
+                *target++ = UNI_REPLACEMENT_CHAR;
+            }
+        } else {
+            /* target is a character in range 0xFFFF - 0x10FFFF. */
+            if (target + 1 >= targetEnd) {
+                source -= (extraBytesToRead + 1); /* Back up source pointer! */
+                result = targetExhausted;
+                break;
+            }
+            ch -= halfBase;
+            *target++ = (UTF16) ((ch >> halfShift) + UNI_SUR_HIGH_START);
+            *target++ = (UTF16) ((ch & halfMask) + UNI_SUR_LOW_START);
+        }
+    }
+    *sourceStart = source;
+    *targetStart = target;
+    return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF32toUTF8(
+        const UTF32 **sourceStart, const UTF32 *sourceEnd,
+        UTF8 **targetStart, UTF8 *targetEnd, ConversionFlags flags) {
+    ConversionResult result = conversionOK;
+    const UTF32 *source = *sourceStart;
+    UTF8 *target = *targetStart;
+    while (source < sourceEnd) {
+        UTF32 ch;
+        unsigned short bytesToWrite = 0;
+        const UTF32 byteMask = 0xBF;
+        const UTF32 byteMark = 0x80;
+        ch = *source++;
+        if (flags == strictConversion) {
+            /* UTF-16 surrogate values are illegal in UTF-32 */
+            if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
+                --source; /* return to the illegal value itself */
+                result = sourceIllegal;
+                break;
+            }
+        }
+        /*
+         * Figure out how many bytes the result will require. Turn any
+         * illegally large UTF32 things (> Plane 17) into replacement chars.
+         */
+        if (ch < (UTF32) 0x80) {
+            bytesToWrite = 1;
+        } else if (ch < (UTF32) 0x800) {
+            bytesToWrite = 2;
+        } else if (ch < (UTF32) 0x10000) {
+            bytesToWrite = 3;
+        } else if (ch <= UNI_MAX_LEGAL_UTF32) {
+            bytesToWrite = 4;
+        } else {
+            bytesToWrite = 3;
+            ch = UNI_REPLACEMENT_CHAR;
+            result = sourceIllegal;
+        }
+
+        target += bytesToWrite;
+        if (target > targetEnd) {
+            --source; /* Back up source pointer! */
+            target -= bytesToWrite;
+            result = targetExhausted;
+            break;
+        }
+        switch (bytesToWrite) { /* note: everything falls through. */
+            case 4:
+                *--target = (UTF8) ((ch | byteMark) & byteMask);
+                ch >>= 6;
+            case 3:
+                *--target = (UTF8) ((ch | byteMark) & byteMask);
+                ch >>= 6;
+            case 2:
+                *--target = (UTF8) ((ch | byteMark) & byteMask);
+                ch >>= 6;
+            case 1:
+                *--target = (UTF8) (ch | firstByteMark[bytesToWrite]);
+        }
+        target += bytesToWrite;
+    }
+    *sourceStart = source;
+    *targetStart = target;
+    return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF8toUTF32(
+        const UTF8 **sourceStart, const UTF8 *sourceEnd,
+        UTF32 **targetStart, UTF32 *targetEnd, ConversionFlags flags) {
+    ConversionResult result = conversionOK;
+    const UTF8 *source = *sourceStart;
+    UTF32 *target = *targetStart;
+    while (source < sourceEnd) {
+        UTF32 ch = 0;
+        unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
+        if (source + extraBytesToRead >= sourceEnd) {
+            result = sourceExhausted;
+            break;
+        }
+        /* Do this check whether lenient or strict */
+        if (!isLegalUTF8(source, extraBytesToRead + 1)) {
+            result = sourceIllegal;
+            break;
+        }
+        /*
+         * The cases all fall through. See "Note A" below.
+         */
+        switch (extraBytesToRead) {
+            case 5:
+                ch += *source++;
+                ch <<= 6;
+            case 4:
+                ch += *source++;
+                ch <<= 6;
+            case 3:
+                ch += *source++;
+                ch <<= 6;
+            case 2:
+                ch += *source++;
+                ch <<= 6;
+            case 1:
+                ch += *source++;
+                ch <<= 6;
+            case 0:
+                ch += *source++;
+        }
+        ch -= offsetsFromUTF8[extraBytesToRead];
+
+        if (target >= targetEnd) {
+            source -= (extraBytesToRead + 1); /* Back up the source pointer! */
+            result = targetExhausted;
+            break;
+        }
+        if (ch <= UNI_MAX_LEGAL_UTF32) {
+            /*
+             * UTF-16 surrogate values are illegal in UTF-32, and anything
+             * over Plane 17 (> 0x10FFFF) is illegal.
+             */
+            if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
+                if (flags == strictConversion) {
+                    source -= (extraBytesToRead + 1); /* return to the illegal value itself */
+                    result = sourceIllegal;
+                    break;
+                } else {
+                    *target++ = UNI_REPLACEMENT_CHAR;
+                }
+            } else {
+                *target++ = ch;
+            }
+        } else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */
+            result = sourceIllegal;
+            *target++ = UNI_REPLACEMENT_CHAR;
+        }
+    }
+    *sourceStart = source;
+    *targetStart = target;
+    return result;
+}
+
+/* ---------------------------------------------------------------------
+
+    Note A.
+    The fall-through switches in UTF-8 reading code save a
+    temp variable, some decrements & conditionals.  The switches
+    are equivalent to the following loop:
+	{
+	    int tmpBytesToRead = extraBytesToRead+1;
+	    do {
+		ch += *source++;
+		--tmpBytesToRead;
+		if (tmpBytesToRead) ch <<= 6;
+	    } while (tmpBytesToRead > 0);
+	}
+    In UTF-8 writing code, the switches on "bytesToWrite" are
+    similarly unrolled loops.
+
+   --------------------------------------------------------------------- */
+
+const unsigned char *utf32toutf8(wchar_t *source, unsigned char *target, size_t size,  int *len){
+
+    wchar_t *s_start;
+    unsigned char *t_start;
+
+    s_start = source;
+    t_start = target;
+
+    if (ConvertUTF32toUTF8((const UTF32**) &s_start, (UTF32*)s_start + wcslen(source), (UTF8**)&t_start, (UTF8*)t_start + size, strictConversion) == conversionOK) {
+        *len = t_start - target;
+    }
+    else {
+        *len = 0;
+    }
+    target[*len] = '\0';
+    return (const unsigned char*)target;
+}
+
+
+unsigned char *utf16toutf8(unsigned short *source, unsigned char *target, size_t size,  int *len){
+
+    unsigned short *s_start;
+    unsigned char *t_start;
+
+    s_start = source;
+    t_start = target;
+
+    if (ConvertUTF16toUTF8((const UTF16**) &s_start, (UTF16*)s_start + strlen((const char*)source) / 2, (UTF8**)&t_start, (UTF8*)t_start + size, strictConversion) == conversionOK) {
+        *len = t_start - target;
+    }
+    else {
+        *len = 0;
+    }
+    target[*len] = '\0';
+    return target;
+}
+
+unsigned short *utf8toutf16(unsigned char *source, unsigned short *target, size_t size,  int *len)
+{
+    unsigned char *s_start;
+    unsigned short *t_start;
+
+    s_start = source;
+    t_start = target;
+
+    if (ConvertUTF8toUTF16((const UTF8 **)&s_start, s_start + strlen((const char*)source), &t_start, t_start + size, strictConversion) == conversionOK) {
+        *len = t_start - target;
+    }
+    else {
+        *len = 0;
+    }
+
+    return target;
+}
+
+u_int32_t next_char(unsigned char **string) {
+
+    int len = strlen((const char*)*string);
+    unsigned char ch[4];
+	int i = 0;
+
+    if (len < 4){
+        for (i = 0; i < len; i++)
+            ch[i] = (*string)[i];
+    }
+    else {
+        ch[0] = (*string)[0];
+        ch[1] = (*string)[1];
+        ch[2] = (*string)[2];
+        ch[3] = (*string)[3];
+    }
+
+    if(ch[0] < 0x80) {
+        *string =  (*string + 1);
+        return ch[0];
+    }
+    else if (ch[0] >= 0xc0 && ch[0] <= 0xdf) {
+        *string = (*string + 2);
+        return ch[1] << 8 | ch[0];
+    }
+    else if (ch[0] >= 0xe0 && ch[0] <= 0xef) {
+        *string = (*string + 3);
+        return ch[2] << 16 | ch[1] << 8 | ch[0];
+    }
+    else if (ch[0] >= 0xf0 && ch[0] <= 0xf7) {
+        *string = (*string + 4);
+        return ch[3] << 24 | ch[2] << 16 | ch[1] << 8 | ch[0];
+    }
+
+    return *(u_int32_t*)ch;
+}
+
+
+int utf8len(unsigned char *string)
+{
+    unsigned char *end;
+    int ret = 0;
+
+    end = string + strlen((const char*)string);
+    while(string < end) {
+        next_char(&string);
+        ret++;
+    }
+    return ret;
+}
+
+int is_acsii(unsigned char *string)
+{
+    while(*string) {
+        if (*string >= 0x80)
+            return 0;
+        string++;
+    }
+    return 1;
+}
+
+size_t utf8_get_size(unsigned char *source, size_t num)
+{
+	size_t ret = 0;
+
+	unsigned char *cur = source;
+	while (num-- && *cur) {
+		next_char(&cur);
+	}
+	ret = cur - source;
+
+	return ret;
+}
diff --git a/mbtk/mbtk_lib/src/mbtk_utils.c b/mbtk/mbtk_lib/src/mbtk_utils.c
new file mode 100755
index 0000000..3d61daf
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_utils.c
@@ -0,0 +1,539 @@
+#include <stdio.h>
+#include <time.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <sys/un.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <string.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#include "mbtk_type.h"
+#include "mbtk_utils.h"
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+#define LOG_TAG "mbtk_utils"
+#include "mbtk_log.h"
+
+#define MBTK_AT_BUF_SIZE 2048
+#define MBTK_AT_CLIENT_SOCKET "/data/sock_mbtk_at"
+#define PROC_CMDLINE "/proc/cmdline"
+#define TIME_FORMAT "%F %T"
+
+static int at_fd = -1;
+static bool at_timeout = FALSE;
+
+static void
+at_timer_alrm(int signo)
+{
+    LOGW("AT Timeout.[%d]\n",signo);
+    at_timeout = TRUE;
+}
+
+
+/*
+* Exec shell command.
+*/
+bool mbtk_cmd_line
+(
+    const char *cmd,
+    char *buf,
+    int buf_size
+)
+{
+    FILE *fcmd;
+    bool result = FALSE;
+    fcmd = popen(cmd, "r");
+    memset(buf, 0, buf_size);
+    if(fcmd)
+    {
+        int pos = 0;
+        int len = 0;
+
+        while(!ferror(fcmd) && !feof(fcmd))
+        {
+            if(buf_size - pos == 0)
+            {
+                break;
+            }
+            len = fread(buf + pos,1,buf_size - pos,fcmd);
+            if(len > 0)
+                pos += len;
+        }
+
+        if(buf_size == pos)
+            buf[buf_size - 1] = '\0';
+
+        pclose(fcmd);
+        result = TRUE;
+    }
+
+    LOGV("%s [result:%d]: %s",cmd,result,buf);
+
+    return result;
+}
+
+bool mbtk_cmd_line_ex
+(
+    const char *cmd,
+    mbtk_cmd_cb_func cb
+)
+{
+#define BUFF_SIZE 1024
+    FILE *fcmd;
+    bool result = FALSE;
+    // Get stdout and stderr data.
+    // xxx 2>&1
+    char buff[BUFF_SIZE + 1] = {0};
+    snprintf(buff, BUFF_SIZE + 1, "%s 2>&1", cmd);
+    fcmd = popen(buff, "r");
+    if(!cb)
+    {
+        return FALSE;
+    }
+    if(fcmd)
+    {
+        int len = 0;
+        if(setvbuf(fcmd, NULL, _IOLBF, BUFF_SIZE)) {
+            LOGE("setvbuf() fail:%d", errno);
+        }
+        errno = 0;
+        LOGI("ferror - %d,feof - %d",ferror(fcmd),feof(fcmd));
+        while(!ferror(fcmd) && !feof(fcmd))
+        {
+            memset(buff, 0, BUFF_SIZE + 1);
+            len = fread(buff,1,BUFF_SIZE,fcmd);
+            if(len > 0)
+            {
+                cb(buff,len);
+            }
+            else
+            {
+                LOGE("len - %d,errno - %d",len,errno);
+            }
+        }
+
+        pclose(fcmd);
+        result = TRUE;
+
+        cb(NULL,0);
+    }
+    else
+    {
+        LOGE("popen() fail.[errno=%d]",errno);
+        cb(NULL,0);
+    }
+
+    return result;
+}
+
+
+
+#if 1
+// Send msg to stanet_daemon
+int mbtk_send_at(const void *at_req,void* at_rsp,int rsp_size,int timeout)
+{
+    if(at_fd < 0)
+    {
+        struct sockaddr_un servaddr;
+        at_fd = socket(AF_LOCAL,SOCK_STREAM,0);
+        if(at_fd < 0)
+        {
+            LOGE("socket fail.(%d)\n",errno);
+            at_fd = -1;
+            return -1;
+        }
+
+        // Set O_NONBLOCK
+//        int flags = fcntl(at_fd, F_GETFL, 0);
+//        if (flags < 0) {
+//            LOGE("Get flags error:%s\n", strerror(errno));
+//            return -1;
+//        }
+//        flags |= O_NONBLOCK;
+//        if (fcntl(at_fd, F_SETFL, flags) < 0) {
+//            LOGE("Set flags error:%s\n", strerror(errno));
+//            return -1;
+//        }
+
+        memset(&servaddr,0x0,sizeof(servaddr));
+        servaddr.sun_family = AF_LOCAL;
+        strcpy(servaddr.sun_path,MBTK_AT_CLIENT_SOCKET);
+
+        if(connect(at_fd,(struct sockaddr*)&servaddr,sizeof(servaddr)) < 0)
+        {
+            LOGE("connect fail.(%d)\n",errno);
+            close(at_fd);
+            at_fd = -1;
+            return -1;
+        }
+    }
+
+    at_timeout = FALSE;
+    int n = write(at_fd,at_req,strlen((char*)at_req));
+    if(n == -1)
+    {
+        LOGE("write fail[%d].\n",errno);
+        close(at_fd);
+        at_fd = -1;
+        return -1;
+    }
+
+    // Set timer
+    signal(SIGALRM, at_timer_alrm);
+    struct itimerval    val;
+    // Only time
+    val.it_interval.tv_sec  = 0;
+    val.it_interval.tv_usec = 0;
+    // Time
+    if(timeout >= 1000)
+    {
+        val.it_value.tv_sec  = timeout/1000;
+        val.it_value.tv_usec = timeout%1000;
+    }
+    else
+    {
+        val.it_value.tv_sec  = 0;
+        val.it_value.tv_usec = timeout;
+    }
+    if (setitimer(ITIMER_REAL, &val, NULL) == -1)
+    {
+        LOGE("setitimer fail.[%d]",errno);
+        return -1;
+    }
+
+    memset(at_rsp,0x0,rsp_size);
+    while(!at_timeout)
+    {
+        n = read(at_fd,at_rsp,rsp_size);
+        if(n < 0)
+        {
+            if(errno == EWOULDBLOCK)
+            {
+                usleep(50000);
+                continue;
+            }
+            else
+            {
+                LOGW("read error.[%d]",errno);
+                break;
+            }
+        }
+        else if (n > 0)
+        {
+            LOGI("RSP:%s",(char*)at_rsp);
+            break;
+        }
+        else
+        {
+            LOGW("read error.[%d]",errno);
+            break;
+        }
+    }
+
+    val.it_value.tv_sec = 0;
+    val.it_value.tv_usec = 0;
+    val.it_interval = val.it_value;
+    setitimer(ITIMER_REAL, &val, NULL);
+
+    if(n > 0)
+        return 0;
+
+    return -1;
+}
+#else
+int mbtk_send_at(const void *at_req,void* at_rsp,int rsp_size,int timeout)
+{
+    if(!at_req || !at_rsp || rsp_size <= 0)
+    {
+        LOGE("ARG error.");
+        return -1;
+    }
+
+    // Return "ERROR" if AT fail.
+    const char* result = sendCmd(0,(const char*)at_req);
+    memset(at_rsp,0x0,rsp_size);
+    snprintf(at_rsp,rsp_size,"%s",result);
+
+    return 0;
+}
+#endif
+
+/*
+* Set timer as microseconds.
+*/
+int mbtk_timer_set(mbtk_timer_alrm_func func,uint32 timeout_ms)
+{
+    signal(SIGALRM, func);
+    struct itimerval    val;
+    // Only time
+    val.it_interval.tv_sec  = 0;
+    val.it_interval.tv_usec = 0;
+    // Time
+    if(timeout_ms >= 1000)
+    {
+        val.it_value.tv_sec  = timeout_ms/1000;
+        val.it_value.tv_usec = timeout_ms%1000;
+    }
+    else
+    {
+        val.it_value.tv_sec  = 0;
+        val.it_value.tv_usec = timeout_ms;
+    }
+    if (setitimer(ITIMER_REAL, &val, NULL) == -1)
+    {
+        LOGE("setitimer fail.[errno - %d]",errno);
+        return -1;
+    }
+
+    return 0;
+}
+
+/**
+* Clear current timer.
+*/
+int mbtk_timer_clear()
+{
+    struct itimerval value;
+    value.it_value.tv_sec = 0;
+    value.it_value.tv_usec = 0;
+    value.it_interval = value.it_value;
+    if (setitimer(ITIMER_REAL, &value, NULL) == -1)
+    {
+        LOGE("setitimer fail.[errno - %d]",errno);
+        return -1;
+    }
+
+    return 0;
+}
+
+/* MRD still need to read /proc/cmdline after we drop root permission,
+ * so cache it in this function */
+int mbtk_get_kernel_cmdline(char *buf, int len)
+{
+    static char cmdline[MBTK_CMDLINE_LEN];
+    static int is_init = 0;
+    int ret = -1;
+    int fd;
+
+    if(!buf || len <= 0) return -1;
+
+    if(is_init)
+        goto INITED;
+
+    fd = open(PROC_CMDLINE, O_RDONLY);
+    if (fd < 0)
+        goto ERR_RET;
+
+    ret = read(fd, cmdline, MBTK_CMDLINE_LEN);
+    close(fd);
+
+    if(ret <= 0 || ret > MBTK_CMDLINE_LEN)
+        goto ERR_RET;
+    cmdline[ret - 1] = '\0';
+
+INITED:
+    ret = strlen(cmdline) + 1;
+    if(ret > len)
+        ret = len;
+
+    strncpy(buf, cmdline, ret);
+    buf[ret - 1] = '\0';
+
+    is_init = 1;
+
+    return ret;
+
+ERR_RET:
+    return -1;
+}
+
+/** returns 1 if line starts with prefix, 0 if it does not */
+int strStartsWith(const char *line, const char *prefix)
+{
+    if(prefix == NULL || strlen(prefix) == 0) {
+        return 1;
+    }
+
+    for ( ; *line != '\0' && *prefix != '\0' ; line++, prefix++) {
+        if (*line != *prefix) {
+            return 0;
+        }
+    }
+
+    return *prefix == '\0';
+}
+
+char* mbtk_time_text_get(char *buff, size_t buff_size)
+{
+    long now_nsec = 0;
+    if(buff == NULL || buff_size <= 0) {
+        return NULL;
+    }
+    memset(buff, 0x0, buff_size);
+#if 0
+    time_t now;
+    now = time(&now);
+    if(now == -1) {
+        LOGE("time() fail.");
+        return NULL;
+    }
+    struct tm *now_tm = gmtime(&now);
+#else
+    struct timespec now;
+    if(-1 == clock_gettime(CLOCK_REALTIME, &now)) {
+        LOGE("clock_gettime() fail.");
+        return NULL;
+    }
+
+    struct tm *now_tm = gmtime((time_t*)(&(now.tv_sec)));
+    now_nsec = now.tv_nsec;
+#endif
+
+    if(now_tm == NULL) {
+        LOGE("gmtime() fail.");
+        return NULL;
+    }
+
+    if(0 == strftime(buff, buff_size, TIME_FORMAT, now_tm)) {
+        LOGE("strftime() fail.");
+        return NULL;
+    }
+
+    snprintf(buff + strlen(buff), buff_size - strlen(buff),
+        "-%03ld", now_nsec / 1000000);
+
+    return buff;
+}
+
+mbtk_byteorder_enum mbtk_byteorder_get()
+{
+    union {
+        short a;
+        char c[sizeof(short)];
+    } un;
+    un.a = 0x0102;
+    if(sizeof(short) == 2) {
+        if(un.c[0] == 1 && un.c[1] == 2) {
+            return MBTK_BYTEORDER_BIG;
+        } else if(un.c[0] == 2 && un.c[1] == 1) {
+            return MBTK_BYTEORDER_LITTLE;
+        } else {
+            return MBTK_BYTEORDER_UNKNOWN;
+        }
+    } else {
+        LOGE("Unknown byte order.");
+        return MBTK_BYTEORDER_UNKNOWN;
+    }
+}
+
+uint16 byte_2_uint16(const void *buff, bool big_endian)
+{
+    const uint8* ptr = (const uint8*)buff;
+    if(big_endian) {
+        return (uint16)((ptr[0] << 8) | ptr[1]);
+    } else {
+        return (uint16)((ptr[1] << 8) | ptr[0]);
+    }
+}
+
+int uint16_2_byte(uint16 a, void *buff, bool big_endian)
+{
+    uint8* ptr = (uint8*)buff;
+    if(big_endian) {
+        ptr[0] = (uint8)(a >> 8);
+        ptr[1] = (uint8)a;
+    } else {
+        ptr[1] = (uint8)(a >> 8);
+        ptr[0] = (uint8)a;
+    }
+    return sizeof(uint16);
+}
+
+uint32 byte_2_uint32(const void *buff, bool big_endian)
+{
+    const uint8* ptr = (const uint8*)buff;
+    if(big_endian) {
+        return (uint32)((ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3]);
+    } else {
+        return (uint32)((ptr[3] << 24) | (ptr[2] << 16) | (ptr[1] << 8) | ptr[0]);
+    }
+}
+
+int uint32_2_byte(uint32 a, void *buff, bool big_endian)
+{
+    uint8* ptr = (uint8*)buff;
+    if(big_endian) {
+        ptr[0] = (uint8)(a >> 24);
+        ptr[1] = (uint8)(a >> 16);
+        ptr[2] = (uint8)(a >> 8);
+        ptr[3] = (uint8)a;
+    } else {
+        ptr[3] = (uint8)(a >> 24);
+        ptr[2] = (uint8)(a >> 16);
+        ptr[1] = (uint8)(a >> 8);
+        ptr[0] = (uint8)a;
+    }
+    return sizeof(uint32);
+}
+
+uint64 byte_2_uint64(const void *buff, bool big_endian)
+{
+    const uint8* ptr = (const uint8*)buff;
+    if(big_endian) {
+        return (uint64)(((uint64)ptr[0] << 56) | ((uint64)ptr[1] << 48) | ((uint64)ptr[2] << 40) | ((uint64)ptr[3] << 32) | (ptr[4] << 24) | (ptr[5] << 16) | (ptr[6] << 8) | ptr[7]);
+    } else {
+        return (uint64)(uint64)(((uint64)ptr[7] << 56) | ((uint64)ptr[6] << 48) | ((uint64)ptr[5] << 40) | ((uint64)ptr[4] << 32) | (ptr[3] << 24) | (ptr[2] << 16) | (ptr[1] << 8) | ptr[0]);
+    }
+}
+
+int uint64_2_byte(uint64 a, void *buff, bool big_endian)
+{
+    uint8* ptr = (uint8*)buff;
+    if(big_endian) {
+        ptr[0] = (uint8)(a >> 56);
+        ptr[1] = (uint8)(a >> 48);
+        ptr[2] = (uint8)(a >> 40);
+        ptr[3] = (uint8)(a >> 32);
+        ptr[4] = (uint8)(a >> 24);
+        ptr[5] = (uint8)(a >> 16);
+        ptr[6] = (uint8)(a >> 8);
+        ptr[7] = (uint8)a;
+    } else {
+        ptr[7] = (uint8)(a >> 56);
+        ptr[6] = (uint8)(a >> 48);
+        ptr[5] = (uint8)(a >> 40);
+        ptr[4] = (uint8)(a >> 32);
+        ptr[3] = (uint8)(a >> 24);
+        ptr[2] = (uint8)(a >> 16);
+        ptr[1] = (uint8)(a >> 8);
+        ptr[0] = (uint8)a;
+    }
+    return sizeof(uint64);
+}
+
+void* memdup(const void* data, int data_len)
+{
+    if(data && data_len > 0)
+    {
+        uint8* result = (uint8*)malloc(data_len);
+        if(result == NULL)
+        {
+            return NULL;
+        }
+        memcpy(result, data, data_len);
+        return result;
+    }
+    else
+    {
+        return NULL;
+    }
+}
+
+
diff --git a/mbtk/mbtk_lib/src/ringbuffer.c b/mbtk/mbtk_lib/src/ringbuffer.c
new file mode 100755
index 0000000..d5225cc
--- /dev/null
+++ b/mbtk/mbtk_lib/src/ringbuffer.c
@@ -0,0 +1,90 @@
+#include "ringbuffer.h"
+
+/**
+ * @file
+ * Implementation of ring buffer functions.
+ */
+
+void ring_buffer_init(ring_buffer_t *buffer, char *buf, size_t buf_size) {
+  RING_BUFFER_ASSERT(RING_BUFFER_IS_POWER_OF_TWO(buf_size) == 1);
+  buffer->buffer = buf;
+  buffer->buffer_mask = buf_size - 1;
+  buffer->tail_index = 0;
+  buffer->head_index = 0;
+}
+
+void ring_buffer_queue(ring_buffer_t *buffer, char data) {
+  /* Is buffer full? */
+  if(ring_buffer_is_full(buffer)) {
+    /* Is going to overwrite the oldest byte */
+    /* Increase tail index */
+    buffer->tail_index = ((buffer->tail_index + 1) & RING_BUFFER_MASK(buffer));
+  }
+
+  /* Place data in buffer */
+  buffer->buffer[buffer->head_index] = data;
+  buffer->head_index = ((buffer->head_index + 1) & RING_BUFFER_MASK(buffer));
+}
+
+void ring_buffer_queue_arr(ring_buffer_t *buffer, const char *data, ring_buffer_size_t size) {
+  /* Add bytes; one by one */
+  ring_buffer_size_t i;
+  for(i = 0; i < size; i++) {
+    ring_buffer_queue(buffer, data[i]);
+  }
+}
+
+uint8_t ring_buffer_dequeue(ring_buffer_t *buffer, char *data) {
+  if(ring_buffer_is_empty(buffer)) {
+    /* No items */
+    return 0;
+  }
+
+  *data = buffer->buffer[buffer->tail_index];
+  buffer->tail_index = ((buffer->tail_index + 1) & RING_BUFFER_MASK(buffer));
+  return 1;
+}
+
+ring_buffer_size_t ring_buffer_dequeue_arr(ring_buffer_t *buffer, char *data, ring_buffer_size_t len) {
+  if(ring_buffer_is_empty(buffer)) {
+    /* No items */
+    return 0;
+  }
+
+  char *data_ptr = data;
+  ring_buffer_size_t cnt = 0;
+  while((cnt < len) && ring_buffer_dequeue(buffer, data_ptr)) {
+    cnt++;
+    data_ptr++;
+  }
+  return cnt;
+}
+
+uint8_t ring_buffer_peek(ring_buffer_t *buffer, char *data, ring_buffer_size_t index) {
+  if(index >= ring_buffer_num_items(buffer)) {
+    /* No items at index */
+    return 0;
+  }
+
+  /* Add index to pointer */
+  ring_buffer_size_t data_index = ((buffer->tail_index + index) & RING_BUFFER_MASK(buffer));
+  *data = buffer->buffer[data_index];
+  return 1;
+}
+
+uint8_t ring_buffer_is_empty(ring_buffer_t *buffer) {
+  return (buffer->head_index == buffer->tail_index);
+}
+
+uint8_t ring_buffer_is_full(ring_buffer_t *buffer) {
+  return ((buffer->head_index - buffer->tail_index) & RING_BUFFER_MASK(buffer)) == RING_BUFFER_MASK(buffer);
+}
+
+ring_buffer_size_t ring_buffer_num_items(ring_buffer_t *buffer) {
+  return ((buffer->head_index - buffer->tail_index) & RING_BUFFER_MASK(buffer));
+}
+
+void ring_buffer_clean(ring_buffer_t *buffer) {
+  buffer->tail_index = 0;
+  buffer->head_index = 0;
+}