Browse Source

Linux_SDK_V0.9.5

thead_admin 1 year ago
commit
39a822e2b3
100 changed files with 25149 additions and 0 deletions
  1. 23 0
      .gitignore
  2. 48 0
      README.md
  3. 5 0
      components/aos_port/CMakeLists.txt
  4. 5 0
      components/aos_port/Config.in
  5. 11 0
      components/aos_port/aos.mk
  6. 505 0
      components/aos_port/aos_posix.c
  7. 398 0
      components/aos_port/aos_posix_timer.c
  8. 157 0
      components/aos_port/eventloop/evtloop_device.c
  9. 188 0
      components/aos_port/eventloop/evtloop_event.c
  10. 249 0
      components/aos_port/eventloop/evtloop_main.c
  11. 35 0
      components/aos_port/eventloop/evtloop_main.h
  12. 103 0
      components/aos_port/eventloop/yloop.c
  13. 24 0
      components/aos_port/include/aos/aos.h
  14. 14 0
      components/aos_port/include/aos/crc16.h
  15. 445 0
      components/aos_port/include/aos/debug.h
  16. 19 0
      components/aos_port/include/aos/errno.h
  17. 40 0
      components/aos_port/include/aos/integer.h
  18. 708 0
      components/aos_port/include/aos/kernel.h
  19. 340 0
      components/aos_port/include/aos/list.h
  20. 582 0
      components/aos_port/include/aos/queue.h
  21. 103 0
      components/aos_port/include/aos/ringblk_buf.h
  22. 92 0
      components/aos_port/include/aos/ringbuffer.h
  23. 37 0
      components/aos_port/include/aos/types.h
  24. 74 0
      components/aos_port/include/aos/version.h
  25. 29 0
      components/aos_port/include/aos/wdt.h
  26. 265 0
      components/aos_port/include/aos/yloop.h
  27. 28 0
      components/aos_port/include/yoc/init.h
  28. 74 0
      components/aos_port/include/yoc/lpm.h
  29. 69 0
      components/aos_port/include/yoc/nvram.h
  30. 42 0
      components/aos_port/include/yoc/sysinfo.h
  31. 22 0
      components/aos_port/include/yoc/yoc.h
  32. 43 0
      components/aos_port/list.c
  33. 177 0
      components/aos_port/sysinfo.c
  34. 5 0
      components/cjson/CMakeLists.txt
  35. 5 0
      components/cjson/Config.in
  36. 258 0
      components/cjson/README.md
  37. 11 0
      components/cjson/aos.mk
  38. 297 0
      components/cjson/include/cJSON.h
  39. 88 0
      components/cjson/include/cJSON_Utils.h
  40. 80 0
      components/cjson/package.yaml
  41. 3097 0
      components/cjson/src/cJSON.c
  42. 1470 0
      components/cjson/src/cJSON_Utils.c
  43. 9 0
      components/fota/CMakeLists.txt
  44. 380 0
      components/fota/README.md
  45. 582 0
      components/fota/fota/fota.c
  46. 556 0
      components/fota/fota/fota_cop.c
  47. 210 0
      components/fota/fota/fota_verify.c
  48. 394 0
      components/fota/http/http.c
  49. 39 0
      components/fota/http/http.h
  50. 250 0
      components/fota/include/yoc/fota.h
  51. 126 0
      components/fota/include/yoc/netio.h
  52. 164 0
      components/fota/netio/flash.c
  53. 144 0
      components/fota/netio/http.c
  54. 328 0
      components/fota/netio/httpc.c
  55. 98 0
      components/fota/netio/netio.c
  56. 83 0
      components/fota/package.yaml
  57. 216 0
      components/fota/util/network.c
  58. 34 0
      components/fota/util/network.h
  59. 7 0
      components/fota_service/CMakeLists.txt
  60. 338 0
      components/fota_service/dbus/fota-introspect.c
  61. 103 0
      components/fota_service/dbus/fota-new.c
  62. 681 0
      components/fota_service/dbus/fota-server.c
  63. 341 0
      components/fota_service/fotax.c
  64. 124 0
      components/fota_service/include/dbus_new_helpers.h
  65. 43 0
      components/fota_service/include/fota.h
  66. 37 0
      components/fota_service/include/fota_debug.h
  67. 34 0
      components/fota_service/include/fota_server.h
  68. 61 0
      components/fota_service/include/fotax.h
  69. 594 0
      components/fota_service/include/utils/common.h
  70. 167 0
      components/fota_service/include/utils/fota_buf.h
  71. 42 0
      components/fota_service/include/utils/includes.h
  72. 100 0
      components/fota_service/include/utils/list.h
  73. 688 0
      components/fota_service/include/utils/os.h
  74. 71 0
      components/fota_service/include/utils/trace.h
  75. 477 0
      components/fota_service/lib/fota_buf.c
  76. 29 0
      components/fota_service/lib/fota_debug.c
  77. 5 0
      components/httpclient/CMakeLists.txt
  78. 167 0
      components/httpclient/README.md
  79. 41 0
      components/httpclient/build.mk
  80. 533 0
      components/httpclient/examples/http_examples.c
  81. 68 0
      components/httpclient/include/http_auth.h
  82. 475 0
      components/httpclient/include/http_client.h
  83. 129 0
      components/httpclient/include/http_header.h
  84. 348 0
      components/httpclient/include/http_parser.h
  85. 46 0
      components/httpclient/include/http_tls.h
  86. 98 0
      components/httpclient/include/http_utils.h
  87. 77 0
      components/httpclient/package.yaml
  88. 157 0
      components/httpclient/src/http_auth.c
  89. 1321 0
      components/httpclient/src/http_client.c
  90. 242 0
      components/httpclient/src/http_header.c
  91. 2469 0
      components/httpclient/src/http_parser.c
  92. 48 0
      components/httpclient/src/http_tls.c
  93. 127 0
      components/httpclient/src/http_utils.c
  94. 5 0
      components/kv/CMakeLists.txt
  95. 99 0
      components/kv/include/aos/kv.h
  96. 53 0
      components/kv/include/aos/nvram.h
  97. 169 0
      components/kv/kv_aos.c
  98. 111 0
      components/kv/kv_fct.c
  99. 421 0
      components/kv/kv_linux.c
  100. 125 0
      components/kv/kv_linux.h

+ 23 - 0
.gitignore

@@ -0,0 +1,23 @@
+fota-client
+fota-test0
+*.swp
+*.asm
+*.o
+tags
+
+CMakeCache.txt
+CMakeFiles/
+cmake_install.cmake
+install_manifest.txt
+
+Makefile
+libfota.so
+fota-demo-c
+fota-demo-cpp
+
+*build
+install
+
+.vscode
+
+kvtool

+ 48 - 0
README.md

@@ -0,0 +1,48 @@
+## 安装 pkg-config
+```
+sudo apt install pkg-config
+```
+
+## 修改环境变量 
+
+在 文件 env.sh 中 修改
+```
+export SYSROOT_PATH=~/.local/riscv64-toolchain/sysroots
+```
+
+路径到 D1_SDK 的安装 sysroot 路径
+
+## 设置环境变量
+
+```
+source env.sh
+```
+
+## 编译
+
+```
+cd solutions/fota-service
+mkdir build
+cd build
+cmake ..
+make
+```
+
+## solution 增加组件
+
+solutions/fota-service/CMakeLists.txt 文件中 增加 COMPONENT_LIST 即可
+```
+list(APPEND COMPONENT_LIST
+            kv
+            ulog)
+
+```
+
+## 如何增加一个三方库依赖
+
+```
+pkg_check_modules (LIBS REQUIRED IMPORTED_TARGET gio-2.0 dbus-1)
+```
+
+在该语句后面增加三方库名称
+注意:名称会有版本号后缀,可以去 ~/.local/riscv64-toolchain/sysroots/riscv64-oe-linux/usr/lib/pkgconfig 目录搜索对应的具体名称

+ 5 - 0
components/aos_port/CMakeLists.txt

@@ -0,0 +1,5 @@
+file(GLOB_RECURSE LIBSOURCE "${COMPONENTS_DIR}/aos_port/*.c")
+
+include_directories(${COMPONENTS_DIR}/aos_port/include)
+
+add_library(aos_port STATIC ${LIBSOURCE})

+ 5 - 0
components/aos_port/Config.in

@@ -0,0 +1,5 @@
+config AOS_COMP_CJSON
+    bool "cJSON library"
+    default n
+    help
+        cJSON aims to be the dumbest possible parser that you can get your job done with.

+ 11 - 0
components/aos_port/aos.mk

@@ -0,0 +1,11 @@
+NAME := cjson
+
+$(NAME)_MBINS_TYPE := share
+$(NAME)_VERSION := 1.0.2
+$(NAME)_SUMMARY := An ultra-lightweight, portable, single-file, simple-as-can-be ANSI-C compliant JSON parser, under MIT license.
+
+GLOBAL_INCLUDES += include
+# don't modify to L_CFLAGS, because CONFIG_CJSON_WITHOUT_DOUBLE should enable global
+#$(NAME)_CFLAGS  += -DCONFIG_CJSON_WITHOUT_DOUBLE
+
+$(NAME)_SOURCES := src/cJSON.c src/cJPath.c

+ 505 - 0
components/aos_port/aos_posix.c

@@ -0,0 +1,505 @@
+/*
+ * Copyright (C) 2015-2017 Alibaba Group Holding Limited
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/prctl.h>
+#include <pthread.h>
+#include <errno.h>
+
+#undef WITH_LWIP
+#undef WITH_SAL
+#include <poll.h>
+#include <aos/kernel.h>
+#include <ulog/ulog.h>
+
+#define TAG "AOS"
+
+void aos_reboot(void)
+{
+    exit(0);
+}
+
+int aos_get_hz(void)
+{
+    return 100;
+}
+
+const char *aos_version_get(void)
+{
+    return "aos-linux-xxx";
+}
+
+struct targ {
+    const char *name;
+    void (*fn)(void *);
+    void *arg;
+};
+
+static void *dfl_entry(void *arg)
+{
+    struct targ *targ = arg;
+    void (*fn)(void *) = targ->fn;
+    void *farg = targ->arg;
+    prctl(PR_SET_NAME, (unsigned long)targ->name, 0, 0, 0);
+    free(targ);
+
+    fn(farg);
+
+    return 0;
+}
+
+int aos_task_new(const char *name, void (*fn)(void *), void *arg,
+                 int stack_size)
+{
+    int ret;
+    pthread_t th;
+    struct targ *targ = malloc(sizeof(*targ));
+    targ->name = strdup(name);
+    targ->fn = fn;
+    targ->arg = arg;
+    ret = pthread_create(&th, NULL, dfl_entry, targ);
+    if (ret == 0) ret = pthread_detach(th);
+    return ret;
+}
+
+int aos_task_new_ext(aos_task_t *task, const char *name, void (*fn)(void *), void *arg,
+                     int stack_size, int prio)
+{
+    return aos_task_new(name, fn, arg, stack_size);
+}
+
+void aos_task_exit(int code)
+{
+    int ret;
+    pthread_exit(&ret);
+}
+
+const char *aos_task_name(void)
+{
+    static char name[16];
+    prctl(PR_GET_NAME, (unsigned long)name, 0, 0, 0);
+    return name;
+}
+
+int aos_task_key_create(aos_task_key_t *key)
+{
+    return pthread_key_create(key, NULL);
+}
+
+void aos_task_key_delete(aos_task_key_t key)
+{
+    pthread_key_delete(key);
+}
+
+int aos_task_setspecific(aos_task_key_t key, void *vp)
+{
+    return pthread_setspecific(key, vp);
+}
+
+void *aos_task_getspecific(aos_task_key_t key)
+{
+    return pthread_getspecific(key);
+}
+
+int aos_mutex_new(aos_mutex_t *mutex)
+{
+    pthread_mutex_t *mtx = malloc(sizeof(*mtx));
+    pthread_mutexattr_t attr;
+    pthread_mutexattr_init(&attr);
+    
+    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK_NP);
+
+    pthread_mutex_init(mtx, &attr);
+    mutex->hdl = mtx;
+    return 0;
+}
+
+void aos_mutex_free(aos_mutex_t *mutex)
+{
+    pthread_mutex_destroy(mutex->hdl);
+    free(mutex->hdl);
+}
+
+int aos_mutex_lock(aos_mutex_t *mutex, unsigned int timeout)
+{
+    if (!mutex)
+        return -EINVAL;
+
+    if (timeout == AOS_WAIT_FOREVER) {
+        pthread_mutex_lock(mutex->hdl);
+    } else {
+        struct timespec abs_timeout;
+
+        abs_timeout.tv_sec  = timeout / 1000;
+        abs_timeout.tv_nsec = timeout % 1000 * 1000000;
+
+        return pthread_mutex_timedlock(mutex->hdl, &abs_timeout);
+    }
+    return 0;
+}
+
+int aos_mutex_unlock(aos_mutex_t *mutex)
+{
+    if (mutex) {
+        pthread_mutex_unlock(mutex->hdl);
+    }
+    return 0;
+}
+
+int aos_mutex_is_valid(aos_mutex_t *mutex)
+{
+    return mutex->hdl != NULL;
+}
+
+#include <semaphore.h>
+int aos_sem_new(aos_sem_t *sem, int count)
+{
+    sem_t *s = malloc(sizeof(*s));
+    sem_init(s, 0, count);
+    sem->hdl = s;
+    return 0;
+}
+
+void aos_sem_free(aos_sem_t *sem)
+{
+    if (sem == NULL) {
+        return;
+    }
+
+    sem_destroy(sem->hdl);
+    free(sem->hdl);
+}
+
+int aos_sem_wait(aos_sem_t *sem, unsigned int timeout)
+{
+    int sec;
+    int nsec;
+
+    if (sem == NULL) {
+        return -EINVAL;
+    }
+
+    if (timeout == AOS_WAIT_FOREVER) {
+        return sem_wait(sem->hdl);
+    } else if (timeout == 0) {
+        return sem_trywait(sem->hdl);
+    }
+
+    struct timespec ts;
+    clock_gettime(CLOCK_REALTIME, &ts);
+
+    sec = timeout / 1000;
+    nsec = (timeout % 1000) * 1000;
+
+    ts.tv_nsec += nsec;
+    sec += (ts.tv_nsec / 1000000000);
+    ts.tv_nsec %= 1000000000;
+    ts.tv_sec += sec;
+
+    return sem_timedwait(sem->hdl, &ts);
+}
+
+void aos_sem_signal(aos_sem_t *sem)
+{
+    if (sem == NULL) {
+        return;
+    }
+
+    sem_post(sem->hdl);
+}
+
+int aos_sem_is_valid(aos_sem_t *sem)
+{
+    return sem && sem->hdl != NULL;
+}
+
+void aos_sem_signal_all(aos_sem_t *sem)
+{
+    sem_post(sem->hdl);
+}
+
+struct queue {
+    int fds[2];
+    void *buf;
+    int size;
+    int msg_size;
+};
+
+int aos_queue_new(aos_queue_t *queue, void *buf, unsigned int size, int max_msg)
+{
+    struct queue *q = malloc(sizeof(*q));
+    pipe(q->fds);
+    q->buf = buf;
+    q->size = size;
+    q->msg_size = max_msg;
+    queue->hdl = q;
+    return 0;
+}
+
+void aos_queue_free(aos_queue_t *queue)
+{
+    struct queue *q = queue->hdl;
+    close(q->fds[0]);
+    close(q->fds[1]);
+    free(q);
+}
+
+int aos_queue_send(aos_queue_t *queue, void *msg, unsigned int size)
+{
+    struct queue *q = queue->hdl;
+    write(q->fds[1], msg, size);
+    return 0;
+}
+
+int aos_queue_recv(aos_queue_t *queue, unsigned int ms, void *msg,
+                   unsigned int *size)
+{
+    struct queue *q = queue->hdl;
+    struct pollfd rfd = {
+        .fd = q->fds[0],
+        .events = POLLIN,
+    };
+
+    poll(&rfd, 1, ms);
+    if (rfd.revents & POLLIN) {
+        int len = read(q->fds[0], msg, q->msg_size);
+        *size = len;
+        return len < 0 ? -1 : 0;
+    }
+
+    return -1;
+}
+
+int aos_queue_is_valid(aos_queue_t *queue)
+{
+    return queue->hdl != NULL;
+}
+
+void *aos_queue_buf_ptr(aos_queue_t *queue)
+{
+    struct queue *q = queue->hdl;
+    return q->buf;
+}
+
+struct work {
+    void (*fn)(void *);
+    void *arg;
+    int dly;
+};
+
+int aos_work_init(aos_work_t *work, void (*fn)(void *), void *arg, int dly)
+{
+    struct work *w = malloc(sizeof(*w));
+    w->fn = fn;
+    w->arg = arg;
+    w->dly = dly;
+    work->hdl = w;
+    return 0;
+}
+
+void aos_work_destroy(aos_work_t *work)
+{
+    free(work->hdl);
+}
+
+int aos_work_run(aos_workqueue_t *workqueue, aos_work_t *work)
+{
+    return aos_work_sched(work);
+}
+
+static void worker_entry(void *arg)
+{
+    struct work *w = arg;
+    if (w->dly) {
+        usleep(w->dly * 1000);
+    }
+    w->fn(w->arg);
+}
+
+int aos_work_sched(aos_work_t *work)
+{
+    struct work *w = work->hdl;
+    return aos_task_new("worker", worker_entry, w, 8192);
+}
+
+int aos_work_cancel(aos_work_t *work)
+{
+    return -1;
+}
+
+void *aos_zalloc(unsigned int size)
+{
+    return calloc(size, 1);
+}
+
+void *aos_zalloc_check(unsigned int size)
+{
+    return calloc(size, 1);
+}
+
+void *aos_malloc(unsigned int size)
+{
+    return malloc(size);
+}
+
+void *aos_calloc(unsigned int size, int num)
+{
+    void *ptr = malloc(size * num);
+    if (ptr) {
+        memset (ptr, 0, size * num);
+    }
+
+    return ptr;
+}
+
+void *aos_realloc(void *mem, unsigned int size)
+{
+    return realloc(mem, size);
+}
+
+void aos_alloc_trace(void *addr, size_t allocator)
+{
+}
+
+void aos_free(void *mem)
+{
+    free(mem);
+}
+
+long long aos_now()
+{
+  struct timespec ts;
+  if (clock_gettime(CLOCK_BOOTTIME, &ts) == -1) {
+    LOGE(TAG, "%s unable to get current time: %s",
+              __func__, strerror(errno));
+    return 0;
+  }
+
+  return (ts.tv_sec * 1000000000LL) + (ts.tv_nsec);
+}
+
+long long aos_now_ms()
+{
+  struct timespec ts;
+  if (clock_gettime(CLOCK_BOOTTIME, &ts) == -1) {
+    LOGE(TAG, "%s unable to get current time: %s",
+              __func__, strerror(errno));
+    return 0;
+  }
+
+  return (ts.tv_sec * 1000LL) + (ts.tv_nsec / 1000000LL);
+}
+
+void aos_msleep(int ms)
+{
+    usleep(ms * 1000);
+}
+
+void aos_init(void)
+{
+}
+
+void aos_start(void)
+{
+    while (1) {
+        usleep(1000 * 1000 * 100);
+    }
+}
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <dirent.h>
+void dumpsys_task_func(void)
+{
+    DIR *proc = opendir("/proc/self/task");
+    while (1) {
+        struct dirent *ent = readdir(proc);
+        if (!ent) {
+            break;
+        }
+        if (ent->d_name[0] == '.') {
+            continue;
+        }
+
+        char fn[128];
+        snprintf(fn, sizeof fn, "/proc/self/task/%s/comm", ent->d_name);
+        FILE *fp = fopen(fn, "r");
+        if (!fp) {
+            continue;
+        }
+        bzero(fn, sizeof fn);
+        fread(fn, sizeof(fn) - 1, 1, fp);
+        fclose(fp);
+        printf("%8s - %s", ent->d_name, fn);
+    }
+    closedir(proc);
+}
+
+int aos_event_get(aos_event_t *event, unsigned int flags, unsigned char opt,
+                  unsigned int *actl_flags, unsigned int timeout)
+{
+    int sec;
+    int nsec;
+
+    if (event == NULL) {
+        return -EINVAL;
+    }
+
+    if (timeout == AOS_WAIT_FOREVER) {
+        return sem_wait(event->hdl);
+    } else if (timeout == 0) {
+        return sem_trywait(event->hdl);
+    }
+
+    struct timespec ts;
+    clock_gettime(CLOCK_REALTIME, &ts);
+
+    sec = timeout / 1000;
+    nsec = (timeout % 1000) * 1000;
+
+    ts.tv_nsec += nsec;
+    sec += (ts.tv_nsec / 1000000000);
+    ts.tv_nsec %= 1000000000;
+    ts.tv_sec += sec;
+
+    return sem_timedwait(event->hdl, &ts);
+}
+
+int aos_event_set(aos_event_t *event, unsigned int flags, unsigned char opt)
+{
+    if (event == NULL) {
+        return -EINVAL;
+    }
+
+    sem_post(event->hdl);
+
+    return 0;
+}
+
+int aos_event_is_valid(aos_event_t *event)
+{
+    return event && event->hdl != NULL;
+}
+
+void aos_event_free(aos_event_t *event)
+{
+    if (event == NULL) {
+        return;
+    }
+
+    sem_destroy(event->hdl);
+    free(event->hdl);
+}
+
+int aos_event_new(aos_event_t *event, unsigned int flags)
+{
+    sem_t *s = malloc(sizeof(*s));
+    sem_init(s, 0, 0);
+    event->hdl = s;
+    return 0;
+}

+ 398 - 0
components/aos_port/aos_posix_timer.c

@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2015-2017 Alibaba Group Holding Limited
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <assert.h>
+#include <time.h>
+#include <errno.h>
+#include <signal.h>
+#define _GNU_SOURCE
+#include <pthread.h>
+#include <semaphore.h>
+#include "aos/list.h"
+
+#include <aos/kernel.h>
+
+#include <ulog/ulog.h>
+#define TAG "TIMER"
+
+#define CLOCKID CLOCK_MONOTONIC
+
+// #include <aos/aos.h>
+
+static timer_t s_timerid;
+static dlist_t s_aos_timer_listhead;
+static pthread_mutex_t s_list_mutex;
+static sem_t     s_timer_sem;
+
+typedef void (*timer_cb_t)(void *timer, void *args);
+
+typedef struct timer_s {
+    timer_cb_t     handler;
+    void             *args;
+    uint64_t          timeout;
+    uint64_t          start_ms;
+    int               repeat;  
+} p_timer_t;
+
+typedef struct timer_list {
+    dlist_t node;
+    p_timer_t *timer;
+} timer_list_t ;
+
+static void timer_callback(void *ptr) {
+    sem_post(&s_timer_sem);
+}
+
+static uint64_t now(void)
+{
+  struct timespec ts;
+  if (clock_gettime(CLOCK_BOOTTIME, &ts) == -1) {
+    LOGE(TAG, "%s unable to get current time: %s",
+              __func__, strerror(errno));
+    return 0;
+  }
+
+  return (ts.tv_sec * 1000LL) + (ts.tv_nsec / 1000000LL);
+}
+
+// static void start_timer(uint64_t timerout_ms)
+// {
+//     struct itimerspec wakeup_time;
+//     memset(&wakeup_time, 0, sizeof(wakeup_time));
+
+//     // LOGD(TAG, "%s timer start %d\n", __func__, timerout_ms);
+
+//     wakeup_time.it_value.tv_sec = (timerout_ms / 1000);
+//     wakeup_time.it_value.tv_nsec = (timerout_ms % 1000) * 1000000LL;
+
+//     if (timer_settime(s_timerid, TIMER_ABSTIME, &wakeup_time, NULL) == -1)
+//         LOGE(TAG, "%s unable to set timer: %s",
+//                     __func__, strerror(errno));
+// }
+
+static void restart_timer(uint64_t timerout_ms)
+{
+    struct itimerspec wakeup_time;
+    memset(&wakeup_time, 0, sizeof(wakeup_time));
+
+    // LOGD(TAG, "%s timeout %d\n", __func__, timerout_ms);
+
+    /* stop the timer */
+    timer_settime(s_timerid, TIMER_ABSTIME, &wakeup_time, NULL);
+
+    wakeup_time.it_value.tv_sec = (timerout_ms / 1000);
+    wakeup_time.it_value.tv_nsec = (timerout_ms % 1000) * 1000000LL;
+
+    if (timer_settime(s_timerid, TIMER_ABSTIME, &wakeup_time, NULL) == -1)
+        LOGE(TAG, "%s unable to set timer: %s",
+                    __func__, strerror(errno));
+}
+
+static void add_node_to_list(p_timer_t *timer)
+{
+    timer_list_t* node = malloc(sizeof(timer_list_t));
+    timer_list_t* tmp_node;
+
+    if (node == NULL) {
+        LOGE(TAG, "OOM\n");
+        return;
+    }
+
+    timer->start_ms = now() + timer->timeout;
+    node->timer = timer;
+
+
+    if (!dlist_empty(&s_aos_timer_listhead)) {
+        dlist_for_each_entry(&s_aos_timer_listhead, tmp_node, timer_list_t, node) {
+            if (tmp_node->timer->start_ms > timer->start_ms) {
+                break;
+            }
+        }
+
+        dlist_add_tail(&node->node, &tmp_node->node);
+    } else {
+        dlist_add_tail(&node->node, &s_aos_timer_listhead);
+    }
+}
+
+static int is_in_list(p_timer_t *timer)
+{
+    timer_list_t* tmp_node;
+
+    dlist_for_each_entry(&s_aos_timer_listhead, tmp_node, timer_list_t, node) {
+        if (tmp_node->timer == timer) {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+static void* timer_dispatch(void *args)
+{
+    while(1) {
+        sem_wait(&s_timer_sem);
+
+        // LOGD(TAG, "%s entry 1\n", __func__);
+
+        if (dlist_empty(&s_aos_timer_listhead)) {
+            continue;
+        }
+
+        timer_list_t* tmp_node;
+
+        // LOGD(TAG, "%s entry 2\n", __func__);
+
+        pthread_mutex_lock(&s_list_mutex);
+
+        while (1) {
+            int del = 0;
+            p_timer_t *tmp_timer;
+            uint64_t timeout_ms = now();
+
+            dlist_for_each_entry(&s_aos_timer_listhead, tmp_node, timer_list_t, node) {
+                if (tmp_node->timer->start_ms <= timeout_ms) {
+                    tmp_timer = tmp_node->timer;
+                    tmp_node->timer->handler(tmp_node->timer, tmp_node->timer->args);
+                    // LOGD(TAG, "%s handler\n", __func__);
+                    del = 1;
+                    break;
+                }
+            }
+
+            if (del == 0) {
+                break;
+            } else {
+                /* check the list for avoid call aos_timer_stop in timer->handle */
+                if (is_in_list(tmp_timer) && tmp_timer->start_ms <= timeout_ms) {
+                    dlist_del(&tmp_node->node);
+
+                    if (tmp_timer->repeat == 1) {
+                        add_node_to_list(tmp_timer);
+                    }
+                    free(tmp_node);
+                }
+            }
+        }
+
+        if (!dlist_empty(&s_aos_timer_listhead)) {
+            tmp_node = dlist_first_entry(&s_aos_timer_listhead, timer_list_t, node);
+
+            // LOGD(TAG, "%s timer start %p\n", __func__, tmp_node->timer);
+
+            restart_timer(tmp_node->timer->start_ms);
+        }
+
+        pthread_mutex_unlock(&s_list_mutex);
+    }
+
+    return NULL;
+}
+
+static void lazy_init()
+{
+    static int init = 0;
+
+    if (init == 0) {
+        struct sigevent sigevent;
+
+        memset(&sigevent, 0, sizeof(sigevent));
+        sigevent.sigev_notify = SIGEV_THREAD;
+        sigevent.sigev_notify_function = (void (*)(union sigval))timer_callback;
+
+        if (timer_create(CLOCKID, &sigevent, &s_timerid) == -1) {
+            LOGE(TAG, "%s unable to create timer with clock %d: %s\n", __func__, CLOCKID, strerror(errno));
+            assert(NULL);
+        }
+
+        dlist_init(&s_aos_timer_listhead);
+
+        pthread_mutexattr_t attr;
+        pthread_mutexattr_init(&attr);
+        
+        pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK_NP);
+
+        pthread_mutex_init(&s_list_mutex, &attr);
+
+        sem_init(&s_timer_sem, 0, 0);
+
+        pthread_t tid = 0;
+
+        pthread_create(&tid, NULL, timer_dispatch, NULL);
+
+        pthread_setname_np(tid, "timer_task");
+    }
+
+    init = 1;
+}
+
+int aos_timer_new(aos_timer_t *timer, void (*fn)(void *, void *),
+                  void *arg, int ms, int repeat)
+{
+    lazy_init();
+
+    p_timer_t *t = malloc(sizeof(p_timer_t));
+
+    if (t == NULL) {
+        LOGE(TAG, "%s OOM", __func__);
+        return -1;
+    }
+
+    t->handler  = fn;
+    t->args     = arg;
+    t->start_ms = 0;
+    t->repeat   = repeat;
+    t->timeout  = ms;
+
+    timer->hdl = t;
+
+    return 0;
+}
+
+int aos_timer_new_ext(aos_timer_t *timer, void (*fn)(void *, void *),
+                      void *arg, int ms, int repeat, unsigned char auto_run)
+{
+    aos_timer_new(timer, fn, arg, ms, repeat);
+
+    if (auto_run == 1) {
+        return aos_timer_start(timer);
+    } else {
+        return 0;
+    }
+}
+
+int aos_timer_start(aos_timer_t *timer)
+{
+    timer_list_t* tmp_node;
+
+    assert(timer);
+
+    p_timer_t *t = timer->hdl;
+    // LOGD(TAG, "%s timer %p,timeout %u\n", __func__, timer, timeout);
+
+    t->start_ms = now() + t->timeout;
+
+    timer_list_t* node = malloc(sizeof(timer_list_t));
+
+    if (node == NULL) {
+        LOGE(TAG, "OOM\n");
+        return -1;
+    }
+
+    node->timer = t;
+
+    pthread_mutex_lock(&s_list_mutex);
+
+    /* rm the timer if the timer is in the list */
+    dlist_for_each_entry(&s_aos_timer_listhead, tmp_node, timer_list_t, node) {
+        if (tmp_node->timer == t) {
+            dlist_del(&tmp_node->node);
+            free(tmp_node);
+            break;
+        }
+    }
+
+    if (!dlist_empty(&s_aos_timer_listhead)) {
+        int first = 0;
+        dlist_for_each_entry(&s_aos_timer_listhead, tmp_node, timer_list_t, node) {
+            if (tmp_node->timer->start_ms > t->start_ms) {
+                first ++;
+                break;
+            }
+        }
+
+        dlist_add_tail(&node->node, &tmp_node->node);
+
+        /* if the timer is earliest, restart posix timer */
+        if (first == 1) {
+            restart_timer(t->start_ms);
+        }
+    } else {
+        dlist_add_tail(&node->node, &s_aos_timer_listhead);
+
+        restart_timer(node->timer->start_ms);
+    }
+
+    pthread_mutex_unlock(&s_list_mutex);
+
+    return 0;
+}
+
+int aos_timer_stop(aos_timer_t *timer)
+{
+    timer_list_t* tmp_node;
+    assert(timer);
+    p_timer_t *t = timer->hdl;
+
+    pthread_mutex_lock(&s_list_mutex);
+
+    /* rm the timer if the timer is in the list */
+    dlist_for_each_entry(&s_aos_timer_listhead, tmp_node, timer_list_t, node) {
+        if (tmp_node->timer == t) {
+            dlist_del(&tmp_node->node);
+            free(tmp_node);
+            break;
+        }
+    }
+
+    pthread_mutex_unlock(&s_list_mutex);
+    return 0;
+}
+
+void aos_timer_free(aos_timer_t *timer)
+{
+    free(timer->hdl);
+}
+
+int aos_timer_change(aos_timer_t *timer, int ms)
+{
+    assert(timer);
+
+    p_timer_t *t = timer->hdl;
+
+    pthread_mutex_lock(&s_list_mutex);
+    t->timeout  = ms;
+    pthread_mutex_unlock(&s_list_mutex);
+
+    return aos_timer_start(timer);
+}
+
+int aos_timer_change_once(aos_timer_t *timer, int ms)
+{
+    assert(timer);
+
+    p_timer_t *t = timer->hdl;
+
+    pthread_mutex_lock(&s_list_mutex);
+    t->repeat = 0;
+    t->timeout  = ms;
+    pthread_mutex_unlock(&s_list_mutex);
+
+    return aos_timer_start(timer);
+}
+
+int aos_timer_is_valid(aos_timer_t *timer)
+{
+    p_timer_t *t;
+
+    if (timer == NULL) {
+        return 0;
+    }
+
+    t = timer->hdl;
+    if (t == NULL) {
+        return 0;
+    }
+
+    if (t->handler == NULL) {
+        return 0;
+    }
+
+    return 1;
+}

+ 157 - 0
components/aos_port/eventloop/evtloop_device.c

@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <aos/kernel.h>
+#include <aos/list.h>
+#include "evtloop_main.h"
+
+#ifndef DEBUG_MALLOC
+#define DEBUG_MALLOC malloc
+#endif
+
+#ifndef DEBUG_FREE
+#define DEBUG_FREE free
+#endif
+
+typedef struct {
+    aos_mutex_t    mutex;
+    void      *sem;
+    int            counter;
+    dlist_t        bufs;
+    int            cache_count;
+    dlist_t        buf_cache;
+} event_dev_t;
+
+typedef struct {
+    dlist_t node;
+    int len;
+    char buf[];
+} dev_event_t;
+
+static event_dev_t dev_pri;
+int eventloop_local_event_init(void)
+{
+    memset(&dev_pri, 0, sizeof(dev_pri));
+
+    event_dev_t *pdev = &dev_pri;
+
+    aos_mutex_new(&pdev->mutex);
+    dlist_init(&pdev->bufs);
+    dlist_init(&pdev->buf_cache);
+    return 0;
+}
+
+int eventloop_local_event_deinit(void)
+{
+    int i = 0;
+
+    event_dev_t *pdev = &dev_pri;
+    dlist_t *node = NULL;
+
+    /* clear bufs */
+    for (i = 0; i < pdev->counter; i++) {
+        node = (dlist_t *)pdev->bufs.next;
+        dlist_del(node);
+        DEBUG_FREE(node);
+    }
+
+    /* clear buf cache */
+    for (i = 0; i < pdev->cache_count; i++) {
+        node = (dlist_t *)pdev->buf_cache.next;
+        dlist_del(node);
+        DEBUG_FREE(node);
+    }
+
+    aos_mutex_free(&pdev->mutex);
+
+    return 0;
+}
+
+int event_write(const void *buf, size_t len)
+{
+    event_dev_t *pdev = &dev_pri;
+    aos_mutex_lock(&pdev->mutex, AOS_WAIT_FOREVER);
+
+    dev_event_t *evt;
+    evt = (dev_event_t *)pdev->buf_cache.next;
+
+    if (pdev->cache_count > 0 && evt->len == len) {
+        dlist_del(&evt->node);
+        pdev->cache_count --;
+    } else {
+        evt = DEBUG_MALLOC(sizeof(*evt) + len);
+    }
+
+    if (evt == NULL) {
+        len = -1;
+        goto out;
+    }
+
+    pdev->counter ++;
+
+    evt->len = len;
+    memcpy(evt->buf, buf, len);
+    dlist_add_tail(&evt->node, &pdev->bufs);
+
+    if (pdev->sem != NULL) {
+        aos_sem_signal((aos_sem_t *)pdev->sem);
+    }
+
+out:
+    aos_mutex_unlock(&pdev->mutex);
+    return len;
+}
+
+int event_read(void *buf, size_t len)
+{
+    event_dev_t *pdev = &dev_pri;
+
+    int cnt = pdev->counter;
+
+    if (!cnt) {
+        return 0;
+    }
+
+    aos_mutex_lock(&pdev->mutex, AOS_WAIT_FOREVER);
+
+    dev_event_t *evt = (dev_event_t *)pdev->bufs.next;
+    dlist_del(&evt->node);
+    cnt = (len > evt->len) ? evt->len : len;
+    memcpy(buf, evt->buf, cnt);
+
+    if (pdev->cache_count < 4) {
+        dlist_add(&evt->node, &pdev->buf_cache);
+        pdev->cache_count ++;
+    } else {
+        DEBUG_FREE(evt);
+    }
+
+    pdev->counter --;
+
+    aos_mutex_unlock(&pdev->mutex);
+
+    return cnt;
+}
+
+int event_poll(void *reader, bool setup, void *sem)
+{
+    event_dev_t *pdev = &dev_pri;
+
+    if (!setup) {
+        pdev->sem = NULL;
+        return 0;
+    }
+
+    pdev->sem = sem;
+
+    if (pdev->counter) {
+        ((eventloop_sock_t *)reader)->event = 1; /* set event flag*/
+        aos_sem_signal((aos_sem_t *)sem);
+    }
+
+    return 0;
+}
+

+ 188 - 0
components/aos_port/eventloop/evtloop_event.c

@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <aos/yloop.h>
+#include <aos/list.h>
+#include "evtloop_main.h"
+
+#ifndef DEBUG_MALLOC
+#define DEBUG_MALLOC malloc
+#endif
+
+#ifndef DEBUG_FREE
+#define DEBUG_FREE free
+#endif
+
+
+extern int event_write(const void *buf, size_t len);
+extern int event_read(void *buf, size_t len);
+extern int event_poll(void *reader, bool setup, void *sem);
+void * g_current_event_cb = NULL;
+
+typedef struct {
+    dlist_t       node;
+    aos_event_cb  cb;
+    void         *private_data;
+    uint16_t      type_filter;
+} local_event_list_node_t;
+
+static dlist_t g_local_event_list = AOS_DLIST_INIT(g_local_event_list);
+
+static int input_add_event(input_event_t *event)
+{
+    return event_write(event, sizeof(*event));
+}
+
+static void handle_events(input_event_t *event)
+{
+    if (event->type == EV_RPC) {
+        aos_call_t handler = (aos_call_t)event->value;
+        void *arg = (void *)event->extra;
+        handler(arg);
+
+        return;
+    }
+
+    if (event->type == EV_SYS && event->code == CODE_NULL && event->value == VALUE_NULL) {
+        return;
+    } else if (event->type == EV_ALL && event->code == CODE_NULL && event->value == VALUE_NULL) {
+        //LOGD("loop", "loop run test");
+        return;
+    }
+
+    local_event_list_node_t *event_node = NULL;
+    dlist_for_each_entry(&g_local_event_list, event_node, local_event_list_node_t, node) {
+        if (event_node->type_filter != EV_ALL
+            && event_node->type_filter != event->type) {
+            continue;
+        }
+        g_current_event_cb = event_node->cb;
+        (event_node->cb)(event, event_node->private_data);
+        g_current_event_cb = NULL;
+    }
+}
+
+void event_read_cb(void)
+{
+    input_event_t event;
+    int ret = event_read(&event, sizeof(event));
+
+    if (ret == sizeof(event)) {
+        handle_events(&event);
+    }
+}
+
+int aos_post_event(uint16_t type, uint16_t code, unsigned long value)
+{
+    int ret;
+    input_event_t event = {
+        .type  = type,
+        .code  = code,
+        .value = value,
+    };
+
+    ret = input_add_event(&event);
+    if (ret < 0) {
+        return -EPERM;
+    }
+
+    return 0;
+}
+
+#if 0
+int yoc_eloop_register_listener(aos_event_cb cb, void *priv)
+{
+    local_event_list_node_t *event_node = DEBUG_MALLOC(sizeof(local_event_list_node_t));
+
+    if (NULL == event_node) {
+        return -ENOMEM;
+    }
+
+    event_node->cb           = cb;
+    event_node->private_data = priv;
+    event_node->type_filter  = EV_ALL;
+    dlist_add_tail(&event_node->node, &g_local_event_list);
+
+    return YOC_OK;
+}
+#endif
+
+void yoc_eloop_unregister_listener(aos_event_cb cb, void *priv)
+{
+    local_event_list_node_t *event_node = NULL;
+    dlist_for_each_entry(&g_local_event_list, event_node, local_event_list_node_t, node) {
+        if (event_node->cb == cb && event_node->private_data == priv) {
+            dlist_del(&event_node->node);
+            DEBUG_FREE(event_node);
+            return;
+        }
+    }
+}
+
+int aos_register_event_filter(uint16_t type, aos_event_cb cb, void *priv)
+{
+    if (cb == NULL) {
+        return -EINVAL;
+    }
+
+    local_event_list_node_t *event_node = DEBUG_MALLOC(sizeof(local_event_list_node_t));
+    if (NULL == event_node) {
+        return -ENOMEM;
+    }
+
+    event_node->cb           = cb;
+    event_node->private_data = priv;
+    event_node->type_filter  = type;
+
+    dlist_add_tail(&event_node->node, &g_local_event_list);
+
+    return 0;
+}
+
+int aos_unregister_event_filter(uint16_t type, aos_event_cb cb, void *priv)
+{
+    local_event_list_node_t *event_node = NULL;
+
+    if (cb == NULL) {
+        return -EINVAL;
+    }
+
+    dlist_for_each_entry(&g_local_event_list, event_node, local_event_list_node_t, node) {
+        if (event_node->cb == cb && event_node->private_data == priv && event_node->type_filter == type) {
+            dlist_del(&event_node->node);
+            DEBUG_FREE(event_node);
+            return 0;
+        }
+    }
+
+    return -ENOMEM;
+}
+/*
+ * schedule a callback in yos loop main thread
+ */
+int aos_schedule_call(aos_call_t fun, void *arg)
+{
+    int ret;
+
+    input_event_t event = {
+        .type = EV_RPC,
+        .value = (unsigned long)fun,
+        .extra = (unsigned long)arg,
+    };
+
+    if (fun == NULL) {
+        return -EINVAL;
+    }
+
+    ret = input_add_event(&event);
+    if (ret < 0) {
+        return -EPERM;
+    }
+
+    return 0;
+}
+

+ 249 - 0
components/aos_port/eventloop/evtloop_main.c

@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <sys/time.h>
+
+#include <aos/yloop.h>
+#include <aos/kernel.h>
+#include <aos/list.h>
+#include <errno.h>
+#include "evtloop_main.h"
+
+//static const char *TAG = "eventloop";
+
+#ifndef DEBUG_MALLOC
+#define DEBUG_MALLOC malloc
+#endif
+#ifndef DEBUG_REALLOC
+#define DEBUG_REALLOC realloc
+#endif
+#ifndef DEBUG_CALLOC
+#define DEBUG_CALLOC calloc
+#endif
+
+#ifndef DEBUG_FREE
+#define DEBUG_FREE free
+#endif
+
+#define LOOP_WDT_TIMER   (1000 * 10)
+
+typedef void (*eventloop_timeout_cb)(void *private_data);
+
+typedef struct eventloop_timeout_s {
+    dlist_t          next;
+    long long        timeout_ms;
+    void             *private_data;
+    eventloop_timeout_cb        cb;
+    int              ms;
+} eventloop_timeout_t;
+
+typedef struct {
+    dlist_t          timeouts;
+    eventloop_sock_t    reader;
+    uint8_t          max_sock;
+    bool             pending_terminate;
+    bool             terminate;
+} eventloop_ctx_t;
+
+static eventloop_ctx_t  g_main_ctx;
+
+static inline eventloop_ctx_t *get_context(void)
+{
+    return &g_main_ctx;
+}
+
+extern int event_poll(void *reader, bool setup, void *sem);
+extern void event_read_cb(void);
+
+aos_loop_t aos_loop_init(void)
+{
+    memset(&g_main_ctx, 0, sizeof(eventloop_ctx_t));
+
+    g_main_ctx.reader.poll = event_poll;
+    g_main_ctx.reader.cb = event_read_cb;
+
+    eventloop_local_event_init();
+
+    dlist_init(&g_main_ctx.timeouts);
+
+    return &g_main_ctx;
+}
+
+aos_loop_t aos_current_loop(void)
+{
+    return (void *)&g_main_ctx;
+}
+
+static int eventloop_poll(int timeout)
+{
+    eventloop_ctx_t *ctx = get_context();
+
+    aos_sem_t sem;
+    int       ret = 0;
+
+    aos_sem_new(&sem, 0);
+
+    /* clear reader event flag */
+    ctx->reader.event = 0;
+
+    /* poll readers */
+    ctx->reader.poll(&ctx->reader, true, &sem);
+
+    if (!timeout) {
+        ret = 0;
+        goto check_poll;
+    }
+
+    if (timeout == -1) {
+        ret = aos_sem_wait(&sem, AOS_WAIT_FOREVER);
+    } else {
+        ret = aos_sem_wait(&sem, timeout);
+    }
+
+check_poll:
+
+    ctx->reader.poll(&ctx->reader, false, &sem);
+
+    if (ctx->reader.event) {
+        ret++;
+    }
+
+    aos_sem_free(&sem);
+
+    return ret < 0 ? 0 : ret;
+}
+
+void aos_loop_run(void)
+{
+    eventloop_ctx_t *ctx = get_context();
+
+    while (!ctx->terminate) {
+
+        int delayed_ms = -1;
+
+        if (!dlist_empty(&ctx->timeouts)) {
+            eventloop_timeout_t *tmo = dlist_first_entry(&ctx->timeouts, eventloop_timeout_t, next);
+            long long now = aos_now_ms();
+
+            if (now < tmo->timeout_ms) {
+                delayed_ms = tmo->timeout_ms - now;
+            } else {
+                delayed_ms = 0;
+            }
+        }
+
+        int res = eventloop_poll(delayed_ms);
+
+        /* check if some registered timeouts have occurred */
+        if (!dlist_empty(&ctx->timeouts)) {
+            eventloop_timeout_t *tmo = dlist_first_entry(&ctx->timeouts, eventloop_timeout_t, next);
+            long long now = aos_now_ms();
+
+            if (now >= tmo->timeout_ms) {
+                dlist_del(&tmo->next);
+                tmo->cb(tmo->private_data);
+                DEBUG_FREE(tmo);
+            }
+        }
+
+        if (res <= 0) {
+            continue;
+        }
+
+        if (ctx->reader.event) {
+            ctx->reader.cb();
+        }
+    }
+
+    ctx->terminate = 0;
+}
+
+void aos_loop_exit(void)
+{
+    eventloop_ctx_t *ctx = get_context();
+    ctx->terminate = 1;
+
+    aos_post_event(EV_SYS, CODE_SYS_LOOP_EXIT, VALUE_NULL);
+}
+
+void aos_loop_destroy(void)
+{
+    eventloop_ctx_t *ctx = get_context();
+
+    eventloop_local_event_deinit();
+
+
+    while (!dlist_empty(&ctx->timeouts)) {
+        eventloop_timeout_t *timeout = dlist_first_entry(&ctx->timeouts, eventloop_timeout_t,
+                                       next);
+        dlist_del(&timeout->next);
+        DEBUG_FREE(timeout);
+    }
+}
+
+int aos_post_delayed_action(int ms, aos_call_t action, void *param)
+{
+    //int ret;
+    eventloop_timeout_t *tmp;
+    eventloop_timeout_t *timeout;
+    eventloop_ctx_t *ctx = get_context();
+
+    if (action == NULL || ms < 0) {
+        return -EINVAL;
+    }
+
+    timeout = DEBUG_MALLOC(sizeof(*timeout));
+
+    if (timeout == NULL) {
+        return -ENOMEM;
+    }
+
+    timeout->timeout_ms = aos_now_ms() + ms;
+    timeout->private_data = param;
+    timeout->cb = action;
+    timeout->ms = ms;
+
+    dlist_for_each_entry(&ctx->timeouts, tmp, eventloop_timeout_t, next) {
+        if (timeout->timeout_ms < tmp->timeout_ms) {
+            break;
+        }
+    }
+    dlist_add_tail(&timeout->next, &tmp->next);
+
+    /* fix bug: if no timer, loop will wait forever, post event active loop */
+    aos_post_event(EV_SYS, CODE_NULL, VALUE_NULL);
+
+    return 0;
+}
+
+void aos_cancel_delayed_action(int ms, aos_call_t action, void *param)
+{
+    eventloop_ctx_t *ctx = get_context();
+    eventloop_timeout_t *tmp;
+
+    if (action == NULL) {
+        return;
+    }
+
+    dlist_for_each_entry(&ctx->timeouts, tmp, eventloop_timeout_t, next) {
+        if (ms >= 0 && tmp->ms != ms) {
+            continue;
+        }
+
+        if (tmp->cb != action) {
+            continue;
+        }
+
+        if (tmp->private_data != param) {
+            continue;
+        }
+
+        dlist_del(&tmp->next);
+        DEBUG_FREE(tmp);
+        return;
+    }
+}

+ 35 - 0
components/aos_port/eventloop/evtloop_main.h

@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+
+#ifndef _EVENTLOOP_H
+#define _EVENTLOOP_H
+
+#include <stdbool.h>
+#include "aos/yloop.h"
+
+typedef void (*eventloop_sock_cb)(void);
+typedef int (*reader_poll_t)(void *reader, bool setup, void *sem);
+
+typedef struct {
+    int          event;
+    reader_poll_t poll;
+    eventloop_sock_cb cb;
+} eventloop_sock_t;
+
+/**
+ * This function will init per-loop event service
+ * @param[in]  void
+ * @return  0 is OK, -1 is error
+ */
+int eventloop_local_event_init(void);
+
+/**
+ * This function will deinit per-loop event service
+ * @param[in]  void
+ * @return  0 is OK, -1 is error
+ */
+int eventloop_local_event_deinit(void);
+
+#endif /* YLOOP_H */
+

+ 103 - 0
components/aos_port/eventloop/yloop.c

@@ -0,0 +1,103 @@
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "aos/yloop.h"
+#include "aos/kernel.h"
+
+#ifdef CONFIG_KERNEL_WORKQUEUE
+typedef struct work_para {
+    aos_work_t *work;
+    aos_loop_t loop;
+    aos_call_t action;
+    void *arg1;
+    aos_call_t fini_cb;
+    void *arg2;
+} work_par_t;
+
+static void free_wpar(work_par_t *wpar)
+{
+    aos_work_destroy(wpar->work);
+    aos_free(wpar->work);
+    aos_free(wpar);
+}
+
+static void run_my_work(void *arg)
+{
+    work_par_t *wpar = arg;
+
+    wpar->action(wpar->arg1);
+
+    if (wpar->fini_cb) {
+        aos_schedule_call(wpar->fini_cb, wpar->arg2);
+    }
+
+    free_wpar(wpar);
+}
+
+void *aos_loop_schedule_work(int ms, aos_call_t action, void *arg1,
+                             aos_call_t fini_cb, void *arg2)
+{
+    int ret;
+
+    if (action == NULL) {
+        return NULL;
+    }
+
+    aos_work_t *work = aos_malloc(sizeof(*work));
+    work_par_t *wpar = aos_malloc(sizeof(*wpar));
+
+    if (!work || !wpar) {
+        goto err_out;
+    }
+
+    wpar->work = work;
+    wpar->loop = aos_current_loop();
+    wpar->action = action;
+    wpar->arg1 = arg1;
+    wpar->fini_cb = fini_cb;
+    wpar->arg2 = arg2;
+
+    ret = aos_work_init(work, run_my_work, wpar, ms);
+    if (ret != 0) {
+        goto err_out;
+    }
+    ret = aos_work_sched(work);
+    if (ret != 0) {
+        goto err_out;
+    }
+
+    return wpar;
+err_out:
+    aos_free(work);
+    aos_free(wpar);
+
+    return NULL;
+}
+
+void aos_cancel_work(void *w, aos_call_t action, void *arg1)
+{
+    work_par_t *wpar = w;
+
+    if (wpar == NULL) {
+        return;
+    }
+
+    if (wpar->action != action) {
+        return;
+    }
+
+    if (wpar->arg1 != arg1) {
+        return;
+    }
+
+    int ret = aos_work_cancel(wpar->work);
+    if (ret != 0) {
+        return;
+    }
+
+    free_wpar(wpar);
+}
+#endif

+ 24 - 0
components/aos_port/include/aos/aos.h

@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+
+#ifndef AOS_AOS_H
+#define AOS_AOS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <aos/types.h>
+#include <aos/kernel.h>
+// #include <aos/kv.h>
+#include <aos/list.h>
+#include <aos/version.h>
+#include <aos/debug.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AOS_AOS_H */
+

+ 14 - 0
components/aos_port/include/aos/crc16.h

@@ -0,0 +1,14 @@
+#ifndef LIBS_CRC_H
+#define LIBS_CRC_H
+
+/**
+ * crc16 - compute the CRC-16 for the data buffer
+ * @crc:    previous CRC value
+ * @buffer: data pointer
+ * @len:    number of bytes in the buffer
+ *
+ * Returns the updated CRC value.
+ */
+unsigned short crc16(unsigned short crc, unsigned char const *buffer, int len);
+
+#endif

+ 445 - 0
components/aos_port/include/aos/debug.h

@@ -0,0 +1,445 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+
+#ifndef AOS_DEBUG_H
+#define AOS_DEBUG_H
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <ulog/ulog.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SHORT_FILE __FUNCTION__
+
+#ifdef CONFIG_DEBUG
+#define debug_print_assert(A, B, C, D, E, F)                                                       \
+    do {                                                                                           \
+        printf("!!!assert: %s: %d, %s\r\n", D, E, F);                                              \
+        while (1)                                                                                  \
+            ;                                                                                      \
+    } while (0)
+#else
+#define debug_print_assert(A, B, C, D, E, F)
+#endif
+
+#if (!defined(unlikely))
+#define unlikely(EXPRESSSION) !!(EXPRESSSION)
+#endif
+
+#ifdef CONFIG_DEBUG
+
+#define aos_assert(X)                                                                              \
+    do {                                                                                           \
+        if (unlikely(!(X))) {                                                                      \
+            debug_print_assert(0, #X, NULL, SHORT_FILE, __LINE__, __PRETTY_FUNCTION__);            \
+        }                                                                                          \
+    } while (0)
+
+#define except_process(err) aos_except_process(errno, SHORT_FILE, __LINE__,                  \
+                                __PRETTY_FUNCTION__,  __builtin_return_address(0))
+
+#else
+
+#define aos_assert(X)
+#define except_process(err) aos_except_process(errno, NULL, 0, NULL,                   \
+                               __builtin_return_address(0))
+#endif
+
+#ifndef CHECK_PARAM
+#define CHECK_PARAM(x, ret) \
+	do { \
+		if (!(x)) { \
+			return ret; \
+		}\
+	} while (0)
+#endif
+
+#ifndef CHECK_RET_WITH_GOTO
+#define CHECK_RET_WITH_GOTO(x, label) \
+	do { \
+		if (!(x)) { \
+			LOGE(__FILE__, "%s, %d fail.", __FUNCTION__, __LINE__); \
+			goto label; \
+		}\
+	} while (0)
+#endif
+
+#ifndef CHECK_RET_WITH_RET
+#define CHECK_RET_WITH_RET(x, ret) \
+	do { \
+		if (!(x)) { \
+			LOGE(__FILE__, "%s, %d fail.", __FUNCTION__, __LINE__); \
+			return ret; \
+		}\
+	} while (0)
+#endif
+
+#ifndef CHECK_RET_TAG_WITH_GOTO
+#define CHECK_RET_TAG_WITH_GOTO(x, label) \
+	do { \
+		if (!(x)) { \
+			LOGE(TAG, "%s, %d fail", __FUNCTION__, __LINE__); \
+			goto label; \
+		}\
+	} while (0)
+#endif
+
+#ifndef CHECK_RET_TAG_WITH_RET
+#define CHECK_RET_TAG_WITH_RET(x, ret) \
+	do { \
+		if (!(x)) { \
+			LOGE(TAG, "%s, %d fail", __FUNCTION__, __LINE__); \
+			return ret; \
+		}\
+	} while (0)
+#endif
+
+/*
+ * Check that an expression is true (non-zero).
+ * If expression evalulates to false, this prints debugging information (actual expression string, file, line number,
+ * function name, etc.) using the default debugging output method.
+ *
+ * @param[in]  X  expression to be evaluated.
+ */
+
+#if (!defined(aos_check))
+#define aos_check(X, errno)                                                                        \
+    do {                                                                                           \
+        if (unlikely(!(X)))                                                                        \
+            except_process(errno);                                                                 \
+    } while (0)
+#endif
+
+#if (!defined(aos_check_param))
+#define aos_check_param(X) aos_check(X, EINVAL)
+#endif
+
+#if (!defined(aos_check_mem))
+#define aos_check_mem(X) aos_check(X, ENOMEM)
+#endif
+
+#if (!defined(aos_check_return_val))
+#define aos_check_return_val(X, ret)                                                               \
+    do {                                                                                           \
+        if (unlikely(!(X))) {                                                                      \
+            except_process(errno);                                                                 \
+            return ret;                                                                            \
+        }                                                                                          \
+    } while (0)
+#endif
+
+#if (!defined(aos_check_return_einval))
+#define aos_check_return_einval(X) aos_check_return_val(X, -EINVAL)
+#endif
+
+#if (!defined(aos_check_return_null))
+#define aos_check_return_null(X) aos_check_return_val(X, NULL)
+#endif
+
+#if (!defined(aos_check_return_enomem))
+#define aos_check_return_enomem(X) aos_check_return_val(X, -ENOMEM)
+#endif
+
+#if (!defined(aos_check_return))
+#define aos_check_return(X)                                                                        \
+    do {                                                                                           \
+        if (unlikely(!(X))) {                                                                      \
+            except_process(errno);                                                                 \
+            return;                                                                                \
+        }                                                                                          \
+    } while (0)
+#endif
+
+//////////////////////////////////////////////////////////////////////
+
+#if (!defined(check))
+#define check(X)                                                                                   \
+    do {                                                                                           \
+        if (unlikely(!(X)))                                                                        \
+            except_process(0);                                                                     \
+    } while (0)
+#endif
+/*
+ * Check that an expression is true (non-zero) with an explanation.
+ * If expression evalulates to false, this prints debugging information (actual expression string, file, line number,
+ * function name, etc.) using the default debugging output method.
+ *
+ * @param[in]  X       expression to be evaluated.
+ * @param[in]  STR     If the expression evaluate to false, custom string to print.
+ */
+#if (!defined(check_string))
+#define check_string(X, STR)                                                                       \
+    do {                                                                                           \
+        if (unlikely(!(X))) {                                                                      \
+            debug_print_assert(0, #X, STR, SHORT_FILE, __LINE__, __PRETTY_FUNCTION__);             \
+            AOS_ASSERTION_FAIL_ACTION();                                                           \
+        }                                                                                          \
+    } while (1 == 0)
+#endif
+
+/*
+ * Requires that an expression evaluate to true.
+ * If expression evalulates to false, this prints debugging information (actual expression string, file, line number,
+ * function name, etc.) using the default debugging output method then jumps to a label.
+ *
+ * @param[in]  X      expression to be evalulated.
+ * @param[in]  LABEL  if expression evaluate to false,jumps to the LABEL.
+ */
+#if (!defined(require))
+#define require(X, LABEL)                                                                          \
+    do {                                                                                           \
+        if (unlikely(!(X))) {                                                                      \
+            debug_print_assert(0, #X, NULL, SHORT_FILE, __LINE__, __PRETTY_FUNCTION__);            \
+            goto LABEL;                                                                            \
+        }                                                                                          \
+    } while (1 == 0)
+#endif
+
+/*
+ * Requires that an expression evaluate to true with an explanation.
+ * If expression evalulates to false, this prints debugging information (actual expression string, file, line number,
+ * function name, etc.) and a custom explanation string using the default debugging output method then jumps to a label.
+ *
+ * @param[in]  X      expression to be evalulated.
+ * @param[in]  LABEL  if expression evaluate to false,jumps to the LABEL.
+ * @param[in]  STR    if expression evaluate to false,custom explanation string to print.
+ */
+#if (!defined(require_string))
+#define require_string(X, LABEL, STR)                                                              \
+    do {                                                                                           \
+        if (unlikely(!(X))) {                                                                      \
+            debug_print_assert(0, #X, STR, SHORT_FILE, __LINE__, __PRETTY_FUNCTION__);             \
+            goto LABEL;                                                                            \
+        }                                                                                          \
+    } while (1 == 0)
+#endif
+
+/*
+ * Requires that an expression evaluate to true.
+ * If expression evalulates to false, this jumps to a label. No debugging information is printed.
+ *
+ * @param[in]  X      expression to be evalulated
+ * @param[in]  LABEL  if expression evaluate to false,jumps to the LABEL.
+ */
+#if (!defined(require_quiet))
+#define require_quiet(X, LABEL)                                                                    \
+    do {                                                                                           \
+        if (unlikely(!(X))) {                                                                      \
+            goto LABEL;                                                                            \
+        }                                                                                          \
+    } while (1 == 0)
+#endif
+
+/*
+ * Require that an error code is noErr (0).
+ * If the error code is non-0, this prints debugging information (actual expression string, file, line number,
+ * function name, etc.) using the default debugging output method then jumps to a label.
+ *
+ * @param[in]  ERR    error to be evaluated
+ * @param[in]  LABEL  If the error code is non-0,jumps to the LABEL.
+ */
+#if (!defined(require_noerr))
+#define require_noerr(ERR, LABEL)                                                                  \
+    do {                                                                                           \
+        int localErr;                                                                              \
+                                                                                                   \
+        localErr = (int)(ERR);                                                                     \
+        if (unlikely(localErr != 0)) {                                                             \
+            debug_print_assert(localErr, NULL, NULL, SHORT_FILE, __LINE__, __PRETTY_FUNCTION__);   \
+            goto LABEL;                                                                            \
+        }                                                                                          \
+                                                                                                   \
+    } while (1 == 0)
+#endif
+
+/*
+ * Require that an error code is noErr (0) with an explanation.
+ * If the error code is non-0, this prints debugging information (actual expression string, file, line number,
+ * function name, etc.), and a custom explanation string using the default debugging output method using the
+ * default debugging output method then jumps to a label.
+ *
+ * @param[in]  ERR    error to be evaluated
+ * @param[in]  LABEL  If the error code is non-0, jumps to the LABEL.
+ * @param[in]  STR    If the error code is non-0, custom explanation string to print
+ */
+#if (!defined(require_noerr_string))
+#define require_noerr_string(ERR, LABEL, STR)                                                      \
+    do {                                                                                           \
+        int localErr;                                                                              \
+                                                                                                   \
+        localErr = (int)(ERR);                                                                     \
+        if (unlikely(localErr != 0)) {                                                             \
+            debug_print_assert(localErr, NULL, STR, SHORT_FILE, __LINE__, __PRETTY_FUNCTION__);    \
+            goto LABEL;                                                                            \
+        }                                                                                          \
+    } while (1 == 0)
+#endif
+
+/*
+ * Require that an error code is noErr (0)  with an explanation and action to execute otherwise.
+ * If the error code is non-0, this prints debugging information (actual expression string, file, line number,
+ * function name, etc.), and a custom explanation string using the default debugging output method using the
+ * default debugging output method then executes an action and jumps to a label.
+ *
+ * @param[in]  ERR     error to be evaluated.
+ * @param[in]  LABEL   If the error code is non-0, jumps to the LABEL.
+ * @param[in]  ACTION  If the error code is non-0, custom code to executes.
+ * @param[in]  STR     If the error code is non-0, custom explanation string to print.
+ */
+#if (!defined(require_noerr_action_string))
+#define require_noerr_action_string(ERR, LABEL, ACTION, STR)                                       \
+    do {                                                                                           \
+        int localErr;                                                                              \
+                                                                                                   \
+        localErr = (int)(ERR);                                                                     \
+        if (unlikely(localErr != 0)) {                                                             \
+            debug_print_assert(localErr, NULL, STR, SHORT_FILE, __LINE__, __PRETTY_FUNCTION__);    \
+            {                                                                                      \
+                ACTION;                                                                            \
+            }                                                                                      \
+            goto LABEL;                                                                            \
+        }                                                                                          \
+    } while (1 == 0)
+#endif
+
+/*
+ * Require that an error code is noErr (0).
+ * If the error code is non-0, this jumps to a label. No debugging information is printed.
+ *
+ * @param[in]  ERR    error to be evaluated.
+ * @param[in]  LABEL  If the error code is non-0, jumps to the LABEL.
+ */
+#if (!defined(require_noerr_quiet))
+#define require_noerr_quiet(ERR, LABEL)                                                            \
+    do {                                                                                           \
+        if (unlikely((ERR) != 0)) {                                                                \
+            goto LABEL;                                                                            \
+        }                                                                                          \
+    } while (1 == 0)
+#endif
+
+/*
+ * Require that an error code is noErr (0) with an action to execute otherwise.
+ * If the error code is non-0, this prints debugging information (actual expression string, file, line number,
+ * function name, etc.) using the default debugging output method then executes an action and jumps to a label.
+ *
+ * @param[in]  ERR     error to be evaluated.
+ * @param[in]  LABEL   If the error code is non-0, jumps to the LABEL.
+ * @param[in]  ACTION  If the error code is non-0, custom code to executes.
+ */
+#if (!defined(require_noerr_action))
+#define require_noerr_action(ERR, LABEL, ACTION)                                                   \
+    do {                                                                                           \
+        int localErr;                                                                              \
+                                                                                                   \
+        localErr = (int)(ERR);                                                                     \
+        if (unlikely(localErr != 0)) {                                                             \
+            debug_print_assert(localErr, NULL, NULL, SHORT_FILE, __LINE__, __PRETTY_FUNCTION__);   \
+            {                                                                                      \
+                ACTION;                                                                            \
+            }                                                                                      \
+            goto LABEL;                                                                            \
+        }                                                                                          \
+    } while (1 == 0)
+#endif
+
+/*
+ * Require that an error code is noErr (0) with an action to execute otherwise.
+ * If the error code is non-0, this executes an action and jumps to a label. No debugging information is printed.
+ *
+ * @param[in]  ERR     error to be evaluated.
+ * @param[in]  LABEL   If the error code is non-0, jumps to the LABEL.
+ * @param[in]  ACTION  If the error code is non-0, custom code to executes.
+ */
+#if (!defined(require_noerr_action_quiet))
+#define require_noerr_action_quiet(ERR, LABEL, ACTION)                                             \
+    do {                                                                                           \
+        if (unlikely((ERR) != 0)) {                                                                \
+            {                                                                                      \
+                ACTION;                                                                            \
+            }                                                                                      \
+            goto LABEL;                                                                            \
+        }                                                                                          \
+    } while (1 == 0)
+#endif
+
+/*
+ * Requires that an expression evaluate to true with an action to execute otherwise.
+ * If expression evalulates to false, this prints debugging information (actual expression string, file, line number,
+ * function name, etc.) using the default debugging output method then executes an action and jumps to a label.
+ *
+ * @param[in]  X       expression to be evaluated.
+ * @param[in]  LABEL   If the expression evaluate to false, jumps to the LABEL.
+ * @param[in]  ACTION  If the expression evaluate to false, custom code to executes.
+ */
+#if (!defined(require_action))
+#define require_action(X, LABEL, ACTION)                                                           \
+    do {                                                                                           \
+        if (unlikely(!(X))) {                                                                      \
+            debug_print_assert(0, #X, NULL, SHORT_FILE, __LINE__, __PRETTY_FUNCTION__);            \
+            {                                                                                      \
+                ACTION;                                                                            \
+            }                                                                                      \
+            goto LABEL;                                                                            \
+        }                                                                                          \
+    } while (1 == 0)
+#endif
+
+/*
+ * Requires that an expression evaluate to true with an explanation and action to execute otherwise.
+ * If expression evalulates to false, this prints debugging information (actual expression string, file, line number,
+ * function name, etc.) and a custom explanation string using the default debugging output method then executes an
+ * action and jumps to a label.
+ *
+ * @param[in]  X       expression to be evaluated.
+ * @param[in]  LABEL   If the expression evaluate to false, jumps to the LABEL.
+ * @param[in]  ACTION  If the expression evaluate to false, custom code to executes.
+ * @param[in]  STR     If the expression evaluate to false, custom string to print.
+ */
+#if (!defined(require_action_string))
+#define require_action_string(X, LABEL, ACTION, STR)                                               \
+    do {                                                                                           \
+        if (unlikely(!(X))) {                                                                      \
+            debug_print_assert(0, #X, STR, SHORT_FILE, __LINE__, __PRETTY_FUNCTION__);             \
+            {                                                                                      \
+                ACTION;                                                                            \
+            }                                                                                      \
+            goto LABEL;                                                                            \
+        }                                                                                          \
+    } while (1 == 0)
+#endif
+
+/*
+ * Requires that an expression evaluate to true with an action to execute otherwise.
+ * If expression evalulates to false, this executes an action and jumps to a label.
+ * No debugging information is printed.
+ *
+ * @param[in]  X       expression to be evaluated.
+ * @param[in]  LABEL   If the expression evaluate to false, jumps to the LABEL.
+ * @param[in]  ACTION  If the expression evaluate to false, custom code to executes.
+ */
+#if (!defined(require_action_quiet))
+#define require_action_quiet(X, LABEL, ACTION)                                                     \
+    do {                                                                                           \
+        if (unlikely(!(X))) {                                                                      \
+            {                                                                                      \
+                ACTION;                                                                            \
+            }                                                                                      \
+            goto LABEL;                                                                            \
+        }                                                                                          \
+                                                                                                   \
+    } while (1 == 0)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+void aos_except_process(int errno, const char *file, int line, const char *func_name, void *caller);
+
+#endif /* AOS_DEBUG_H */

+ 19 - 0
components/aos_port/include/aos/errno.h

@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+
+#ifndef AOS_ERRNO_H
+#define AOS_ERRNO_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <errno.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AOS_ERRNO_H */
+

+ 40 - 0
components/aos_port/include/aos/integer.h

@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+
+/*-------------------------------------------*/
+/* Integer type definitions for FatFs module */
+/*-------------------------------------------*/
+
+#ifndef FF_INTEGER
+#define FF_INTEGER
+
+#ifdef _WIN32	/* FatFs development platform */
+
+#include <windows.h>
+typedef unsigned __int64 QWORD;
+
+#else			/* Embedded platform */
+
+/* These types MUST be 16-bit or 32-bit */
+typedef int				INT;
+typedef unsigned int	UINT;
+
+/* This type MUST be 8-bit */
+typedef unsigned char	BYTE;
+
+/* These types MUST be 16-bit */
+typedef short			SHORT;
+typedef unsigned short	WORD;
+typedef unsigned short	WCHAR;
+
+/* These types MUST be 32-bit */
+typedef long			LONG;
+typedef unsigned long	DWORD;
+
+/* This type MUST be 64-bit (Remove this for ANSI C (C89) compatibility) */
+typedef unsigned long long QWORD;
+
+#endif
+
+#endif

+ 708 - 0
components/aos_port/include/aos/kernel.h

@@ -0,0 +1,708 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+
+#ifndef AOS_KERNEL_H
+#define AOS_KERNEL_H
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <assert.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define AOS_WAIT_FOREVER    0xffffffffu
+#define AOS_NO_WAIT         0x0
+
+#ifndef AOS_DEFAULT_APP_PRI
+#define AOS_DEFAULT_APP_PRI 32
+#endif
+
+#ifndef AOS_MAX_APP_PRI
+#define AOS_MAX_APP_PRI     60
+#endif
+
+#define AOS_EVENT_AND              0x02u
+#define AOS_EVENT_AND_CLEAR        0x03u
+#define AOS_EVENT_OR               0x00u
+#define AOS_EVENT_OR_CLEAR         0x01u
+
+/// Status code values returned by CSI-kernel functions. 0 - success, negative represents error code ,see errno.h
+typedef int32_t k_status_t;
+
+/// Entry point of a timer call back function.
+typedef void (*aos_timer_cb_t)(void *, void *);
+
+typedef struct {
+    void *hdl;
+} aos_hdl_t;
+
+typedef aos_hdl_t aos_task_t;
+typedef aos_hdl_t aos_mutex_t;
+typedef aos_hdl_t aos_sem_t;
+typedef aos_hdl_t aos_queue_t;
+typedef aos_hdl_t aos_timer_t;
+typedef aos_hdl_t aos_work_t;
+typedef aos_hdl_t aos_event_t;
+
+typedef struct {
+    void *hdl;
+    void *stk;
+} aos_workqueue_t;
+
+typedef unsigned int aos_task_key_t;
+
+#define MS2TICK(ms) krhino_ms_to_ticks(ms)
+int32_t aos_irq_context(void);
+
+// #define aos_assert(x)                             assert(x)
+
+/**
+ * Reboot AliOS.
+ */
+void aos_reboot(void);
+
+/**
+ * Get HZ(ticks per second).
+ *
+ * @return  RHINO_CONFIG_TICKS_PER_SECOND.
+ */
+int aos_get_hz(void);
+
+/**
+ * Get kernel version.
+ *
+ * @return  SYSINFO_KERNEL_VERSION.
+ */
+const char *aos_version_get(void);
+
+/**
+ * Create a task.
+ *
+ * @param[in]  name       task name.
+ * @param[in]  fn         function to run.
+ * @param[in]  arg        argument of the function.
+ * @param[in]  stacksize  stack-size in bytes.
+ *
+ * @return  0: success.
+ */
+int aos_task_new(const char *name, void (*fn)(void *), void *arg, int stack_size);
+
+/**
+ * Create a task.
+ *
+ * @param[in]  task        handle.
+ * @param[in]  name        task name.
+ * @param[in]  fn          task function.
+ * @param[in]  arg         argument of the function..
+ * @param[in]  stack_buf   stack-buf: if stack_buf==NULL, provided by kernel.
+ * @param[in]  stack_size  stack-size in bytes.
+ * @param[in]  prio        priority value, the max is RHINO_CONFIG_USER_PRI_MAX(default 60).
+ *
+ * @return  0: success.
+ */
+int aos_task_new_ext(aos_task_t *task, const char *name, void (*fn)(void *), void *arg,
+                     int stack_size, int prio);
+
+/**
+ * show all tasks info.
+ *
+ */
+void aos_task_show_info(void);
+
+void aos_task_yield();
+
+/**
+ * Exit a task.
+ *
+ * @param[in]  code  not used now.
+ */
+void aos_task_exit(int code);
+
+
+void aos_task_wdt_attach(void (*will)(void *), void *args);
+void aos_task_wdt_detach();
+void aos_task_wdt_feed(int time);
+
+aos_task_t aos_task_self();
+
+/**
+ * Get task name.
+ *
+ * @return  the name of the task
+ */
+const char *aos_task_name(void);
+
+
+const char *aos_task_get_name(aos_task_t *task);
+
+/**
+ * Create a task key.
+ *
+ * @param[in]  key  pointer of key object.
+ *
+ * @return  0: success, -EINVAL: error.
+ */
+int aos_task_key_create(aos_task_key_t *key);
+
+/**
+ * Delete a task key.
+ *
+ * @param[in]  key  key object.
+ */
+void aos_task_key_delete(aos_task_key_t key);
+
+/**
+ * Associate a task-specific value with a key.
+ * Support this functiong need set RHINO_CONFIG_TASK_INFO 1 at k_config.h
+ *
+ * @param[in]  key  key object.
+ * @param[in]  vp   pointer of a task-specific value.
+ *
+ * @return  the check status, 0 is OK, -1 indicates invalid.
+ */
+int aos_task_setspecific(aos_task_key_t key, void *vp);
+
+/**
+ * Get the value currently bound to the specified key.
+ * Support this functiong need set RHINO_CONFIG_TASK_INFO 1 at k_config.h
+ *
+ * @param[in]  key  key object.
+ */
+void *aos_task_getspecific(aos_task_key_t key);
+
+/**
+ * Alloc a mutex.
+ *
+ * @param[in]  mutex  pointer of mutex object, mutex object must be alloced,
+ *                    hdl pointer in aos_mutex_t will refer a kernel obj internally.
+ *
+ * @return  0: success.
+ */
+int aos_mutex_new(aos_mutex_t *mutex);
+
+/**
+ * Free a mutex.
+ *
+ * @param[in]  mutex  mutex object, mem refered by hdl pointer in aos_mutex_t will
+ *                    be freed internally.
+ */
+void aos_mutex_free(aos_mutex_t *mutex);
+
+/**
+ * Lock a mutex.
+ *
+ * @param[in]  mutex    mutex object, it contains kernel obj pointer which aos_mutex_new alloced.
+ * @param[in]  timeout  waiting until timeout in milliseconds.
+ *
+ * @return  0: success.
+ */
+int aos_mutex_lock(aos_mutex_t *mutex, unsigned int timeout);
+
+/**
+ * Unlock a mutex.
+ *
+ * @param[in]  mutex  mutex object, it contains kernel obj pointer which oc_mutex_new alloced.
+ *
+ * @return  0: success.
+ */
+int aos_mutex_unlock(aos_mutex_t *mutex);
+
+/**
+ * This function will check if mutex is valid.
+ *
+ * @param[in]  mutex  pointer to the mutex.
+ *
+ * @return  0: invalid, 1: valid.
+ */
+int aos_mutex_is_valid(aos_mutex_t *mutex);
+
+/**
+ * This function will check if mutex is locked.
+ *
+ * @param[in]  mutex  pointer to the mutex.
+ *
+ * @return  0: no lock, 1: locked.
+ */
+int aos_mutex_is_locked(aos_mutex_t *mutex);
+
+/**
+ * Alloc a semaphore.
+ *
+ * @param[out]  sem    pointer of semaphore object, semaphore object must be alloced,
+ *                     hdl pointer in aos_sem_t will refer a kernel obj internally.
+ * @param[in]   count  initial semaphore counter.
+ *
+ * @return  0:success.
+ */
+int aos_sem_new(aos_sem_t *sem, int count);
+
+/**
+ * Destroy a semaphore.
+ *
+ * @param[in]  sem  pointer of semaphore object, mem refered by hdl pointer
+ *                  in aos_sem_t will be freed internally.
+ */
+void aos_sem_free(aos_sem_t *sem);
+
+/**
+ * Acquire a semaphore.
+ *
+ * @param[in]  sem      semaphore object, it contains kernel obj pointer which aos_sem_new alloced.
+ * @param[in]  timeout  waiting until timeout in milliseconds.
+ *
+ * @return  0: success.
+ */
+int aos_sem_wait(aos_sem_t *sem, unsigned int timeout);
+
+/**
+ * Release a semaphore.
+ *
+ * @param[in]  sem  semaphore object, it contains kernel obj pointer which aos_sem_new alloced.
+ */
+void aos_sem_signal(aos_sem_t *sem);
+
+/**
+ * This function will check if semaphore is valid.
+ *
+ * @param[in]  sem  pointer to the semaphore.
+ *
+ * @return  0: invalid, 1: valid.
+ */
+int aos_sem_is_valid(aos_sem_t *sem);
+
+/**
+ * Release all semaphore.
+ *
+ * @param[in]  sem  semaphore object, it contains kernel obj pointer which aos_sem_new alloced.
+ */
+void aos_sem_signal_all(aos_sem_t *sem);
+
+/**
+ * This function will create an event with an initialization flag set.
+ * This function should not be called from interrupt context.
+ *
+ * @param[in]  event    event object pointer.
+ * @param[in]  flags    initialization flag set(provided by caller).
+ *
+ * @return  0: success.
+ */
+
+int aos_event_new(aos_event_t *event, unsigned int flags);
+
+/**
+ * This function will free an event.
+ * This function shoud not be called from interrupt context.
+ *
+ * @param[in]  event    memory refered by hdl pointer in event will be freed.
+ *
+ * @return  N/A.
+ */
+
+void aos_event_free(aos_event_t *event);
+
+/**
+ * This function will try to get flag set from given event, if the request flag
+ * set is satisfied, it will return immediately, if the request flag set is not
+ * satisfied with timeout(RHINO_WAIT_FOREVER,0xFFFFFFFF), the caller task will be
+ * pended on event until the flag is satisfied, if the request flag is not
+ * satisfied with timeout(RHINO_NO_WAIT, 0x0), it will also return immediately.
+ * Note, this function should not be called from interrupt context because it has
+ * possible to lead context switch and an interrupt has no TCB to save context.
+ *
+ * @param[in]  event        event object pointer.
+ * @param[in]  flags        request flag set.
+ * @param[in]  opt          operation type, such as AND,OR,AND_CLEAR,OR_CLEAR.
+ * @param[out] actl_flags   the internal flags value hold by event.
+ * @param[in]  flags        request flag set.
+ * @param[in]  timeout      max wait time in millisecond.
+ *
+ * @return  0: success.
+ */
+
+int aos_event_get(aos_event_t *event, unsigned int flags, unsigned char opt,
+                  unsigned int *actl_flags, unsigned int timeout);
+
+/**
+* This function will set flag set to given event, and it will check if any task
+* which is pending on the event should be waken up.
+*
+* @param[in]  event    event object pointer.
+* @param[in]  flags    flag set to be set into event.
+* @param[in]  opt      operation type, such as AND,OR.
+*
+* @return  0: success.
+*/
+
+int aos_event_set(aos_event_t *event, unsigned int flags, unsigned char opt);
+
+/**
+ * This function will check if event is valid.
+ *
+ * @param[in]  sem  pointer to the semaphore.
+ *
+ * @return  0: invalid, 1: valid.
+ */
+int aos_event_is_valid(aos_event_t *event);
+
+/**
+ * This function will create a queue.
+ *
+ * @param[in]  queue    pointer to the queue(the space is provided by user).
+ * @param[in]  buf      buf of the queue(provided by user).
+ * @param[in]  size     the bytes of the buf.
+ * @param[in]  max_msg  the max size of the msg.
+ *
+ * @return  0: success.
+ */
+
+int aos_queue_new(aos_queue_t *queue, void *buf, unsigned int size, int max_msg);
+
+/**
+ * This function will delete a queue.
+ *
+ * @param[in]  queue  pointer to the queue.
+ */
+void aos_queue_free(aos_queue_t *queue);
+
+/**
+ * This function will send a msg to the front of a queue.
+ *
+ * @param[in]  queue  pointer to the queue.
+ * @param[in]  msg    msg to send.
+ * @param[in]  size   size of the msg.
+ *
+ * @return  0: success.
+ */
+int aos_queue_send(aos_queue_t *queue, void *msg, unsigned int size);
+
+/**
+ * This function will receive msg from a queue.
+ *
+ * @param[in]   queue  pointer to the queue.
+ * @param[in]   ms     ms to wait before receive.
+ * @param[out]  msg    buf to save msg.
+ * @param[out]  size   size of the msg.
+ *
+ * @return  0: success.
+ */
+int aos_queue_recv(aos_queue_t *queue, unsigned int ms, void *msg, unsigned int *size);
+
+/**
+ * This function will check if queue is valid.
+ *
+ * @param[in]  queue  pointer to the queue.
+ *
+ * @return  0: invalid, 1: valid.
+ */
+int aos_queue_is_valid(aos_queue_t *queue);
+
+/**
+ * This function will return buf ptr if queue is inited.
+ *
+ * @param[in]  queue  pointer to the queue.
+ *
+ * @return  NULL: error.
+ */
+void *aos_queue_buf_ptr(aos_queue_t *queue);
+
+/**
+ * Get number of queued messages in a message queue.
+ *
+ * @param[in]  queue  message queue handle to operate.
+ *
+ * @return  number of queued messages.negative indicates error code.
+ */
+int aos_queue_get_count(aos_queue_t *queue);
+
+/**
+ * This function will create a timer and run auto.
+ *
+ * @param[in]  timer   pointer to the timer.
+ * @param[in]  fn      callbak of the timer.
+ * @param[in]  arg     the argument of the callback.
+ * @param[in]  ms      ms of the normal timer triger.
+ * @param[in]  repeat  repeat or not when the timer is created.
+ *
+ * @return  0: success.
+ */
+int aos_timer_new(aos_timer_t *timer, void (*fn)(void *, void *),
+                  void *arg, int ms, int repeat);
+
+/**
+ * This function will create a timer.
+ *
+ * @param[in]  timer   pointer to the timer.
+ * @param[in]  fn      callbak of the timer.
+ * @param[in]  arg     the argument of the callback.
+ * @param[in]  ms      ms of the normal timer triger.
+ * @param[in]  repeat  repeat or not when the timer is created.
+ * @param[in]  auto_run  run auto or not when the timer is created.
+ *
+ * @return  0: success.
+ */
+int aos_timer_new_ext(aos_timer_t *timer, void (*fn)(void *, void *),
+                      void *arg, int ms, int repeat, unsigned char auto_run);
+
+/**
+ * This function will delete a timer.
+ *
+ * @param[in]  timer  pointer to a timer.
+ */
+void aos_timer_free(aos_timer_t *timer);
+
+/**
+ * This function will start a timer.
+ *
+ * @param[in]  timer  pointer to the timer.
+ *
+ * @return  0: success.
+ */
+int aos_timer_start(aos_timer_t *timer);
+
+/**
+ * This function will stop a timer.
+ *
+ * @param[in]  timer  pointer to the timer.
+ *
+ * @return  0: success.
+ */
+int aos_timer_stop(aos_timer_t *timer);
+
+/**
+ * This function will change attributes of a timer.
+ *
+ * @param[in]  timer  pointer to the timer.
+ * @param[in]  ms     ms of the timer triger.
+ *
+ * @return  0: success.
+ */
+int aos_timer_change(aos_timer_t *timer, int ms);
+
+/**
+ * This function will change attributes of a timer.
+ *
+ * @param[in]  timer  pointer to the timer.
+ * @param[in]  ms     ms of the timer triger.
+ *
+ * @return  0: success.
+ */
+int aos_timer_change_once(aos_timer_t *timer, int ms);
+
+/**
+ * This function will check if timer is valid.
+ *
+ * @param[in]  timer  pointer to the timer.
+ *
+ * @return  0: success.
+ */
+int aos_timer_is_valid(aos_timer_t *timer);
+
+/**
+ * This function will creat a workqueue.
+ *
+ * @param[in]  workqueue   the workqueue to be created.
+ * @param[in]  pri         the priority of the worker.
+ * @param[in]  stack_size  the size of the worker-stack.
+ *
+ * @return  0: success.
+ */
+int aos_workqueue_create(aos_workqueue_t *workqueue, int pri, int stack_size);
+
+/**
+ * This function will delete a workqueue.
+ *
+ * @param[in]  workqueue  the workqueue to be deleted.
+ */
+void aos_workqueue_del(aos_workqueue_t *workqueue);
+
+/**
+ * This function will initialize a work.
+ *
+ * @param[in]  work  the work to be initialized.
+ * @param[in]  fn    the call back function to run.
+ * @param[in]  arg   the paraments of the function.
+ * @param[in]  dly   ms to delay before run.
+ *
+ * @return  0: success.
+ */
+int aos_work_init(aos_work_t *work, void (*fn)(void *), void *arg, int dly);
+
+/**
+ * This function will destroy a work.
+ *
+ * @param[in]  work  the work to be destroied.
+ */
+void aos_work_destroy(aos_work_t *work);
+
+/**
+ * This function will run a work on a workqueue.
+ *
+ * @param[in]  workqueue  the workqueue to run work.
+ * @param[in]  work       the work to run.
+ *
+ * @return  0: success.
+ */
+int aos_work_run(aos_workqueue_t *workqueue, aos_work_t *work);
+
+/**
+ * This function will run a work on the default workqueue.
+ *
+ * @param[in]  work  the work to run.
+ *
+ * @return  0: success.
+ */
+int aos_work_sched(aos_work_t *work);
+
+/**
+ * This function will cancel a work on the default workqueue.
+ *
+ * @param[in]  work  the work to cancel.
+ *
+ * @return  0: success.
+ */
+int aos_work_cancel(aos_work_t *work);
+
+/**
+ * Realloc memory.
+ *
+ * @param[in]  mem   current memory address point.
+ * @param[in]  size  new size of the mem to remalloc.
+ *
+ * @return  NULL: error.
+ */
+void *aos_realloc(void *mem, unsigned int size);
+void *aos_realloc_check(void *mem, unsigned int size);
+
+/**
+ * Alloc memory.
+ *
+ * @param[in]  size  size of the mem to malloc.
+ *
+ * @return  NULL: error.
+ */
+void *aos_malloc(unsigned int size);
+void *aos_malloc_check(unsigned int size);
+
+/**
+ * Alloc memory and clear to zero.
+ *
+ * @param[in]  size  size of the mem to malloc.
+ *
+ * @return  NULL: error.
+ */
+void *aos_zalloc(unsigned int size);
+void *aos_zalloc_check(unsigned int size);
+
+void *aos_calloc(unsigned int size, int num);
+void *aos_calloc_check(unsigned int size, int num);
+
+/**
+ * Trace alloced mems.
+ *
+ * @param[in]  addr       pointer of the mem alloced by malloc.
+ * @param[in]  allocator  buildin_address.
+ */
+void aos_alloc_trace(void *addr, size_t allocator);
+
+/**
+ * Free memory.
+ *
+ * @param[in]  ptr  address point of the mem.
+ */
+void aos_free(void *mem);
+
+/**
+ * Free memory and set NULL.
+ *
+ * @param[in]  *ptr  address point of the mem.
+ */
+void aos_freep(char **ptr);
+
+/**
+ * Get current time in nano seconds.
+ *
+ * @return  elapsed time in nano seconds from system starting.
+ */
+long long aos_now(void);
+
+/**
+ * Get current time in mini seconds.
+ *
+ * @return  elapsed time in mini seconds from system starting.
+ */
+long long aos_now_ms(void);
+
+/**
+ * Msleep.
+ *
+ * @param[in]  ms  sleep time in milliseconds.
+ */
+void aos_msleep(int ms);
+
+/**
+ * Initialize system
+ */
+void aos_init(void);
+
+/**
+ * Start system
+ */
+void aos_start(void);
+
+/// System enter interrupt status.
+/// \return execution status code. \ref k_status_t
+k_status_t aos_kernel_intrpt_enter(void);
+
+/// System exit interrupt status.
+/// \return execution status code. \ref k_status_t
+k_status_t aos_kernel_intrpt_exit(void);
+
+/**
+ * Get aos memory used info.
+ *
+ * @param[out]  total   the total memory can be use.
+ * @param[out]  used    the used memory by malloc.
+ * @param[out]  mfree   the free memory can be use.
+ * @param[out]  peak    the peak memory used.
+ *
+ * @return  execution status code.
+ */
+int aos_get_mminfo(int32_t *total, int32_t *used, int32_t *mfree, int32_t *peak);
+
+/**
+ * Dump aos memory .
+ *
+ * @return  execution status code.
+ */
+int aos_mm_dump(void);
+
+/// Suspend the scheduler.
+void aos_kernel_sched_suspend(void);
+
+/// Resume the scheduler.
+void aos_kernel_sched_resume(void);
+
+uint64_t aos_kernel_tick2ms(uint32_t ticks);
+
+uint64_t aos_kernel_ms2tick(uint32_t ms);
+
+int32_t aos_kernel_suspend(void);
+
+void aos_kernel_resume(int32_t ticks);
+
+typedef void (*except_process_t)(int errno, const char *file, int line, const char *func_name, void *caller);
+void aos_set_except_callback(except_process_t except);
+void aos_set_except_default();
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AOS_KERNEL_H */
+

+ 340 - 0
components/aos_port/include/aos/list.h

@@ -0,0 +1,340 @@
+#ifndef AOS_LIST_H
+#define AOS_LIST_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Get offset of a member variable.
+ *
+ * @param[in]   type     the type of the struct this is embedded in.
+ * @param[in]   member   the name of the variable within the struct.
+ */
+#define aos_offsetof(type, member)   ((size_t)&(((type *)0)->member))
+
+/*
+ * Get the struct for this entry.
+ *
+ * @param[in]   ptr     the list head to take the element from.
+ * @param[in]   type    the type of the struct this is embedded in.
+ * @param[in]   member  the name of the variable within the struct.
+ */
+#define aos_container_of(ptr, type, member) \
+    ((type *) ((char *) (ptr) - aos_offsetof(type, member)))
+
+/* for double link list */
+typedef struct dlist_s {
+    struct dlist_s *prev;
+    struct dlist_s *next;
+} dlist_t;
+
+static inline void __dlist_add(dlist_t *node, dlist_t *prev, dlist_t *next)
+{
+    node->next = next;
+    node->prev = prev;
+
+    prev->next = node;
+    next->prev = node;
+}
+
+/*
+ * Get the struct for this entry.
+ *
+ * @param[in]   addr    the list head to take the element from.
+ * @param[in]   type    the type of the struct this is embedded in.
+ * @param[in]   member  the name of the dlist_t within the struct.
+ */
+#define dlist_entry(addr, type, member) \
+    ((type *)((long)addr - aos_offsetof(type, member)))
+
+
+static inline void dlist_add(dlist_t *node, dlist_t *queue)
+{
+    __dlist_add(node, queue, queue->next);
+}
+
+static inline void dlist_add_tail(dlist_t *node, dlist_t *queue)
+{
+    __dlist_add(node, queue->prev, queue);
+}
+
+static inline void dlist_del(dlist_t *node)
+{
+    dlist_t *prev = node->prev;
+    dlist_t *next = node->next;
+
+    prev->next = next;
+    next->prev = prev;
+}
+
+static inline void dlist_init(dlist_t *node)
+{
+    node->next = node->prev = node;
+}
+
+static inline void INIT_AOS_DLIST_HEAD(dlist_t *list)
+{
+    list->next = list;
+    list->prev = list;
+}
+
+static inline int dlist_empty(const dlist_t *head)
+{
+    return head->next == head;
+}
+
+/*
+ * Initialise the list.
+ *
+ * @param[in]   list    the list to be inited.
+ */
+#define AOS_DLIST_INIT(list)  {&(list), &(list)}
+
+/*
+ * Get the first element from a list
+ *
+ * @param[in]   ptr     the list head to take the element from.
+ * @param[in]   type    the type of the struct this is embedded in.
+ * @param[in]   member  the name of the dlist_t within the struct.
+ */
+#define dlist_first_entry(ptr, type, member) \
+    dlist_entry((ptr)->next, type, member)
+
+/*
+ * Iterate over a list.
+ *
+ * @param[in]   pos     the &struct dlist_t to use as a loop cursor.
+ * @param[in]   head    he head for your list.
+ */
+#define dlist_for_each(pos, head) \
+    for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/*
+ * Iterate over a list safe against removal of list entry.
+ *
+ * @param[in]   pos     the &struct dlist_t to use as a loop cursor.
+ * @param[in]   n       another &struct dlist_t to use as temporary storage.
+ * @param[in]   head    he head for your list.
+ */
+#define dlist_for_each_safe(pos, n, head) \
+    for (pos = (head)->next, n = pos->next; pos != (head); \
+    pos = n, n = pos->next)
+
+/*
+ * Iterate over list of given type.
+ *
+ * @param[in]   queue   he head for your list.
+ * @param[in]   node    the &struct dlist_t to use as a loop cursor.
+ * @param[in]   type    the type of the struct this is embedded in.
+ * @param[in]   member  the name of the dlist_t within the struct.
+ */
+#define dlist_for_each_entry(queue, node, type, member) \
+    for (node = aos_container_of((queue)->next, type, member); \
+         &node->member != (queue); \
+         node = aos_container_of(node->member.next, type, member))
+
+/*
+ * Iterate over list of given type safe against removal of list entry.
+ *
+ * @param[in]   queue   the head for your list.
+ * @param[in]   n       the type * to use as a temp.
+ * @param[in]   node    the type * to use as a loop cursor.
+ * @param[in]   type    the type of the struct this is embedded in.
+ * @param[in]   member  the name of the dlist_t within the struct.
+ */
+#define dlist_for_each_entry_safe(queue, n, node, type, member) \
+    for (node = aos_container_of((queue)->next, type, member),  \
+         n = (queue)->next ? (queue)->next->next : NULL;        \
+         &node->member != (queue);                              \
+         node = aos_container_of(n, type, member), n = n ? n->next : NULL)
+
+/*
+ * Get the struct for this entry.
+ * @param[in]   ptr     the list head to take the element from.
+ * @param[in]   type    the type of the struct this is embedded in.
+ * @param[in]   member  the name of the variable within the struct.
+ */
+#define list_entry(ptr, type, member) \
+        aos_container_of(ptr, type, member)
+
+
+/*
+ * Iterate backwards over list of given type.
+ *
+ * @param[in]   pos     the type * to use as a loop cursor.
+ * @param[in]   head    he head for your list.
+ * @param[in]   member  the name of the dlist_t within the struct.
+ * @param[in]   type    the type of the struct this is embedded in.
+ */
+#define dlist_for_each_entry_reverse(pos, head, member, type) \
+    for (pos = list_entry((head)->prev, type, member);        \
+         &pos->member != (head);                              \
+         pos = list_entry(pos->member.prev, type, member))
+
+
+/*
+ * Get the list length.
+ *
+ * @param[in]  queue  the head for your list.
+ */
+int dlist_entry_number(dlist_t *queue);
+
+
+
+/*
+ * Initialise the list.
+ *
+ * @param[in]   name    the list to be initialized.
+ */
+#define AOS_DLIST_HEAD_INIT(name) { &(name), &(name) }
+
+/*
+ * Initialise the list.
+ *
+ * @param[in]   name    the list to be initialized.
+ */
+#define AOS_DLIST_HEAD(name) \
+        dlist_t name = AOS_DLIST_HEAD_INIT(name)
+
+/* for single link list */
+typedef struct slist_s {
+    struct slist_s *next;
+} slist_t;
+
+static inline void slist_add(slist_t *node, slist_t *head)
+{
+    node->next = head->next;
+    head->next = node;
+}
+
+void slist_add_tail(slist_t *node, slist_t *head);
+
+void slist_del(slist_t *node, slist_t *head);
+
+static inline int slist_empty(const slist_t *head)
+{
+    return !head->next;
+}
+
+static inline void slist_init(slist_t *head)
+{
+    head->next = 0;
+}
+
+static inline slist_t *slist_remove(slist_t *l, slist_t *n)
+{
+    /* remove slist head */
+    struct slist_s *node = l;
+    while (node->next && node->next != n) node = node->next;
+
+    /* remove node */
+    if (node->next != (slist_t *)0) node->next = node->next->next;
+
+    return l;
+}
+
+static inline slist_t *slist_first(slist_t *l)
+{
+    return l->next;
+}
+
+static inline slist_t *slist_tail(slist_t *l)
+{
+    while (l->next) l = l->next;
+
+    return l;
+}
+
+static inline slist_t *slist_next(slist_t *n)
+{
+    return n->next;
+}
+
+/*
+* Iterate over list of given type.
+*
+* @param[in]   queue   he head for your list.
+* @param[in]   node    the type * to use as a loop cursor.
+* @param[in]   type    the type of the struct this is embedded in.
+* @param[in]   member  the name of the slist_t within the struct.
+*/
+#define slist_for_each_entry(queue, node, type, member)        \
+    for (node = (queue)->next? aos_container_of((queue)->next, type, member) : NULL; \
+         node;                                        \
+         node = node->member.next ? aos_container_of(node->member.next, type, member) : NULL)
+
+/*
+ * Iterate over list of given type safe against removal of list entry.
+ *
+ * @param[in]   queue   the head for your list.
+ * @param[in]   tmp     the type * to use as a temp.
+ * @param[in]   node    the type * to use as a loop cursor.
+ * @param[in]   type    the type of the struct this is embedded in.
+ * @param[in]   member  the name of the slist_t within the struct.
+ */
+#define slist_for_each_entry_safe(queue, tmp, node, type, member) \
+    for (node = (queue)->next? aos_container_of((queue)->next, type, member) : NULL, \
+         tmp = (queue)->next ? (queue)->next->next : NULL;        \
+         node;                                           \
+         node = tmp ? aos_container_of(tmp, type, member) : NULL, tmp = tmp ? tmp->next : NULL)
+
+/*
+ * Initialise the list.
+ *
+ * @param[in]   name    the list to be initialized.
+ */
+#define AOS_SLIST_HEAD_INIT(name) {0}
+
+/*
+ * Initialise the list.
+ *
+ * @param[in]   name    the list to be initialized.
+ */
+#define AOS_SLIST_HEAD(name) \
+        slist_t name = AOS_SLIST_HEAD_INIT(name)
+
+/*
+ * Get the struct for this entry.
+ * @param[in]   addr     the list head to take the element from.
+ * @param[in]   type     the type of the struct this is embedded in.
+ * @param[in]   member   the name of the slist_t within the struct.
+ */
+#define slist_entry(addr, type, member) (                                   \
+    addr ? (type *)((long)addr - aos_offsetof(type, member)) : (type *)addr \
+)
+
+/*
+* Get the first element from a list.
+*
+* @param[in]   ptr     the list head to take the element from.
+* @param[in]   type    the type of the struct this is embedded in.
+* @param[in]   member  the name of the slist_t within the struct.
+*/
+#define slist_first_entry(ptr, type, member) \
+    slist_entry((ptr)->next, type, member)
+
+/**
+ * slist_tail_entry - get the tail element from a slist
+ * @ptr:    the slist head to take the element from.
+ * @type:   the type of the struct this is embedded in.
+ * @member: the name of the slist_struct within the struct.
+ *
+ * Note, that slist is expected to be not empty.
+ */
+#define slist_tail_entry(ptr, type, member) \
+    slist_entry(slist_tail(ptr), type, member)
+
+/*
+ * Get the list length.
+ *
+ * @param[in]   queue    the head for your list.
+ */
+int slist_entry_number(slist_t *queue);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AOS_LIST_H */
+

+ 582 - 0
components/aos_port/include/aos/queue.h

@@ -0,0 +1,582 @@
+/*
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  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.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ *	@(#)queue.h	8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef	_SYS_QUEUE_H_
+#define	_SYS_QUEUE_H_
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * lists, simple queues, tail queues, and circular queues.
+ *
+ * A singly-linked list is headed by a single forward pointer. The
+ * elements are singly linked for minimum space and pointer manipulation
+ * overhead at the expense of O(n) removal for arbitrary elements. New
+ * elements can be added to the list after an existing element or at the
+ * head of the list.  Elements being removed from the head of the list
+ * should use the explicit macro for this purpose for optimum
+ * efficiency. A singly-linked list may only be traversed in the forward
+ * direction.  Singly-linked lists are ideal for applications with large
+ * datasets and few or no removals or for implementing a LIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A simple queue is headed by a pair of pointers, one the head of the
+ * list and the other to the tail of the list. The elements are singly
+ * linked to save space, so elements can only be removed from the
+ * head of the list. New elements can be added to the list after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A simple queue may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+/*
+ * List definitions.
+ */
+#define	LIST_HEAD(name, type)						\
+struct name {								\
+	struct type *lh_first;	/* first element */			\
+}
+
+#define	LIST_HEAD_INITIALIZER(head)					\
+	{ NULL }
+
+#define	LIST_ENTRY(type)						\
+struct {								\
+	struct type *le_next;	/* next element */			\
+	struct type **le_prev;	/* address of previous next element */	\
+}
+
+/*
+ * List functions.
+ */
+#define	LIST_INIT(head) do {						\
+	(head)->lh_first = NULL;					\
+} while (/*CONSTCOND*/0)
+
+#define	LIST_INSERT_AFTER(listelm, elm, field) do {			\
+	if (((elm)->field.le_next = (listelm)->field.le_next) != NULL)	\
+		(listelm)->field.le_next->field.le_prev =		\
+		    &(elm)->field.le_next;				\
+	(listelm)->field.le_next = (elm);				\
+	(elm)->field.le_prev = &(listelm)->field.le_next;		\
+} while (/*CONSTCOND*/0)
+
+#define	LIST_INSERT_BEFORE(listelm, elm, field) do {			\
+	(elm)->field.le_prev = (listelm)->field.le_prev;		\
+	(elm)->field.le_next = (listelm);				\
+	*(listelm)->field.le_prev = (elm);				\
+	(listelm)->field.le_prev = &(elm)->field.le_next;		\
+} while (/*CONSTCOND*/0)
+
+#define	LIST_INSERT_HEAD(head, elm, field) do {				\
+	if (((elm)->field.le_next = (head)->lh_first) != NULL)		\
+		(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+	(head)->lh_first = (elm);					\
+	(elm)->field.le_prev = &(head)->lh_first;			\
+} while (/*CONSTCOND*/0)
+
+#define	LIST_REMOVE(elm, field) do {					\
+	if ((elm)->field.le_next != NULL)				\
+		(elm)->field.le_next->field.le_prev = 			\
+		    (elm)->field.le_prev;				\
+	*(elm)->field.le_prev = (elm)->field.le_next;			\
+} while (/*CONSTCOND*/0)
+
+#define	LIST_FOREACH(var, head, field)					\
+	for ((var) = ((head)->lh_first);				\
+		(var);							\
+		(var) = ((var)->field.le_next))
+
+/*
+ * List access methods.
+ */
+#define	LIST_EMPTY(head)		((head)->lh_first == NULL)
+#define	LIST_FIRST(head)		((head)->lh_first)
+#define	LIST_NEXT(elm, field)		((elm)->field.le_next)
+
+
+/*
+ * Singly-linked List definitions.
+ */
+#define	SLIST_HEAD(name, type)						\
+struct name {								\
+	struct type *slh_first;	/* first element */			\
+}
+
+#define	SLIST_HEAD_INITIALIZER(head)					\
+	{ NULL }
+
+#define	SLIST_ENTRY(type)						\
+struct {								\
+	struct type *sle_next;	/* next element */			\
+}
+
+/*
+ * Singly-linked List functions.
+ */
+#define	SLIST_INIT(head) do {						\
+	(head)->slh_first = NULL;					\
+} while (/*CONSTCOND*/0)
+
+#define	SLIST_INSERT_AFTER(slistelm, elm, field) do {			\
+	(elm)->field.sle_next = (slistelm)->field.sle_next;		\
+	(slistelm)->field.sle_next = (elm);				\
+} while (/*CONSTCOND*/0)
+
+#define	SLIST_INSERT_HEAD(head, elm, field) do {			\
+	(elm)->field.sle_next = (head)->slh_first;			\
+	(head)->slh_first = (elm);					\
+} while (/*CONSTCOND*/0)
+
+#define	SLIST_REMOVE_HEAD(head, field) do {				\
+	(head)->slh_first = (head)->slh_first->field.sle_next;		\
+} while (/*CONSTCOND*/0)
+
+#define	SLIST_REMOVE(head, elm, type, field) do {			\
+	if ((head)->slh_first == (elm)) {				\
+		SLIST_REMOVE_HEAD((head), field);			\
+	}								\
+	else {								\
+		struct type *curelm = (head)->slh_first;		\
+		while(curelm->field.sle_next != (elm))			\
+			curelm = curelm->field.sle_next;		\
+		curelm->field.sle_next =				\
+		    curelm->field.sle_next->field.sle_next;		\
+	}								\
+} while (/*CONSTCOND*/0)
+
+#define	SLIST_FOREACH(var, head, field)					\
+	for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next)
+
+/*
+ * Singly-linked List access methods.
+ */
+#define	SLIST_EMPTY(head)	((head)->slh_first == NULL)
+#define	SLIST_FIRST(head)	((head)->slh_first)
+#define	SLIST_NEXT(elm, field)	((elm)->field.sle_next)
+
+
+/*
+ * Singly-linked Tail queue declarations.
+ */
+#define	STAILQ_HEAD(name, type)					\
+struct name {								\
+	struct type *stqh_first;	/* first element */			\
+	struct type **stqh_last;	/* addr of last next element */		\
+}
+
+#define	STAILQ_HEAD_INITIALIZER(head)					\
+	{ NULL, &(head).stqh_first }
+
+#define	STAILQ_ENTRY(type)						\
+struct {								\
+	struct type *stqe_next;	/* next element */			\
+}
+
+/*
+ * Singly-linked Tail queue functions.
+ */
+#define	STAILQ_INIT(head) do {						\
+	(head)->stqh_first = NULL;					\
+	(head)->stqh_last = &(head)->stqh_first;				\
+} while (/*CONSTCOND*/0)
+
+#define	STAILQ_INSERT_HEAD(head, elm, field) do {			\
+	if (((elm)->field.stqe_next = (head)->stqh_first) == NULL)	\
+		(head)->stqh_last = &(elm)->field.stqe_next;		\
+	(head)->stqh_first = (elm);					\
+} while (/*CONSTCOND*/0)
+
+#define	STAILQ_INSERT_TAIL(head, elm, field) do {			\
+	(elm)->field.stqe_next = NULL;					\
+	*(head)->stqh_last = (elm);					\
+	(head)->stqh_last = &(elm)->field.stqe_next;			\
+} while (/*CONSTCOND*/0)
+
+#define	STAILQ_INSERT_AFTER(head, listelm, elm, field) do {		\
+	if (((elm)->field.stqe_next = (listelm)->field.stqe_next) == NULL)\
+		(head)->stqh_last = &(elm)->field.stqe_next;		\
+	(listelm)->field.stqe_next = (elm);				\
+} while (/*CONSTCOND*/0)
+
+#define	STAILQ_REMOVE_HEAD(head, field) do {				\
+	if (((head)->stqh_first = (head)->stqh_first->field.stqe_next) == NULL) \
+		(head)->stqh_last = &(head)->stqh_first;			\
+} while (/*CONSTCOND*/0)
+
+#define	STAILQ_REMOVE(head, elm, type, field) do {			\
+	if ((head)->stqh_first == (elm)) {				\
+		STAILQ_REMOVE_HEAD((head), field);			\
+	} else {							\
+		struct type *curelm = (head)->stqh_first;		\
+		while (curelm->field.stqe_next != (elm))			\
+			curelm = curelm->field.stqe_next;		\
+		if ((curelm->field.stqe_next =				\
+			curelm->field.stqe_next->field.stqe_next) == NULL) \
+			    (head)->stqh_last = &(curelm)->field.stqe_next; \
+	}								\
+} while (/*CONSTCOND*/0)
+
+#define	STAILQ_FOREACH(var, head, field)				\
+	for ((var) = ((head)->stqh_first);				\
+		(var);							\
+		(var) = ((var)->field.stqe_next))
+
+#define	STAILQ_CONCAT(head1, head2) do {				\
+	if (!STAILQ_EMPTY((head2))) {					\
+		*(head1)->stqh_last = (head2)->stqh_first;		\
+		(head1)->stqh_last = (head2)->stqh_last;		\
+		STAILQ_INIT((head2));					\
+	}								\
+} while (/*CONSTCOND*/0)
+
+/*
+ * Singly-linked Tail queue access methods.
+ */
+#define	STAILQ_EMPTY(head)	((head)->stqh_first == NULL)
+#define	STAILQ_FIRST(head)	((head)->stqh_first)
+#define	STAILQ_NEXT(elm, field)	((elm)->field.stqe_next)
+
+
+/*
+ * Simple queue definitions.
+ */
+#define	SIMPLEQ_HEAD(name, type)					\
+struct name {								\
+	struct type *sqh_first;	/* first element */			\
+	struct type **sqh_last;	/* addr of last next element */		\
+}
+
+#define	SIMPLEQ_HEAD_INITIALIZER(head)					\
+	{ NULL, &(head).sqh_first }
+
+#define	SIMPLEQ_ENTRY(type)						\
+struct {								\
+	struct type *sqe_next;	/* next element */			\
+}
+
+/*
+ * Simple queue functions.
+ */
+#define	SIMPLEQ_INIT(head) do {						\
+	(head)->sqh_first = NULL;					\
+	(head)->sqh_last = &(head)->sqh_first;				\
+} while (/*CONSTCOND*/0)
+
+#define	SIMPLEQ_INSERT_HEAD(head, elm, field) do {			\
+	if (((elm)->field.sqe_next = (head)->sqh_first) == NULL)	\
+		(head)->sqh_last = &(elm)->field.sqe_next;		\
+	(head)->sqh_first = (elm);					\
+} while (/*CONSTCOND*/0)
+
+#define	SIMPLEQ_INSERT_TAIL(head, elm, field) do {			\
+	(elm)->field.sqe_next = NULL;					\
+	*(head)->sqh_last = (elm);					\
+	(head)->sqh_last = &(elm)->field.sqe_next;			\
+} while (/*CONSTCOND*/0)
+
+#define	SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do {		\
+	if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
+		(head)->sqh_last = &(elm)->field.sqe_next;		\
+	(listelm)->field.sqe_next = (elm);				\
+} while (/*CONSTCOND*/0)
+
+#define	SIMPLEQ_REMOVE_HEAD(head, field) do {				\
+	if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
+		(head)->sqh_last = &(head)->sqh_first;			\
+} while (/*CONSTCOND*/0)
+
+#define	SIMPLEQ_REMOVE(head, elm, type, field) do {			\
+	if ((head)->sqh_first == (elm)) {				\
+		SIMPLEQ_REMOVE_HEAD((head), field);			\
+	} else {							\
+		struct type *curelm = (head)->sqh_first;		\
+		while (curelm->field.sqe_next != (elm))			\
+			curelm = curelm->field.sqe_next;		\
+		if ((curelm->field.sqe_next =				\
+			curelm->field.sqe_next->field.sqe_next) == NULL) \
+			    (head)->sqh_last = &(curelm)->field.sqe_next; \
+	}								\
+} while (/*CONSTCOND*/0)
+
+#define	SIMPLEQ_FOREACH(var, head, field)				\
+	for ((var) = ((head)->sqh_first);				\
+		(var);							\
+		(var) = ((var)->field.sqe_next))
+
+/*
+ * Simple queue access methods.
+ */
+#define	SIMPLEQ_EMPTY(head)		((head)->sqh_first == NULL)
+#define	SIMPLEQ_FIRST(head)		((head)->sqh_first)
+#define	SIMPLEQ_NEXT(elm, field)	((elm)->field.sqe_next)
+
+
+/*
+ * Tail queue definitions.
+ */
+#define	_TAILQ_HEAD(name, type, qual)					\
+struct name {								\
+	qual type *tqh_first;		/* first element */		\
+	qual type *qual *tqh_last;	/* addr of last next element */	\
+}
+#define TAILQ_HEAD(name, type)	_TAILQ_HEAD(name, struct type,)
+
+#define	TAILQ_HEAD_INITIALIZER(head)					\
+	{ NULL, &(head).tqh_first }
+
+#define	_TAILQ_ENTRY(type, qual)					\
+struct {								\
+	qual type *tqe_next;		/* next element */		\
+	qual type *qual *tqe_prev;	/* address of previous next element */\
+}
+#define TAILQ_ENTRY(type)	_TAILQ_ENTRY(struct type,)
+
+/*
+ * Tail queue functions.
+ */
+#define	TAILQ_INIT(head) do {						\
+	(head)->tqh_first = NULL;					\
+	(head)->tqh_last = &(head)->tqh_first;				\
+} while (/*CONSTCOND*/0)
+
+#define	TAILQ_INSERT_HEAD(head, elm, field) do {			\
+	if (((elm)->field.tqe_next = (head)->tqh_first) != NULL)	\
+		(head)->tqh_first->field.tqe_prev =			\
+		    &(elm)->field.tqe_next;				\
+	else								\
+		(head)->tqh_last = &(elm)->field.tqe_next;		\
+	(head)->tqh_first = (elm);					\
+	(elm)->field.tqe_prev = &(head)->tqh_first;			\
+} while (/*CONSTCOND*/0)
+
+#define	TAILQ_INSERT_TAIL(head, elm, field) do {			\
+	(elm)->field.tqe_next = NULL;					\
+	(elm)->field.tqe_prev = (head)->tqh_last;			\
+	*(head)->tqh_last = (elm);					\
+	(head)->tqh_last = &(elm)->field.tqe_next;			\
+} while (/*CONSTCOND*/0)
+
+#define	TAILQ_INSERT_AFTER(head, listelm, elm, field) do {		\
+	if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+		(elm)->field.tqe_next->field.tqe_prev = 		\
+		    &(elm)->field.tqe_next;				\
+	else								\
+		(head)->tqh_last = &(elm)->field.tqe_next;		\
+	(listelm)->field.tqe_next = (elm);				\
+	(elm)->field.tqe_prev = &(listelm)->field.tqe_next;		\
+} while (/*CONSTCOND*/0)
+
+#define	TAILQ_INSERT_BEFORE(listelm, elm, field) do {			\
+	(elm)->field.tqe_prev = (listelm)->field.tqe_prev;		\
+	(elm)->field.tqe_next = (listelm);				\
+	*(listelm)->field.tqe_prev = (elm);				\
+	(listelm)->field.tqe_prev = &(elm)->field.tqe_next;		\
+} while (/*CONSTCOND*/0)
+
+#define	TAILQ_REMOVE(head, elm, field) do {				\
+	if (((elm)->field.tqe_next) != NULL)				\
+		(elm)->field.tqe_next->field.tqe_prev = 		\
+		    (elm)->field.tqe_prev;				\
+	else								\
+		(head)->tqh_last = (elm)->field.tqe_prev;		\
+	*(elm)->field.tqe_prev = (elm)->field.tqe_next;			\
+} while (/*CONSTCOND*/0)
+
+#define	TAILQ_FOREACH(var, head, field)					\
+	for ((var) = ((head)->tqh_first);				\
+		(var);							\
+		(var) = ((var)->field.tqe_next))
+
+#define	TAILQ_FOREACH_REVERSE(var, head, headname, field)		\
+	for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last));	\
+		(var);							\
+		(var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last)))
+
+#define	TAILQ_CONCAT(head1, head2, field) do {				\
+	if (!TAILQ_EMPTY(head2)) {					\
+		*(head1)->tqh_last = (head2)->tqh_first;		\
+		(head2)->tqh_first->field.tqe_prev = (head1)->tqh_last;	\
+		(head1)->tqh_last = (head2)->tqh_last;			\
+		TAILQ_INIT((head2));					\
+	}								\
+} while (/*CONSTCOND*/0)
+
+/*
+ * Tail queue access methods.
+ */
+#define	TAILQ_EMPTY(head)		((head)->tqh_first == NULL)
+#define	TAILQ_FIRST(head)		((head)->tqh_first)
+#define	TAILQ_NEXT(elm, field)		((elm)->field.tqe_next)
+
+#define	TAILQ_LAST(head, headname) \
+	(*(((struct headname *)((head)->tqh_last))->tqh_last))
+#define	TAILQ_PREV(elm, headname, field) \
+	(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+
+
+/*
+ * Circular queue definitions.
+ */
+#define	CIRCLEQ_HEAD(name, type)					\
+struct name {								\
+	struct type *cqh_first;		/* first element */		\
+	struct type *cqh_last;		/* last element */		\
+}
+
+#define	CIRCLEQ_HEAD_INITIALIZER(head)					\
+	{ (void *)&head, (void *)&head }
+
+#define	CIRCLEQ_ENTRY(type)						\
+struct {								\
+	struct type *cqe_next;		/* next element */		\
+	struct type *cqe_prev;		/* previous element */		\
+}
+
+/*
+ * Circular queue functions.
+ */
+#define	CIRCLEQ_INIT(head) do {						\
+	(head)->cqh_first = (void *)(head);				\
+	(head)->cqh_last = (void *)(head);				\
+} while (/*CONSTCOND*/0)
+
+#define	CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do {		\
+	(elm)->field.cqe_next = (listelm)->field.cqe_next;		\
+	(elm)->field.cqe_prev = (listelm);				\
+	if ((listelm)->field.cqe_next == (void *)(head))		\
+		(head)->cqh_last = (elm);				\
+	else								\
+		(listelm)->field.cqe_next->field.cqe_prev = (elm);	\
+	(listelm)->field.cqe_next = (elm);				\
+} while (/*CONSTCOND*/0)
+
+#define	CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do {		\
+	(elm)->field.cqe_next = (listelm);				\
+	(elm)->field.cqe_prev = (listelm)->field.cqe_prev;		\
+	if ((listelm)->field.cqe_prev == (void *)(head))		\
+		(head)->cqh_first = (elm);				\
+	else								\
+		(listelm)->field.cqe_prev->field.cqe_next = (elm);	\
+	(listelm)->field.cqe_prev = (elm);				\
+} while (/*CONSTCOND*/0)
+
+#define	CIRCLEQ_INSERT_HEAD(head, elm, field) do {			\
+	(elm)->field.cqe_next = (head)->cqh_first;			\
+	(elm)->field.cqe_prev = (void *)(head);				\
+	if ((head)->cqh_last == (void *)(head))				\
+		(head)->cqh_last = (elm);				\
+	else								\
+		(head)->cqh_first->field.cqe_prev = (elm);		\
+	(head)->cqh_first = (elm);					\
+} while (/*CONSTCOND*/0)
+
+#define	CIRCLEQ_INSERT_TAIL(head, elm, field) do {			\
+	(elm)->field.cqe_next = (void *)(head);				\
+	(elm)->field.cqe_prev = (head)->cqh_last;			\
+	if ((head)->cqh_first == (void *)(head))			\
+		(head)->cqh_first = (elm);				\
+	else								\
+		(head)->cqh_last->field.cqe_next = (elm);		\
+	(head)->cqh_last = (elm);					\
+} while (/*CONSTCOND*/0)
+
+#define	CIRCLEQ_REMOVE(head, elm, field) do {				\
+	if ((elm)->field.cqe_next == (void *)(head))			\
+		(head)->cqh_last = (elm)->field.cqe_prev;		\
+	else								\
+		(elm)->field.cqe_next->field.cqe_prev =			\
+		    (elm)->field.cqe_prev;				\
+	if ((elm)->field.cqe_prev == (void *)(head))			\
+		(head)->cqh_first = (elm)->field.cqe_next;		\
+	else								\
+		(elm)->field.cqe_prev->field.cqe_next =			\
+		    (elm)->field.cqe_next;				\
+} while (/*CONSTCOND*/0)
+
+#define	CIRCLEQ_FOREACH(var, head, field)				\
+	for ((var) = ((head)->cqh_first);				\
+		(var) != (const void *)(head);				\
+		(var) = ((var)->field.cqe_next))
+
+#define	CIRCLEQ_FOREACH_REVERSE(var, head, field)			\
+	for ((var) = ((head)->cqh_last);				\
+		(var) != (const void *)(head);				\
+		(var) = ((var)->field.cqe_prev))
+
+/*
+ * Circular queue access methods.
+ */
+#define	CIRCLEQ_EMPTY(head)		((head)->cqh_first == (void *)(head))
+#define	CIRCLEQ_FIRST(head)		((head)->cqh_first)
+#define	CIRCLEQ_LAST(head)		((head)->cqh_last)
+#define	CIRCLEQ_NEXT(elm, field)	((elm)->field.cqe_next)
+#define	CIRCLEQ_PREV(elm, field)	((elm)->field.cqe_prev)
+
+#define CIRCLEQ_LOOP_NEXT(head, elm, field)				\
+	(((elm)->field.cqe_next == (void *)(head))			\
+	    ? ((head)->cqh_first)					\
+	    : (elm->field.cqe_next))
+#define CIRCLEQ_LOOP_PREV(head, elm, field)				\
+	(((elm)->field.cqe_prev == (void *)(head))			\
+	    ? ((head)->cqh_last)					\
+	    : (elm->field.cqe_prev))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* sys/queue.h */

+ 103 - 0
components/aos_port/include/aos/ringblk_buf.h

@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2006-2018, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2018-08-25     armink       the first version
+ */
+
+#ifndef _RINGBLK_BUF_H_
+#define _RINGBLK_BUF_H_
+
+/*
+ * Introduction:
+ * The rbb is the ring buffer which is composed with many blocks. It is different from the ring buffer.
+ * The ring buffer is only composed with chars. The rbb put and get supported zero copies. So the rbb
+ * is very suitable for put block and get block by a certain order. Such as DMA block transmit,
+ * communicate frame send/recv, and so on.
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <aos/list.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum rbb_status {
+    /* unused status when first initialize or after blk_free() */
+    RBB_BLK_UNUSED,
+    /* initialized status after blk_alloc() */
+    RBB_BLK_INITED,
+    /* put status after blk_put() */
+    RBB_BLK_PUT,
+    /* get status after blk_get() */
+    RBB_BLK_GET,
+};
+typedef enum rbb_status rbb_status_t;
+
+/**
+ * the block of rbb
+ */
+struct rbb_blk {
+    rbb_status_t status :8;
+    /* less then 2^24 */
+    size_t size :24;
+    uint8_t *buf;
+    slist_t list;
+};
+typedef struct rbb_blk *rbb_blk_t;
+
+/**
+ * Rbb block queue: the blocks (from block1->buf to blockn->buf) memory which on this queue is continuous.
+ */
+struct rbb_blk_queue {
+    rbb_blk_t blocks;
+    size_t blk_num;
+};
+typedef struct rbb_blk_queue *rbb_blk_queue_t;
+
+/**
+ * ring block buffer
+ */
+struct _rbb {
+    uint8_t *buf;
+    size_t buf_size;
+    /* all of blocks */
+    rbb_blk_t blk_set;
+    size_t blk_max_num;
+    /* saved the initialized and put status blocks */
+    slist_t blk_list;
+};
+typedef struct _rbb *rbb_t;
+
+/* rbb (ring block buffer) API */
+void rbb_init(rbb_t rbb, uint8_t *buf, size_t buf_size, rbb_blk_t block_set, size_t blk_max_num);
+rbb_t rbb_create(size_t buf_size, size_t blk_max_num);
+void rbb_destroy(rbb_t rbb);
+size_t rbb_get_buf_size(rbb_t rbb);
+
+/* rbb block API */
+rbb_blk_t rbb_blk_alloc(rbb_t rbb, size_t blk_size);
+void rbb_blk_put(rbb_blk_t block);
+rbb_blk_t rbb_blk_get(rbb_t rbb);
+size_t rbb_blk_size(rbb_blk_t block);
+uint8_t *rbb_blk_buf(rbb_blk_t block);
+void rbb_blk_free(rbb_t rbb, rbb_blk_t block);
+
+/* rbb block queue API */
+size_t rbb_blk_queue_get(rbb_t rbb, size_t queue_data_len, rbb_blk_queue_t blk_queue);
+size_t rbb_blk_queue_len(rbb_blk_queue_t blk_queue);
+uint8_t *rbb_blk_queue_buf(rbb_blk_queue_t blk_queue);
+void rbb_blk_queue_free(rbb_t rbb, rbb_blk_queue_t blk_queue);
+size_t rbb_next_blk_queue_len(rbb_t rbb);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RINGBLK_BUF_H_ */

+ 92 - 0
components/aos_port/include/aos/ringbuffer.h

@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+#ifndef __RINGBUFFER__
+#define __RINGBUFFER__
+
+#include <stdio.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+    uint8_t *buffer;
+    uint32_t length;
+    uint32_t ridx;
+    uint32_t widx;
+} dev_ringbuf_t;
+
+/**
+ * @brief  create a ring buffer
+ * @param[in] length length space of the ring buffer
+ * @return  pointer to ring buffer
+ */
+int ringbuffer_create(dev_ringbuf_t *ringbuffer, char *buffer, int length);
+
+/**
+ * @brief   destroy the ring buffer
+ * @param[in] buffer pointer to the ring buffer
+ * @return  null
+ */
+void ringbuffer_destroy(dev_ringbuf_t *buffer);
+
+/**
+ * @brief   read data from ring buffer.
+ * @param[in] buffer pointer to the ring buffer
+ * @param[in] target pointer to buffer for data to read from ring buffer
+ * @param[in] amount amount of data items to read
+ * @return  number of actual read data
+ */
+int ringbuffer_read(dev_ringbuf_t *buffer, uint8_t *target, uint32_t amount);
+
+/**
+ * @brief   write data to ring buffer
+ * @param[in] buffer pointer to the ring buffer
+ * @param[in] data pointer to buffer for data to write to ring buffer
+ * @param[in] length length of data items to write
+ * @return  0 for success
+ */
+int ringbuffer_write(dev_ringbuf_t *buffer, uint8_t *data, uint32_t length);
+
+/**
+ * @brief   is the ring buffer empty?
+ * @param[in] buffer pointer to the ring buffer
+ * @return  0 for success
+ */
+int ringbuffer_empty(dev_ringbuf_t *buffer);
+/**
+ * @brief   is the ring buffer full?
+ * @param[in] buffer pointer to the ring buffer
+ * @return  0 for success
+ */
+int ringbuffer_full(dev_ringbuf_t *buffer);
+/**
+ * @brief   available write space to ring buffer
+ * @param[in] buffer pointer to the ring buffer
+ * @return   number of write spcae
+ */
+int ringbuffer_available_write_space(dev_ringbuf_t *buffer);
+/**
+ * @brief    available read space to ring buffer
+ * @param[in] buffer pointer to the ring buffer
+ * @return  number of read spcae
+ */
+int ringbuffer_available_read_space(dev_ringbuf_t *buffer);
+
+#define ringbuffer_available_write_space(B) (\
+        (B)->length - ringbuffer_available_read_space(B))
+
+#define ringbuffer_full(B) (ringbuffer_available_write_space(B) == 0)
+
+#define ringbuffer_empty(B) (ringbuffer_available_read_space((B)) == 0)
+
+#define ringbuffer_clear(B) ((B)->widx = (B)->ridx = 0)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+

+ 37 - 0
components/aos_port/include/aos/types.h

@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+
+#ifndef AOS_TYPES_H
+#define AOS_TYPES_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define AOS_EXPORT(...)
+
+#ifdef __cplusplus
+}
+#endif
+
+#if !defined(__BEGIN_DECLS__) || !defined(__END_DECLS__)
+
+#if defined(__cplusplus)
+#  define __BEGIN_DECLS__ extern "C" {
+#  define __END_DECLS__   }
+#else
+#  define __BEGIN_DECLS__
+#  define __END_DECLS__
+#endif
+
+#endif
+
+
+#endif /* AOS_TYPES_H */
+

+ 74 - 0
components/aos_port/include/aos/version.h

@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+
+#ifndef AOS_VERSION_H
+#define AOS_VERSION_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * Get aos product model.
+ *
+ * @return  model success, 0 failure.
+ */
+const char *aos_get_product_model(void);
+
+/**
+ * Get aos os version.
+ *
+ * @return  os version success, 0 failure.
+ */
+char *aos_get_os_version(void);
+
+/**
+ * Get aos app version.
+ *
+ * @return  app version success, 0 failure.
+ */
+char *aos_get_app_version(void);
+
+/**
+ * Set aos app version.
+ *
+ * @return  0 success, otherwise failed.
+ */
+int aos_set_app_version(const char *version);
+
+/**
+ * Get aos changelog.
+ *
+ * @return  changelog success, 0 failure.
+ */
+char *aos_get_changelog(void);
+
+/**
+ * Set aos changelog.
+ *
+ * @return  0 success, otherwise failed.
+ */
+int aos_set_changelog(const char *changelog);
+
+/**
+ * Get aos kernel version.
+ *
+ * @return  kernel version success, 0 failure.
+ */
+const char *aos_get_kernel_version(void);
+
+/**
+ * Get aos device name.
+ *
+ * @return  device name success, 0 failure.
+ */
+const char *aos_get_device_name(void);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AOS_VERSION_H */
+

+ 29 - 0
components/aos_port/include/aos/wdt.h

@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+
+#ifndef __AOS_WDT_H__
+#define __AOS_WDT_H__
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+uint32_t aos_wdt_index();
+void aos_wdt_debug(int en);
+int aos_wdt_hw_enable(int id, int ms);
+void aos_wdt_hw_disable(int id);
+void aos_wdt_attach(uint32_t index, void (*will)(void *), void *args);
+void aos_wdt_detach(uint32_t index);
+int  aos_wdt_exists(uint32_t index);
+void aos_wdt_feed(uint32_t index, int max_time);
+void aos_wdt_show(uint32_t index);
+void aos_wdt_showall();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 265 - 0
components/aos_port/include/aos/yloop.h

@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+
+#ifndef AOS_YLOOP_H
+#define AOS_YLOOP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+/** @defgroup Framework API
+ *  @{
+ */
+
+#ifndef DOXYGEN_MODE
+
+/*General value define */
+#define  VALUE_FAIL            0
+#define  VALUE_SUCCESS         1
+#define  VALUE_NULL            0xffffffff
+#define  EV_NULL               0xffff
+#define  CODE_NULL             0xffff
+
+/* special event filter */
+#define EV_ALL                       0
+
+/** system event */
+#define EV_SYS                    0x0001
+#define CODE_SYS_LOOP_EXIT        0
+#define CODE_SYS_ON_READY         1
+
+/** WiFi event */
+#define  EV_NET                   0x0002
+#define  CODE_NET_CMD_RECONNECT   1
+#define  CODE_NET_CMD_INIT        2
+#define  CODE_NET_CMD_RESET       3
+#define  CODE_NET_ON_CONNECTED    4
+#define  CODE_NET_ON_DISCONNECT   5
+#define  CODE_NET_ON_GOT_IP       6
+#define  CODE_NET_ON_SHOUDOWN     7
+
+#define  VALUE_NET_ETH            1
+#define  VALUE_NET_WIFI           2
+#define  VALUE_NET_NBIOT          3
+
+/** remote procedure call */
+#define EV_RPC                      0x0100
+
+/** YunIO event */
+#define EV_YIO                      0x0101
+#define CODE_YIO_ON_CONNECTED       4
+#define CODE_YIO_ON_DISCONNECTED    5
+#define CODE_YIO_ON_CONNECT_FAILED  7
+#define CODE_YIO_ON_RECV_FAILED     8
+#define CODE_YIO_ON_RECV_SUCCESS    9
+#define CODE_YIO_ON_SEND_FAILED     10
+#define CODE_YIO_ON_SEND_SUCCESS    11
+#define CODE_YIO_ON_HEARTBIT_ERROR  12
+#define CODE_YIO_ON_SLEEP           13
+
+/** FOTA event */
+#define EV_FOTA                     0x0102
+#define CODE_FOTA_START             1
+#define CODE_FOTA_END               2
+#define VALUE_FOTA_UPDATED          0
+
+/** Combo event */
+#define EV_BZ_COMBO                 0x0106
+#define CODE_COMBO_AP_INFO_READY    1
+
+/** SELF RECOVER event */
+#define EV_RCV                      0x0200
+
+/** user app start */
+#define EV_USER     0x1000
+
+#endif
+
+/**
+ * @struct input_event_t
+ * @brief yos event structure
+ */
+typedef struct {
+    /** The time event is generated, auto filled by yos event system */
+    uint32_t time;
+    /** Event type, value < 0x1000 are used by yos system */
+    uint16_t type;
+    /** Defined according to type */
+    uint16_t code;
+    /** Defined according to type/code */
+    unsigned long value;
+    /** Defined according to type/code */
+    unsigned long extra;
+} input_event_t;
+
+/** Event callback */
+typedef void (*aos_event_cb)(input_event_t *event, void *private_data);
+
+/** Delayed execution callback */
+typedef void (*aos_call_t)(void *arg);
+
+typedef void *aos_loop_t;
+//typedef void (*aos_poll_call_t)(int fd, void *arg);
+
+/**
+ * Register system event filter callback.
+ *
+ * @param[in]  type  event type interested.
+ * @param[in]  cb    system event callback.
+ * @param[in]  priv  private data past to cb.
+ *
+ * @return  the operation status, 0 is OK, others is error.
+ */
+int aos_register_event_filter(uint16_t type, aos_event_cb cb, void *priv);
+
+/**
+ * Unregister native event callback.
+ *
+ * @param[in]  type  event type interested.
+ * @param[in]  cb    system event callback.
+ * @param[in]  priv  private data past to cb.
+ *
+ * @return  the operation status, 0 is OK, others is error.
+ */
+int aos_unregister_event_filter(uint16_t type, aos_event_cb cb, void *priv);
+
+/**
+ * Post local event.
+ *
+ * @param[in]  type   event type.
+ * @param[in]  code   event code.
+ * @param[in]  value  event value.
+ *
+ * @return  the operation status, 0 is OK,others is error.
+ */
+int aos_post_event(uint16_t type, uint16_t code, unsigned long  value);
+
+/**
+ * Register a poll event in main loop.
+ *
+ * @param[in]  fd      poll fd.
+ * @param[in]  action  action to be executed.
+ * @param[in]  param   private data past to action.
+ *
+ * @return  the operation status, 0 is OK,others is error.
+ */
+/* not support */
+//int aos_poll_read_fd(int fd, aos_poll_call_t action, void *param);
+
+/**
+ * Cancel a poll event to be executed in main loop.
+ *
+ * @param[in]  fd      poll fd.
+ * @param[in]  action  action to be executed.
+ * @param[in]  param   private data past to action.
+ */
+/* not support */
+//void aos_cancel_poll_read_fd(int fd, aos_poll_call_t action, void *param);
+
+/**
+ * Post a delayed action to be executed in main loop.
+ *
+ * @param[in]  ms      milliseconds to wait.
+ * @param[in]  action  action to be executed.
+ * @param[in]  arg     private data past to action.
+ *
+ * @return  the operation status, 0 is OK,others is error.
+ */
+int aos_post_delayed_action(int ms, aos_call_t action, void *arg);
+
+/**
+ * Cancel a delayed action to be executed in main loop.
+ *
+ * @param[in]  ms      milliseconds to wait, -1 means don't care.
+ * @param[in]  action  action to be executed.
+ * @param[in]  arg     private data past to action.
+ */
+void aos_cancel_delayed_action(int ms, aos_call_t action, void *arg);
+
+
+/**
+ * Schedule a callback in next event loop.
+ * Unlike aos_post_delayed_action,
+ * this function can be called from non-aos-main-loop context.
+
+ * @param[in]  action  action to be executed.
+ * @param[in]  arg     private data past to action.
+ *
+ * @return  the operation status, <0 is error,others is OK.
+ */
+int aos_schedule_call(aos_call_t action, void *arg);
+
+/**
+ * Init a per-task event loop.
+ *
+ * @return  the handler of aos_loop_t,NULL failure,others success.
+ */
+aos_loop_t aos_loop_init(void);
+
+/**
+ * Get current event loop.
+ *
+ * @return  default event loop.
+ */
+aos_loop_t aos_current_loop(void);
+
+/**
+ * Start event loop.
+ */
+void aos_loop_run(void);
+
+/**
+ * Exit event loop, aos_loop_run() will return.
+ */
+void aos_loop_exit(void);
+
+/**
+ * Free event loop resources.
+ */
+void aos_loop_destroy(void);
+
+/**
+ * Schedule a callback specified event loop.
+ *
+ * @param[in]  loop    event loop to be scheduled, NULL for default main loop.
+ * @param[in]  action  action to be executed.
+ * @param[in]  arg     private data past to action.
+ *
+ * @return  the operation status, <0 is error,others is OK.
+ */
+/* not support */
+//int aos_loop_schedule_call(aos_loop_t *loop, aos_call_t action, void *arg);
+
+/**
+ * Schedule a work to be executed in workqueue.
+ *
+ * @param[in]  ms       milliseconds to delay before execution, 0 means immediately.
+ * @param[in]  action   action to be executed.
+ * @param[in]  arg1     private data past to action.
+ * @param[in]  fini_cb  finish callback to be executed after action is done in current event loop.
+ * @param[in]  arg2     data past to fini_cb.
+ *
+ * @return  work handle,NULL failure,others is OK.
+ */
+void *aos_loop_schedule_work(int ms, aos_call_t action, void *arg1,
+                             aos_call_t fini_cb, void *arg2);
+
+/**
+ * Cancel a work.
+ *
+ * @param[in]  work    work to be cancelled.
+ * @param[in]  action  action to be executed.
+ * @param[in]  arg1    private data past to action.
+ */
+void aos_cancel_work(void *work, aos_call_t action, void *arg1);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AOS_YLOOP_H */
+

+ 28 - 0
components/aos_port/include/yoc/init.h

@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+
+#ifndef YOC_INCLUDE_INIT_H
+#define YOC_INCLUDE_INIT_H
+
+#include <stdint.h>
+#include <devices/console_uart.h>
+#include <devices/apdu_uart.h>
+#include <yoc/lpm.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* board */
+void board_init(void);
+void board_base_init(void);
+void board_audio_init(void);
+void board_enter_lpm(pm_policy_t policy);
+void board_leave_lpm(pm_policy_t policy);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 74 - 0
components/aos_port/include/yoc/lpm.h

@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+
+#ifndef YOC_LPM_H
+#define YOC_LPM_H
+
+#include <time.h>
+#include <aos/list.h>
+#include <aos/aos.h>
+#include <drv/gpio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define KV_LPM_POLICY   "lpm_policy"
+#define KV_LPM_LS_THRES "lpm_ls"
+#define KV_LPM_DS_THRES "lpm_ds"
+
+typedef enum  {
+    LPM_EVENT_WAKUP,
+    LPM_EVENT_SUSPEND,
+} lpm_event_e;
+
+typedef enum {
+    LPM_POLICY_NO_POWER_SAVE,
+    LPM_POLICY_LOW_POWER,
+    LPM_POLICY_DEEP_SLEEP,
+} pm_policy_t;
+
+typedef struct {
+    slist_t     next;
+
+    void *dev;
+    void *param;
+} pm_dev_node_t;
+
+#define LPM_DEV_MASK_GENERAL_ADC   (1<<0)
+#define LPM_DEV_MASK_GENERAL_GPIO1 (1<<1)
+#define LPM_DEV_MASK_GENERAL_GPIO2 (1<<2)
+
+typedef void (*lpm_event_cb_t)(pm_policy_t policy, lpm_event_e event);
+typedef struct {
+    pm_policy_t policy;     //0: no low power, 1: low power, 2: support deep sleep
+    uint32_t    agree_halt; //execution context will be lost
+    uint32_t    alarm_ms;
+    uint32_t    ls_ms;      //low power sleep threshold
+    uint32_t    ds_ms;      //deep sleep threshold
+    uint32_t    suspend_tick;
+    uint32_t    lpm_dev_mask; /* 标记位置一对应的模块不进入低功耗 */
+    aos_mutex_t mutex;
+    aos_task_t  task_handle;
+    aos_sem_t   sem;
+    void        (*lpm_handle)(void);
+    lpm_event_cb_t wake_cb;
+} pm_ctx_t;
+
+void pm_init(lpm_event_cb_t pm_cb);
+void pm_config_mask(uint32_t lpm_dev_mask);
+void pm_config_policy(pm_policy_t policy);
+pm_policy_t pm_get_policy(void);
+void pm_agree_halt(uint32_t ms);
+#ifdef CONFIG_CSI_V2
+void setup_wake_irq(csi_gpio_t *handle, csi_gpio_mode_t mode);
+#else
+void setup_wake_irq(gpio_pin_handle_t handle, gpio_irq_mode_e mode);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 69 - 0
components/aos_port/include/yoc/nvram.h

@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+
+#ifndef YOC_NVRAM_H
+#define YOC_NVRAM_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * This function will init nvram.
+ *
+ * @param[in]   partname   the data pair of the key, less than 64 bytes
+ * @return  0 on success, negative error on failure.
+ */
+int nvram_init(const char *partname);
+
+/**
+ * This function will reset nvram, clear all data.
+ *
+ * @return  0 on success, negative error on failure.
+ */
+int nvram_reset();
+
+/**
+ * This function will get data from the factory setting area.
+ *
+ * @param[in]   key   the data pair of the key, less than 64 bytes
+ * @param[in]   len   the length of the buffer
+ * @param[out]  value   the buffer that will store the data
+ * @return  the length of the data value, error code otherwise
+ */
+int nvram_get_val(const char *key, char *value, int len);
+
+/**
+ * This function will set data to the factory setting area.
+ *
+ * @param[in]   key   the data pair of the key, less than 64 bytes
+ * @param[in]   value the data pair of the value, delete the pair if value == NULL
+ * @return  0 on success, negative error on failure.
+ */
+int nvram_set_val(const char *key, char *value);
+
+
+/**
+ * This function will get ali iot info from factory setting area.
+ *
+ * @param[out] product_key output ali iot product key
+ * @param[in&out] product_key_len in buffer len, out real len
+ * @param[out] device_name output ali iot device name
+ * @param[in&out] device_name_len in buffer len, out real len
+ * @param[out] device_secret output ali iot device device secret
+ * @param[in&out] device_secret_len in buffer len, out real len
+ */
+int nvram_get_iot_info(char *product_key, uint32_t *product_key_len,
+                       char *device_name, uint32_t *device_name_len,
+                       char *device_secret, uint32_t *device_secret_len);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+

+ 42 - 0
components/aos_port/include/yoc/sysinfo.h

@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+
+#ifndef YOC_SYSINFO_H
+#define YOC_SYSINFO_H
+
+#include <aos/version.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Get aos device id(id2/cid).
+ *
+ * @return  device name success, 0 failure.
+ */
+char *aos_get_device_id(void);
+
+/**
+ * Get yoc get imsi.
+ *
+ * @return  imsi string.
+ */
+const char *aos_get_imsi(void);
+
+/**
+ * Get yoc get imei.
+ *
+ * @return  imei string.
+ */
+const char *aos_get_imei(void);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AOS_VERSION_H */
+

+ 22 - 0
components/aos_port/include/yoc/yoc.h

@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+
+#ifndef YOC_YOC_H
+#define YOC_YOC_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <yoc/lpm.h>
+// #include <yoc/partition.h>
+#include <yoc/init.h>
+#include <yoc/sysinfo.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 43 - 0
components/aos_port/list.c

@@ -0,0 +1,43 @@
+#include <aos/list.h>
+
+void slist_add_tail(slist_t *node, slist_t *head)
+{
+    while (head->next) {
+        head = head->next;
+    }
+
+    slist_add(node, head);
+}
+
+void slist_del(slist_t *node, slist_t *head)
+{
+    while (head->next) {
+        if (head->next == node) {
+            head->next = node->next;
+            break;
+        }
+
+        head = head->next;
+    }
+}
+
+int slist_entry_number(slist_t *queue)
+{
+    int num;
+    slist_t *cur = queue;
+    for (num=0; cur->next; cur=cur->next, num++)
+        ;
+
+    return num;
+}
+
+int dlist_entry_number(dlist_t *queue)
+{
+    int num;
+    dlist_t *cur = queue;
+    for (num=0; cur->next != queue; cur=cur->next, num++)
+        ;
+
+    return num;
+}
+

+ 177 - 0
components/aos_port/sysinfo.c

@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <yoc/nvram.h>
+#include <yoc/sysinfo.h>
+// #include <mtb.h>
+
+#ifndef SYSINFO_WEAK
+#define SYSINFO_WEAK __attribute__((weak))
+#endif
+
+#ifndef CONFIG_SDK_VERSION
+#define CONFIG_SDK_VERSION "V7.2.2_20200319"
+#endif
+
+
+
+SYSINFO_WEAK const char *aos_get_product_model(void)
+{
+#ifdef CONFIG_PRODUCT_MODEL
+    const char *ret = CONFIG_PRODUCT_MODEL;
+#else
+    static char ret[64];
+    int         res;
+    res = nvram_get_val("model", ret, sizeof(ret));
+
+    if (res <= 0) {
+        return NULL;
+    }
+
+#endif
+    return (const char *)ret;
+}
+
+char *aos_get_os_version(void)
+{
+    char *ret = CONFIG_SDK_VERSION;
+
+    return (char *)ret;
+}
+
+char *aos_get_app_version(void)
+{
+    int32_t     ret;
+    static char str_version[65] = {
+        0,
+    };
+    uint32_t len = sizeof(str_version) - 1;
+
+    if (str_version[0] == 0) {
+#ifdef CONFIG_APP_VERSION_FROM_KV
+        ret = aos_kv_getstring("app_version", str_version, sizeof(str_version));
+#else
+#ifdef CONFIG_TEE_CA
+        ret = csi_tee_get_sys_os_version((uint8_t *)str_version, &len);
+#else
+        ret = nvram_get_val("app_version", str_version, sizeof(str_version));
+#endif
+#endif /* CONFIG_APP_VERSION_FROM_KV */
+        if (ret < 0) {
+            str_version[0] = 0;
+            return NULL;
+        }
+
+        str_version[len] = 0;
+    }
+
+    return str_version;
+}
+
+int aos_set_app_version(const char *version)
+{
+    return nvram_set_val("app_version", (char *)version);
+}
+
+char *aos_get_changelog(void)
+{
+    int32_t ret;
+    static char str_changelog[512] = {
+        0,
+    };
+    uint32_t len = sizeof(str_changelog) - 1;
+
+    if (str_changelog[0] == 0) {
+        ret = nvram_get_val("changelog", str_changelog, sizeof(str_changelog));
+        if (ret < 0) {
+            str_changelog[0] = 0;
+            return NULL;
+        }
+
+        str_changelog[len] = 0;
+    }
+
+    return str_changelog;
+}
+
+int aos_set_changelog(const char *changelog)
+{
+    return nvram_set_val("changelog", (char *)changelog);
+}
+
+SYSINFO_WEAK const char *aos_get_kernel_version(void)
+{
+    const char *ret = "AOS-R-1.2.0";
+    return ret;
+}
+
+const char *aos_get_device_name(void)
+{
+    int ret;
+    static char namebuf[64] = {0};
+
+    if (namebuf[0] == 0) {
+        memset(namebuf, 0, sizeof(namebuf));
+        ret = nvram_get_val("DEVICENAME", namebuf, sizeof(namebuf));
+        if (ret < 0) {
+            return NULL;
+        }
+    }
+    return namebuf;
+}
+
+char *aos_get_device_id(void)
+{
+    int ret;
+    static char cidbuf[64] = {0};
+
+    if (cidbuf[0] == 0) {
+        memset(cidbuf, 0, sizeof(cidbuf));
+#ifdef CONFIG_DEVICEID_FROM_KV
+        ret = aos_kv_getstring("device_id", cidbuf, sizeof(cidbuf));
+#else
+#ifdef CONFIG_TEE_CA
+        {
+            uint32_t len = sizeof(cidbuf) - 1;
+            ret = csi_tee_get_cid((uint8_t *)cidbuf, (uint32_t *)&len);
+        }
+#else
+        ret = nvram_get_val("device_id", cidbuf, sizeof(cidbuf));
+#endif /* CONFIG_TEE_CA */
+#endif /* CONFIG_DEVICEID_FROM_KV */
+        if (ret < 0) {
+            return NULL;
+        }
+    }
+    return cidbuf;
+}
+
+SYSINFO_WEAK const char *aos_get_imsi(void)
+{
+    static char ret[32] = {0};
+    int         res;
+    res = nvram_get_val("imsi", ret, sizeof(ret));
+
+    if (res <= 0) {
+        return NULL;
+    }
+
+    return (const char *)ret;
+}
+
+SYSINFO_WEAK const char *aos_get_imei(void)
+{
+    static char ret[32] = {0};
+    int         res;
+    res = nvram_get_val("imei", ret, sizeof(ret));
+
+    if (res <= 0) {
+        return NULL;
+    }
+
+    return (const char *)ret;
+}

+ 5 - 0
components/cjson/CMakeLists.txt

@@ -0,0 +1,5 @@
+file(GLOB_RECURSE LIBSOURCE "${COMPONENTS_DIR}/cjson/src/*.c")
+
+include_directories(${COMPONENTS_DIR}/cjson/include)
+
+add_library(cjson STATIC ${LIBSOURCE})

+ 5 - 0
components/cjson/Config.in

@@ -0,0 +1,5 @@
+config AOS_COMP_CJSON
+    bool "cJSON library"
+    default n
+    help
+        cJSON aims to be the dumbest possible parser that you can get your job done with.

+ 258 - 0
components/cjson/README.md

@@ -0,0 +1,258 @@
+apps/netutils/json/README.txt
+=============================
+
+This directory contains logic taken from the cJSON project:
+
+http://sourceforge.net/projects/cjson/
+
+This corresponds to SVN revision r42 (with lots of changes for NuttX coding
+standards).  As of r42, the SVN repository was last updated on 2011-10-10
+so I presume that the code is stable and there is no risk of maintaining
+duplicate logic in the NuttX repository.
+
+Contents
+========
+
+  o License
+  o Welcome to cJSON
+
+License
+=======
+
+  Copyright (c) 2009 Dave Gamble
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+
+Welcome to cJSON
+================
+
+cJSON aims to be the dumbest possible parser that you can get your job done with.
+It's a single file of C, and a single header file.
+
+JSON is described best here: http://www.json.org/
+It's like XML, but fat-free. You use it to move data around, store things, or just
+generally represent your program's state.
+
+First up, how do I build?
+Add cJSON.c to your project, and put cJSON.h somewhere in the header search path.
+For example, to build the test app:
+
+gcc cJSON.c test.c -o test -lm
+./test
+
+As a library, cJSON exists to take away as much legwork as it can, but not get in your way.
+As a point of pragmatism (i.e. ignoring the truth), I'm going to say that you can use it
+in one of two modes: Auto and Manual. Let's have a quick run-through.
+
+I lifted some JSON from this page: http://www.json.org/fatfree.html
+That page inspired me to write cJSON, which is a parser that tries to share the same
+philosophy as JSON itself. Simple, dumb, out of the way.
+
+Some JSON:
+{
+    "name": "Jack (\"Bee\") Nimble",
+    "format": {
+        "type":       "rect",
+        "width":      1920,
+        "height":     1080,
+        "interlace":  false,
+        "frame rate": 24
+    }
+}
+
+Assume that you got this from a file, a webserver, or magic JSON elves, whatever,
+you have a char * to it. Everything is a cJSON struct.
+Get it parsed:
+    cJSON *root = cJSON_Parse(my_json_string);
+
+This is an object. We're in C. We don't have objects. But we do have structs.
+What's the framerate?
+
+    cJSON *format = cJSON_GetObjectItem(root,"format");
+    int framerate = cJSON_GetObjectItem(format,"frame rate")->valueint;
+
+Want to change the framerate?
+    cJSON_GetObjectItem(format,"frame rate")->valueint=25;
+
+Back to disk?
+    char *rendered=cJSON_Print(root);
+
+Finished? Delete the root (this takes care of everything else).
+    cJSON_Delete(root);
+
+That's AUTO mode. If you're going to use Auto mode, you really ought to check pointers
+before you dereference them. If you want to see how you'd build this struct in code?
+    cJSON *root,*fmt;
+    root=cJSON_CreateObject();
+    cJSON_AddItemToObject(root, "name", cJSON_CreateString("Jack (\"Bee\") Nimble"));
+    cJSON_AddItemToObject(root, "format", fmt=cJSON_CreateObject());
+    cJSON_AddStringToObject(fmt,"type",        "rect");
+    cJSON_AddNumberToObject(fmt,"width",        1920);
+    cJSON_AddNumberToObject(fmt,"height",        1080);
+    cJSON_AddFalseToObject (fmt,"interlace");
+    cJSON_AddNumberToObject(fmt,"frame rate",    24);
+
+Hopefully we can agree that's not a lot of code? There's no overhead, no unnecessary setup.
+Look at test.c for a bunch of nice examples, mostly all ripped off the json.org site, and
+a few from elsewhere.
+
+What about manual mode? First up you need some detail.
+Let's cover how the cJSON objects represent the JSON data.
+cJSON doesn't distinguish arrays from objects in handling; just type.
+Each cJSON has, potentially, a child, siblings, value, a name.
+
+The root object has: Object Type and a Child
+The Child has name "name", with value "Jack ("Bee") Nimble", and a sibling:
+Sibling has type Object, name "format", and a child.
+That child has type String, name "type", value "rect", and a sibling:
+Sibling has type Number, name "width", value 1920, and a sibling:
+Sibling has type Number, name "height", value 1080, and a sibling:
+Sibling hs type False, name "interlace", and a sibling:
+Sibling has type Number, name "frame rate", value 24
+
+Here's the structure:
+typedef struct cJSON {
+    struct cJSON *next,*prev;
+    struct cJSON *child;
+
+    int type;
+
+    char *valuestring;
+    int valueint;
+    double valuedouble;
+
+    char *string;
+} cJSON;
+
+By default all values are 0 unless set by virtue of being meaningful.
+
+next/prev is a doubly linked list of siblings. next takes you to your sibling,
+prev takes you back from your sibling to you.
+Only objects and arrays have a "child", and it's the head of the doubly linked list.
+A "child" entry will have prev==0, but next potentially points on. The last sibling has next=0.
+The type expresses Null/True/False/Number/String/Array/Object, all of which are #defined in
+cJSON.h
+
+A Number has valueint and valuedouble. If you're expecting an int, read valueint, if not read
+valuedouble.
+
+Any entry which is in the linked list which is the child of an object will have a "string"
+which is the "name" of the entry. When I said "name" in the above example, that's "string".
+"string" is the JSON name for the 'variable name' if you will.
+
+Now you can trivially walk the lists, recursively, and parse as you please.
+You can invoke cJSON_Parse to get cJSON to parse for you, and then you can take
+the root object, and traverse the structure (which is, formally, an N-tree),
+and tokenise as you please. If you wanted to build a callback style parser, this is how
+you'd do it (just an example, since these things are very specific):
+
+void parse_and_callback(cJSON *item,const char *prefix)
+{
+    while (item)
+    {
+        char *newprefix=malloc(strlen(prefix)+strlen(item->name)+2);
+        sprintf(newprefix,"%s/%s",prefix,item->name);
+        int dorecurse=callback(newprefix, item->type, item);
+        if (item->child && dorecurse) parse_and_callback(item->child,newprefix);
+        item=item->next;
+        free(newprefix);
+    }
+}
+
+The prefix process will build you a separated list, to simplify your callback handling.
+The 'dorecurse' flag would let the callback decide to handle sub-arrays on it's own, or
+let you invoke it per-item. For the item above, your callback might look like this:
+
+int callback(const char *name,int type,cJSON *item)
+{
+    if (!strcmp(name,"name"))    { /* populate name */ }
+    else if (!strcmp(name,"format/type")    { /* handle "rect" */ }
+    else if (!strcmp(name,"format/width")    { /* 800 */ }
+    else if (!strcmp(name,"format/height")    { /* 600 */ }
+    else if (!strcmp(name,"format/interlace")    { /* false */ }
+    else if (!strcmp(name,"format/frame rate")    { /* 24 */ }
+    return 1;
+}
+
+Alternatively, you might like to parse iteratively.
+You'd use:
+
+void parse_object(cJSON *item)
+{
+    int i; for (i=0;i<cJSON_GetArraySize(item);i++)
+    {
+        cJSON *subitem=cJSON_GetArrayItem(item,i);
+        // handle subitem.
+    }
+}
+
+Or, for PROPER manual mode:
+
+void parse_object(cJSON *item)
+{
+    cJSON *subitem=item->child;
+    while (subitem)
+    {
+        // handle subitem
+        if (subitem->child) parse_object(subitem->child);
+
+        subitem=subitem->next;
+    }
+}
+
+Of course, this should look familiar, since this is just a stripped-down version
+of the callback-parser.
+
+This should cover most uses you'll find for parsing. The rest should be possible
+to infer.. and if in doubt, read the source! There's not a lot of it! ;)
+
+In terms of constructing JSON data, the example code above is the right way to do it.
+You can, of course, hand your sub-objects to other functions to populate.
+Also, if you find a use for it, you can manually build the objects.
+For instance, suppose you wanted to build an array of objects?
+
+cJSON *objects[24];
+
+cJSON *Create_array_of_anything(cJSON **items,int num)
+{
+    int i;cJSON *prev, *root=cJSON_CreateArray();
+    for (i=0;i<24;i++)
+    {
+        if (!i)    root->child=objects[i];
+        else    prev->next=objects[i], objects[i]->prev=prev;
+        prev=objects[i];
+    }
+    return root;
+}
+
+and simply: Create_array_of_anything(objects,24);
+
+cJSON doesn't make any assumptions about what order you create things in.
+You can attach the objects, as above, and later add children to each
+of those objects.
+
+As soon as you call cJSON_Print, it renders the structure to text.
+
+The test.c code shows how to handle a bunch of typical cases. If you uncomment
+the code, it'll load, parse and print a bunch of test files, also from json.org,
+which are more complex than I'd care to try and stash into a const char array[].
+
+Enjoy cJSON!
+
+- Dave Gamble, Aug 2009

+ 11 - 0
components/cjson/aos.mk

@@ -0,0 +1,11 @@
+NAME := cjson
+
+$(NAME)_MBINS_TYPE := share
+$(NAME)_VERSION := 1.0.2
+$(NAME)_SUMMARY := An ultra-lightweight, portable, single-file, simple-as-can-be ANSI-C compliant JSON parser, under MIT license.
+
+GLOBAL_INCLUDES += include
+# don't modify to L_CFLAGS, because CONFIG_CJSON_WITHOUT_DOUBLE should enable global
+#$(NAME)_CFLAGS  += -DCONFIG_CJSON_WITHOUT_DOUBLE
+
+$(NAME)_SOURCES := src/cJSON.c src/cJPath.c

+ 297 - 0
components/cjson/include/cJSON.h

@@ -0,0 +1,297 @@
+/*
+  Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#ifndef cJSON__h
+#define cJSON__h
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
+#define __WINDOWS__
+#endif
+
+#ifdef __WINDOWS__
+
+/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention.  For windows you have 3 define options:
+
+CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
+CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
+CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol
+
+For *nix builds that support visibility attribute, you can define similar behavior by
+
+setting default visibility to hidden by adding
+-fvisibility=hidden (for gcc)
+or
+-xldscope=hidden (for sun cc)
+to CFLAGS
+
+then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
+
+*/
+
+#define CJSON_CDECL __cdecl
+#define CJSON_STDCALL __stdcall
+
+/* export symbols by default, this is necessary for copy pasting the C and header file */
+#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
+#define CJSON_EXPORT_SYMBOLS
+#endif
+
+#if defined(CJSON_HIDE_SYMBOLS)
+#define CJSON_PUBLIC(type)   type CJSON_STDCALL
+#elif defined(CJSON_EXPORT_SYMBOLS)
+#define CJSON_PUBLIC(type)   __declspec(dllexport) type CJSON_STDCALL
+#elif defined(CJSON_IMPORT_SYMBOLS)
+#define CJSON_PUBLIC(type)   __declspec(dllimport) type CJSON_STDCALL
+#endif
+#else /* !__WINDOWS__ */
+#define CJSON_CDECL
+#define CJSON_STDCALL
+
+#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
+#define CJSON_PUBLIC(type)   __attribute__((visibility("default"))) type
+#else
+#define CJSON_PUBLIC(type) type
+#endif
+#endif
+
+/* project version */
+#define CJSON_VERSION_MAJOR 1
+#define CJSON_VERSION_MINOR 7
+#define CJSON_VERSION_PATCH 13
+
+#include <stddef.h>
+
+/* cJSON Types: */
+#define cJSON_Invalid (0)
+#define cJSON_False  (1 << 0)
+#define cJSON_True   (1 << 1)
+#define cJSON_NULL   (1 << 2)
+#define cJSON_Number (1 << 3)
+#define cJSON_String (1 << 4)
+#define cJSON_Array  (1 << 5)
+#define cJSON_Object (1 << 6)
+#define cJSON_Raw    (1 << 7) /* raw json */
+
+#define cJSON_IsReference 256
+#define cJSON_StringIsConst 512
+
+/* The cJSON structure: */
+typedef struct cJSON
+{
+    /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
+    struct cJSON *next;
+    struct cJSON *prev;
+    /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
+    struct cJSON *child;
+
+    /* The type of the item, as above. */
+    int type;
+
+    /* The item's string, if type==cJSON_String  and type == cJSON_Raw */
+    char *valuestring;
+    /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
+    int valueint;
+    /* The item's number, if type==cJSON_Number */
+    double valuedouble;
+
+    /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
+    char *string;
+} cJSON;
+
+typedef struct cJSON_Hooks
+{
+      /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */
+      void *(CJSON_CDECL *malloc_fn)(size_t sz);
+      void (CJSON_CDECL *free_fn)(void *ptr);
+} cJSON_Hooks;
+
+typedef int cJSON_bool;
+
+/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
+ * This is to prevent stack overflows. */
+#ifndef CJSON_NESTING_LIMIT
+#define CJSON_NESTING_LIMIT 1000
+#endif
+
+/* returns the version of cJSON as a string */
+CJSON_PUBLIC(const char*) cJSON_Version(void);
+
+/* Supply malloc, realloc and free functions to cJSON */
+CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
+
+/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
+/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
+CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
+/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
+/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated);
+
+/* Render a cJSON entity to text for transfer/storage. */
+CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
+/* Render a cJSON entity to text for transfer/storage without any formatting. */
+CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
+/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
+CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
+/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
+/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
+CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
+/* Delete a cJSON entity and all subentities. */
+CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
+
+/* Returns the number of items in an array (or object). */
+CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
+/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */
+CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
+/* Get item "string" from object. Case insensitive. */
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
+CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
+/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
+CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
+
+/* Check item type and return its value */
+CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item);
+CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item);
+
+/* These functions check the type of an item */
+CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
+
+/* These calls create a cJSON item of the appropriate type. */
+CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
+CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
+CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
+/* raw json */
+CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
+CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
+
+/* Create a string where valuestring references a string so
+ * it will not be freed by cJSON_Delete */
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
+/* Create an object/array that only references it's elements so
+ * they will not be freed by cJSON_Delete */
+CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
+CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
+
+/* These utilities create an Array of count items.
+ * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/
+CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
+CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
+CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count);
+
+/* Append item to the specified array/object. */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
+/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
+ * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
+ * writing to `item->string` */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
+/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
+
+/* Remove/Detach items from Arrays/Objects. */
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
+CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
+
+/* Update array items. */
+CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem);
+
+/* Duplicate a cJSON item */
+CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
+/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
+ * need to be released. With recurse!=0, it will duplicate any children connected to the item.
+ * The item->next and ->prev pointers are always zero on return from Duplicate. */
+/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
+ * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
+CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);
+
+/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings.
+ * The input pointer json cannot point to a read-only address area, such as a string constant, 
+ * but should point to a readable and writable adress area. */
+CJSON_PUBLIC(void) cJSON_Minify(char *json);
+
+/* Helper functions for creating and adding items to an object at the same time.
+ * They return the added item or NULL on failure. */
+CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
+CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
+CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
+CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
+CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);
+
+/* When assigning an integer value, it needs to be propagated to valuedouble too. */
+#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
+/* helper for the cJSON_SetNumberValue macro */
+CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
+#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
+/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */
+CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring);
+
+/* Macro for iterating over an array or object */
+#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
+
+/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
+CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
+CJSON_PUBLIC(void) cJSON_free(void *object);
+
+//#ifndef fabs
+//#define fabs(a) ((a > 0)? a : -a)
+//#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 88 - 0
components/cjson/include/cJSON_Utils.h

@@ -0,0 +1,88 @@
+/*
+  Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#ifndef cJSON_Utils__h
+#define cJSON_Utils__h
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "cJSON.h"
+
+/* Implement RFC6901 (https://tools.ietf.org/html/rfc6901) JSON Pointer spec. */
+CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointer(cJSON * const object, const char *pointer);
+CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointerCaseSensitive(cJSON * const object, const char *pointer);
+
+/* Implement RFC6902 (https://tools.ietf.org/html/rfc6902) JSON Patch spec. */
+/* NOTE: This modifies objects in 'from' and 'to' by sorting the elements by their key */
+CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON * const from, cJSON * const to);
+CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatchesCaseSensitive(cJSON * const from, cJSON * const to);
+/* Utility for generating patch array entries. */
+CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON * const array, const char * const operation, const char * const path, const cJSON * const value);
+/* Returns 0 for success. */
+CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON * const object, const cJSON * const patches);
+CJSON_PUBLIC(int) cJSONUtils_ApplyPatchesCaseSensitive(cJSON * const object, const cJSON * const patches);
+
+/*
+// Note that ApplyPatches is NOT atomic on failure. To implement an atomic ApplyPatches, use:
+//int cJSONUtils_AtomicApplyPatches(cJSON **object, cJSON *patches)
+//{
+//    cJSON *modme = cJSON_Duplicate(*object, 1);
+//    int error = cJSONUtils_ApplyPatches(modme, patches);
+//    if (!error)
+//    {
+//        cJSON_Delete(*object);
+//        *object = modme;
+//    }
+//    else
+//    {
+//        cJSON_Delete(modme);
+//    }
+//
+//    return error;
+//}
+// Code not added to library since this strategy is a LOT slower.
+*/
+
+/* Implement RFC7386 (https://tools.ietf.org/html/rfc7396) JSON Merge Patch spec. */
+/* target will be modified by patch. return value is new ptr for target. */
+CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, const cJSON * const patch);
+CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatchCaseSensitive(cJSON *target, const cJSON * const patch);
+/* generates a patch to move from -> to */
+/* NOTE: This modifies objects in 'from' and 'to' by sorting the elements by their key */
+CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON * const from, cJSON * const to);
+CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatchCaseSensitive(cJSON * const from, cJSON * const to);
+
+/* Given a root object and a target object, construct a pointer from one to the other. */
+CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(const cJSON * const object, const cJSON * const target);
+
+/* Sorts the members of the object into alphabetical order. */
+CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON * const object);
+CJSON_PUBLIC(void) cJSONUtils_SortObjectCaseSensitive(cJSON * const object);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 80 - 0
components/cjson/package.yaml

@@ -0,0 +1,80 @@
+
+## 第一部分: 基础信息
+name: cjson                                         # <必选项> 包名称 (符合C语言变量命名规则),长度少于等于64字节
+version: v7.4.y                                     # <必选项> 组件版本号
+description: 超轻量级的 C 语言 json 解析库          # <必选项> 建议至少20字以上
+type: common                                        # <必选项> 组件类型,为:solution, chip, board, common, sdk
+tag: 通用组件                                       # <可选项> 组件分类,缺省值: ''
+keywords:                                           # <可选项> 标签,会影响到组件被搜索的效果,合理的标签很重要
+  - base
+  - JSON
+author:                                             # <可选项> 原作者信息
+  name: zhuzhg
+  email: zhiguo@xxxx.com
+license: Apache license v2.0                        # <可选项> 源代码的许可证,要确保所有代码、文件的许可证不冲突。如:MIT,Apache license v2.0,BSD
+
+## 第二部分:依赖信息
+#           指定该组件依赖的组件及版本,版本支持条件比较,支持:>=v1.0, >v1.0, ==v1.0, <=v1.0, <v1.0, v1.0
+#           未指定条件时,默认为 ==,如 v1.0 与 ==v1.0
+# sdk_chip:                                # <可选项> 该组件依赖sdk组件,合理的依赖才能保证组件能编译、使用
+#   - sdk_chip_csky_dummy: v7.4.y
+#   - sdk_chip_riscv_dummy: v7.4.y
+# depends:                                 # <可选项> 该组件依赖其他的组件,合理的依赖才能保证组件能编译、使用
+#   - minilibc: v7.2.0
+#   - aos: >=v7.2.0
+depends:
+  - aos: v7.4.y
+
+## 第四部分:编译连接信息
+# build_config:                            # <可选项> 编译配置项
+#   include:                               # <可选项> 编译时,影响编译器的-I 参数 ,全局有效
+#     - src                                #        include 只能是该软件包下的目录,不能使用外部目录
+#   internal_include:                      # <可选项> 编译时,影响编译器的-I 参数 ,组件内有效
+#     - include
+#   cflag: ''                              # <可选项> C 编译器所需要要的编译参数
+#   cxxflag: ''                            # <可选项> CXX 编译器所需要要的编译参数
+#   asmflag: ''                            # <可选项> 汇编器所需要要参数
+#   define:                                # <可选项> 宏定义, 增加编译器的-D 选项,如:
+#     XXX: 1                               #   -DXXX=1
+#     AAA: 1                               #   -DAAA
+#     STR: "abc"                           #   -DSTR="abc"
+#   libs:                                  # 该组件中支持的二进制静态库,如:libxxx.a, libyyy.a
+#     - xxx                                #   -lxxx
+#     - yyy                                #   -lyyy
+#   libpath:                               # 指定静态库所在的路径(相对于该组件路径)
+#     - libs                               #   -Llibs
+build_config:
+  include:
+    - include
+
+# source_file:                             # <可选项> 指定参与编译的源代码文件,支持通配符,采用相对路径
+#   - src/*.c                              # 例:组件 src 目录下所有的扩展名为 c 的源代码文件
+source_file:
+  - "src/*.c"
+
+## 第五部分:配置信息
+# def_config:                              # 组件的可配置项
+#   CONFIG_DEBUG: y
+#   CONFIG_PARAM_NOT_CHECK: y
+#   CONFIG_CLI: y
+def_config:
+  CONFIG_CJSON_WITHOUT_DOUBLE: y
+
+## 第六部分:安装信息
+# install:
+#   - dest: include/                       # 安装的目的路径 dest是相对路径,通常是相对于YoC SDK 安装目录
+#     source:                              # 安装源列表
+#      - src/*.h                           # 支持通配符,相对路径
+install:
+  - dest: "include/"
+    source:
+      - "include/*.h"
+
+## 第七部分:导出部分
+# export:
+#   - dest: "<SOLUTION_PATH>/generated/data" # 安装的目的路径 dest是相对路径
+#     source:                                # 安装源列表
+#       - "bootimgs/boot"
+#       - "bootimgs/tee"
+#       - "bootimgs/mtb"
+#       - "configs/config.yaml"

+ 3097 - 0
components/cjson/src/cJSON.c

@@ -0,0 +1,3097 @@
+/*
+  Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+/* cJSON */
+/* JSON parser in C. */
+
+/* disable warnings about old C89 functions in MSVC */
+#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER)
+#define _CRT_SECURE_NO_DEPRECATE
+#endif
+
+#ifdef __GNUC__
+#pragma GCC visibility push(default)
+#endif
+#if defined(_MSC_VER)
+#pragma warning (push)
+/* disable warning about single line comments in system headers */
+#pragma warning (disable : 4001)
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <ctype.h>
+#include <float.h>
+
+#ifdef ENABLE_LOCALES
+#include <locale.h>
+#endif
+
+#if defined(_MSC_VER)
+#pragma warning (pop)
+#endif
+#ifdef __GNUC__
+#pragma GCC visibility pop
+#endif
+
+#include "cJSON.h"
+
+/* define our own boolean type */
+#ifdef true
+#undef true
+#endif
+#define true ((cJSON_bool)1)
+
+#ifdef false
+#undef false
+#endif
+#define false ((cJSON_bool)0)
+
+/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */
+#ifndef isinf
+#define isinf(d) (isnan((d - d)) && !isnan(d))
+#endif
+#ifndef isnan
+#define isnan(d) (d != d)
+#endif
+
+#ifndef NAN
+#define NAN 0.0/0.0
+#endif
+
+typedef struct {
+    const unsigned char *json;
+    size_t position;
+} error;
+static error global_error = { NULL, 0 };
+
+CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void)
+{
+    return (const char*) (global_error.json + global_error.position);
+}
+
+CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item)
+{
+    if (!cJSON_IsString(item))
+    {
+        return NULL;
+    }
+
+    return item->valuestring;
+}
+
+CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item)
+{
+    if (!cJSON_IsNumber(item))
+    {
+        return (double) NAN;
+    }
+
+    return item->valuedouble;
+}
+
+/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */
+#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 13)
+    #error cJSON.h and cJSON.c have different versions. Make sure that both have the same.
+#endif
+
+CJSON_PUBLIC(const char*) cJSON_Version(void)
+{
+    static char version[15];
+    sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH);
+
+    return version;
+}
+
+/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */
+static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2)
+{
+    if ((string1 == NULL) || (string2 == NULL))
+    {
+        return 1;
+    }
+
+    if (string1 == string2)
+    {
+        return 0;
+    }
+
+    for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++)
+    {
+        if (*string1 == '\0')
+        {
+            return 0;
+        }
+    }
+
+    return tolower(*string1) - tolower(*string2);
+}
+
+typedef struct internal_hooks
+{
+    void *(CJSON_CDECL *allocate)(size_t size);
+    void (CJSON_CDECL *deallocate)(void *pointer);
+    void *(CJSON_CDECL *reallocate)(void *pointer, size_t size);
+} internal_hooks;
+
+#if 1//defined(_MSC_VER)
+/* work around MSVC error C2322: '...' address of dllimport '...' is not static */
+static void * CJSON_CDECL internal_malloc(size_t size)
+{
+    return malloc(size);
+}
+static void CJSON_CDECL internal_free(void *pointer)
+{
+    free(pointer);
+}
+static void * CJSON_CDECL internal_realloc(void *pointer, size_t size)
+{
+    return realloc(pointer, size);
+}
+#else
+#include "aos/kernel.h"
+#define internal_malloc aos_malloc
+#define internal_free aos_free
+#define internal_realloc aos_realloc
+#endif
+
+/* strlen of character literals resolved at compile time */
+#define static_strlen(string_literal) (sizeof(string_literal) - sizeof(""))
+
+static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc };
+
+static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks)
+{
+    size_t length = 0;
+    unsigned char *copy = NULL;
+
+    if (string == NULL)
+    {
+        return NULL;
+    }
+
+    length = strlen((const char*)string) + sizeof("");
+    copy = (unsigned char*)hooks->allocate(length);
+    if (copy == NULL)
+    {
+        return NULL;
+    }
+    memcpy(copy, string, length);
+
+    return copy;
+}
+
+CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks)
+{
+    if (hooks == NULL)
+    {
+        /* Reset hooks */
+        global_hooks.allocate = malloc;
+        global_hooks.deallocate = free;
+        global_hooks.reallocate = realloc;
+        return;
+    }
+
+    global_hooks.allocate = malloc;
+    if (hooks->malloc_fn != NULL)
+    {
+        global_hooks.allocate = hooks->malloc_fn;
+    }
+
+    global_hooks.deallocate = free;
+    if (hooks->free_fn != NULL)
+    {
+        global_hooks.deallocate = hooks->free_fn;
+    }
+
+    /* use realloc only if both free and malloc are used */
+    global_hooks.reallocate = NULL;
+    if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free))
+    {
+        global_hooks.reallocate = realloc;
+    }
+}
+
+/* Internal constructor. */
+static cJSON *cJSON_New_Item(const internal_hooks * const hooks)
+{
+    cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON));
+    if (node)
+    {
+        memset(node, '\0', sizeof(cJSON));
+    }
+
+    return node;
+}
+
+/* Delete a cJSON structure. */
+CJSON_PUBLIC(void) cJSON_Delete(cJSON *item)
+{
+    cJSON *next = NULL;
+    while (item != NULL)
+    {
+        next = item->next;
+        if (!(item->type & cJSON_IsReference) && (item->child != NULL))
+        {
+            cJSON_Delete(item->child);
+        }
+        if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL))
+        {
+            global_hooks.deallocate(item->valuestring);
+        }
+        if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
+        {
+            global_hooks.deallocate(item->string);
+        }
+        global_hooks.deallocate(item);
+        item = next;
+    }
+}
+
+/* get the decimal point character of the current locale */
+static unsigned char get_decimal_point(void)
+{
+#ifdef ENABLE_LOCALES
+    struct lconv *lconv = localeconv();
+    return (unsigned char) lconv->decimal_point[0];
+#else
+    return '.';
+#endif
+}
+
+typedef struct
+{
+    const unsigned char *content;
+    size_t length;
+    size_t offset;
+    size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */
+    internal_hooks hooks;
+} parse_buffer;
+
+/* check if the given size is left to read in a given parse buffer (starting with 1) */
+#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length))
+/* check if the buffer can be accessed at the given index (starting with 0) */
+#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length))
+#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index))
+/* get a pointer to the buffer at the position */
+#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset)
+
+/* Parse the input text to generate a number, and populate the result into item. */
+static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer)
+{
+    double number = 0;
+    unsigned char *after_end = NULL;
+    unsigned char number_c_string[64];
+    unsigned char decimal_point = get_decimal_point();
+    size_t i = 0;
+
+    if ((input_buffer == NULL) || (input_buffer->content == NULL))
+    {
+        return false;
+    }
+
+    /* copy the number into a temporary buffer and replace '.' with the decimal point
+     * of the current locale (for strtod)
+     * This also takes care of '\0' not necessarily being available for marking the end of the input */
+    for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++)
+    {
+        switch (buffer_at_offset(input_buffer)[i])
+        {
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+            case '+':
+            case '-':
+            case 'e':
+            case 'E':
+                number_c_string[i] = buffer_at_offset(input_buffer)[i];
+                break;
+
+            case '.':
+                number_c_string[i] = decimal_point;
+                break;
+
+            default:
+                goto loop_end;
+        }
+    }
+loop_end:
+    number_c_string[i] = '\0';
+
+    number = strtod((const char*)number_c_string, (char**)&after_end);
+    if (number_c_string == after_end)
+    {
+        return false; /* parse_error */
+    }
+
+    item->valuedouble = number;
+
+    /* use saturation in case of overflow */
+    if (number >= INT_MAX)
+    {
+        item->valueint = INT_MAX;
+    }
+    else if (number <= (double)INT_MIN)
+    {
+        item->valueint = INT_MIN;
+    }
+    else
+    {
+        item->valueint = (int)number;
+    }
+
+    item->type = cJSON_Number;
+
+    input_buffer->offset += (size_t)(after_end - number_c_string);
+    return true;
+}
+
+/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */
+CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number)
+{
+    if (number >= INT_MAX)
+    {
+        object->valueint = INT_MAX;
+    }
+    else if (number <= (double)INT_MIN)
+    {
+        object->valueint = INT_MIN;
+    }
+    else
+    {
+        object->valueint = (int)number;
+    }
+
+    return object->valuedouble = number;
+}
+
+CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring)
+{
+    char *copy = NULL;
+    /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */
+    if (!(object->type & cJSON_String) || (object->type & cJSON_IsReference))
+    {
+        return NULL;
+    }
+    if (strlen(valuestring) <= strlen(object->valuestring))
+    {
+        strcpy(object->valuestring, valuestring);
+        return object->valuestring;
+    }
+    copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks);
+    if (copy == NULL)
+    {
+        return NULL;
+    }
+    if (object->valuestring != NULL)
+    {
+        cJSON_free(object->valuestring);
+    }
+    object->valuestring = copy;
+
+    return copy;
+}
+
+typedef struct
+{
+    unsigned char *buffer;
+    size_t length;
+    size_t offset;
+    size_t depth; /* current nesting depth (for formatted printing) */
+    cJSON_bool noalloc;
+    cJSON_bool format; /* is this print a formatted print */
+    internal_hooks hooks;
+} printbuffer;
+
+/* realloc printbuffer if necessary to have at least "needed" bytes more */
+static unsigned char* ensure(printbuffer * const p, size_t needed)
+{
+    unsigned char *newbuffer = NULL;
+    size_t newsize = 0;
+
+    if ((p == NULL) || (p->buffer == NULL))
+    {
+        return NULL;
+    }
+
+    if ((p->length > 0) && (p->offset >= p->length))
+    {
+        /* make sure that offset is valid */
+        return NULL;
+    }
+
+    if (needed > INT_MAX)
+    {
+        /* sizes bigger than INT_MAX are currently not supported */
+        return NULL;
+    }
+
+    needed += p->offset + 1;
+    if (needed <= p->length)
+    {
+        return p->buffer + p->offset;
+    }
+
+    if (p->noalloc) {
+        return NULL;
+    }
+
+    /* calculate new buffer size */
+    if (needed > (INT_MAX / 2))
+    {
+        /* overflow of int, use INT_MAX if possible */
+        if (needed <= INT_MAX)
+        {
+            newsize = INT_MAX;
+        }
+        else
+        {
+            return NULL;
+        }
+    }
+    else
+    {
+        newsize = needed * 2;
+    }
+
+    if (p->hooks.reallocate != NULL)
+    {
+        /* reallocate with realloc if available */
+        newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize);
+        if (newbuffer == NULL)
+        {
+            p->hooks.deallocate(p->buffer);
+            p->length = 0;
+            p->buffer = NULL;
+
+            return NULL;
+        }
+    }
+    else
+    {
+        /* otherwise reallocate manually */
+        newbuffer = (unsigned char*)p->hooks.allocate(newsize);
+        if (!newbuffer)
+        {
+            p->hooks.deallocate(p->buffer);
+            p->length = 0;
+            p->buffer = NULL;
+
+            return NULL;
+        }
+        if (newbuffer)
+        {
+            memcpy(newbuffer, p->buffer, p->offset + 1);
+        }
+        p->hooks.deallocate(p->buffer);
+    }
+    p->length = newsize;
+    p->buffer = newbuffer;
+
+    return newbuffer + p->offset;
+}
+
+/* calculate the new length of the string in a printbuffer and update the offset */
+static void update_offset(printbuffer * const buffer)
+{
+    const unsigned char *buffer_pointer = NULL;
+    if ((buffer == NULL) || (buffer->buffer == NULL))
+    {
+        return;
+    }
+    buffer_pointer = buffer->buffer + buffer->offset;
+
+    buffer->offset += strlen((const char*)buffer_pointer);
+}
+
+/* securely comparison of floating-point variables */
+static cJSON_bool compare_double(double a, double b)
+{
+    double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b);
+    return (fabs(a - b) <= maxVal * DBL_EPSILON);
+}
+
+/* Render the number nicely from the given item into a string. */
+static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer)
+{
+    unsigned char *output_pointer = NULL;
+    double d = item->valuedouble;
+    int length = 0;
+    size_t i = 0;
+    unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */
+    unsigned char decimal_point = get_decimal_point();
+    double test = 0.0;
+
+    if (output_buffer == NULL)
+    {
+        return false;
+    }
+
+    /* This checks for NaN and Infinity */
+    if (isnan(d) || isinf(d))
+    {
+        length = sprintf((char*)number_buffer, "null");
+    }
+    else
+    {
+        /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */
+        length = sprintf((char*)number_buffer, "%1.15g", d);
+
+        /* Check whether the original double can be recovered */
+        if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d))
+        {
+            /* If not, print with 17 decimal places of precision */
+            length = sprintf((char*)number_buffer, "%1.17g", d);
+        }
+    }
+
+    /* sprintf failed or buffer overrun occurred */
+    if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1)))
+    {
+        return false;
+    }
+
+    /* reserve appropriate space in the output */
+    output_pointer = ensure(output_buffer, (size_t)length + sizeof(""));
+    if (output_pointer == NULL)
+    {
+        return false;
+    }
+
+    /* copy the printed number to the output and replace locale
+     * dependent decimal point with '.' */
+    for (i = 0; i < ((size_t)length); i++)
+    {
+        if (number_buffer[i] == decimal_point)
+        {
+            output_pointer[i] = '.';
+            continue;
+        }
+
+        output_pointer[i] = number_buffer[i];
+    }
+    output_pointer[i] = '\0';
+
+    output_buffer->offset += (size_t)length;
+
+    return true;
+}
+
+/* parse 4 digit hexadecimal number */
+static unsigned parse_hex4(const unsigned char * const input)
+{
+    unsigned int h = 0;
+    size_t i = 0;
+
+    for (i = 0; i < 4; i++)
+    {
+        /* parse digit */
+        if ((input[i] >= '0') && (input[i] <= '9'))
+        {
+            h += (unsigned int) input[i] - '0';
+        }
+        else if ((input[i] >= 'A') && (input[i] <= 'F'))
+        {
+            h += (unsigned int) 10 + input[i] - 'A';
+        }
+        else if ((input[i] >= 'a') && (input[i] <= 'f'))
+        {
+            h += (unsigned int) 10 + input[i] - 'a';
+        }
+        else /* invalid */
+        {
+            return 0;
+        }
+
+        if (i < 3)
+        {
+            /* shift left to make place for the next nibble */
+            h = h << 4;
+        }
+    }
+
+    return h;
+}
+
+/* converts a UTF-16 literal to UTF-8
+ * A literal can be one or two sequences of the form \uXXXX */
+static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer)
+{
+    long unsigned int codepoint = 0;
+    unsigned int first_code = 0;
+    const unsigned char *first_sequence = input_pointer;
+    unsigned char utf8_length = 0;
+    unsigned char utf8_position = 0;
+    unsigned char sequence_length = 0;
+    unsigned char first_byte_mark = 0;
+
+    if ((input_end - first_sequence) < 6)
+    {
+        /* input ends unexpectedly */
+        goto fail;
+    }
+
+    /* get the first utf16 sequence */
+    first_code = parse_hex4(first_sequence + 2);
+
+    /* check that the code is valid */
+    if (((first_code >= 0xDC00) && (first_code <= 0xDFFF)))
+    {
+        goto fail;
+    }
+
+    /* UTF16 surrogate pair */
+    if ((first_code >= 0xD800) && (first_code <= 0xDBFF))
+    {
+        const unsigned char *second_sequence = first_sequence + 6;
+        unsigned int second_code = 0;
+        sequence_length = 12; /* \uXXXX\uXXXX */
+
+        if ((input_end - second_sequence) < 6)
+        {
+            /* input ends unexpectedly */
+            goto fail;
+        }
+
+        if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u'))
+        {
+            /* missing second half of the surrogate pair */
+            goto fail;
+        }
+
+        /* get the second utf16 sequence */
+        second_code = parse_hex4(second_sequence + 2);
+        /* check that the code is valid */
+        if ((second_code < 0xDC00) || (second_code > 0xDFFF))
+        {
+            /* invalid second half of the surrogate pair */
+            goto fail;
+        }
+
+
+        /* calculate the unicode codepoint from the surrogate pair */
+        codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF));
+    }
+    else
+    {
+        sequence_length = 6; /* \uXXXX */
+        codepoint = first_code;
+    }
+
+    /* encode as UTF-8
+     * takes at maximum 4 bytes to encode:
+     * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
+    if (codepoint < 0x80)
+    {
+        /* normal ascii, encoding 0xxxxxxx */
+        utf8_length = 1;
+    }
+    else if (codepoint < 0x800)
+    {
+        /* two bytes, encoding 110xxxxx 10xxxxxx */
+        utf8_length = 2;
+        first_byte_mark = 0xC0; /* 11000000 */
+    }
+    else if (codepoint < 0x10000)
+    {
+        /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */
+        utf8_length = 3;
+        first_byte_mark = 0xE0; /* 11100000 */
+    }
+    else if (codepoint <= 0x10FFFF)
+    {
+        /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */
+        utf8_length = 4;
+        first_byte_mark = 0xF0; /* 11110000 */
+    }
+    else
+    {
+        /* invalid unicode codepoint */
+        goto fail;
+    }
+
+    /* encode as utf8 */
+    for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--)
+    {
+        /* 10xxxxxx */
+        (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF);
+        codepoint >>= 6;
+    }
+    /* encode first byte */
+    if (utf8_length > 1)
+    {
+        (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF);
+    }
+    else
+    {
+        (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F);
+    }
+
+    *output_pointer += utf8_length;
+
+    return sequence_length;
+
+fail:
+    return 0;
+}
+
+/* Parse the input text into an unescaped cinput, and populate item. */
+static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer)
+{
+    const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1;
+    const unsigned char *input_end = buffer_at_offset(input_buffer) + 1;
+    unsigned char *output_pointer = NULL;
+    unsigned char *output = NULL;
+
+    /* not a string */
+    if (buffer_at_offset(input_buffer)[0] != '\"')
+    {
+        goto fail;
+    }
+
+    {
+        /* calculate approximate size of the output (overestimate) */
+        size_t allocation_length = 0;
+        size_t skipped_bytes = 0;
+        while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"'))
+        {
+            /* is escape sequence */
+            if (input_end[0] == '\\')
+            {
+                if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length)
+                {
+                    /* prevent buffer overflow when last input character is a backslash */
+                    goto fail;
+                }
+                skipped_bytes++;
+                input_end++;
+            }
+            input_end++;
+        }
+        if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"'))
+        {
+            goto fail; /* string ended unexpectedly */
+        }
+
+        /* This is at most how much we need for the output */
+        allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes;
+        output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof(""));
+        if (output == NULL)
+        {
+            goto fail; /* allocation failure */
+        }
+    }
+
+    output_pointer = output;
+    /* loop through the string literal */
+    while (input_pointer < input_end)
+    {
+        if (*input_pointer != '\\')
+        {
+            *output_pointer++ = *input_pointer++;
+        }
+        /* escape sequence */
+        else
+        {
+            unsigned char sequence_length = 2;
+            if ((input_end - input_pointer) < 1)
+            {
+                goto fail;
+            }
+
+            switch (input_pointer[1])
+            {
+                case 'b':
+                    *output_pointer++ = '\b';
+                    break;
+                case 'f':
+                    *output_pointer++ = '\f';
+                    break;
+                case 'n':
+                    *output_pointer++ = '\n';
+                    break;
+                case 'r':
+                    *output_pointer++ = '\r';
+                    break;
+                case 't':
+                    *output_pointer++ = '\t';
+                    break;
+                case '\"':
+                case '\\':
+                case '/':
+                    *output_pointer++ = input_pointer[1];
+                    break;
+
+                /* UTF-16 literal */
+                case 'u':
+                    sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer);
+                    if (sequence_length == 0)
+                    {
+                        /* failed to convert UTF16-literal to UTF-8 */
+                        goto fail;
+                    }
+                    break;
+
+                default:
+                    goto fail;
+            }
+            input_pointer += sequence_length;
+        }
+    }
+
+    /* zero terminate the output */
+    *output_pointer = '\0';
+
+    item->type = cJSON_String;
+    item->valuestring = (char*)output;
+
+    input_buffer->offset = (size_t) (input_end - input_buffer->content);
+    input_buffer->offset++;
+
+    return true;
+
+fail:
+    if (output != NULL)
+    {
+        input_buffer->hooks.deallocate(output);
+    }
+
+    if (input_pointer != NULL)
+    {
+        input_buffer->offset = (size_t)(input_pointer - input_buffer->content);
+    }
+
+    return false;
+}
+
+/* Render the cstring provided to an escaped version that can be printed. */
+static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer)
+{
+    const unsigned char *input_pointer = NULL;
+    unsigned char *output = NULL;
+    unsigned char *output_pointer = NULL;
+    size_t output_length = 0;
+    /* numbers of additional characters needed for escaping */
+    size_t escape_characters = 0;
+
+    if (output_buffer == NULL)
+    {
+        return false;
+    }
+
+    /* empty string */
+    if (input == NULL)
+    {
+        output = ensure(output_buffer, sizeof("\"\""));
+        if (output == NULL)
+        {
+            return false;
+        }
+        strcpy((char*)output, "\"\"");
+
+        return true;
+    }
+
+    /* set "flag" to 1 if something needs to be escaped */
+    for (input_pointer = input; *input_pointer; input_pointer++)
+    {
+        switch (*input_pointer)
+        {
+            case '\"':
+            case '\\':
+            case '\b':
+            case '\f':
+            case '\n':
+            case '\r':
+            case '\t':
+                /* one character escape sequence */
+                escape_characters++;
+                break;
+            default:
+                if (*input_pointer < 32)
+                {
+                    /* UTF-16 escape sequence uXXXX */
+                    escape_characters += 5;
+                }
+                break;
+        }
+    }
+    output_length = (size_t)(input_pointer - input) + escape_characters;
+
+    output = ensure(output_buffer, output_length + sizeof("\"\""));
+    if (output == NULL)
+    {
+        return false;
+    }
+
+    /* no characters have to be escaped */
+    if (escape_characters == 0)
+    {
+        output[0] = '\"';
+        memcpy(output + 1, input, output_length);
+        output[output_length + 1] = '\"';
+        output[output_length + 2] = '\0';
+
+        return true;
+    }
+
+    output[0] = '\"';
+    output_pointer = output + 1;
+    /* copy the string */
+    for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++)
+    {
+        if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\'))
+        {
+            /* normal character, copy */
+            *output_pointer = *input_pointer;
+        }
+        else
+        {
+            /* character needs to be escaped */
+            *output_pointer++ = '\\';
+            switch (*input_pointer)
+            {
+                case '\\':
+                    *output_pointer = '\\';
+                    break;
+                case '\"':
+                    *output_pointer = '\"';
+                    break;
+                case '\b':
+                    *output_pointer = 'b';
+                    break;
+                case '\f':
+                    *output_pointer = 'f';
+                    break;
+                case '\n':
+                    *output_pointer = 'n';
+                    break;
+                case '\r':
+                    *output_pointer = 'r';
+                    break;
+                case '\t':
+                    *output_pointer = 't';
+                    break;
+                default:
+                    /* escape and print as unicode codepoint */
+                    sprintf((char*)output_pointer, "u%04x", *input_pointer);
+                    output_pointer += 4;
+                    break;
+            }
+        }
+    }
+    output[output_length + 1] = '\"';
+    output[output_length + 2] = '\0';
+
+    return true;
+}
+
+/* Invoke print_string_ptr (which is useful) on an item. */
+static cJSON_bool print_string(const cJSON * const item, printbuffer * const p)
+{
+    return print_string_ptr((unsigned char*)item->valuestring, p);
+}
+
+/* Predeclare these prototypes. */
+static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer);
+static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer);
+static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer);
+static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer);
+static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer);
+static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer);
+
+/* Utility to jump whitespace and cr/lf */
+static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer)
+{
+    if ((buffer == NULL) || (buffer->content == NULL))
+    {
+        return NULL;
+    }
+
+    if (cannot_access_at_index(buffer, 0))
+    {
+        return buffer;
+    }
+
+    while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32))
+    {
+       buffer->offset++;
+    }
+
+    if (buffer->offset == buffer->length)
+    {
+        buffer->offset--;
+    }
+
+    return buffer;
+}
+
+/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */
+static parse_buffer *skip_utf8_bom(parse_buffer * const buffer)
+{
+    if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0))
+    {
+        return NULL;
+    }
+
+    if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0))
+    {
+        buffer->offset += 3;
+    }
+
+    return buffer;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)
+{
+    size_t buffer_length;
+
+    if (NULL == value)
+    {
+        return NULL;
+    }
+
+    /* Adding null character size due to require_null_terminated. */
+    buffer_length = strlen(value) + sizeof("");
+
+    return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated);
+}
+
+/* Parse an object - create a new root, and populate. */
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated)
+{
+    parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } };
+    cJSON *item = NULL;
+
+    /* reset error position */
+    global_error.json = NULL;
+    global_error.position = 0;
+
+    if (value == NULL || 0 == buffer_length)
+    {
+        goto fail;
+    }
+
+    buffer.content = (const unsigned char*)value;
+    buffer.length = buffer_length;
+    buffer.offset = 0;
+    buffer.hooks = global_hooks;
+
+    item = cJSON_New_Item(&global_hooks);
+    if (item == NULL) /* memory fail */
+    {
+        goto fail;
+    }
+
+    if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer))))
+    {
+        /* parse failure. ep is set. */
+        goto fail;
+    }
+
+    /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
+    if (require_null_terminated)
+    {
+        buffer_skip_whitespace(&buffer);
+        if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0')
+        {
+            goto fail;
+        }
+    }
+    if (return_parse_end)
+    {
+        *return_parse_end = (const char*)buffer_at_offset(&buffer);
+    }
+
+    return item;
+
+fail:
+    if (item != NULL)
+    {
+        cJSON_Delete(item);
+    }
+
+    if (value != NULL)
+    {
+        error local_error;
+        local_error.json = (const unsigned char*)value;
+        local_error.position = 0;
+
+        if (buffer.offset < buffer.length)
+        {
+            local_error.position = buffer.offset;
+        }
+        else if (buffer.length > 0)
+        {
+            local_error.position = buffer.length - 1;
+        }
+
+        if (return_parse_end != NULL)
+        {
+            *return_parse_end = (const char*)local_error.json + local_error.position;
+        }
+
+        global_error = local_error;
+    }
+
+    return NULL;
+}
+
+/* Default options for cJSON_Parse */
+CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value)
+{
+    return cJSON_ParseWithOpts(value, 0, 0);
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length)
+{
+    return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0);
+}
+
+#define cjson_min(a, b) (((a) < (b)) ? (a) : (b))
+
+static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks)
+{
+    static const size_t default_buffer_size = 256;
+    printbuffer buffer[1];
+    unsigned char *printed = NULL;
+
+    memset(buffer, 0, sizeof(buffer));
+
+    /* create buffer */
+    buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size);
+    buffer->length = default_buffer_size;
+    buffer->format = format;
+    buffer->hooks = *hooks;
+    if (buffer->buffer == NULL)
+    {
+        goto fail;
+    }
+
+    /* print the value */
+    if (!print_value(item, buffer))
+    {
+        goto fail;
+    }
+    update_offset(buffer);
+
+    /* check if reallocate is available */
+    if (hooks->reallocate != NULL)
+    {
+        printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1);
+        if (printed == NULL) {
+            goto fail;
+        }
+        buffer->buffer = NULL;
+    }
+    else /* otherwise copy the JSON over to a new buffer */
+    {
+        printed = (unsigned char*) hooks->allocate(buffer->offset + 1);
+        if (printed == NULL)
+        {
+            goto fail;
+        }
+        memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1));
+        printed[buffer->offset] = '\0'; /* just to be sure */
+
+        /* free the buffer */
+        hooks->deallocate(buffer->buffer);
+    }
+
+    return printed;
+
+fail:
+    if (buffer->buffer != NULL)
+    {
+        hooks->deallocate(buffer->buffer);
+    }
+
+    if (printed != NULL)
+    {
+        hooks->deallocate(printed);
+    }
+
+    return NULL;
+}
+
+/* Render a cJSON item/entity/structure to text. */
+CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item)
+{
+    return (char*)print(item, true, &global_hooks);
+}
+
+CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item)
+{
+    return (char*)print(item, false, &global_hooks);
+}
+
+CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt)
+{
+    printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
+
+    if (prebuffer < 0)
+    {
+        return NULL;
+    }
+
+    p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer);
+    if (!p.buffer)
+    {
+        return NULL;
+    }
+
+    p.length = (size_t)prebuffer;
+    p.offset = 0;
+    p.noalloc = false;
+    p.format = fmt;
+    p.hooks = global_hooks;
+
+    if (!print_value(item, &p))
+    {
+        global_hooks.deallocate(p.buffer);
+        return NULL;
+    }
+
+    return (char*)p.buffer;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format)
+{
+    printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
+
+    if ((length < 0) || (buffer == NULL))
+    {
+        return false;
+    }
+
+    p.buffer = (unsigned char*)buffer;
+    p.length = (size_t)length;
+    p.offset = 0;
+    p.noalloc = true;
+    p.format = format;
+    p.hooks = global_hooks;
+
+    return print_value(item, &p);
+}
+
+/* Parser core - when encountering text, process appropriately. */
+static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer)
+{
+    if ((input_buffer == NULL) || (input_buffer->content == NULL))
+    {
+        return false; /* no input */
+    }
+
+    /* parse the different types of values */
+    /* null */
+    if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0))
+    {
+        item->type = cJSON_NULL;
+        input_buffer->offset += 4;
+        return true;
+    }
+    /* false */
+    if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0))
+    {
+        item->type = cJSON_False;
+        input_buffer->offset += 5;
+        return true;
+    }
+    /* true */
+    if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0))
+    {
+        item->type = cJSON_True;
+        item->valueint = 1;
+        input_buffer->offset += 4;
+        return true;
+    }
+    /* string */
+    if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"'))
+    {
+        return parse_string(item, input_buffer);
+    }
+    /* number */
+    if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9'))))
+    {
+        return parse_number(item, input_buffer);
+    }
+    /* array */
+    if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '['))
+    {
+        return parse_array(item, input_buffer);
+    }
+    /* object */
+    if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{'))
+    {
+        return parse_object(item, input_buffer);
+    }
+
+    return false;
+}
+
+/* Render a value to text. */
+static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer)
+{
+    unsigned char *output = NULL;
+
+    if ((item == NULL) || (output_buffer == NULL))
+    {
+        return false;
+    }
+
+    switch ((item->type) & 0xFF)
+    {
+        case cJSON_NULL:
+            output = ensure(output_buffer, 5);
+            if (output == NULL)
+            {
+                return false;
+            }
+            strcpy((char*)output, "null");
+            return true;
+
+        case cJSON_False:
+            output = ensure(output_buffer, 6);
+            if (output == NULL)
+            {
+                return false;
+            }
+            strcpy((char*)output, "false");
+            return true;
+
+        case cJSON_True:
+            output = ensure(output_buffer, 5);
+            if (output == NULL)
+            {
+                return false;
+            }
+            strcpy((char*)output, "true");
+            return true;
+
+        case cJSON_Number:
+            return print_number(item, output_buffer);
+
+        case cJSON_Raw:
+        {
+            size_t raw_length = 0;
+            if (item->valuestring == NULL)
+            {
+                return false;
+            }
+
+            raw_length = strlen(item->valuestring) + sizeof("");
+            output = ensure(output_buffer, raw_length);
+            if (output == NULL)
+            {
+                return false;
+            }
+            memcpy(output, item->valuestring, raw_length);
+            return true;
+        }
+
+        case cJSON_String:
+            return print_string(item, output_buffer);
+
+        case cJSON_Array:
+            return print_array(item, output_buffer);
+
+        case cJSON_Object:
+            return print_object(item, output_buffer);
+
+        default:
+            return false;
+    }
+}
+
+/* Build an array from input text. */
+static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer)
+{
+    cJSON *head = NULL; /* head of the linked list */
+    cJSON *current_item = NULL;
+
+    if (input_buffer->depth >= CJSON_NESTING_LIMIT)
+    {
+        return false; /* to deeply nested */
+    }
+    input_buffer->depth++;
+
+    if (buffer_at_offset(input_buffer)[0] != '[')
+    {
+        /* not an array */
+        goto fail;
+    }
+
+    input_buffer->offset++;
+    buffer_skip_whitespace(input_buffer);
+    if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']'))
+    {
+        /* empty array */
+        goto success;
+    }
+
+    /* check if we skipped to the end of the buffer */
+    if (cannot_access_at_index(input_buffer, 0))
+    {
+        input_buffer->offset--;
+        goto fail;
+    }
+
+    /* step back to character in front of the first element */
+    input_buffer->offset--;
+    /* loop through the comma separated array elements */
+    do
+    {
+        /* allocate next item */
+        cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
+        if (new_item == NULL)
+        {
+            goto fail; /* allocation failure */
+        }
+
+        /* attach next item to list */
+        if (head == NULL)
+        {
+            /* start the linked list */
+            current_item = head = new_item;
+        }
+        else
+        {
+            /* add to the end and advance */
+            current_item->next = new_item;
+            new_item->prev = current_item;
+            current_item = new_item;
+        }
+
+        /* parse next value */
+        input_buffer->offset++;
+        buffer_skip_whitespace(input_buffer);
+        if (!parse_value(current_item, input_buffer))
+        {
+            goto fail; /* failed to parse value */
+        }
+        buffer_skip_whitespace(input_buffer);
+    }
+    while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
+
+    if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']')
+    {
+        goto fail; /* expected end of array */
+    }
+
+success:
+    input_buffer->depth--;
+
+    if (head != NULL) {
+        head->prev = current_item;
+    }
+
+    item->type = cJSON_Array;
+    item->child = head;
+
+    input_buffer->offset++;
+
+    return true;
+
+fail:
+    if (head != NULL)
+    {
+        cJSON_Delete(head);
+    }
+
+    return false;
+}
+
+/* Render an array to text */
+static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer)
+{
+    unsigned char *output_pointer = NULL;
+    size_t length = 0;
+    cJSON *current_element = item->child;
+
+    if (output_buffer == NULL)
+    {
+        return false;
+    }
+
+    /* Compose the output array. */
+    /* opening square bracket */
+    output_pointer = ensure(output_buffer, 1);
+    if (output_pointer == NULL)
+    {
+        return false;
+    }
+
+    *output_pointer = '[';
+    output_buffer->offset++;
+    output_buffer->depth++;
+
+    while (current_element != NULL)
+    {
+        if (!print_value(current_element, output_buffer))
+        {
+            return false;
+        }
+        update_offset(output_buffer);
+        if (current_element->next)
+        {
+            length = (size_t) (output_buffer->format ? 2 : 1);
+            output_pointer = ensure(output_buffer, length + 1);
+            if (output_pointer == NULL)
+            {
+                return false;
+            }
+            *output_pointer++ = ',';
+            if(output_buffer->format)
+            {
+                *output_pointer++ = ' ';
+            }
+            *output_pointer = '\0';
+            output_buffer->offset += length;
+        }
+        current_element = current_element->next;
+    }
+
+    output_pointer = ensure(output_buffer, 2);
+    if (output_pointer == NULL)
+    {
+        return false;
+    }
+    *output_pointer++ = ']';
+    *output_pointer = '\0';
+    output_buffer->depth--;
+
+    return true;
+}
+
+/* Build an object from the text. */
+static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer)
+{
+    cJSON *head = NULL; /* linked list head */
+    cJSON *current_item = NULL;
+
+    if (input_buffer->depth >= CJSON_NESTING_LIMIT)
+    {
+        return false; /* to deeply nested */
+    }
+    input_buffer->depth++;
+
+    if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{'))
+    {
+        goto fail; /* not an object */
+    }
+
+    input_buffer->offset++;
+    buffer_skip_whitespace(input_buffer);
+    if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}'))
+    {
+        goto success; /* empty object */
+    }
+
+    /* check if we skipped to the end of the buffer */
+    if (cannot_access_at_index(input_buffer, 0))
+    {
+        input_buffer->offset--;
+        goto fail;
+    }
+
+    /* step back to character in front of the first element */
+    input_buffer->offset--;
+    /* loop through the comma separated array elements */
+    do
+    {
+        /* allocate next item */
+        cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
+        if (new_item == NULL)
+        {
+            goto fail; /* allocation failure */
+        }
+
+        /* attach next item to list */
+        if (head == NULL)
+        {
+            /* start the linked list */
+            current_item = head = new_item;
+        }
+        else
+        {
+            /* add to the end and advance */
+            current_item->next = new_item;
+            new_item->prev = current_item;
+            current_item = new_item;
+        }
+
+        /* parse the name of the child */
+        input_buffer->offset++;
+        buffer_skip_whitespace(input_buffer);
+        if (!parse_string(current_item, input_buffer))
+        {
+            goto fail; /* failed to parse name */
+        }
+        buffer_skip_whitespace(input_buffer);
+
+        /* swap valuestring and string, because we parsed the name */
+        current_item->string = current_item->valuestring;
+        current_item->valuestring = NULL;
+
+        if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':'))
+        {
+            goto fail; /* invalid object */
+        }
+
+        /* parse the value */
+        input_buffer->offset++;
+        buffer_skip_whitespace(input_buffer);
+        if (!parse_value(current_item, input_buffer))
+        {
+            goto fail; /* failed to parse value */
+        }
+        buffer_skip_whitespace(input_buffer);
+    }
+    while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
+
+    if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}'))
+    {
+        goto fail; /* expected end of object */
+    }
+
+success:
+    input_buffer->depth--;
+
+    if (head != NULL) {
+        head->prev = current_item;
+    }
+
+    item->type = cJSON_Object;
+    item->child = head;
+
+    input_buffer->offset++;
+    return true;
+
+fail:
+    if (head != NULL)
+    {
+        cJSON_Delete(head);
+    }
+
+    return false;
+}
+
+/* Render an object to text. */
+static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer)
+{
+    unsigned char *output_pointer = NULL;
+    size_t length = 0;
+    cJSON *current_item = item->child;
+
+    if (output_buffer == NULL)
+    {
+        return false;
+    }
+
+    /* Compose the output: */
+    length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */
+    output_pointer = ensure(output_buffer, length + 1);
+    if (output_pointer == NULL)
+    {
+        return false;
+    }
+
+    *output_pointer++ = '{';
+    output_buffer->depth++;
+    if (output_buffer->format)
+    {
+        *output_pointer++ = '\n';
+    }
+    output_buffer->offset += length;
+
+    while (current_item)
+    {
+        if (output_buffer->format)
+        {
+            size_t i;
+            output_pointer = ensure(output_buffer, output_buffer->depth);
+            if (output_pointer == NULL)
+            {
+                return false;
+            }
+            for (i = 0; i < output_buffer->depth; i++)
+            {
+                *output_pointer++ = '\t';
+            }
+            output_buffer->offset += output_buffer->depth;
+        }
+
+        /* print key */
+        if (!print_string_ptr((unsigned char*)current_item->string, output_buffer))
+        {
+            return false;
+        }
+        update_offset(output_buffer);
+
+        length = (size_t) (output_buffer->format ? 2 : 1);
+        output_pointer = ensure(output_buffer, length);
+        if (output_pointer == NULL)
+        {
+            return false;
+        }
+        *output_pointer++ = ':';
+        if (output_buffer->format)
+        {
+            *output_pointer++ = '\t';
+        }
+        output_buffer->offset += length;
+
+        /* print value */
+        if (!print_value(current_item, output_buffer))
+        {
+            return false;
+        }
+        update_offset(output_buffer);
+
+        /* print comma if not last */
+        length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0));
+        output_pointer = ensure(output_buffer, length + 1);
+        if (output_pointer == NULL)
+        {
+            return false;
+        }
+        if (current_item->next)
+        {
+            *output_pointer++ = ',';
+        }
+
+        if (output_buffer->format)
+        {
+            *output_pointer++ = '\n';
+        }
+        *output_pointer = '\0';
+        output_buffer->offset += length;
+
+        current_item = current_item->next;
+    }
+
+    output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2);
+    if (output_pointer == NULL)
+    {
+        return false;
+    }
+    if (output_buffer->format)
+    {
+        size_t i;
+        for (i = 0; i < (output_buffer->depth - 1); i++)
+        {
+            *output_pointer++ = '\t';
+        }
+    }
+    *output_pointer++ = '}';
+    *output_pointer = '\0';
+    output_buffer->depth--;
+
+    return true;
+}
+
+/* Get Array size/item / object item. */
+CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array)
+{
+    cJSON *child = NULL;
+    size_t size = 0;
+
+    if (array == NULL)
+    {
+        return 0;
+    }
+
+    child = array->child;
+
+    while(child != NULL)
+    {
+        size++;
+        child = child->next;
+    }
+
+    /* FIXME: Can overflow here. Cannot be fixed without breaking the API */
+
+    return (int)size;
+}
+
+static cJSON* get_array_item(const cJSON *array, size_t index)
+{
+    cJSON *current_child = NULL;
+
+    if (array == NULL)
+    {
+        return NULL;
+    }
+
+    current_child = array->child;
+    while ((current_child != NULL) && (index > 0))
+    {
+        index--;
+        current_child = current_child->next;
+    }
+
+    return current_child;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index)
+{
+    if (index < 0)
+    {
+        return NULL;
+    }
+
+    return get_array_item(array, (size_t)index);
+}
+
+static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive)
+{
+    cJSON *current_element = NULL;
+
+    if ((object == NULL) || (name == NULL))
+    {
+        return NULL;
+    }
+
+    current_element = object->child;
+    if (case_sensitive)
+    {
+        while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0))
+        {
+            current_element = current_element->next;
+        }
+    }
+    else
+    {
+        while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0))
+        {
+            current_element = current_element->next;
+        }
+    }
+
+    if ((current_element == NULL) || (current_element->string == NULL)) {
+        return NULL;
+    }
+
+    return current_element;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string)
+{
+    return get_object_item(object, string, false);
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string)
+{
+    return get_object_item(object, string, true);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string)
+{
+    return cJSON_GetObjectItem(object, string) ? 1 : 0;
+}
+
+/* Utility for array list handling. */
+static void suffix_object(cJSON *prev, cJSON *item)
+{
+    prev->next = item;
+    item->prev = prev;
+}
+
+/* Utility for handling references. */
+static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks)
+{
+    cJSON *reference = NULL;
+    if (item == NULL)
+    {
+        return NULL;
+    }
+
+    reference = cJSON_New_Item(hooks);
+    if (reference == NULL)
+    {
+        return NULL;
+    }
+
+    memcpy(reference, item, sizeof(cJSON));
+    reference->string = NULL;
+    reference->type |= cJSON_IsReference;
+    reference->next = reference->prev = NULL;
+    return reference;
+}
+
+static cJSON_bool add_item_to_array(cJSON *array, cJSON *item)
+{
+    cJSON *child = NULL;
+
+    if ((item == NULL) || (array == NULL) || (array == item))
+    {
+        return false;
+    }
+
+    child = array->child;
+    /*
+     * To find the last item in array quickly, we use prev in array
+     */
+    if (child == NULL)
+    {
+        /* list is empty, start new one */
+        array->child = item;
+        item->prev = item;
+        item->next = NULL;
+    }
+    else
+    {
+        /* append to the end */
+        if (child->prev)
+        {
+            suffix_object(child->prev, item);
+            array->child->prev = item;
+        }
+        else
+        {
+            while (child->next)
+            {
+                child = child->next;
+            }
+            suffix_object(child, item);
+            array->child->prev = item;
+        }
+    }
+
+    return true;
+}
+
+/* Add item to array/object. */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item)
+{
+    return add_item_to_array(array, item);
+}
+
+#if defined(__clang__) || (defined(__GNUC__)  && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
+    #pragma GCC diagnostic push
+#endif
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wcast-qual"
+#endif
+/* helper function to cast away const */
+static void* cast_away_const(const void* string)
+{
+    return (void*)string;
+}
+#if defined(__clang__) || (defined(__GNUC__)  && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
+    #pragma GCC diagnostic pop
+#endif
+
+
+static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key)
+{
+    char *new_key = NULL;
+    int new_type = cJSON_Invalid;
+
+    if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item))
+    {
+        return false;
+    }
+
+    if (constant_key)
+    {
+        new_key = (char*)cast_away_const(string);
+        new_type = item->type | cJSON_StringIsConst;
+    }
+    else
+    {
+        new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks);
+        if (new_key == NULL)
+        {
+            return false;
+        }
+
+        new_type = item->type & ~cJSON_StringIsConst;
+    }
+
+    if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
+    {
+        hooks->deallocate(item->string);
+    }
+
+    item->string = new_key;
+    item->type = new_type;
+
+    return add_item_to_array(object, item);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item)
+{
+    return add_item_to_object(object, string, item, &global_hooks, false);
+}
+
+/* Add an item to an object with constant string as key */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item)
+{
+    return add_item_to_object(object, string, item, &global_hooks, true);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)
+{
+    if (array == NULL)
+    {
+        return false;
+    }
+
+    return add_item_to_array(array, create_reference(item, &global_hooks));
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item)
+{
+    if ((object == NULL) || (string == NULL))
+    {
+        return false;
+    }
+
+    return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false);
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name)
+{
+    cJSON *null = cJSON_CreateNull();
+    if (add_item_to_object(object, name, null, &global_hooks, false))
+    {
+        return null;
+    }
+
+    cJSON_Delete(null);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name)
+{
+    cJSON *true_item = cJSON_CreateTrue();
+    if (add_item_to_object(object, name, true_item, &global_hooks, false))
+    {
+        return true_item;
+    }
+
+    cJSON_Delete(true_item);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name)
+{
+    cJSON *false_item = cJSON_CreateFalse();
+    if (add_item_to_object(object, name, false_item, &global_hooks, false))
+    {
+        return false_item;
+    }
+
+    cJSON_Delete(false_item);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean)
+{
+    cJSON *bool_item = cJSON_CreateBool(boolean);
+    if (add_item_to_object(object, name, bool_item, &global_hooks, false))
+    {
+        return bool_item;
+    }
+
+    cJSON_Delete(bool_item);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number)
+{
+    cJSON *number_item = cJSON_CreateNumber(number);
+    if (add_item_to_object(object, name, number_item, &global_hooks, false))
+    {
+        return number_item;
+    }
+
+    cJSON_Delete(number_item);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string)
+{
+    cJSON *string_item = cJSON_CreateString(string);
+    if (add_item_to_object(object, name, string_item, &global_hooks, false))
+    {
+        return string_item;
+    }
+
+    cJSON_Delete(string_item);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw)
+{
+    cJSON *raw_item = cJSON_CreateRaw(raw);
+    if (add_item_to_object(object, name, raw_item, &global_hooks, false))
+    {
+        return raw_item;
+    }
+
+    cJSON_Delete(raw_item);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name)
+{
+    cJSON *object_item = cJSON_CreateObject();
+    if (add_item_to_object(object, name, object_item, &global_hooks, false))
+    {
+        return object_item;
+    }
+
+    cJSON_Delete(object_item);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name)
+{
+    cJSON *array = cJSON_CreateArray();
+    if (add_item_to_object(object, name, array, &global_hooks, false))
+    {
+        return array;
+    }
+
+    cJSON_Delete(array);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item)
+{
+    if ((parent == NULL) || (item == NULL))
+    {
+        return NULL;
+    }
+
+    if (item != parent->child)
+    {
+        /* not the first element */
+        item->prev->next = item->next;
+    }
+    if (item->next != NULL)
+    {
+        /* not the last element */
+        item->next->prev = item->prev;
+    }
+
+    if (item == parent->child)
+    {
+        /* first element */
+        parent->child = item->next;
+    }
+    else if (item->next == NULL)
+    {
+        /* last element */
+        parent->child->prev = item->prev;
+    }
+
+    /* make sure the detached item doesn't point anywhere anymore */
+    item->prev = NULL;
+    item->next = NULL;
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which)
+{
+    if (which < 0)
+    {
+        return NULL;
+    }
+
+    return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which));
+}
+
+CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which)
+{
+    cJSON_Delete(cJSON_DetachItemFromArray(array, which));
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string)
+{
+    cJSON *to_detach = cJSON_GetObjectItem(object, string);
+
+    return cJSON_DetachItemViaPointer(object, to_detach);
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string)
+{
+    cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string);
+
+    return cJSON_DetachItemViaPointer(object, to_detach);
+}
+
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string)
+{
+    cJSON_Delete(cJSON_DetachItemFromObject(object, string));
+}
+
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string)
+{
+    cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string));
+}
+
+/* Replace array/object items with new ones. */
+CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem)
+{
+    cJSON *after_inserted = NULL;
+
+    if (which < 0)
+    {
+        return false;
+    }
+
+    after_inserted = get_array_item(array, (size_t)which);
+    if (after_inserted == NULL)
+    {
+        return add_item_to_array(array, newitem);
+    }
+
+    newitem->next = after_inserted;
+    newitem->prev = after_inserted->prev;
+    after_inserted->prev = newitem;
+    if (after_inserted == array->child)
+    {
+        array->child = newitem;
+    }
+    else
+    {
+        newitem->prev->next = newitem;
+    }
+    return true;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement)
+{
+    if ((parent == NULL) || (replacement == NULL) || (item == NULL))
+    {
+        return false;
+    }
+
+    if (replacement == item)
+    {
+        return true;
+    }
+
+    replacement->next = item->next;
+    replacement->prev = item->prev;
+
+    if (replacement->next != NULL)
+    {
+        replacement->next->prev = replacement;
+    }
+    if (parent->child == item)
+    {
+        if (parent->child->prev == parent->child)
+        {
+            replacement->prev = replacement;
+        }
+        parent->child = replacement;
+    }
+    else
+    {   /*
+         * To find the last item in array quickly, we use prev in array.
+         * We can't modify the last item's next pointer where this item was the parent's child
+         */
+        if (replacement->prev != NULL)
+        {
+            replacement->prev->next = replacement;
+        }
+        if (replacement->next == NULL)
+        {
+            parent->child->prev = replacement;
+        }
+    }
+
+    item->next = NULL;
+    item->prev = NULL;
+    cJSON_Delete(item);
+
+    return true;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem)
+{
+    if (which < 0)
+    {
+        return false;
+    }
+
+    return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem);
+}
+
+static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive)
+{
+    if ((replacement == NULL) || (string == NULL))
+    {
+        return false;
+    }
+
+    /* replace the name in the replacement */
+    if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL))
+    {
+        cJSON_free(replacement->string);
+    }
+    replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
+    replacement->type &= ~cJSON_StringIsConst;
+
+    return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem)
+{
+    return replace_item_in_object(object, string, newitem, false);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem)
+{
+    return replace_item_in_object(object, string, newitem, true);
+}
+
+/* Create basic types: */
+CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if(item)
+    {
+        item->type = cJSON_NULL;
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if(item)
+    {
+        item->type = cJSON_True;
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if(item)
+    {
+        item->type = cJSON_False;
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if(item)
+    {
+        item->type = boolean ? cJSON_True : cJSON_False;
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if(item)
+    {
+        item->type = cJSON_Number;
+        item->valuedouble = num;
+
+        /* use saturation in case of overflow */
+        if (num >= INT_MAX)
+        {
+            item->valueint = INT_MAX;
+        }
+        else if (num <= (double)INT_MIN)
+        {
+            item->valueint = INT_MIN;
+        }
+        else
+        {
+            item->valueint = (int)num;
+        }
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if(item)
+    {
+        item->type = cJSON_String;
+        item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
+        if(!item->valuestring)
+        {
+            cJSON_Delete(item);
+            return NULL;
+        }
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if (item != NULL)
+    {
+        item->type = cJSON_String | cJSON_IsReference;
+        item->valuestring = (char*)cast_away_const(string);
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if (item != NULL) {
+        item->type = cJSON_Object | cJSON_IsReference;
+        item->child = (cJSON*)cast_away_const(child);
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) {
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if (item != NULL) {
+        item->type = cJSON_Array | cJSON_IsReference;
+        item->child = (cJSON*)cast_away_const(child);
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if(item)
+    {
+        item->type = cJSON_Raw;
+        item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks);
+        if(!item->valuestring)
+        {
+            cJSON_Delete(item);
+            return NULL;
+        }
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if(item)
+    {
+        item->type=cJSON_Array;
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if (item)
+    {
+        item->type = cJSON_Object;
+    }
+
+    return item;
+}
+
+/* Create Arrays: */
+CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count)
+{
+    size_t i = 0;
+    cJSON *n = NULL;
+    cJSON *p = NULL;
+    cJSON *a = NULL;
+
+    if ((count < 0) || (numbers == NULL))
+    {
+        return NULL;
+    }
+
+    a = cJSON_CreateArray();
+    for(i = 0; a && (i < (size_t)count); i++)
+    {
+        n = cJSON_CreateNumber(numbers[i]);
+        if (!n)
+        {
+            cJSON_Delete(a);
+            return NULL;
+        }
+        if(!i)
+        {
+            a->child = n;
+        }
+        else
+        {
+            suffix_object(p, n);
+        }
+        p = n;
+    }
+
+    return a;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count)
+{
+    size_t i = 0;
+    cJSON *n = NULL;
+    cJSON *p = NULL;
+    cJSON *a = NULL;
+
+    if ((count < 0) || (numbers == NULL))
+    {
+        return NULL;
+    }
+
+    a = cJSON_CreateArray();
+
+    for(i = 0; a && (i < (size_t)count); i++)
+    {
+        n = cJSON_CreateNumber((double)numbers[i]);
+        if(!n)
+        {
+            cJSON_Delete(a);
+            return NULL;
+        }
+        if(!i)
+        {
+            a->child = n;
+        }
+        else
+        {
+            suffix_object(p, n);
+        }
+        p = n;
+    }
+
+    return a;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count)
+{
+    size_t i = 0;
+    cJSON *n = NULL;
+    cJSON *p = NULL;
+    cJSON *a = NULL;
+
+    if ((count < 0) || (numbers == NULL))
+    {
+        return NULL;
+    }
+
+    a = cJSON_CreateArray();
+
+    for(i = 0;a && (i < (size_t)count); i++)
+    {
+        n = cJSON_CreateNumber(numbers[i]);
+        if(!n)
+        {
+            cJSON_Delete(a);
+            return NULL;
+        }
+        if(!i)
+        {
+            a->child = n;
+        }
+        else
+        {
+            suffix_object(p, n);
+        }
+        p = n;
+    }
+
+    return a;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count)
+{
+    size_t i = 0;
+    cJSON *n = NULL;
+    cJSON *p = NULL;
+    cJSON *a = NULL;
+
+    if ((count < 0) || (strings == NULL))
+    {
+        return NULL;
+    }
+
+    a = cJSON_CreateArray();
+
+    for (i = 0; a && (i < (size_t)count); i++)
+    {
+        n = cJSON_CreateString(strings[i]);
+        if(!n)
+        {
+            cJSON_Delete(a);
+            return NULL;
+        }
+        if(!i)
+        {
+            a->child = n;
+        }
+        else
+        {
+            suffix_object(p,n);
+        }
+        p = n;
+    }
+
+    return a;
+}
+
+/* Duplication */
+CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse)
+{
+    cJSON *newitem = NULL;
+    cJSON *child = NULL;
+    cJSON *next = NULL;
+    cJSON *newchild = NULL;
+
+    /* Bail on bad ptr */
+    if (!item)
+    {
+        goto fail;
+    }
+    /* Create new item */
+    newitem = cJSON_New_Item(&global_hooks);
+    if (!newitem)
+    {
+        goto fail;
+    }
+    /* Copy over all vars */
+    newitem->type = item->type & (~cJSON_IsReference);
+    newitem->valueint = item->valueint;
+    newitem->valuedouble = item->valuedouble;
+    if (item->valuestring)
+    {
+        newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks);
+        if (!newitem->valuestring)
+        {
+            goto fail;
+        }
+    }
+    if (item->string)
+    {
+        newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks);
+        if (!newitem->string)
+        {
+            goto fail;
+        }
+    }
+    /* If non-recursive, then we're done! */
+    if (!recurse)
+    {
+        return newitem;
+    }
+    /* Walk the ->next chain for the child. */
+    child = item->child;
+    while (child != NULL)
+    {
+        newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */
+        if (!newchild)
+        {
+            goto fail;
+        }
+        if (next != NULL)
+        {
+            /* If newitem->child already set, then crosswire ->prev and ->next and move on */
+            next->next = newchild;
+            newchild->prev = next;
+            next = newchild;
+        }
+        else
+        {
+            /* Set newitem->child and move to it */
+            newitem->child = newchild;
+            next = newchild;
+        }
+        child = child->next;
+    }
+
+    return newitem;
+
+fail:
+    if (newitem != NULL)
+    {
+        cJSON_Delete(newitem);
+    }
+
+    return NULL;
+}
+
+static void skip_oneline_comment(char **input)
+{
+    *input += static_strlen("//");
+
+    for (; (*input)[0] != '\0'; ++(*input))
+    {
+        if ((*input)[0] == '\n') {
+            *input += static_strlen("\n");
+            return;
+        }
+    }
+}
+
+static void skip_multiline_comment(char **input)
+{
+    *input += static_strlen("/*");
+
+    for (; (*input)[0] != '\0'; ++(*input))
+    {
+        if (((*input)[0] == '*') && ((*input)[1] == '/'))
+        {
+            *input += static_strlen("*/");
+            return;
+        }
+    }
+}
+
+static void minify_string(char **input, char **output) {
+    (*output)[0] = (*input)[0];
+    *input += static_strlen("\"");
+    *output += static_strlen("\"");
+
+
+    for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) {
+        (*output)[0] = (*input)[0];
+
+        if ((*input)[0] == '\"') {
+            (*output)[0] = '\"';
+            *input += static_strlen("\"");
+            *output += static_strlen("\"");
+            return;
+        } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) {
+            (*output)[1] = (*input)[1];
+            *input += static_strlen("\"");
+            *output += static_strlen("\"");
+        }
+    }
+}
+
+CJSON_PUBLIC(void) cJSON_Minify(char *json)
+{
+    char *into = json;
+
+    if (json == NULL)
+    {
+        return;
+    }
+
+    while (json[0] != '\0')
+    {
+        switch (json[0])
+        {
+            case ' ':
+            case '\t':
+            case '\r':
+            case '\n':
+                json++;
+                break;
+
+            case '/':
+                if (json[1] == '/')
+                {
+                    skip_oneline_comment(&json);
+                }
+                else if (json[1] == '*')
+                {
+                    skip_multiline_comment(&json);
+                } else {
+                    json++;
+                }
+                break;
+
+            case '\"':
+                minify_string(&json, (char**)&into);
+                break;
+
+            default:
+                into[0] = json[0];
+                json++;
+                into++;
+        }
+    }
+
+    /* and null-terminate. */
+    *into = '\0';
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_Invalid;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_False;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & 0xff) == cJSON_True;
+}
+
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & (cJSON_True | cJSON_False)) != 0;
+}
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_NULL;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_Number;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_String;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_Array;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_Object;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_Raw;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive)
+{
+    if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a))
+    {
+        return false;
+    }
+
+    /* check if type is valid */
+    switch (a->type & 0xFF)
+    {
+        case cJSON_False:
+        case cJSON_True:
+        case cJSON_NULL:
+        case cJSON_Number:
+        case cJSON_String:
+        case cJSON_Raw:
+        case cJSON_Array:
+        case cJSON_Object:
+            break;
+
+        default:
+            return false;
+    }
+
+    /* identical objects are equal */
+    if (a == b)
+    {
+        return true;
+    }
+
+    switch (a->type & 0xFF)
+    {
+        /* in these cases and equal type is enough */
+        case cJSON_False:
+        case cJSON_True:
+        case cJSON_NULL:
+            return true;
+
+        case cJSON_Number:
+            if (compare_double(a->valuedouble, b->valuedouble))
+            {
+                return true;
+            }
+            return false;
+
+        case cJSON_String:
+        case cJSON_Raw:
+            if ((a->valuestring == NULL) || (b->valuestring == NULL))
+            {
+                return false;
+            }
+            if (strcmp(a->valuestring, b->valuestring) == 0)
+            {
+                return true;
+            }
+
+            return false;
+
+        case cJSON_Array:
+        {
+            cJSON *a_element = a->child;
+            cJSON *b_element = b->child;
+
+            for (; (a_element != NULL) && (b_element != NULL);)
+            {
+                if (!cJSON_Compare(a_element, b_element, case_sensitive))
+                {
+                    return false;
+                }
+
+                a_element = a_element->next;
+                b_element = b_element->next;
+            }
+
+            /* one of the arrays is longer than the other */
+            if (a_element != b_element) {
+                return false;
+            }
+
+            return true;
+        }
+
+        case cJSON_Object:
+        {
+            cJSON *a_element = NULL;
+            cJSON *b_element = NULL;
+            cJSON_ArrayForEach(a_element, a)
+            {
+                /* TODO This has O(n^2) runtime, which is horrible! */
+                b_element = get_object_item(b, a_element->string, case_sensitive);
+                if (b_element == NULL)
+                {
+                    return false;
+                }
+
+                if (!cJSON_Compare(a_element, b_element, case_sensitive))
+                {
+                    return false;
+                }
+            }
+
+            /* doing this twice, once on a and b to prevent true comparison if a subset of b
+             * TODO: Do this the proper way, this is just a fix for now */
+            cJSON_ArrayForEach(b_element, b)
+            {
+                a_element = get_object_item(a, b_element->string, case_sensitive);
+                if (a_element == NULL)
+                {
+                    return false;
+                }
+
+                if (!cJSON_Compare(b_element, a_element, case_sensitive))
+                {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        default:
+            return false;
+    }
+}
+
+CJSON_PUBLIC(void *) cJSON_malloc(size_t size)
+{
+    return global_hooks.allocate(size);
+}
+
+CJSON_PUBLIC(void) cJSON_free(void *object)
+{
+    global_hooks.deallocate(object);
+}

+ 1470 - 0
components/cjson/src/cJSON_Utils.c

@@ -0,0 +1,1470 @@
+/*
+  Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+/* disable warnings about old C89 functions in MSVC */
+#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER)
+#define _CRT_SECURE_NO_DEPRECATE
+#endif
+
+#ifdef __GNUCC__
+#pragma GCC visibility push(default)
+#endif
+#if defined(_MSC_VER)
+#pragma warning (push)
+/* disable warning about single line comments in system headers */
+#pragma warning (disable : 4001)
+#endif
+
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+#include <math.h>
+#include <float.h>
+#include <math.h>
+
+#if defined(_MSC_VER)
+#pragma warning (pop)
+#endif
+#ifdef __GNUCC__
+#pragma GCC visibility pop
+#endif
+
+#include "cJSON_Utils.h"
+
+/* define our own boolean type */
+#ifdef true
+#undef true
+#endif
+#define true ((cJSON_bool)1)
+
+#ifdef false
+#undef false
+#endif
+#define false ((cJSON_bool)0)
+
+static unsigned char* cJSONUtils_strdup(const unsigned char* const string)
+{
+    size_t length = 0;
+    unsigned char *copy = NULL;
+
+    length = strlen((const char*)string) + sizeof("");
+    copy = (unsigned char*) cJSON_malloc(length);
+    if (copy == NULL)
+    {
+        return NULL;
+    }
+    memcpy(copy, string, length);
+
+    return copy;
+}
+
+/* string comparison which doesn't consider NULL pointers equal */
+static int compare_strings(const unsigned char *string1, const unsigned char *string2, const cJSON_bool case_sensitive)
+{
+    if ((string1 == NULL) || (string2 == NULL))
+    {
+        return 1;
+    }
+
+    if (string1 == string2)
+    {
+        return 0;
+    }
+
+    if (case_sensitive)
+    {
+        return strcmp((const char*)string1, (const char*)string2);
+    }
+
+    for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++)
+    {
+        if (*string1 == '\0')
+        {
+            return 0;
+        }
+    }
+
+    return tolower(*string1) - tolower(*string2);
+}
+
+/* securely comparison of floating-point variables */
+static cJSON_bool compare_double(double a, double b)
+{
+    double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b);
+    return (fabs(a - b) <= maxVal * DBL_EPSILON);
+}
+
+
+/* Compare the next path element of two JSON pointers, two NULL pointers are considered unequal: */
+static cJSON_bool compare_pointers(const unsigned char *name, const unsigned char *pointer, const cJSON_bool case_sensitive)
+{
+    if ((name == NULL) || (pointer == NULL))
+    {
+        return false;
+    }
+
+    for (; (*name != '\0') && (*pointer != '\0') && (*pointer != '/'); (void)name++, pointer++) /* compare until next '/' */
+    {
+        if (*pointer == '~')
+        {
+            /* check for escaped '~' (~0) and '/' (~1) */
+            if (((pointer[1] != '0') || (*name != '~')) && ((pointer[1] != '1') || (*name != '/')))
+            {
+                /* invalid escape sequence or wrong character in *name */
+                return false;
+            }
+            else
+            {
+                pointer++;
+            }
+        }
+        else if ((!case_sensitive && (tolower(*name) != tolower(*pointer))) || (case_sensitive && (*name != *pointer)))
+        {
+            return false;
+        }
+    }
+    if (((*pointer != 0) && (*pointer != '/')) != (*name != 0))
+    {
+        /* one string has ended, the other not */
+        return false;;
+    }
+
+    return true;
+}
+
+/* calculate the length of a string if encoded as JSON pointer with ~0 and ~1 escape sequences */
+static size_t pointer_encoded_length(const unsigned char *string)
+{
+    size_t length;
+    for (length = 0; *string != '\0'; (void)string++, length++)
+    {
+        /* character needs to be escaped? */
+        if ((*string == '~') || (*string == '/'))
+        {
+            length++;
+        }
+    }
+
+    return length;
+}
+
+/* copy a string while escaping '~' and '/' with ~0 and ~1 JSON pointer escape codes */
+static void encode_string_as_pointer(unsigned char *destination, const unsigned char *source)
+{
+    for (; source[0] != '\0'; (void)source++, destination++)
+    {
+        if (source[0] == '/')
+        {
+            destination[0] = '~';
+            destination[1] = '1';
+            destination++;
+        }
+        else if (source[0] == '~')
+        {
+            destination[0] = '~';
+            destination[1] = '0';
+            destination++;
+        }
+        else
+        {
+            destination[0] = source[0];
+        }
+    }
+
+    destination[0] = '\0';
+}
+
+CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(const cJSON * const object, const cJSON * const target)
+{
+    size_t child_index = 0;
+    cJSON *current_child = 0;
+
+    if ((object == NULL) || (target == NULL))
+    {
+        return NULL;
+    }
+
+    if (object == target)
+    {
+        /* found */
+        return (char*)cJSONUtils_strdup((const unsigned char*)"");
+    }
+
+    /* recursively search all children of the object or array */
+    for (current_child = object->child; current_child != NULL; (void)(current_child = current_child->next), child_index++)
+    {
+        unsigned char *target_pointer = (unsigned char*)cJSONUtils_FindPointerFromObjectTo(current_child, target);
+        /* found the target? */
+        if (target_pointer != NULL)
+        {
+            if (cJSON_IsArray(object))
+            {
+                /* reserve enough memory for a 64 bit integer + '/' and '\0' */
+                unsigned char *full_pointer = (unsigned char*)cJSON_malloc(strlen((char*)target_pointer) + 20 + sizeof("/"));
+                /* check if conversion to unsigned long is valid
+                 * This should be eliminated at compile time by dead code elimination
+                 * if size_t is an alias of unsigned long, or if it is bigger */
+                if (child_index > ULONG_MAX)
+                {
+                    cJSON_free(target_pointer);
+                    cJSON_free(full_pointer);
+                    return NULL;
+                }
+                sprintf((char*)full_pointer, "/%lu%s", (unsigned long)child_index, target_pointer); /* /<array_index><path> */
+                cJSON_free(target_pointer);
+
+                return (char*)full_pointer;
+            }
+
+            if (cJSON_IsObject(object))
+            {
+                unsigned char *full_pointer = (unsigned char*)cJSON_malloc(strlen((char*)target_pointer) + pointer_encoded_length((unsigned char*)current_child->string) + 2);
+                full_pointer[0] = '/';
+                encode_string_as_pointer(full_pointer + 1, (unsigned char*)current_child->string);
+                strcat((char*)full_pointer, (char*)target_pointer);
+                cJSON_free(target_pointer);
+
+                return (char*)full_pointer;
+            }
+
+            /* reached leaf of the tree, found nothing */
+            cJSON_free(target_pointer);
+            return NULL;
+        }
+    }
+
+    /* not found */
+    return NULL;
+}
+
+/* non broken version of cJSON_GetArrayItem */
+static cJSON *get_array_item(const cJSON *array, size_t item)
+{
+    cJSON *child = array ? array->child : NULL;
+    while ((child != NULL) && (item > 0))
+    {
+        item--;
+        child = child->next;
+    }
+
+    return child;
+}
+
+static cJSON_bool decode_array_index_from_pointer(const unsigned char * const pointer, size_t * const index)
+{
+    size_t parsed_index = 0;
+    size_t position = 0;
+
+    if ((pointer[0] == '0') && ((pointer[1] != '\0') && (pointer[1] != '/')))
+    {
+        /* leading zeroes are not permitted */
+        return 0;
+    }
+
+    for (position = 0; (pointer[position] >= '0') && (pointer[0] <= '9'); position++)
+    {
+        parsed_index = (10 * parsed_index) + (size_t)(pointer[position] - '0');
+
+    }
+
+    if ((pointer[position] != '\0') && (pointer[position] != '/'))
+    {
+        return 0;
+    }
+
+    *index = parsed_index;
+
+    return 1;
+}
+
+static cJSON *get_item_from_pointer(cJSON * const object, const char * pointer, const cJSON_bool case_sensitive)
+{
+    cJSON *current_element = object;
+
+    if (pointer == NULL)
+    {
+        return NULL;
+    }
+
+    /* follow path of the pointer */
+    while ((pointer[0] == '/') && (current_element != NULL))
+    {
+        pointer++;
+        if (cJSON_IsArray(current_element))
+        {
+            size_t index = 0;
+            if (!decode_array_index_from_pointer((const unsigned char*)pointer, &index))
+            {
+                return NULL;
+            }
+
+            current_element = get_array_item(current_element, index);
+        }
+        else if (cJSON_IsObject(current_element))
+        {
+            current_element = current_element->child;
+            /* GetObjectItem. */
+            while ((current_element != NULL) && !compare_pointers((unsigned char*)current_element->string, (const unsigned char*)pointer, case_sensitive))
+            {
+                current_element = current_element->next;
+            }
+        }
+        else
+        {
+            return NULL;
+        }
+
+        /* skip to the next path token or end of string */
+        while ((pointer[0] != '\0') && (pointer[0] != '/'))
+        {
+            pointer++;
+        }
+    }
+
+    return current_element;
+}
+
+CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointer(cJSON * const object, const char *pointer)
+{
+    return get_item_from_pointer(object, pointer, false);
+}
+
+CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointerCaseSensitive(cJSON * const object, const char *pointer)
+{
+    return get_item_from_pointer(object, pointer, true);
+}
+
+/* JSON Patch implementation. */
+static void decode_pointer_inplace(unsigned char *string)
+{
+    unsigned char *decoded_string = string;
+
+    if (string == NULL) {
+        return;
+    }
+
+    for (; *string; (void)decoded_string++, string++)
+    {
+        if (string[0] == '~')
+        {
+            if (string[1] == '0')
+            {
+                decoded_string[0] = '~';
+            }
+            else if (string[1] == '1')
+            {
+                decoded_string[1] = '/';
+            }
+            else
+            {
+                /* invalid escape sequence */
+                return;
+            }
+
+            string++;
+        }
+    }
+
+    decoded_string[0] = '\0';
+}
+
+/* non-broken cJSON_DetachItemFromArray */
+static cJSON *detach_item_from_array(cJSON *array, size_t which)
+{
+    cJSON *c = array->child;
+    while (c && (which > 0))
+    {
+        c = c->next;
+        which--;
+    }
+    if (!c)
+    {
+        /* item doesn't exist */
+        return NULL;
+    }
+    if (c->prev)
+    {
+        /* not the first element */
+        c->prev->next = c->next;
+    }
+    if (c->next)
+    {
+        c->next->prev = c->prev;
+    }
+    if (c==array->child)
+    {
+        array->child = c->next;
+    }
+    /* make sure the detached item doesn't point anywhere anymore */
+    c->prev = c->next = NULL;
+
+    return c;
+}
+
+/* detach an item at the given path */
+static cJSON *detach_path(cJSON *object, const unsigned char *path, const cJSON_bool case_sensitive)
+{
+    unsigned char *parent_pointer = NULL;
+    unsigned char *child_pointer = NULL;
+    cJSON *parent = NULL;
+    cJSON *detached_item = NULL;
+
+    /* copy path and split it in parent and child */
+    parent_pointer = cJSONUtils_strdup(path);
+    if (parent_pointer == NULL) {
+        goto cleanup;
+    }
+
+    child_pointer = (unsigned char*)strrchr((char*)parent_pointer, '/'); /* last '/' */
+    if (child_pointer == NULL)
+    {
+        goto cleanup;
+    }
+    /* split strings */
+    child_pointer[0] = '\0';
+    child_pointer++;
+
+    parent = get_item_from_pointer(object, (char*)parent_pointer, case_sensitive);
+    decode_pointer_inplace(child_pointer);
+
+    if (cJSON_IsArray(parent))
+    {
+        size_t index = 0;
+        if (!decode_array_index_from_pointer(child_pointer, &index))
+        {
+            goto cleanup;
+        }
+        detached_item = detach_item_from_array(parent, index);
+    }
+    else if (cJSON_IsObject(parent))
+    {
+        detached_item = cJSON_DetachItemFromObject(parent, (char*)child_pointer);
+    }
+    else
+    {
+        /* Couldn't find object to remove child from. */
+        goto cleanup;
+    }
+
+cleanup:
+    if (parent_pointer != NULL)
+    {
+        cJSON_free(parent_pointer);
+    }
+
+    return detached_item;
+}
+
+/* sort lists using mergesort */
+static cJSON *sort_list(cJSON *list, const cJSON_bool case_sensitive)
+{
+    cJSON *first = list;
+    cJSON *second = list;
+    cJSON *current_item = list;
+    cJSON *result = list;
+    cJSON *result_tail = NULL;
+
+    if ((list == NULL) || (list->next == NULL))
+    {
+        /* One entry is sorted already. */
+        return result;
+    }
+
+    while ((current_item != NULL) && (current_item->next != NULL) && (compare_strings((unsigned char*)current_item->string, (unsigned char*)current_item->next->string, case_sensitive) < 0))
+    {
+        /* Test for list sorted. */
+        current_item = current_item->next;
+    }
+    if ((current_item == NULL) || (current_item->next == NULL))
+    {
+        /* Leave sorted lists unmodified. */
+        return result;
+    }
+
+    /* reset pointer to the beginning */
+    current_item = list;
+    while (current_item != NULL)
+    {
+        /* Walk two pointers to find the middle. */
+        second = second->next;
+        current_item = current_item->next;
+        /* advances current_item two steps at a time */
+        if (current_item != NULL)
+        {
+            current_item = current_item->next;
+        }
+    }
+    if ((second != NULL) && (second->prev != NULL))
+    {
+        /* Split the lists */
+        second->prev->next = NULL;
+        second->prev = NULL;
+    }
+
+    /* Recursively sort the sub-lists. */
+    first = sort_list(first, case_sensitive);
+    second = sort_list(second, case_sensitive);
+    result = NULL;
+
+    /* Merge the sub-lists */
+    while ((first != NULL) && (second != NULL))
+    {
+        cJSON *smaller = NULL;
+        if (compare_strings((unsigned char*)first->string, (unsigned char*)second->string, case_sensitive) < 0)
+        {
+            smaller = first;
+        }
+        else
+        {
+            smaller = second;
+        }
+
+        if (result == NULL)
+        {
+            /* start merged list with the smaller element */
+            result_tail = smaller;
+            result = smaller;
+        }
+        else
+        {
+            /* add smaller element to the list */
+            result_tail->next = smaller;
+            smaller->prev = result_tail;
+            result_tail = smaller;
+        }
+
+        if (first == smaller)
+        {
+            first = first->next;
+        }
+        else
+        {
+            second = second->next;
+        }
+    }
+
+    if (first != NULL)
+    {
+        /* Append rest of first list. */
+        if (result == NULL)
+        {
+            return first;
+        }
+        result_tail->next = first;
+        first->prev = result_tail;
+    }
+    if (second != NULL)
+    {
+        /* Append rest of second list */
+        if (result == NULL)
+        {
+            return second;
+        }
+        result_tail->next = second;
+        second->prev = result_tail;
+    }
+
+    return result;
+}
+
+static void sort_object(cJSON * const object, const cJSON_bool case_sensitive)
+{
+    if (object == NULL)
+    {
+        return;
+    }
+    object->child = sort_list(object->child, case_sensitive);
+}
+
+static cJSON_bool compare_json(cJSON *a, cJSON *b, const cJSON_bool case_sensitive)
+{
+    if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)))
+    {
+        /* mismatched type. */
+        return false;
+    }
+    switch (a->type & 0xFF)
+    {
+        case cJSON_Number:
+            /* numeric mismatch. */
+            if ((a->valueint != b->valueint) || (!compare_double(a->valuedouble, b->valuedouble)))
+            {
+                return false;
+            }
+            else
+            {
+                return true;
+            }
+
+        case cJSON_String:
+            /* string mismatch. */
+            if (strcmp(a->valuestring, b->valuestring) != 0)
+            {
+                return false;
+            }
+            else
+            {
+                return true;
+            }
+
+        case cJSON_Array:
+            for ((void)(a = a->child), b = b->child; (a != NULL) && (b != NULL); (void)(a = a->next), b = b->next)
+            {
+                cJSON_bool identical = compare_json(a, b, case_sensitive);
+                if (!identical)
+                {
+                    return false;
+                }
+            }
+
+            /* array size mismatch? (one of both children is not NULL) */
+            if ((a != NULL) || (b != NULL))
+            {
+                return false;
+            }
+            else
+            {
+                return true;
+            }
+
+        case cJSON_Object:
+            sort_object(a, case_sensitive);
+            sort_object(b, case_sensitive);
+            for ((void)(a = a->child), b = b->child; (a != NULL) && (b != NULL); (void)(a = a->next), b = b->next)
+            {
+                cJSON_bool identical = false;
+                /* compare object keys */
+                if (compare_strings((unsigned char*)a->string, (unsigned char*)b->string, case_sensitive))
+                {
+                    /* missing member */
+                    return false;
+                }
+                identical = compare_json(a, b, case_sensitive);
+                if (!identical)
+                {
+                    return false;
+                }
+            }
+
+            /* object length mismatch (one of both children is not null) */
+            if ((a != NULL) || (b != NULL))
+            {
+                return false;
+            }
+            else
+            {
+                return true;
+            }
+
+        default:
+            break;
+    }
+
+    /* null, true or false */
+    return true;
+}
+
+/* non broken version of cJSON_InsertItemInArray */
+static cJSON_bool insert_item_in_array(cJSON *array, size_t which, cJSON *newitem)
+{
+    cJSON *child = array->child;
+    while (child && (which > 0))
+    {
+        child = child->next;
+        which--;
+    }
+    if (which > 0)
+    {
+        /* item is after the end of the array */
+        return 0;
+    }
+    if (child == NULL)
+    {
+        cJSON_AddItemToArray(array, newitem);
+        return 1;
+    }
+
+    /* insert into the linked list */
+    newitem->next = child;
+    newitem->prev = child->prev;
+    child->prev = newitem;
+
+    /* was it at the beginning */
+    if (child == array->child)
+    {
+        array->child = newitem;
+    }
+    else
+    {
+        newitem->prev->next = newitem;
+    }
+
+    return 1;
+}
+
+static cJSON *get_object_item(const cJSON * const object, const char* name, const cJSON_bool case_sensitive)
+{
+    if (case_sensitive)
+    {
+        return cJSON_GetObjectItemCaseSensitive(object, name);
+    }
+
+    return cJSON_GetObjectItem(object, name);
+}
+
+enum patch_operation { INVALID, ADD, REMOVE, REPLACE, MOVE, COPY, TEST };
+
+static enum patch_operation decode_patch_operation(const cJSON * const patch, const cJSON_bool case_sensitive)
+{
+    cJSON *operation = get_object_item(patch, "op", case_sensitive);
+    if (!cJSON_IsString(operation))
+    {
+        return INVALID;
+    }
+
+    if (strcmp(operation->valuestring, "add") == 0)
+    {
+        return ADD;
+    }
+
+    if (strcmp(operation->valuestring, "remove") == 0)
+    {
+        return REMOVE;
+    }
+
+    if (strcmp(operation->valuestring, "replace") == 0)
+    {
+        return REPLACE;
+    }
+
+    if (strcmp(operation->valuestring, "move") == 0)
+    {
+        return MOVE;
+    }
+
+    if (strcmp(operation->valuestring, "copy") == 0)
+    {
+        return COPY;
+    }
+
+    if (strcmp(operation->valuestring, "test") == 0)
+    {
+        return TEST;
+    }
+
+    return INVALID;
+}
+
+/* overwrite and existing item with another one and free resources on the way */
+static void overwrite_item(cJSON * const root, const cJSON replacement)
+{
+    if (root == NULL)
+    {
+        return;
+    }
+
+    if (root->string != NULL)
+    {
+        cJSON_free(root->string);
+    }
+    if (root->valuestring != NULL)
+    {
+        cJSON_free(root->valuestring);
+    }
+    if (root->child != NULL)
+    {
+        cJSON_Delete(root->child);
+    }
+
+    memcpy(root, &replacement, sizeof(cJSON));
+}
+
+static int apply_patch(cJSON *object, const cJSON *patch, const cJSON_bool case_sensitive)
+{
+    cJSON *path = NULL;
+    cJSON *value = NULL;
+    cJSON *parent = NULL;
+    enum patch_operation opcode = INVALID;
+    unsigned char *parent_pointer = NULL;
+    unsigned char *child_pointer = NULL;
+    int status = 0;
+
+    path = get_object_item(patch, "path", case_sensitive);
+    if (!cJSON_IsString(path))
+    {
+        /* malformed patch. */
+        status = 2;
+        goto cleanup;
+    }
+
+    opcode = decode_patch_operation(patch, case_sensitive);
+    if (opcode == INVALID)
+    {
+        status = 3;
+        goto cleanup;
+    }
+    else if (opcode == TEST)
+    {
+        /* compare value: {...} with the given path */
+        status = !compare_json(get_item_from_pointer(object, path->valuestring, case_sensitive), get_object_item(patch, "value", case_sensitive), case_sensitive);
+        goto cleanup;
+    }
+
+    /* special case for replacing the root */
+    if (path->valuestring[0] == '\0')
+    {
+        if (opcode == REMOVE)
+        {
+            static const cJSON invalid = { NULL, NULL, NULL, cJSON_Invalid, NULL, 0, 0, NULL};
+
+            overwrite_item(object, invalid);
+
+            status = 0;
+            goto cleanup;
+        }
+
+        if ((opcode == REPLACE) || (opcode == ADD))
+        {
+            value = get_object_item(patch, "value", case_sensitive);
+            if (value == NULL)
+            {
+                /* missing "value" for add/replace. */
+                status = 7;
+                goto cleanup;
+            }
+
+            value = cJSON_Duplicate(value, 1);
+            if (value == NULL)
+            {
+                /* out of memory for add/replace. */
+                status = 8;
+                goto cleanup;
+            }
+
+            overwrite_item(object, *value);
+
+            /* delete the duplicated value */
+            cJSON_free(value);
+            value = NULL;
+
+            /* the string "value" isn't needed */
+            if (object->string != NULL)
+            {
+                cJSON_free(object->string);
+                object->string = NULL;
+            }
+
+            status = 0;
+            goto cleanup;
+        }
+    }
+
+    if ((opcode == REMOVE) || (opcode == REPLACE))
+    {
+        /* Get rid of old. */
+        cJSON *old_item = detach_path(object, (unsigned char*)path->valuestring, case_sensitive);
+        if (old_item == NULL)
+        {
+            status = 13;
+            goto cleanup;
+        }
+        cJSON_Delete(old_item);
+        if (opcode == REMOVE)
+        {
+            /* For Remove, this job is done. */
+            status = 0;
+            goto cleanup;
+        }
+    }
+
+    /* Copy/Move uses "from". */
+    if ((opcode == MOVE) || (opcode == COPY))
+    {
+        cJSON *from = get_object_item(patch, "from", case_sensitive);
+        if (from == NULL)
+        {
+            /* missing "from" for copy/move. */
+            status = 4;
+            goto cleanup;
+        }
+
+        if (opcode == MOVE)
+        {
+            value = detach_path(object, (unsigned char*)from->valuestring, case_sensitive);
+        }
+        if (opcode == COPY)
+        {
+            value = get_item_from_pointer(object, from->valuestring, case_sensitive);
+        }
+        if (value == NULL)
+        {
+            /* missing "from" for copy/move. */
+            status = 5;
+            goto cleanup;
+        }
+        if (opcode == COPY)
+        {
+            value = cJSON_Duplicate(value, 1);
+        }
+        if (value == NULL)
+        {
+            /* out of memory for copy/move. */
+            status = 6;
+            goto cleanup;
+        }
+    }
+    else /* Add/Replace uses "value". */
+    {
+        value = get_object_item(patch, "value", case_sensitive);
+        if (value == NULL)
+        {
+            /* missing "value" for add/replace. */
+            status = 7;
+            goto cleanup;
+        }
+        value = cJSON_Duplicate(value, 1);
+        if (value == NULL)
+        {
+            /* out of memory for add/replace. */
+            status = 8;
+            goto cleanup;
+        }
+    }
+
+    /* Now, just add "value" to "path". */
+
+    /* split pointer in parent and child */
+    parent_pointer = cJSONUtils_strdup((unsigned char*)path->valuestring);
+    child_pointer = (unsigned char*)strrchr((char*)parent_pointer, '/');
+    if (child_pointer != NULL)
+    {
+        child_pointer[0] = '\0';
+        child_pointer++;
+    }
+    parent = get_item_from_pointer(object, (char*)parent_pointer, case_sensitive);
+    decode_pointer_inplace(child_pointer);
+
+    /* add, remove, replace, move, copy, test. */
+    if ((parent == NULL) || (child_pointer == NULL))
+    {
+        /* Couldn't find object to add to. */
+        status = 9;
+        goto cleanup;
+    }
+    else if (cJSON_IsArray(parent))
+    {
+        if (strcmp((char*)child_pointer, "-") == 0)
+        {
+            cJSON_AddItemToArray(parent, value);
+            value = NULL;
+        }
+        else
+        {
+            size_t index = 0;
+            if (!decode_array_index_from_pointer(child_pointer, &index))
+            {
+                status = 11;
+                goto cleanup;
+            }
+
+            if (!insert_item_in_array(parent, index, value))
+            {
+                status = 10;
+                goto cleanup;
+            }
+            value = NULL;
+        }
+    }
+    else if (cJSON_IsObject(parent))
+    {
+        if (case_sensitive)
+        {
+            cJSON_DeleteItemFromObjectCaseSensitive(parent, (char*)child_pointer);
+        }
+        else
+        {
+            cJSON_DeleteItemFromObject(parent, (char*)child_pointer);
+        }
+        cJSON_AddItemToObject(parent, (char*)child_pointer, value);
+        value = NULL;
+    }
+    else /* parent is not an object */
+    {
+        /* Couldn't find object to add to. */
+        status = 9;
+        goto cleanup;
+    }
+
+cleanup:
+    if (value != NULL)
+    {
+        cJSON_Delete(value);
+    }
+    if (parent_pointer != NULL)
+    {
+        cJSON_free(parent_pointer);
+    }
+
+    return status;
+}
+
+CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON * const object, const cJSON * const patches)
+{
+    const cJSON *current_patch = NULL;
+    int status = 0;
+
+    if (!cJSON_IsArray(patches))
+    {
+        /* malformed patches. */
+        return 1;
+    }
+
+    if (patches != NULL)
+    {
+        current_patch = patches->child;
+    }
+
+    while (current_patch != NULL)
+    {
+        status = apply_patch(object, current_patch, false);
+        if (status != 0)
+        {
+            return status;
+        }
+        current_patch = current_patch->next;
+    }
+
+    return 0;
+}
+
+CJSON_PUBLIC(int) cJSONUtils_ApplyPatchesCaseSensitive(cJSON * const object, const cJSON * const patches)
+{
+    const cJSON *current_patch = NULL;
+    int status = 0;
+
+    if (!cJSON_IsArray(patches))
+    {
+        /* malformed patches. */
+        return 1;
+    }
+
+    if (patches != NULL)
+    {
+        current_patch = patches->child;
+    }
+
+    while (current_patch != NULL)
+    {
+        status = apply_patch(object, current_patch, true);
+        if (status != 0)
+        {
+            return status;
+        }
+        current_patch = current_patch->next;
+    }
+
+    return 0;
+}
+
+static void compose_patch(cJSON * const patches, const unsigned char * const operation, const unsigned char * const path, const unsigned char *suffix, const cJSON * const value)
+{
+    cJSON *patch = NULL;
+
+    if ((patches == NULL) || (operation == NULL) || (path == NULL))
+    {
+        return;
+    }
+
+    patch = cJSON_CreateObject();
+    if (patch == NULL)
+    {
+        return;
+    }
+    cJSON_AddItemToObject(patch, "op", cJSON_CreateString((const char*)operation));
+
+    if (suffix == NULL)
+    {
+        cJSON_AddItemToObject(patch, "path", cJSON_CreateString((const char*)path));
+    }
+    else
+    {
+        size_t suffix_length = pointer_encoded_length(suffix);
+        size_t path_length = strlen((const char*)path);
+        unsigned char *full_path = (unsigned char*)cJSON_malloc(path_length + suffix_length + sizeof("/"));
+
+        sprintf((char*)full_path, "%s/", (const char*)path);
+        encode_string_as_pointer(full_path + path_length + 1, suffix);
+
+        cJSON_AddItemToObject(patch, "path", cJSON_CreateString((const char*)full_path));
+        cJSON_free(full_path);
+    }
+
+    if (value != NULL)
+    {
+        cJSON_AddItemToObject(patch, "value", cJSON_Duplicate(value, 1));
+    }
+    cJSON_AddItemToArray(patches, patch);
+}
+
+CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON * const array, const char * const operation, const char * const path, const cJSON * const value)
+{
+    compose_patch(array, (const unsigned char*)operation, (const unsigned char*)path, NULL, value);
+}
+
+static void create_patches(cJSON * const patches, const unsigned char * const path, cJSON * const from, cJSON * const to, const cJSON_bool case_sensitive)
+{
+    if ((from == NULL) || (to == NULL))
+    {
+        return;
+    }
+
+    if ((from->type & 0xFF) != (to->type & 0xFF))
+    {
+        compose_patch(patches, (const unsigned char*)"replace", path, 0, to);
+        return;
+    }
+
+    switch (from->type & 0xFF)
+    {
+        case cJSON_Number:
+            if ((from->valueint != to->valueint) || !compare_double(from->valuedouble, to->valuedouble))
+            {
+                compose_patch(patches, (const unsigned char*)"replace", path, NULL, to);
+            }
+            return;
+
+        case cJSON_String:
+            if (strcmp(from->valuestring, to->valuestring) != 0)
+            {
+                compose_patch(patches, (const unsigned char*)"replace", path, NULL, to);
+            }
+            return;
+
+        case cJSON_Array:
+        {
+            size_t index = 0;
+            cJSON *from_child = from->child;
+            cJSON *to_child = to->child;
+            unsigned char *new_path = (unsigned char*)cJSON_malloc(strlen((const char*)path) + 20 + sizeof("/")); /* Allow space for 64bit int. log10(2^64) = 20 */
+
+            /* generate patches for all array elements that exist in both "from" and "to" */
+            for (index = 0; (from_child != NULL) && (to_child != NULL); (void)(from_child = from_child->next), (void)(to_child = to_child->next), index++)
+            {
+                /* check if conversion to unsigned long is valid
+                 * This should be eliminated at compile time by dead code elimination
+                 * if size_t is an alias of unsigned long, or if it is bigger */
+                if (index > ULONG_MAX)
+                {
+                    cJSON_free(new_path);
+                    return;
+                }
+                sprintf((char*)new_path, "%s/%lu", path, (unsigned long)index); /* path of the current array element */
+                create_patches(patches, new_path, from_child, to_child, case_sensitive);
+            }
+
+            /* remove leftover elements from 'from' that are not in 'to' */
+            for (; (from_child != NULL); (void)(from_child = from_child->next))
+            {
+                /* check if conversion to unsigned long is valid
+                 * This should be eliminated at compile time by dead code elimination
+                 * if size_t is an alias of unsigned long, or if it is bigger */
+                if (index > ULONG_MAX)
+                {
+                    cJSON_free(new_path);
+                    return;
+                }
+                sprintf((char*)new_path, "%lu", (unsigned long)index);
+                compose_patch(patches, (const unsigned char*)"remove", path, new_path, NULL);
+            }
+            /* add new elements in 'to' that were not in 'from' */
+            for (; (to_child != NULL); (void)(to_child = to_child->next), index++)
+            {
+                compose_patch(patches, (const unsigned char*)"add", path, (const unsigned char*)"-", to_child);
+            }
+            cJSON_free(new_path);
+            return;
+        }
+
+        case cJSON_Object:
+        {
+            cJSON *from_child = NULL;
+            cJSON *to_child = NULL;
+            sort_object(from, case_sensitive);
+            sort_object(to, case_sensitive);
+
+            from_child = from->child;
+            to_child = to->child;
+            /* for all object values in the object with more of them */
+            while ((from_child != NULL) || (to_child != NULL))
+            {
+                int diff;
+                if (from_child == NULL)
+                {
+                    diff = 1;
+                }
+                else if (to_child == NULL)
+                {
+                    diff = -1;
+                }
+                else
+                {
+                    diff = compare_strings((unsigned char*)from_child->string, (unsigned char*)to_child->string, case_sensitive);
+                }
+
+                if (diff == 0)
+                {
+                    /* both object keys are the same */
+                    size_t path_length = strlen((const char*)path);
+                    size_t from_child_name_length = pointer_encoded_length((unsigned char*)from_child->string);
+                    unsigned char *new_path = (unsigned char*)cJSON_malloc(path_length + from_child_name_length + sizeof("/"));
+
+                    sprintf((char*)new_path, "%s/", path);
+                    encode_string_as_pointer(new_path + path_length + 1, (unsigned char*)from_child->string);
+
+                    /* create a patch for the element */
+                    create_patches(patches, new_path, from_child, to_child, case_sensitive);
+                    cJSON_free(new_path);
+
+                    from_child = from_child->next;
+                    to_child = to_child->next;
+                }
+                else if (diff < 0)
+                {
+                    /* object element doesn't exist in 'to' --> remove it */
+                    compose_patch(patches, (const unsigned char*)"remove", path, (unsigned char*)from_child->string, NULL);
+
+                    from_child = from_child->next;
+                }
+                else
+                {
+                    /* object element doesn't exist in 'from' --> add it */
+                    compose_patch(patches, (const unsigned char*)"add", path, (unsigned char*)to_child->string, to_child);
+
+                    to_child = to_child->next;
+                }
+            }
+            return;
+        }
+
+        default:
+            break;
+    }
+}
+
+CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON * const from, cJSON * const to)
+{
+    cJSON *patches = NULL;
+
+    if ((from == NULL) || (to == NULL))
+    {
+        return NULL;
+    }
+
+    patches = cJSON_CreateArray();
+    create_patches(patches, (const unsigned char*)"", from, to, false);
+
+    return patches;
+}
+
+CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatchesCaseSensitive(cJSON * const from, cJSON * const to)
+{
+    cJSON *patches = NULL;
+
+    if ((from == NULL) || (to == NULL))
+    {
+        return NULL;
+    }
+
+    patches = cJSON_CreateArray();
+    create_patches(patches, (const unsigned char*)"", from, to, true);
+
+    return patches;
+}
+
+CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON * const object)
+{
+    sort_object(object, false);
+}
+
+CJSON_PUBLIC(void) cJSONUtils_SortObjectCaseSensitive(cJSON * const object)
+{
+    sort_object(object, true);
+}
+
+static cJSON *merge_patch(cJSON *target, const cJSON * const patch, const cJSON_bool case_sensitive)
+{
+    cJSON *patch_child = NULL;
+
+    if (!cJSON_IsObject(patch))
+    {
+        /* scalar value, array or NULL, just duplicate */
+        cJSON_Delete(target);
+        return cJSON_Duplicate(patch, 1);
+    }
+
+    if (!cJSON_IsObject(target))
+    {
+        cJSON_Delete(target);
+        target = cJSON_CreateObject();
+    }
+
+    patch_child = patch->child;
+    while (patch_child != NULL)
+    {
+        if (cJSON_IsNull(patch_child))
+        {
+            /* NULL is the indicator to remove a value, see RFC7396 */
+            if (case_sensitive)
+            {
+                cJSON_DeleteItemFromObjectCaseSensitive(target, patch_child->string);
+            }
+            else
+            {
+                cJSON_DeleteItemFromObject(target, patch_child->string);
+            }
+        }
+        else
+        {
+            cJSON *replace_me = NULL;
+            cJSON *replacement = NULL;
+
+            if (case_sensitive)
+            {
+                replace_me = cJSON_DetachItemFromObjectCaseSensitive(target, patch_child->string);
+            }
+            else
+            {
+                replace_me = cJSON_DetachItemFromObject(target, patch_child->string);
+            }
+
+            replacement = merge_patch(replace_me, patch_child, case_sensitive);
+            if (replacement == NULL)
+            {
+                return NULL;
+            }
+
+            cJSON_AddItemToObject(target, patch_child->string, replacement);
+        }
+        patch_child = patch_child->next;
+    }
+    return target;
+}
+
+CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, const cJSON * const patch)
+{
+    return merge_patch(target, patch, false);
+}
+
+CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatchCaseSensitive(cJSON *target, const cJSON * const patch)
+{
+    return merge_patch(target, patch, true);
+}
+
+static cJSON *generate_merge_patch(cJSON * const from, cJSON * const to, const cJSON_bool case_sensitive)
+{
+    cJSON *from_child = NULL;
+    cJSON *to_child = NULL;
+    cJSON *patch = NULL;
+    if (to == NULL)
+    {
+        /* patch to delete everything */
+        return cJSON_CreateNull();
+    }
+    if (!cJSON_IsObject(to) || !cJSON_IsObject(from))
+    {
+        return cJSON_Duplicate(to, 1);
+    }
+
+    sort_object(from, case_sensitive);
+    sort_object(to, case_sensitive);
+
+    from_child = from->child;
+    to_child = to->child;
+    patch = cJSON_CreateObject();
+    while (from_child || to_child)
+    {
+        int diff;
+        if (from_child != NULL)
+        {
+            if (to_child != NULL)
+            {
+                diff = strcmp(from_child->string, to_child->string);
+            }
+            else
+            {
+                diff = -1;
+            }
+        }
+        else
+        {
+            diff = 1;
+        }
+
+        if (diff < 0)
+        {
+            /* from has a value that to doesn't have -> remove */
+            cJSON_AddItemToObject(patch, from_child->string, cJSON_CreateNull());
+
+            from_child = from_child->next;
+        }
+        else if (diff > 0)
+        {
+            /* to has a value that from doesn't have -> add to patch */
+            cJSON_AddItemToObject(patch, to_child->string, cJSON_Duplicate(to_child, 1));
+
+            to_child = to_child->next;
+        }
+        else
+        {
+            /* object key exists in both objects */
+            if (!compare_json(from_child, to_child, case_sensitive))
+            {
+                /* not identical --> generate a patch */
+                cJSON_AddItemToObject(patch, to_child->string, cJSONUtils_GenerateMergePatch(from_child, to_child));
+            }
+
+            /* next key in the object */
+            from_child = from_child->next;
+            to_child = to_child->next;
+        }
+    }
+    if (patch->child == NULL)
+    {
+        /* no patch generated */
+        cJSON_Delete(patch);
+        return NULL;
+    }
+
+    return patch;
+}
+
+CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON * const from, cJSON * const to)
+{
+    return generate_merge_patch(from, to, false);
+}
+
+CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatchCaseSensitive(cJSON * const from, cJSON * const to)
+{
+    return generate_merge_patch(from, to, true);
+}

+ 9 - 0
components/fota/CMakeLists.txt

@@ -0,0 +1,9 @@
+file(GLOB_RECURSE LIBSOURCE "${COMPONENTS_DIR}/fota/*.c")
+
+ADD_DEFINITIONS(-DCONFIG_FOTA_USE_HTTPC=1)
+
+include_directories(${COMPONENTS_DIR}/fota/include)
+include_directories(${COMPONENTS_DIR}/fota/http)
+include_directories(${COMPONENTS_DIR}/fota)
+
+add_library(fota STATIC ${LIBSOURCE})

+ 380 - 0
components/fota/README.md

@@ -0,0 +1,380 @@
+# 空中下载软件服务(FOTA)
+
+## 概述
+
+YoC定义了一个FOTA任务用于维护FOTA新版本检测、状态机转换维护和下载控制,抽象了netio用于固件包的下载保存。即: 
+
+1. 新版本检测
+2. 新版本固件包(差分、全量)下载
+
+### FOTA流程与状态
+
+FOTA模块设计了4个状态转化: 
+
+- FOTA_INIT
+
+- FOTA_DOWNLOAD
+
+- FOTA_STOP
+
+- FOTA_FINISH
+
+用户启动FOTA后状态为FOTA_INIT,当检测到新版本时,转入FOTA_DOWNLOAD状态,并开始下载固件包。下载失败之后会重新下载或者复位下载流程,取决于重试次数(retry count)。 用户可以通过自定义的方式(添加fota_event_cb回调函数)来根据FOTA在各个阶段报出的事件来执行相应的动作(fota event层),事件回调函数返回值代表不同的含义: 
+
+- 0: 处理完用户事件后继续原先的默认流程;
+
+- 非零: 处理完用户事件后不会继续原先的默认流程,此时用户需要定义自己的流程去向。
+
+用户定义代码示例如下:
+
+```c
+static int fota_event_cb(void *arg, fota_event_e event) //return 0: still do the default handle      not zero: only do the user handle
+{
+    fota_t *fota = (fota_t *)arg;
+    switch (event) {
+        case FOTA_EVENT_VERSION:
+            LOGD(TAG, "FOTA VERSION :%x", fota->status);
+            break;
+
+        case FOTA_EVENT_START:
+            LOGD(TAG, "FOTA START :%x", fota->status);
+            break;
+
+        case FOTA_EVENT_FAIL:
+            LOGD(TAG, "FOTA FAIL :%x", fota->status);
+            break;
+
+        case FOTA_EVENT_FINISH:
+            LOGD(TAG, "FOTA FINISH :%x", fota->status);
+            break;
+
+        default:
+            break;
+    }
+    return 0;
+}
+```
+
+### FOTA 平台对接
+
+FOTA框架支持接入不同的FOTA云平台。如: COP、COAP 不同的FOTA平台需要通过对接fota_cls,实现该结构体的操作。
+
+```c
+typedef struct fota_cls {
+const char *name;
+int (*init)(void);
+int (*version_check)(fota_info_t *info);
+int (*finish)(void);
+int (*fail)(void);
+} fota_cls_t;
+```
+
+通过`int fota_register(const fota_cls_t *cls)`接口注册。
+
+### FOTA 下载
+
+FOTA框架对FOTA下载和存放进行了抽象,形成netio 类,用于可通过netio类来对接不同的下载和存放方式,例如对接ftp下载方式,对接新的 uart 存放方式等。 通过实现结构体 netio_cls 来对接
+
+```c
+struct netio_cls {
+const char *name;
+int (*open)(netio_t *io, const char *path);
+int (*close)(netio_t *io);
+int (*read)(netio_t *io, uint8_t *buffer, int length, int timeoutms);
+int (*write)(netio_t *io, uint8_t *buffer, int length, int timeoutms);
+int (*remove)(netio_t *io);
+int (*seek)(netio_t *io, size_t offset, int whence);
+};
+```
+
+用户需要实现 netio_cls 接口的 open、close、read、write、remove、seek接口,并通过`netio_register`接口注册进FOTA。
+
+### FOTA 升级
+
+固件下载完毕之后会设备会重启进入固件升级模式,等待升级完毕之后设备正常启动。一个完整的FOTA流程结束。
+
+## 组件安装
+
+```bash
+yoc init
+yoc install fota
+```
+
+## 配置
+
+无。
+
+## 接口列表
+
+| 函数                | 说明                                                         |
+| :-------------------| :----------------------------------------------------------- |
+| fota_upgrade        | 开始下载镜像                                                 |
+| fota_do_check       | 强制检测版本                                                 |
+| fota_start          | 创建FOTA服务                                                 |
+| fota_stop           | 停止FOTA功能,退出FOTA服务                                   |
+| fota_finish         | FOTA完成,调用用户实现的finish接口                           |
+| fota_fail           | FOTA失败,调用用户实现的fail接口,并释放FOTA资源,但不释放FOTA句柄 |
+| fota_config         | 配置FOTA参数                                                 |
+| fota_set_auto_check | 设置是否自动不断检测服务器固件版本并升级的功能               |
+| fota_get_auto_check | 获取是否自动检测判断                                         |
+| fota_get_status     | 获取升级状态                                                 |
+| fota_open           | fota初始化                                                   |
+| fota_close          | 关闭FOTA功能,释放所有资源                                   |
+| fota_register_cop   | 注册为cop平台,即从cop平台下载固件                           |
+| fota_register_coap  | 注册为coap平台,即从coap平台下载固件                         |
+| fota_register       | 注册平台接口                                                 |
+
+
+
+## 接口详细说明
+
+### fota_upgrade
+
+`int fota_upgrade(fota_t *fota);`
+
+- 功能描述:
+  - 开始下载镜像。
+
+- 参数:
+  - `fota`: fota 句柄。
+
+- 返回值:
+  - 0: 下载镜像成功。
+  - 1: 下载镜像失败。
+
+### fota_do_check
+
+`void fota_do_check(fota_t *fota);`
+
+- 功能描述:
+  - 强制检测版本。
+
+- 参数:
+  - `fota`: fota 句柄。
+
+- 返回值:
+  - 0: 检测成功。
+  - 1: 检测失败。
+
+### fota_start
+
+`int fota_start(fota_t *fota);`
+
+- 功能描述:
+  - 创建FOTA服务。
+
+- 参数:
+  - `fota`:fota 句柄。
+
+- 返回值:
+  - 0: 创建FOTA服务成功。
+  - 1: 创建FOTA服务失败。
+
+### fota_stop
+
+`int fota_stop(fota_t *fota);`
+
+- 功能描述:
+  - 停止FOTA功能,退出FOTA服务。
+
+- 参数:
+  - `fota`: fota 句柄。
+
+- 返回值:
+  - 0: 成功。
+  - 1: 失败。
+
+### fota_finish
+
+`void fota_finish(fota_t *fota);`
+
+- 功能描述:
+  - FOTA完成,调用用户实现的finish接口。
+
+- 参数:
+  - `fota`: fota 句柄。
+
+- 返回值:无。
+
+### fota_fail
+
+`void fota_fail(fota_t *fota);`
+
+- 功能描述:
+  - FOTA失败,调用用户实现的fail接口,并释放FOTA资源,但不释放FOTA句柄。
+  
+- 参数:
+  - `fota`: fota 句柄。
+  
+- 返回值:无。
+
+### fota_config
+
+`void fota_config(fota_t *fota, fota_config_t *config);`
+
+- 功能描述:
+  - 配置FOTA参数。
+  
+- 参数:
+  - `fota`: fota 句柄。
+  - `config`: 配置数据。
+  
+- 返回值:无
+
+### fota_set_auto_check
+
+`void fota_set_auto_check(fota_t *fota, int enable);`
+
+- 功能描述:
+  - 设置是否自动不断检测服务器固件版本并升级的功能。
+  
+- 参数:
+  - `fota`: fota 句柄。
+  - `enable`: 使能标记。
+  
+- 返回值:无。
+
+### fota_get_auto_check
+
+`int fota_get_auto_check(fota_t *fota);`
+
+- 功能描述:
+  -   获取是否自动检测判断。
+  
+- 参数:
+  - `fota`: fota 句柄。
+  
+- 返回值:
+  - 0: 表示不自动检测。
+  - 1: 表示自动检测。
+
+### fota_get_status
+
+`fota_status_e fota_get_status(fota_t *fota);`
+
+- 功能描述:
+  -   获取升级状态。
+  
+- 参数:
+  - `fota`: fota 句柄。
+  
+- 返回值:
+  - fota_status_e类型。
+
+### fota_open
+
+`fota_t *fota_open(const char *fota_name, const char *dst, fota_event_cb_t event_cb);`
+
+- 功能描述:
+  -   fota初始化。
+  
+- 参数:
+  - `fota_name`:FOTA平台名字,比如"cop"。
+  - `dst`:差分包存储url。
+  - `event_cb`:用户事件回调。
+  
+- 返回值:
+  - 非空: fota句柄。
+  - NULL: 失败。
+
+### fota_close
+
+`int fota_close(fota_t *fota);`
+
+- 功能描述:
+  - 关闭FOTA功能,释放所有资源。
+  
+- 参数:
+  - `fota`:fota 句柄。
+  
+- 返回值:
+  - 0: 关闭FOTA成功。
+  - 1: 关闭FOTA失败。
+
+### fota_register_cop
+
+`int fota_register_cop(void);`
+
+- 功能描述:
+  - 注册为cop平台,即从cop平台下载固件。
+  
+- 参数:
+  - 无。
+  
+- 返回值:
+  - 0: 注册cop成功。
+  - 1: 注册cop失败。
+
+### fota_register_coap
+
+`int fota_register_coap(void);`
+
+- 功能描述:
+  - 注册为coap平台,即从coap平台下载固件。
+  
+- 参数:
+  - 无。
+  
+- 返回值:
+  - 0: 注册cop成功。
+  - 1: 注册cop失败。
+
+### fota_register
+
+`int fota_register(const fota_cls_t *cls);`
+
+- 功能描述:
+  -   注册平台接口。
+  
+- 参数:
+  - `cls`:不同平台实现的接口集合,具体实现接口见`fota_cls_t`。
+  
+- 返回值:
+  - 0: 注册平台接口成功。
+  - 1: 注册平台接口失败。
+
+## 示例
+
+### FOTA初始化示例
+
+cop平台FOTA模块的初始化过程: 
+
+```c
+void app_fota_init(void)
+{
+    int ret;
+    LOGI(TAG, "======> %s\r\n", aos_get_app_version());
+    int fota_en = 1;
+    ret = aos_kv_getint(KV_FOTA_ENABLED, &fota_en);
+    if (ret == 0 && fota_en == 0) {
+        return;
+    }
+
+    fota_register_cop();
+    netio_register_http();
+    netio_register_flash();
+    g_fota_handle = fota_open("cop", "flash://misc", fota_event_cb);
+    g_fota_handle->auto_check_en = 1;
+    g_fota_handle->sleep_time = 60000;
+    g_fota_handle->timeoutms = 10000;
+    g_fota_handle->retry_count = 0;
+    fota_start(g_fota_handle);
+    }
+```
+
+## 诊断错误码
+
+无。
+
+## 运行资源
+
+无。
+
+## 依赖资源
+
+- csi
+- aos
+
+## 组件参考
+
+无。

+ 582 - 0
components/fota/fota/fota.c

@@ -0,0 +1,582 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <aos/list.h>
+#include <aos/kernel.h>
+#include <aos/kv.h>
+#include <yoc/netio.h>
+#include <yoc/fota.h>
+#include <ulog/ulog.h>
+
+#define TAG "fota"
+
+typedef struct fota_netio_list {
+    slist_t next;
+    const fota_cls_t *cls;
+} fota_cls_node_t;
+
+static AOS_SLIST_HEAD(fota_cls_list);
+
+int fota_register(const fota_cls_t *cls)
+{
+    fota_cls_node_t *node = aos_malloc(sizeof(fota_cls_node_t));
+
+    if (node) {
+        node->cls = cls;
+        slist_add_tail(&node->next, &fota_cls_list);
+        return 0;
+    }
+
+    return -1;
+}
+
+fota_t *fota_open(const char *fota_name, const char *dst, fota_event_cb_t event_cb)
+{
+    fota_cls_node_t *node;
+    fota_t *fota = NULL;
+    fota_config_t config = { 3000, 3000, 0, 30000, 0 };
+
+    if (!(fota_name && dst)) {
+        LOGE(TAG, "fota open e.");
+        return NULL;
+    }
+
+    slist_for_each_entry(&fota_cls_list, node, fota_cls_node_t, next) {
+        if (strcmp(node->cls->name, fota_name) == 0) {
+            fota = aos_zalloc(sizeof(fota_t));
+            if (fota == NULL) {
+                LOGE(TAG, "malloc fota failed.");
+                return NULL;
+            }
+            fota->to_path = strdup(dst);
+            fota->cls = node->cls;
+            fota->event_cb = event_cb;
+            memcpy(&fota->config, &config, sizeof(fota_config_t));
+            if (fota->cls->init)
+                fota->cls->init(&fota->info);
+
+            LOGD(TAG, "fota: 0x%x path:%s", fota, fota->to_path);
+            break;
+        }
+    }
+
+    if (fota == NULL) {
+        LOGE(TAG, "fota open e. %s cls not found", fota_name);
+        return NULL;
+    }
+
+    aos_sem_new(&fota->sem, 0);
+    aos_sem_new(&fota->do_check_event, 0);
+    aos_sem_new(&fota->sem_download, 0);
+    return fota;
+}
+
+static int fota_version_check(fota_t *fota, fota_info_t *info) {
+    if (fota && fota->cls && fota->cls->version_check) {
+        int ret = fota->cls->version_check(info);
+        if (ret != 0) {
+            return -1;
+        }
+        if (fota->from_path == NULL) {
+            fota->from_path = strdup(info->fota_url);
+        } else {
+            aos_free(fota->from_path);
+            fota->from_path = strdup(info->fota_url);
+        }
+        LOGD(TAG, "get image url: %s", fota->from_path);
+        return ret;
+    }
+
+    return -1;
+}
+
+static void fota_finish(fota_t *fota, fota_info_t *info)
+{
+    if (fota->cls->finish)
+        fota->cls->finish(info);
+}
+
+static void fota_fail(fota_t *fota, fota_info_t *info)
+{
+    if (fota->cls->fail)
+        fota->cls->fail(info);
+}
+
+static int fota_prepare(fota_t *fota)
+{
+    if (!(fota->from_path && fota->to_path)) {
+        LOGE(TAG, "fota->from_path or fota->to_path is NULL");
+        return -EINVAL;
+    }
+    LOGD(TAG, "###fota->from_path:%s, fota->to_path:%s", fota->from_path, fota->to_path);
+    fota->buffer = aos_malloc(CONFIG_FOTA_BUFFER_SIZE);
+    fota->from = netio_open(fota->from_path);
+    fota->to = netio_open(fota->to_path);
+
+    if (fota->buffer == NULL || fota->from == NULL || fota->to == NULL) {
+        if (fota->buffer == NULL) {
+            LOGD(TAG, "fota->buffer e");
+        } else if (fota->from == NULL) {
+            LOGD(TAG, "fota->from e");
+        } else if (fota->to == NULL) {
+            LOGD(TAG, "fota->to e");
+        }
+        goto error;
+    }
+
+    if (aos_kv_getint(KV_FOTA_OFFSET, &fota->offset) < 0) {
+        if (aos_kv_setint(KV_FOTA_OFFSET, 0) < 0) {
+            goto error;
+        }
+        fota->offset = 0;
+    }
+
+    LOGI(TAG, "FOTA seek %d", fota->offset);
+
+    if (netio_seek(fota->from, fota->offset, SEEK_SET) != 0) {
+        LOGD(TAG, "from seek error");
+        goto error;
+    }
+
+    if (netio_seek(fota->to, fota->offset, SEEK_SET) != 0) {
+        LOGD(TAG, "to seek error");
+        goto error;
+    }
+
+    fota->status = FOTA_DOWNLOAD;
+    fota->total_size = fota->from->size;
+    LOGD(TAG, "fota prepare ok.");
+    return 0;
+
+error:
+    if (fota->buffer) {
+        aos_free(fota->buffer);
+        fota->buffer = NULL;
+    }
+    if (fota->from) {
+        netio_close(fota->from);
+        fota->from = NULL;
+    }
+    if (fota->to) {
+        netio_close(fota->to);
+        fota->to = NULL;
+    }
+
+    return -1;
+}
+
+static void fota_release(fota_t *fota)
+{
+    LOGD(TAG, "%s,%d", __func__, __LINE__);
+    if (fota->buffer) {
+        aos_free(fota->buffer);
+        fota->buffer = NULL;
+    }
+
+    if (fota->from) {
+        netio_close(fota->from);
+        fota->from = NULL;
+    }
+
+    if (fota->to) {
+        netio_close(fota->to);
+        fota->to = NULL;
+    }
+}
+
+static void fota_task(void *arg)
+{
+    fota_t *fota = (fota_t *)arg;
+    int retry = fota->config.retry_count;
+
+    LOGD(TAG, "fota_task start: %s", fota->to_path);
+    while (!fota->quit) {
+        if (fota->status == FOTA_INIT) {
+            LOGD(TAG, "fota_task FOTA_INIT! wait......");
+            aos_sem_wait(&fota->do_check_event, -1);
+            if (fota->quit) {
+                break;
+            }
+            if (fota->status == FOTA_DOWNLOAD) {
+                continue;
+            }
+            retry = fota->config.retry_count;
+            if (fota->event_cb) {
+                fota->error_code = FOTA_ERROR_NULL;
+                fota->event_cb(arg, FOTA_EVENT_START);
+            }
+
+            if (fota_version_check(fota, &fota->info) == 0) {
+                if (fota->event_cb) {
+                    fota->error_code = FOTA_ERROR_NULL;
+                    fota->event_cb(arg, FOTA_EVENT_VERSION);
+                }
+                if (fota->config.auto_check_en > 0) {
+                    if (fota_prepare(fota) < 0) {
+                        LOGE(TAG, "fota_prepare failed");
+                        if (fota->event_cb) {
+                            fota->error_code = FOTA_ERROR_PREPARE;
+                            fota->event_cb(arg, FOTA_EVENT_VERSION);
+                        }
+                    }
+                }
+            } else {
+                if (fota->event_cb) {
+                    fota->error_code = FOTA_ERROR_VERSION_CHECK;
+                    fota->event_cb(arg, FOTA_EVENT_VERSION);
+                }
+            }
+            if (fota->config.auto_check_en > 0) {
+                if (fota->status == FOTA_DOWNLOAD) {
+                    aos_sem_signal(&fota->sem_download);
+                    continue;
+                }
+                aos_msleep(fota->config.sleep_time);
+                aos_sem_signal(&fota->do_check_event);
+            }
+        } else if (fota->status == FOTA_DOWNLOAD) {
+            LOGD(TAG, "fota_task download! wait......");
+            aos_sem_wait(&fota->sem_download, -1);
+            if (fota->quit) {
+                break;
+            }
+
+            int size = netio_read(fota->from, fota->buffer, CONFIG_FOTA_BUFFER_SIZE, fota->config.read_timeoutms);
+            fota->total_size = fota->from->size;
+            LOGD(TAG, "fota_task FOTA_DOWNLOAD! total:%d offset:%d", fota->from->size, fota->to->offset);
+            LOGD(TAG, "##read: %d", size);
+            if (size < 0) {
+                LOGD(TAG, "read size < 0 %d", size);
+                if (size == -2) {
+                    LOGW(TAG, "reconnect again");
+                    aos_sem_signal(&fota->sem_download);
+                    continue;
+                }
+                if (fota->event_cb) {
+                    fota->error_code = FOTA_ERROR_NET_READ;
+                    fota->event_cb(arg, FOTA_EVENT_PROGRESS);
+                }
+                fota->status = FOTA_ABORT;
+                LOGD(TAG, "fota abort");
+                continue;
+            } else if (size == 0) {
+                // download finish
+                LOGD(TAG, "read size 0.");
+                if (fota->event_cb) {
+                    fota->error_code = FOTA_ERROR_NULL;
+                    fota->event_cb(arg, FOTA_EVENT_VERIFY);
+                }
+                int verify = fota_data_verify();
+                fota_finish(fota, &fota->info);
+                fota_release(fota);
+                aos_kv_del(KV_FOTA_OFFSET);
+                if (verify != 0) {
+                    LOGE(TAG, "fota data verify failed.");
+                    fota->error_code = FOTA_ERROR_VERIFY;
+                    if (fota->event_cb)
+                        fota->event_cb(arg, FOTA_EVENT_PROGRESS);
+                    // goto init status
+                    fota->status = FOTA_INIT;
+                    if (fota->config.auto_check_en > 0) {
+                        aos_msleep(fota->config.sleep_time);
+                        aos_sem_signal(&fota->do_check_event);
+                    }
+                } else {
+                    LOGD(TAG, "fota data verify ok.");
+#ifdef CONFIG_DL_FINISH_FLAG_POWSAVE
+                    aos_kv_setint(KV_FOTA_FINISH, 1);
+#endif
+                    fota->status = FOTA_FINISH;
+                    fota->error_code = FOTA_ERROR_NULL;
+                    if (fota->event_cb)
+                        fota->event_cb(arg, FOTA_EVENT_FINISH);
+                    if (fota->config.auto_check_en > 0) {
+                        fota_restart(fota, 0);
+                    }
+                }
+                continue;
+            }
+#ifdef CONFIG_DL_FINISH_FLAG_POWSAVE
+            if (fota->offset == 0) {
+                LOGD(TAG, "set fota_finish to 0");
+                if (aos_kv_setint(KV_FOTA_FINISH, 0) < 0) {
+                    goto write_err;
+                }
+            }
+#endif
+            size = netio_write(fota->to, fota->buffer, size, fota->config.write_timeoutms);
+            LOGI(TAG, "write size: %d", size);
+            if (size > 0) {
+                if (aos_kv_setint(KV_FOTA_OFFSET, fota->offset + size) < 0) {
+                    goto write_err;
+                }
+                fota->offset += size;
+                if (fota->event_cb) {
+                    fota->error_code = FOTA_ERROR_NULL;
+                    fota->event_cb(arg, FOTA_EVENT_PROGRESS);
+                }
+                aos_sem_signal(&fota->sem_download);
+            } else {
+write_err:
+                // flash write error
+                LOGE(TAG, "flash write size error.");
+                if (fota->event_cb) {
+                    fota->error_code = FOTA_ERROR_WRITE;
+                    fota->event_cb(arg, FOTA_EVENT_PROGRESS);
+                }
+                fota->status = FOTA_ABORT;
+            }
+        } else if (fota->status == FOTA_ABORT) {
+            LOGD(TAG, "fota_task FOTA_ABORT!");
+            if (retry != 0) {
+                LOGW(TAG, "fota retry: %d!", retry);
+                retry--;
+                fota->status = FOTA_DOWNLOAD;
+                aos_msleep(fota->config.sleep_time);
+                aos_sem_signal(&fota->sem_download);
+            } else {
+                retry = fota->config.retry_count;
+                fota_fail(fota, &fota->info);
+                fota_release(fota);
+                fota->status = FOTA_INIT;
+                if (fota->config.auto_check_en > 0) {
+                    aos_msleep(fota->config.sleep_time);
+                    aos_sem_signal(&fota->do_check_event);
+                }
+            }
+        } else {
+            // finish state will come here.
+            LOGD(TAG, "##fota status:%d", fota->status);
+            aos_msleep(fota->config.sleep_time);
+        }
+    }
+
+    LOGD(TAG, "force quit need release source");
+    fota->status = 0; // need reset for next restart
+    fota_release(fota);
+
+    if (fota->event_cb) {
+        fota->error_code = FOTA_ERROR_NULL;
+        fota->event_cb(arg, FOTA_EVENT_QUIT);
+    }
+
+    /* will free fota */
+    aos_sem_signal(&fota->sem);
+}
+
+void fota_do_check(fota_t *fota)
+{
+    LOGD(TAG, "fota do check signal........");
+    if (fota && (fota->status == FOTA_INIT || fota->status == FOTA_FINISH)) {
+        fota->status = FOTA_INIT;
+        aos_sem_signal(&fota->do_check_event);
+    }
+}
+
+int fota_start(fota_t *fota)
+{
+    if (fota == NULL) {
+        return -EINVAL;
+    }
+
+    if (fota->status == 0) {
+        fota->status = FOTA_INIT;
+        fota->quit = 0;
+#ifdef CONFIG_DL_FINISH_FLAG_POWSAVE
+        int isfinish;
+        if (aos_kv_getint(KV_FOTA_FINISH, &isfinish) < 0) {
+            isfinish = 0;
+        }
+        if (isfinish > 0) {
+            LOGD(TAG, "come to fota finish state.");
+            fota->status = FOTA_FINISH;
+        }
+#endif
+        if (aos_task_new_ext(&fota->task, "fota", fota_task, fota, CONFIG_FOTA_TASK_STACK_SIZE, 45) != 0) {
+            fota->quit = 1;
+            fota->status = 0;
+            LOGE(TAG, "fota task create failed.");
+            return -1;
+        }
+        if (fota->config.auto_check_en > 0) {
+            fota_do_check(fota);
+        }
+    }
+
+    return 0;
+}
+
+int fota_stop(fota_t *fota)
+{
+    if (fota == NULL) {
+        return -EINVAL;
+    }
+    LOGD(TAG, "%s,%d", __func__, __LINE__);
+
+    fota->quit = 1;
+    aos_sem_signal(&fota->do_check_event);
+    aos_sem_signal(&fota->sem_download);
+
+    return 0;
+}
+
+int fota_close(fota_t *fota)
+{
+    if (fota == NULL) {
+        return -EINVAL;
+    }
+    LOGD(TAG, "%s,%d", __func__, __LINE__);
+
+    fota->quit = 1;
+    aos_sem_signal(&fota->do_check_event);
+    aos_sem_signal(&fota->sem_download);
+    aos_sem_wait(&fota->sem, -1);
+    aos_sem_free(&fota->sem);
+    aos_sem_free(&fota->do_check_event);
+    aos_sem_free(&fota->sem_download);
+
+    if (fota->from_path) aos_free(fota->from_path);
+    if (fota->to_path) aos_free(fota->to_path);
+    if (fota->buffer) aos_free(fota->buffer);
+    if (fota->from) netio_close(fota->from);
+    if (fota->to) netio_close(fota->to);
+
+    aos_free(fota);
+
+    return 0;
+}
+
+int fota_download(fota_t *fota)
+{
+    if (fota == NULL) {
+        LOGE(TAG, "fota param null.");
+        return -EINVAL;
+    }
+    LOGD(TAG, "fota download, status:%d", fota->status);
+    if (fota->status == FOTA_ABORT || fota->status == FOTA_INIT || fota->status == FOTA_FINISH) {
+        if (fota->status == FOTA_INIT || fota->status == FOTA_FINISH) {
+            if (fota_prepare(fota)) {
+                LOGE(TAG, "fota_prepare failed");
+                if (fota->event_cb) {
+                    fota->error_code = FOTA_ERROR_PREPARE;
+                    fota->event_cb(fota, FOTA_EVENT_VERSION);
+                }
+                return -1;
+            }
+            aos_sem_signal(&fota->do_check_event);
+        } else {
+            fota->status = FOTA_DOWNLOAD;
+        }
+        LOGD(TAG, "signal download.");
+        aos_sem_signal(&fota->sem_download);
+        return 0;
+    }
+    LOGW(TAG, "the status is not allow to download.");
+    return -1;
+}
+
+static void timer_thread(void *timer, void *args)
+{
+    fota_t *fota = (fota_t *)args;
+    LOGD(TAG, "timer_thread, fota:0x%x", fota);
+    if (fota) {
+        LOGD(TAG, "report restart event.");
+        if (fota->event_cb) {
+            fota->error_code = FOTA_ERROR_NULL;
+            fota->event_cb(fota, FOTA_EVENT_RESTART);
+        }
+        aos_timer_stop(&fota->restart_timer);
+        aos_timer_free(&fota->restart_timer);
+        LOGD(TAG, "stop and delete timer ok.");
+        return;
+    }
+    LOGE(TAG, "timer task params error.");
+}
+
+int fota_restart(fota_t *fota, int delay_ms)
+{
+    if (fota == NULL) {
+        LOGE(TAG, "fota param null.");
+        return -EINVAL;
+    }
+    LOGD(TAG, "%s,%d, delay_ms:%d", __func__, __LINE__, delay_ms);
+    if (fota->status != FOTA_FINISH) {
+        LOGE(TAG, "fota status is not FOTA_FINISH, cant restart to upgrade.");
+        return -1;
+    }
+    if (fota->cls && fota->cls->restart) {
+        if (delay_ms > 0) {
+            int ret = aos_timer_new_ext(&fota->restart_timer, timer_thread, fota, delay_ms, 0, 1);
+            if (ret < 0) {
+                LOGE(TAG, "timer_create error [%d]!\n", errno);
+                return -1;
+            }
+            LOGD(TAG, "set timer ok.");
+            return 0;
+        }
+#ifdef CONFIG_DL_FINISH_FLAG_POWSAVE
+        if (aos_kv_setint(KV_FOTA_FINISH, 0) < 0) {
+            LOGE(TAG, "set finish 0 error.");
+            return -1;
+        }
+#endif
+        fota->cls->restart();
+    }
+    return 0;
+}
+
+void fota_config(fota_t *fota, fota_config_t *config)
+{
+    if (!(fota && config)) {
+        LOGE(TAG, "fota or config param null.");
+        return;
+    }
+    memcpy(&fota->config, config, sizeof(fota_config_t));
+}
+
+fota_config_t *fota_get_config(fota_t *fota)
+{
+    if (fota == NULL) {
+        LOGE(TAG, "fota param null.");
+        return NULL;
+    }
+    return &fota->config;
+}
+
+void fota_set_auto_check(fota_t *fota, int enable)
+{
+    if (fota)
+        fota->config.auto_check_en = enable;
+}
+
+int fota_get_auto_check(fota_t *fota)
+{
+    if (fota)
+        return fota->config.auto_check_en;
+    return 0;
+}
+
+fota_status_e fota_get_status(fota_t *fota)
+{
+    if (fota == NULL) {
+        LOGE(TAG, "fota param null.");
+        return 0;
+    }
+    return fota->status;
+}
+
+int64_t fota_get_size(fota_t *fota, const char *name)
+{
+    if (fota == NULL) {
+        LOGE(TAG, "fota param null.");
+        return -EINVAL;
+    }
+    if (fota->cls && fota->cls->get_size) {
+        return fota->cls->get_size(name);
+    }
+    return 0;
+}

+ 556 - 0
components/fota/fota/fota_cop.c

@@ -0,0 +1,556 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+#include <errno.h>
+#include <yoc/fota.h>
+#include <yoc/netio.h>
+#include <aos/kernel.h>
+#include <aos/kv.h>
+#include <aos/version.h>
+#include <ulog/ulog.h>
+#include <yoc/sysinfo.h>
+
+#define COP_IMG_URL "cop_img_url"
+#define COP_VERSION "cop_version"
+#define TO_URL      "flash://misc"
+
+#define TAG "fotacop"
+
+static void cop_res_release(fota_info_t *info)
+{
+    if (info) {
+        if (info->fota_url) {
+            aos_free(info->fota_url);
+            info->fota_url = NULL;
+        }
+        if (info->local_changelog) {
+            aos_free(info->local_changelog);
+            info->local_changelog = NULL;
+        }
+        if (info->changelog) {
+            aos_free(info->changelog);
+            info->changelog = NULL;
+        }
+        if (info->cur_version) {
+            aos_free(info->cur_version);
+            info->cur_version = NULL;
+        }
+        if (info->new_version) {
+            aos_free(info->new_version);
+            info->new_version = NULL;
+        }
+    }
+}
+
+#if CONFIG_FOTA_USE_HTTPC == 1
+#include <http_client.h>
+#include <cJSON.h>
+static int cop_get_ota_url(char *ota_url, int len)
+{
+    int ret = -1;
+
+    ret = aos_kv_getstring("otaurl", ota_url, len);
+    if (ret < 0) {
+        strcpy(ota_url, "http://occ.t-head.cn/api/image/ota/pull");
+    }
+
+    return ret;
+}
+
+static int _http_event_handler(http_client_event_t *evt)
+{
+    switch(evt->event_id) {
+        case HTTP_EVENT_ERROR:
+            LOGD(TAG, "HTTP_EVENT_ERROR");
+            break;
+        case HTTP_EVENT_ON_CONNECTED:
+            LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
+            break;
+        case HTTP_EVENT_HEADER_SENT:
+            LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
+            break;
+        case HTTP_EVENT_ON_HEADER:
+            // LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
+            break;
+        case HTTP_EVENT_ON_DATA:
+            // LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
+            break;
+        case HTTP_EVENT_ON_FINISH:
+            LOGD(TAG, "HTTP_EVENT_ON_FINISH");
+            break;
+        case HTTP_EVENT_DISCONNECTED:
+            LOGD(TAG, "HTTP_EVENT_DISCONNECTED");
+            break;
+    }
+    return 0;
+}
+
+static bool process_again(int status_code)
+{
+    switch (status_code) {
+        case HttpStatus_MovedPermanently:
+        case HttpStatus_Found:
+        case HttpStatus_TemporaryRedirect:
+        case HttpStatus_Unauthorized:
+            return true;
+        default:
+            return false;
+    }
+    return false;
+}
+
+static http_errors_t _http_handle_response_code(http_client_handle_t http_client, int status_code, char *buffer, int buf_size, int data_size)
+{
+    http_errors_t err;
+    if (status_code == HttpStatus_MovedPermanently || status_code == HttpStatus_Found || status_code == HttpStatus_TemporaryRedirect) {
+        err = http_client_set_redirection(http_client);
+        if (err != HTTP_CLI_OK) {
+            LOGE(TAG, "URL redirection Failed");
+            return err;
+        }
+    } else if (status_code == HttpStatus_Unauthorized) {
+        return HTTP_CLI_FAIL;
+    } else if(status_code == HttpStatus_NotFound || status_code == HttpStatus_Forbidden) {
+        LOGE(TAG, "File not found(%d)", status_code);
+        return HTTP_CLI_FAIL;
+    } else if (status_code == HttpStatus_InternalError) {
+        LOGE(TAG, "Server error occurred(%d)", status_code);
+        return HTTP_CLI_FAIL;
+    }
+
+    // process_again() returns true only in case of redirection.
+    if (data_size > 0 && process_again(status_code)) {
+        /*
+        *  In case of redirection, http_client_read() is called
+        *  to clear the response buffer of http_client.
+        */
+        int data_read;
+        while (data_size > buf_size) {
+            data_read = http_client_read(http_client, buffer, buf_size);
+            if (data_read <= 0) {
+                return HTTP_CLI_OK;
+            }
+            data_size -= buf_size;
+        }
+        data_read = http_client_read(http_client, buffer, data_size);
+        if (data_read <= 0) {
+            return HTTP_CLI_OK;
+        }
+    }
+    return HTTP_CLI_OK;
+}
+
+static http_errors_t _http_connect(http_client_handle_t http_client, const char *payload, char *buffer, int buf_size)
+{
+#define MAX_REDIRECTION_COUNT 10
+    http_errors_t err = HTTP_CLI_FAIL;
+    int status_code = 0, header_ret;
+    int redirect_counter = 0;
+
+    do {
+        if (redirect_counter++ > MAX_REDIRECTION_COUNT) {
+            LOGE(TAG, "redirect_counter is max");
+            return HTTP_CLI_FAIL;
+        }
+        if (process_again(status_code)) {
+            LOGD(TAG, "process again,status code:%d", status_code);
+        }
+        err = http_client_open(http_client, strlen(payload));
+        if (err != HTTP_CLI_OK) {
+            LOGE(TAG, "Failed to open HTTP connection");
+            return err;
+        }
+        int wlen = http_client_write(http_client, payload, strlen(payload));
+        if (wlen < 0) {
+            LOGE(TAG, "Write payload failed");
+            return HTTP_CLI_FAIL;
+        }
+        LOGD(TAG, "write payload ok...");
+        header_ret = http_client_fetch_headers(http_client);
+        if (header_ret < 0) {
+            LOGE(TAG, "header_ret:%d", header_ret);
+            return header_ret;
+        }
+        LOGD(TAG, "header_ret:%d", header_ret);
+        status_code = http_client_get_status_code(http_client);
+        LOGD(TAG, "status code:%d", status_code);
+        err = _http_handle_response_code(http_client, status_code, buffer, buf_size, header_ret);
+        if (err != HTTP_CLI_OK) {
+            LOGE(TAG, "e handle resp code:%d", err);
+            return err;
+        }
+    } while (process_again(status_code));
+    return err;
+}
+
+static void _http_cleanup(http_client_handle_t client)
+{
+    if (client) {
+        http_client_cleanup(client);
+    }
+}
+
+static int cop_version_check(fota_info_t *info) {
+    int ret = 0, rc;
+    char *payload = NULL;
+    char getvalue[64];
+    cJSON *js = NULL;
+    char *buffer = NULL;
+    http_errors_t err;
+    http_client_config_t config = {0};
+    http_client_handle_t client = NULL;
+
+    buffer = aos_zalloc(BUFFER_SIZE + 1);
+    if (buffer == NULL) {
+        ret = -ENOMEM;
+        goto out;
+    }
+    if ((payload = aos_malloc(156)) == NULL) {
+        ret = -ENOMEM;
+        goto out;
+    }
+#if 1
+    char * dev_id = aos_get_device_id();
+    char * pro_model = (char *)aos_get_product_model();
+    char * app_ver = aos_get_app_version();
+    snprintf(payload, 156, "{\"cid\":\"%s\",\"model\":\"%s\",\"version\":\"%s\"}",
+             dev_id ? dev_id : "null", pro_model ? pro_model : "null", app_ver ? app_ver : "null");
+#else
+    //snprintf(payload, 100, "{\"cid\":\"%s\",\"model\":\"%s\",\"version\":\"%s\"}",
+    //         "00A2C6FB32241D9423F5AF00", "hlb_test", "1.0.0-20181125.1321-R-hlb_tes");
+    // FIXME: for test.
+    snprintf(payload, 156, "{\"cid\":\"%s\",\"model\":\"%s\",\"version\":\"%s\"}",
+            "e6d0d5480440000008376eb1e834a765", "pangu", "1.1.1-20201027.2235-R-pangu");
+#endif
+    LOGD(TAG, "check: %s", payload);
+
+    memset(getvalue, 0, sizeof(getvalue));
+    cop_get_ota_url(getvalue, sizeof(getvalue));
+    LOGD(TAG, "ota url:%s", getvalue);
+
+    config.method = HTTP_METHOD_POST;
+    config.url = getvalue;
+    config.timeout_ms = 10000;
+    config.buffer_size = BUFFER_SIZE;
+    config.event_handler = _http_event_handler;
+    LOGD(TAG, "http client init start.");
+    client = http_client_init(&config);
+    if (!client) {
+        LOGE(TAG, "Client init e");
+        ret = -1;
+        goto out;
+    }
+    LOGD(TAG, "http client init ok.");
+    http_client_set_header(client, "Content-Type", "application/json");
+    http_client_set_header(client, "Connection", "keep-alive");
+    http_client_set_header(client, "Cache-Control", "no-cache");
+    err = _http_connect(client, payload, buffer, BUFFER_SIZE);
+    if (err != HTTP_CLI_OK) {
+        LOGE(TAG, "Client connect e");
+        ret = -1;
+        goto out;
+    }
+    int read_len = http_client_read(client, buffer, BUFFER_SIZE);
+    if (read_len <= 0) {
+        ret = -1;
+        goto out;
+    }
+    buffer[read_len] = 0;
+    LOGD(TAG, "resp: %s", buffer);
+
+    js = cJSON_Parse(buffer);
+    if (js == NULL) {
+        ret = -1;
+        LOGW(TAG, "cJSON_Parse failed");
+        goto out;
+    }
+
+    cJSON *code = cJSON_GetObjectItem(js, "code");
+    if (!(code && cJSON_IsNumber(code))) {
+        ret = -1;
+        LOGW(TAG, "get code failed");
+        goto out;
+    }
+    LOGD(TAG, "code: %d", code->valueint);
+    if (code->valueint < 0) {
+        ret = -1;
+        goto out;
+    }
+
+    cJSON *result = cJSON_GetObjectItem(js, "result");
+    if (!(result && cJSON_IsObject(result))) {
+        LOGW(TAG, "get result failed");
+        ret = -1;
+        goto out;
+    }
+
+    cJSON *version = cJSON_GetObjectItem(result, "version");
+    if (!(version && cJSON_IsString(version))) {
+        LOGW(TAG, "get version failed");
+        ret = -1;
+        goto out;
+    }
+    LOGD(TAG, "version: %s", version->valuestring);
+    aos_kv_setstring(COP_VERSION, version->valuestring);
+
+    cJSON *url = cJSON_GetObjectItem(result, "url");
+    if (!(url && cJSON_IsString(url))) {
+        ret = -1;
+        LOGW(TAG, "get url failed");
+        goto out;
+    }
+    LOGD(TAG, "url: %s", url->valuestring);
+
+    char *urlbuf = aos_malloc(156);
+    if (urlbuf == NULL) {
+        ret = -1;
+        goto out;
+    }
+    rc = aos_kv_getstring(COP_IMG_URL, urlbuf, 156);
+
+    if (rc <= 0) {
+        aos_kv_setstring(COP_IMG_URL, url->valuestring);
+    } else {
+        if (strcmp(url->valuestring, urlbuf) == 0) {
+            aos_kv_getint("fota_offset", &rc);
+            LOGI(TAG, "continue fota :%d", rc);
+        } else {
+            aos_kv_setstring(COP_IMG_URL, url->valuestring);
+            aos_kv_setint("fota_offset", 0);
+            LOGI(TAG, "restart fota");
+        }
+    }
+    aos_free(urlbuf);
+
+    if (info->fota_url) {
+        aos_free(info->fota_url);
+        info->fota_url = NULL;
+    }
+    info->fota_url = strdup(url->valuestring);
+    LOGD(TAG, "get url: %s", info->fota_url);
+out:
+    if (buffer) aos_free(buffer);
+    if (payload) aos_free(payload);
+    if (js) cJSON_Delete(js);
+    _http_cleanup(client);
+    return ret;
+}
+
+#else
+
+#include "../http/http.h"
+#include "util/network.h"
+char *json_getvalue(char *body, char *key, int *len)
+{
+    char key_buf[16];
+    char *start_ptr, *end_ptr;
+
+    snprintf(key_buf, 16, "\"%s\":", key);
+
+    start_ptr = strstr(body, key_buf);
+
+    if (start_ptr == NULL || body == start_ptr) {
+        LOGD(TAG, "key");
+        return NULL;
+    }
+
+    start_ptr += strlen(key_buf);
+
+    while ((*start_ptr == ' ') || (*start_ptr == '\"') || (*start_ptr == 0)) {
+        start_ptr++;
+    }
+
+    end_ptr = start_ptr;
+
+    while (((*end_ptr != ',') && (*end_ptr != '\"')) && (*end_ptr != 0)) {
+        end_ptr++;
+    }
+
+    if (start_ptr == end_ptr) {
+        return NULL;
+    }
+
+    if (*end_ptr == '\"') {
+        end_ptr --;
+    }
+
+    *len = end_ptr - start_ptr + 1;
+
+    return start_ptr;
+}
+
+static int cop_get_ota_url(char *ota_url, int len)
+{
+    int ret = -1;
+
+    ret = aos_kv_getstring("otaurl", ota_url, len);
+    if (ret < 0) {
+        strcpy(ota_url, "http://occ.t-head.cn/api/image/ota/pull");
+    }
+
+    return ret;
+}
+
+static int cop_version_check(fota_info_t *info) {
+    http_t *http;
+    char *payload;
+    char *body;
+    int value_len;
+    char *cptr;
+    char getvalue[64];
+    int rc;
+
+    if ((payload = aos_malloc(156)) == NULL) {
+        return -1;
+    }
+
+    snprintf(payload, 156, "{\"cid\":\"%s\",\"model\":\"%s\",\"version\":\"%s\"}",
+             aos_get_device_id(), aos_get_product_model(), aos_get_app_version());
+    //snprintf(payload, 100, "{\"cid\":\"%s\",\"model\":\"%s\",\"version\":\"%s\"}",
+    //         "00A2C6FB32241D9423F5AF00", "hlb_test", "1.0.0-20181125.1321-R-hlb_tes");
+
+    LOGD(TAG, "check: %s", payload);
+
+    memset(getvalue, 0, sizeof(getvalue));
+    cop_get_ota_url(getvalue, sizeof(getvalue));
+
+    if ((http = http_init(getvalue)) == NULL) {
+        LOGD(TAG, "e cop http init");
+        aos_free(payload);
+        return -1;
+    }
+    memset(http->buffer, 0, BUFFER_SIZE);
+    http->buffer_offset = 0;
+    http_head_sets(http, "Host", http->host);
+    http_head_seti(http, "Content-Length", strlen(payload));
+    http_head_sets(http, "Connection", "keep-alive");
+    http_head_sets(http, "Content-Type", "application/json");
+    http_head_sets(http, "Cache-Control", "no-cache");
+
+    http_post(http, payload, 10000);
+
+    if (http_wait_resp(http, &body, 10000) < 0) {
+        http_deinit(http);
+        aos_free(payload);
+        LOGD(TAG, "recv failed");
+        return -1;
+    }
+
+    aos_free(payload);
+    LOGD(TAG, "resp body: %s", body);
+    cptr = json_getvalue(body, "code", &value_len);
+
+    if ((cptr == NULL) || (atoi(cptr) < 0)) {
+        LOGW(TAG, "rsp format");
+        http_deinit(http);
+        return -1;
+    }
+
+    cptr = json_getvalue(body, "version", &value_len);
+    if (cptr == NULL) {
+        LOGD(TAG, "rsp format %s", "version");
+        http_deinit(http);
+        return -1;
+    }
+    char *ver = aos_malloc(value_len + 1);
+    if (!ver) {
+        return -1;
+    }
+    memcpy(ver, cptr, value_len);
+    ver[value_len] = 0;
+    LOGD(TAG, "%s: %s", COP_VERSION, ver);
+    aos_kv_setstring(COP_VERSION, ver);
+    aos_free(ver);
+
+    cptr = json_getvalue(body, "url", &value_len);
+
+    if (cptr == NULL) {
+        LOGD(TAG, "rsp format %s", "url");
+        http_deinit(http);
+        return -1;
+    }
+
+    if (http->url) {
+        aos_free(http->url);
+        http->url = NULL;
+    }
+    http->url = calloc(value_len + 1, 1);
+
+    if (http->url == NULL) {
+        LOGD(TAG, "realloc failed");
+        http_deinit(http);
+        return -1;
+    }
+
+    if (strstr(cptr, "https://")) {
+        snprintf(http->url, value_len, "http://%s", cptr + strlen("https://"));
+    } else {
+        memcpy(http->url, cptr, value_len);
+    }
+
+    char *buffer = aos_malloc(156);
+
+    if (buffer == NULL) {
+        http_deinit(http);
+        return -1;
+    }
+
+    rc = aos_kv_getstring(COP_IMG_URL, buffer, 156);
+
+    if (rc <= 0) {
+        aos_kv_setstring(COP_IMG_URL, http->url);
+    } else {
+        if (strcmp(http->url, buffer) == 0) {
+            aos_kv_getint("fota_offset", &rc);
+            LOGI(TAG, "continue fota :%d", rc);
+        } else {
+            aos_kv_setstring(COP_IMG_URL, http->url);
+            aos_kv_setint("fota_offset", 0);
+            LOGI(TAG, "restart fota");
+        }
+    }
+    aos_free(buffer);
+    if (info->fota_url) {
+        aos_free(info->fota_url);
+        info->fota_url = NULL;
+    }
+    if (info->changelog) {
+        aos_free(info->changelog);
+        info->changelog = NULL;
+    }
+    if (info->new_version) {
+        aos_free(info->new_version);
+        info->new_version = NULL;
+    }
+    info->fota_url = strdup(http->url);
+    LOGD(TAG, "get url: %s", info->fota_url);
+    http_deinit(http);
+    return 0;
+}
+#endif /* CONFIG_FOTA_USE_HTTPC */
+
+static int finish(fota_info_t *info)
+{
+    cop_res_release(info);
+    return 0;
+}
+
+static int fail(fota_info_t *info)
+{
+    cop_res_release(info);
+    return 0;
+}
+
+const fota_cls_t fota_cop_cls = {
+    "cop",
+    NULL,
+    cop_version_check,
+    finish,
+    fail,
+    NULL
+};
+
+int fota_register_cop(void)
+{
+    return fota_register(&fota_cop_cls);
+}

+ 210 - 0
components/fota/fota/fota_verify.c

@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+#include <errno.h>
+#include <string.h>
+#include "yoc/fota.h"
+#include <ulog/ulog.h>
+
+#define TAG "fotav"
+
+#ifndef __linux__
+#include <yoc/partition.h>
+#include <verify.h>
+#include <verify_wrapper.h>
+
+#define DUMP_DATA_EN 0
+
+typedef struct {
+    uint32_t magic;
+    uint8_t ver;
+    uint8_t image_sum;
+    uint8_t digest_type;
+    uint8_t signature_type;
+    uint32_t mnft_off;
+    uint8_t head_len;
+    uint8_t pad_type;
+    uint8_t rsvd[2];
+} fota_head_info_t;
+
+#if DUMP_DATA_EN
+static void dump_data(uint8_t *data, int32_t len)
+{
+    int32_t i;
+
+    for (i = 0; i < len; i++) {
+        if (i % 16 == 0) {
+            printf("\n");
+        }
+
+        printf("%02x ", data[i]);
+    }
+
+    printf("\n");
+}
+#endif
+
+#if defined(CONFIG_FOTA_DATA_IN_RAM) && CONFIG_FOTA_DATA_IN_RAM > 0
+/**
+ * @brief  获取存储fota数据的ram地址
+ * @return address
+ */
+__attribute__((weak)) unsigned long fota_data_address_get(void)
+{
+    return 0;
+}
+#endif
+
+__attribute__((weak)) int fota_data_verify(void)
+{
+#define FOTA_DATA_MAGIC 0x45474d49
+#define BUF_SIZE 512
+    int ret;
+    uint8_t *buffer;
+    uint8_t *hash_out;
+    uint32_t olen;
+    int fota_data_offset, image_size;
+    int signature_len, hash_len;
+    int signature_offset, hash_offset;
+    digest_sch_e digest_type;
+    signature_sch_e sign_type;
+    partition_t partition;
+    partition_info_t *partition_info;
+    fota_head_info_t *head;
+    unsigned long data_address_start;
+
+    LOGD(TAG, "start fota verify...");
+    ret = 0;
+    buffer = NULL;
+    hash_out = NULL;
+#if CONFIG_FOTA_DATA_IN_RAM > 0
+    (void)partition;
+    (void)partition_info;
+    data_address_start = fota_data_address_get();
+    if (data_address_start == 0) {
+        LOGE(TAG, "fota data address[0x%x] error.", data_address_start);
+        return -1;
+    }
+    head = (fota_head_info_t *)data_address_start;
+    fota_data_offset = 0;
+    buffer = aos_malloc(BUF_SIZE);
+#else
+    (void)data_address_start;
+    partition = partition_open("misc");
+    if (partition < 0) {
+        LOGE(TAG, "flash open e.");
+        return -1;
+    }
+    partition_info = hal_flash_get_info(partition);
+    if (partition_info == NULL) {
+        ret = -EIO;
+        goto out;
+    }
+    fota_data_offset = partition_info->sector_size << 1;
+    buffer = aos_malloc(BUF_SIZE);
+    if (buffer == NULL) {
+        ret = -ENOMEM;
+        goto out;
+    }
+    if (partition_read(partition, fota_data_offset, buffer, BUF_SIZE) < 0) {
+        ret = -EIO;
+        goto out;
+    }
+    head = (fota_head_info_t *)buffer;
+#endif
+    if (head->magic != FOTA_DATA_MAGIC) {
+        LOGE(TAG, "app fota data magic e");
+        ret = -1;
+        goto out;
+    }
+    image_size = head->mnft_off;
+    digest_type = head->digest_type;
+    sign_type = head->signature_type;
+    hash_len = get_length_with_digest_type(digest_type);
+    signature_len = get_length_with_signature_type(sign_type);
+    signature_offset = image_size + fota_data_offset;
+    hash_offset = signature_offset + 256;
+
+    LOGD(TAG, "image_size:%d", image_size);
+    LOGD(TAG, "digest_type:%d", digest_type);
+    LOGD(TAG, "sign_type:%d", sign_type);
+    LOGD(TAG, "hash_len:%d", hash_len);
+    LOGD(TAG, "signature_len:%d", signature_len);
+    LOGD(TAG, "signature_offset:%d", signature_offset);
+    LOGD(TAG, "hash_offset:%d", hash_offset);
+
+    hash_out = aos_malloc(hash_len);
+    if (hash_out == NULL) {
+        ret = -ENOMEM;
+        goto out;
+    }
+#if CONFIG_FOTA_DATA_IN_RAM > 0
+    ret = hash_calc_start(digest_type, (const uint8_t *)(fota_data_offset + data_address_start), image_size, hash_out, &olen, 1);
+    if (ret != 0) {
+        LOGE(TAG, "hash calc failed.");
+        goto out;
+    }
+    memcpy(buffer, (void *)(hash_offset + data_address_start), hash_len);
+#else
+    ret = hash_calc_start(digest_type, (const uint8_t *)fota_data_offset + partition_info->base_addr + partition_info->start_addr, image_size, hash_out, &olen, 0);
+    if (ret != 0) {
+        LOGE(TAG, "hash calc failed.");
+        goto out;
+    }
+    if (partition_read(partition, hash_offset, buffer, hash_len) < 0) {
+        ret = -EIO;
+        goto out;
+    }
+#endif
+
+#if CONFIG_FOTA_IMG_AUTHENTICITY_NOT_CHECK == 0
+    // TODO: not support yet
+    uint8_t *key;
+    int key_len = 0;
+    if (partition_read(partition, signature_offset, buffer, signature_len) < 0) {
+        ret = -EIO;
+        goto out;
+    }
+#if DUMP_DATA_EN
+    dump_data(buffer, signature_len);
+#endif
+    ret = signature_verify_start(digest_type, sign_type, key, ken_len, hash_out, hash_len, buffer, signature_len);
+    if (ret != 0) {
+        LOGE(TAG, "signature verify failed.");
+        goto out;
+    }
+    LOGD(TAG, "fota data verify ok...");
+#else
+    (void)sign_type;
+    (void)signature_len;
+#if DUMP_DATA_EN
+    dump_data(buffer, hash_len);
+    dump_data(hash_out, hash_len);
+#endif
+    if (memcmp(hash_out, buffer, hash_len) != 0) {
+        LOGE(TAG, "!!!fota data hash v e..");
+        ret = -EIO;
+        goto out;
+    }
+    LOGI(TAG, "###fota data hash v ok.");
+#endif /*CONFIG_FOTA_IMG_AUTHENTICITY_NOT_CHECK*/
+
+out:
+    if (buffer)
+        aos_free(buffer);
+    if (hash_out)
+        aos_free(hash_out);
+#if CONFIG_FOTA_DATA_IN_RAM == 0
+    partition_close(partition);
+#endif
+    return ret;
+}
+
+#else
+
+__attribute__((weak)) int fota_data_verify(void)
+{
+    return 0;
+}
+
+#endif /*__linux__*/

+ 394 - 0
components/fota/http/http.c

@@ -0,0 +1,394 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include "ulog/ulog.h"
+#include <aos/kernel.h>
+#include <yoc/fota.h>
+#include "http.h"
+
+#define TAG "http"
+
+http_t *http_init(const char *path)
+{
+    char *ip, *port_str;
+    char *end_point;
+    char *host = NULL;
+    int port;
+
+    // FIXME: only support HTTP now.
+    if (path == NULL || !strstr(path, "http://")) {
+        return NULL;
+    }
+
+    http_t *http = aos_zalloc_check(sizeof(http_t));
+
+    http->url = strdup(path);
+    if (http->url == NULL) {
+        goto error;
+    }
+
+    host = http->url + sizeof("http://") - 1;
+    if (strlen(host) == 0) {
+        goto error;
+    }
+    http->port = 80;
+
+    ip = http->url + strlen("http://");
+
+    port_str = strrchr(host, ':');
+
+    if (port_str != NULL) {
+        *port_str = 0;
+        port_str ++;
+        port = strtol(port_str, &end_point, 10);
+        http->port = port;
+    } else {
+        port = 80;
+        end_point = strchr(ip, '/');
+    }
+
+    if (end_point != NULL) {
+        http->path = strdup(end_point);
+        LOGD(TAG, "http: path %s", http->path);
+
+        if (http->path == NULL) {
+            goto error;
+        }
+    } else {
+        goto error;
+    }
+
+    http->buffer = (uint8_t *)aos_malloc(BUFFER_SIZE);
+    if (http->buffer == NULL) {
+        goto error;
+    }
+    memset(http->buffer, 0, BUFFER_SIZE);
+
+    *end_point = 0;
+    http->host = strdup(ip);
+    if (http->host == NULL) {
+        goto error;
+    }
+
+    LOGD(TAG, "http connect: %s:%d", http->host, port);
+
+    network_init(&http->net);
+    if (http->net.net_connect(&http->net, http->host, port, SOCK_STREAM) != 0) {
+        goto error;
+    }
+
+    return http;
+
+error:
+    if (http) {
+        if (http->url) aos_free(http->url);
+        if (http->path) aos_free(http->path);
+        if (http->host) aos_free(http->host);
+        if (http->buffer) aos_free(http->buffer);
+        aos_free(http);
+    }
+
+    return NULL;
+}
+
+int http_head_sets(http_t *http, const char *key, const char *value)
+{
+    int len;
+    if (http->buffer == NULL) {
+        return -1;
+    }
+
+    len = snprintf((char*)http->buffer + http->buffer_offset, BUFFER_SIZE - http->buffer_offset,
+            "%s: %s\r\n", key, value);
+    if (len <= 0) {
+        return -1;
+    }
+
+    http->buffer_offset += len;
+
+    return 0;
+}
+
+int http_head_seti(http_t *http, const char *key, int value)
+{
+    int len;
+    if (http->buffer == NULL) {
+        return -1;
+    }
+
+    len = snprintf((char*)http->buffer + http->buffer_offset, BUFFER_SIZE - http->buffer_offset,
+            "%s: %d\r\n", key, value);
+    if (len <= 0) {
+        return -1;
+    }
+
+    http->buffer_offset += len;
+
+    return 0;
+}
+
+int http_post(http_t *http, char *playload, int timeoutms)
+{
+    char *temp_buff;
+    if ((temp_buff = strdup((char *)http->buffer)) == NULL) {
+        return -1;
+    }
+
+    // temp_buff = strdup((char *)http->buffer);
+    memset(http->buffer, 0, BUFFER_SIZE);
+
+    snprintf((char *)http->buffer, BUFFER_SIZE, "POST %s HTTP/1.1\r\n", http->path);
+
+    strcat((char *)http->buffer, temp_buff);
+
+    strcat((char *)http->buffer, "\r\n");
+
+    strcat((char *)http->buffer, playload);
+
+    // LOGD(TAG, "http post sendbuffer: %s", http->buffer);
+
+    aos_free(temp_buff);
+
+    return http->net.net_write(&http->net, http->buffer, strlen((char*)http->buffer), timeoutms);
+}
+
+int http_get(http_t *http, int timeoutms)
+{
+    char *temp_buff;
+    if ((temp_buff = strdup((char *)http->buffer)) == NULL) {
+        return -1;
+    }
+
+    // temp_buff = strdup((char *)http->buffer);
+    memset(http->buffer, 0, BUFFER_SIZE);
+
+    snprintf((char *)http->buffer, BUFFER_SIZE, "GET %s HTTP/1.1\r\n", http->path);
+
+    strcat((char *)http->buffer, temp_buff);
+
+    strcat((char *)http->buffer, "\r\n");
+
+    // LOGD(TAG, "http get sendbuffer: %s", http->buffer);
+
+    aos_free(temp_buff);
+
+    return http->net.net_write(&http->net, http->buffer, strlen((char*)http->buffer), timeoutms);
+}
+
+static int http_parse_head(http_t *http, char **head)
+{
+    char *content;
+    char *endpoint;
+    int content_len;
+    char *code_str;
+    int code;
+    char *end_point;
+
+    *head = strstr((char*)http->buffer, "\r\n\r\n");
+
+    if (*head == NULL) {
+        return 0;
+    }
+
+    *head += 4;
+
+    // FIXME: server connection close, need reconnect
+    if (strstr((char*)http->buffer, "Connection: close")) {
+        return -2;
+    }
+
+    if ((content = strstr((char*)http->buffer, "Content-Length: ")) == NULL) {
+        code_str = strstr((char *)http->buffer, "HTTP/1.1");
+
+        code = atoi(code_str + strlen("HTTP/1.1"));
+        if (code != 206 && code != 200) {
+            LOGD(TAG, "http code :%d", code);
+            return -1;
+        }
+
+        content_len = strtol(*head, &end_point, 16);
+
+        if (end_point == NULL) {
+            return 0;
+        }
+    } else {
+        // content_len = atoi(content + strlen("Content-Length: "));
+        content_len = strtol(content + strlen("Content-Length: "), &endpoint, 10);
+
+        if (endpoint == NULL || *endpoint != '\r') {
+            return -1;
+        }
+    }
+
+    // LOGD(TAG, "http parse head: %s %d", http->buffer, content_len);
+    return content_len;
+}
+
+static int net_quick_reconnect(http_t *http)
+{
+    LOGW(TAG, "i need reconnect http");
+    http->net.net_disconncet(&http->net);
+    if (http->net.net_connect(&http->net, http->host, http->port, SOCK_STREAM) != 0) {
+        LOGE(TAG, "reconnect failed!");
+        return -1;
+    }
+    return 0;
+}
+
+static int net_read(int fd, unsigned char *buffer, int len, int timeout_ms)
+{
+    struct timeval interval = {timeout_ms / 1000, (timeout_ms % 1000) * 1000};
+
+    if (interval.tv_sec < 0 || (interval.tv_sec == 0 && interval.tv_usec <= 100)) {
+        interval.tv_sec = 0;
+        interval.tv_usec = 10000;
+    }
+
+    if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&interval,
+                   sizeof(struct timeval))) {
+        return -1;
+    }
+
+    int rc = recv(fd, buffer, len, 0);
+
+    return rc;
+}
+
+int http_wait_resp(http_t *http, char **head_end, int timeoutms)
+{
+    int recv_len = 0;
+    int len;
+    int content_len;
+
+again:
+    recv_len = 0;
+    *head_end = NULL;
+    content_len = 0;
+    memset(http->buffer, 0, BUFFER_SIZE);
+
+    while(recv_len < BUFFER_SIZE) {
+        int pre_read = BUFFER_SIZE - recv_len;
+        if(pre_read > 512) {
+            pre_read = 512;
+        }
+
+        len = net_read(http->net.fd, http->buffer + recv_len, pre_read, timeoutms);
+        //printf("pre read %d\n", len);
+
+        if (len <= 0) {
+            LOGE(TAG, "net read len=%d errno=%d", len, errno);
+            return -1;
+        }
+
+        recv_len += len;
+
+        if ((content_len = http_parse_head(http, head_end)) == 0) {
+            continue;
+        }
+
+        if (content_len < 0) {
+            if (content_len == -2) {
+                if (net_quick_reconnect(http) < 0) {
+                    return 0;
+                }
+                return -2;
+            }
+            return -1;
+        } else {
+            break;
+        }
+    }
+
+    if (content_len <= 0 && !(*head_end)) {
+        goto again;
+    }
+
+    int head_len = (*head_end - (char *)http->buffer);
+    int total_len = content_len + head_len;
+    //int left_len = content_len + head_len - len;
+    //printf("head len %d, left_len %d, total %d, recv len %d\n", head_len, left_len, total_len, recv_len);
+
+    if ( total_len > BUFFER_SIZE ) {
+        LOGE(TAG, "total len %d", total_len);
+        return -1;
+    }
+
+    // LOGD(TAG, "buffer: %.*s", *head_end - (char*)http_ins->buffer, http_ins->buffer);
+
+    while(recv_len < total_len) {
+        if (content_len <= recv_len - (*head_end - (char *)http->buffer)) {
+            break;
+        }
+
+        len = http->net.net_read(&http->net, http->buffer + recv_len, total_len - recv_len, timeoutms);
+
+        //printf("left read %d\n", len);
+        if (len <= 0) {
+            LOGE(TAG, "net read len=%d errno=%d", len, errno);
+            return -1;
+        }
+
+        recv_len += len;
+    }
+
+    return content_len;
+}
+
+char *http_head_get(http_t *http, char *key, int *length)
+{
+    char *temp_key;
+    char *temp_value;
+    char *temp_end;
+
+    if ((temp_key = strstr((char *)http->buffer, key)) == NULL) {
+        LOGD(TAG, "no key %s", key);
+        return NULL;
+    }
+
+    if ((temp_value = strstr(temp_key, ":")) == NULL) {
+        LOGD(TAG, "no delimiter");
+        return NULL;
+    }
+
+    if ((temp_end = strstr(temp_value, "\r\n")) == NULL) {
+        LOGD(TAG, "no end");
+        return NULL;
+    }
+
+    *length = (int)(temp_end - temp_value - 2) >= 0 ? (int)(temp_end - temp_value - 2) : 0;// 1 space
+
+    return temp_value + 1;
+}
+
+char *http_read_data(http_t *http)
+{
+    char *buffer;
+
+    buffer = strstr((char *)http->buffer, "\r\n\r\n");
+
+    if (buffer == NULL) {
+        return NULL;
+    }
+
+    return buffer + 4;
+}
+
+int http_deinit(http_t *http)
+{
+    if (http) {
+        http->net.net_disconncet(&http->net);
+
+        if (http->url) aos_free(http->url);
+        if (http->path) aos_free(http->path);
+        if (http->host) aos_free(http->host);
+        if (http->buffer) aos_free(http->buffer);
+        aos_free(http);
+    }
+
+    return 0;
+}

+ 39 - 0
components/fota/http/http.h

@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+
+#ifndef __HTTP_H__
+#define __HTTP_H__
+
+#include "util/network.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct http {
+    char *path;
+    char *host;
+    uint8_t *buffer;
+    int buffer_offset;
+    char *url;
+    int port;
+    network_t net;
+} http_t;
+
+// char *json_getvalue(char *body, char *key, int *len);
+
+http_t *http_init(const char *path);
+int http_head_sets(http_t *http, const char *key, const char *value);
+int http_head_seti(http_t *http, const char *key, int value);
+int http_post(http_t *http, char *playload, int timeoutms);
+int http_get(http_t *http, int timeoutms);
+int http_wait_resp(http_t *http, char **head_end, int timeoutms);
+char *http_head_get(http_t *http, char *key, int *length);
+char *http_read_data(http_t *http);
+int http_deinit(http_t *http);
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 250 - 0
components/fota/include/yoc/fota.h

@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+
+#ifndef __FOTA_H__
+#define __FOTA_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include <stdint.h>
+#include <time.h>
+#include <aos/list.h>
+#include <aos/kernel.h>
+#include <yoc/netio.h>
+
+#define FW_URL_KEY  "FW_URL_KEY"
+#define KV_FOTA_OFFSET "fota_offset"
+#define KV_FOTA_CLOUD_PLATFORM "fota_cldp"
+#define KV_FOTA_FROM_URL "fota_fromurl"
+#define KV_FOTA_TO_URL "fota_tourl"
+#define KV_FOTA_READ_TIMEOUTMS "fota_rtmout"
+#define KV_FOTA_WRITE_TIMEOUTMS "fota_wtmout"
+#define KV_FOTA_RETRY_COUNT "fota_retry"
+#define KV_FOTA_SLEEP_TIMEMS "fota_slptm"
+#define KV_FOTA_AUTO_CHECK "fota_autock"
+#define KV_FOTA_FINISH "fota_finish"
+
+#ifndef CONFIG_FOTA_TASK_STACK_SIZE
+#define CONFIG_FOTA_TASK_STACK_SIZE (4 * 1024)
+#endif
+
+// use httpclient
+#ifndef CONFIG_FOTA_USE_HTTPC
+#define CONFIG_FOTA_USE_HTTPC 0
+#endif
+
+#ifndef CONFIG_FOTA_DATA_IN_RAM
+#define CONFIG_FOTA_DATA_IN_RAM 0
+#endif
+
+typedef struct fota fota_t;
+
+typedef enum {
+    FOTA_EVENT_START = 0,       /*!< Start the fota version check and download steps */
+    FOTA_EVENT_VERSION,         /*!< Check version from server ok */
+    FOTA_EVENT_PROGRESS,        /*!< Downloading the fota data */
+    FOTA_EVENT_FAIL,            /*!< This event occurs when there are any errors during execution */
+    FOTA_EVENT_VERIFY,          /*!< verify fota data */
+    FOTA_EVENT_FINISH,          /*!< fota download flow finish */
+    FOTA_EVENT_QUIT,            /*!< Fota task quit */
+    FOTA_EVENT_RESTART          /*!< real want to restart */
+} fota_event_e;
+
+typedef enum {
+    FOTA_ERROR_NULL = 0,
+    FOTA_ERROR_VERSION_CHECK,
+    FOTA_ERROR_PREPARE,
+    FOTA_ERROR_NET_SEEK,
+    FOTA_ERROR_NET_READ,
+    FOTA_ERROR_WRITE,
+    FOTA_ERROR_MALLOC,
+    FOTA_ERROR_VERIFY
+} fota_error_code_e;
+
+typedef enum fota_status {
+    FOTA_INIT = 1,          /*!< create fota task, wait for version check */
+    FOTA_DOWNLOAD = 2,      /*!< start to download fota data */
+    FOTA_ABORT = 3,         /*!< read or write exception */
+    FOTA_FINISH = 4,        /*!< download finish */
+} fota_status_e;
+
+typedef struct {
+    char *cur_version;     /*!< the local image version, read from kv*/
+    char *local_changelog; /*!< the local image changelog, read from kv*/
+    char *new_version;     /*!< the incoming image version, read from cloud server*/
+    char *changelog;       /*!< the incoming image changelog, read from cloud server*/
+    char *fota_url;        /*!< the incoming image url, read from cloud server*/
+    int timestamp;         /*!< the incoming image timestamp, read from cloud server*/
+} fota_info_t;
+
+typedef struct fota_cls {
+    const char *name;
+    int (*init)(fota_info_t *info);
+    int (*version_check)(fota_info_t *info);
+    int (*finish)(fota_info_t *info);
+    int (*fail)(fota_info_t *info);
+    int (*restart)(void);
+    int64_t (*get_size)(const char *name);
+} fota_cls_t;
+
+typedef struct {
+    int read_timeoutms;         /*!< read timeout, millisecond */
+    int write_timeoutms;        /*!< write timeout, millisecond */
+    int retry_count;            /*!< when download abort, it will retry to download again in retry_count times */
+    int sleep_time;             /*!< the sleep time for auto-check task */
+    int auto_check_en;          /*!< whether check version automatic */
+} fota_config_t;
+
+typedef int (*fota_event_cb_t)(void *fota, fota_event_e event);   ///< fota Event call back.
+
+struct fota {
+    const fota_cls_t *cls;          /*!< the fota server ops */
+
+    netio_t *from;                  /*!< the read netio handle */
+    netio_t *to;                    /*!< the write netio handle */
+    fota_status_e status;           /*!< the fota status, see enum `fota_status_e` */
+    char *from_path;                /*!< where the fota data read from, url format */
+    char *to_path;                  /*!< where the fota data write to, url format*/
+    uint8_t *buffer;                /*!< buffer for reading data from net */
+    int offset;                     /*!< downloaded data bytes */
+    int total_size;                 /*!< total length of fota data */
+    int quit;                       /*!< fota task quit flag */
+    aos_task_t task;                /*!< fota task handle */
+    aos_sem_t sem;                  /*!< semaphore for waiting fota task quit */
+    aos_sem_t sem_download;         /*!< semaphore for starting download */
+    aos_sem_t do_check_event;       /*!< the semaphore for checking version loop or force */
+    fota_event_cb_t event_cb;       /*!< the event callback */
+    fota_error_code_e error_code;   /*!< fota error code, get it when event occurs */
+    fota_config_t config;           /*!< fota config */
+    fota_info_t info;               /*!< fota information */
+    aos_timer_t restart_timer;      /*!< the timer to norify to restart */
+    void *private;                  /*!< user data context */
+};
+
+/**
+ * @brief  创建FOTA服务
+ * @param  [in] fota: fota 句柄
+ * @return 0 on success, -1 on failed
+ */
+int fota_start(fota_t *fota);
+
+/**
+ * @brief  停止FOTA功能,退出FOTA服务
+ * @param  [in] fota: fota 句柄
+ * @return 0 on success, -1 on failed
+ */
+int fota_stop(fota_t *fota);
+
+/**
+ * @brief  开始下载
+ * @param  [in] fota: fota 句柄
+ * @return 0 on success, -1 on failed
+ */
+int fota_download(fota_t *fota);
+
+/**
+ * @brief  触发重启并开始升级
+ * @param  [in] fota: fota 句柄
+ * @param  [in] delay_ms: 延时多少毫秒才重启
+ * @return 0 on success, -1 on failed
+ */
+int fota_restart(fota_t *fota, int delay_ms);
+
+/**
+ * @brief  配置FOTA参数
+ * @param  [in] fota: fota 句柄
+ * @param  [in] config: 配置数据指针,具体见 `fota_config_t`
+ */
+void fota_config(fota_t *fota, fota_config_t *config);
+
+/**
+ * @brief  获取FOTA参数
+ * @param  [in] fota: fota 句柄
+ * @return 配置数据指针,具体见 `fota_config_t`
+ */
+fota_config_t *fota_get_config(fota_t *fota);
+
+/**
+ * @brief  设置是否自动不断检测服务器固件版本并升级的功能
+ * @param  [in] fota: fota 句柄
+ * @param  [in] enable: 0表示不自动检测,1表示自动检测
+ */
+void fota_set_auto_check(fota_t *fota, int enable);
+
+/**
+ * @brief  获取是否自动检测判断
+ * @param  [in] fota: fota 句柄
+ * @return 0表示不自动检测,1表示自动检测
+ */
+int fota_get_auto_check(fota_t *fota);
+
+/**
+ * @brief  获取升级状态
+ * @param  [in] fota: fota 句柄
+ * @return fota_status_e
+ */
+fota_status_e fota_get_status(fota_t *fota);
+
+/**
+ * @brief  获取剩余可用空间
+ * @param  [in] fota: fota 句柄
+ * @param  [in] name:分区名字
+ * @return  可用空间,>= 0 on success, -1 on failed
+ */
+int64_t fota_get_size(fota_t *fota, const char *name);
+
+/**
+ * @brief  fota初始化
+ * @param  [in] fota_name: FOTA平台名字,比如"cop",
+ * @param  [in] dst: 差分包存储url
+ * @param  [in] event_cb: 用户事件回调
+ * @return fota句柄或者NULL
+ */
+fota_t *fota_open(const char *fota_name, const char *dst, fota_event_cb_t event_cb);
+
+/**
+ * @brief  关闭FOTA功能,释放所有资源
+ * @param  [in] fota: fota 句柄
+ * @return 0 on success, -1 on failed
+ */
+int fota_close(fota_t *fota);
+
+/**
+ * @brief  强制检测版本
+ * @param  [in] fota: fota 句柄
+ */
+void fota_do_check(fota_t *fota);
+
+/**
+ * @brief  注册为cop平台,即从cop平台下载固件
+ * @return 0 on success, -1 on failed
+ */
+int fota_register_cop(void);
+
+/**
+ * @brief  注册平台接口
+ * @param  [in] cls: 不同平台实现的接口集合,具体实现接口见`fota_cls_t`
+ * @return 0 on success, -1 on failed
+ */
+int fota_register(const fota_cls_t *cls);
+
+/**
+ * @brief  对已经下载好的FOTA数据进行校验,(用户可自定义)
+ * @return 0 on success, -1 on failed
+ */
+int fota_data_verify(void);
+
+#if CONFIG_FOTA_DATA_IN_RAM > 0
+/**
+ * @brief  获取存储fota数据的ram地址,(用户可自定义)
+ * @return address
+ */
+unsigned long fota_data_address_get(void);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif

+ 126 - 0
components/fota/include/yoc/netio.h

@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+
+#ifndef YOC_NETIO_H
+#define YOC_NETIO_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <aos/list.h>
+
+#ifndef CONFIG_FOTA_BUFFER_SIZE
+#define CONFIG_FOTA_BUFFER_SIZE 512
+#define BUFFER_SIZE 2048
+#else
+#define BUFFER_SIZE (CONFIG_FOTA_BUFFER_SIZE * 2)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct netio_cls netio_cls_t;
+
+typedef struct {
+    const netio_cls_t *cls;     /*!< netio ops */
+    size_t offset;              /*!< offset for seek */
+    size_t size;                /*!< file size or partition size */
+    size_t block_size;          /*!< the size for transmission(sector size) */
+
+    void *private;              /*!< user data */
+} netio_t;
+
+struct netio_cls {
+    const char *name;
+    int (*open)(netio_t *io, const char *path);
+    int (*close)(netio_t *io);
+
+    int (*read)(netio_t *io, uint8_t *buffer, int length, int timeoutms);
+    int (*write)(netio_t *io, uint8_t *buffer, int length, int timeoutms);
+    int (*remove)(netio_t *io);
+    int (*seek)(netio_t *io, size_t offset, int whence);
+
+    void *private;
+};
+
+/**
+ * @brief  netio 注册
+ * @param  [in] cls: netio 操作接口
+ * @return 0 on success, -1 on failed
+ */
+int netio_register(const netio_cls_t *cls);
+
+/**
+ * @brief  将http操作功能注册到netio
+ * @return 0 on success, -1 on failed
+ */
+int netio_register_http(void);
+
+/**
+ * @brief  将http client操作功能注册到netio
+ * @param  [in] cert: https证书,可以为NULL
+ * @return 0 on success, -1 on failed
+ */
+int netio_register_httpc(const char *cert);
+
+/**
+ * @brief  将flash操作功能注册到netio
+ * @return 0 on success, -1 on failed
+ */
+int netio_register_flash(void);
+
+/**
+ * @brief  将coap操作功能注册到netio
+ * @return 0 on success, -1 on failed
+ */
+int netio_register_coap(void);
+
+/**
+ * @brief  netio 打开
+ * @param  [in] path: 下载或者储存 url
+ * @return netio_t句柄或者NULL
+ */
+netio_t *netio_open(const char *path);
+
+/**
+ * @brief  netio 关闭
+ * @param  [in] io: netio句柄
+ * @return 0 on success, -1 on failed
+ */
+int netio_close(netio_t *io);
+
+/**
+ * @brief  netio 读取
+ * @param  [in] io: netio句柄
+ * @param  [in] buffer: 用于存放读取数据的buffer
+ * @param  [in] lenght: buffer大小
+ * @param  [in] timeoutms: 超时时长
+ * @return 0表示文件读完,-1超时失败,否则为读取的长度
+ */
+int netio_read(netio_t *io, uint8_t *buffer, size_t lenght, int timeoutms);
+
+/**
+ * @brief  netio 写
+ * @param  [in] io: netio句柄
+ * @param  [in] buffer: 用于存放读取数据的buffer
+ * @param  [in] lenght: buffer大小
+ * @param  [in] timeoutms: 超时时长
+ * @return -1超时失败,否则为写入的长度
+ */
+int netio_write(netio_t *io, uint8_t *buffer, size_t lenght, int timeoutms);
+
+/**
+ * @brief  netio 寻址
+ * @param  [in] io: netio句柄
+ * @param  [in] offset:偏移
+ * @param  [in] whence:偏移方向
+ * @return 0 on success, -1 on failed
+ */
+int netio_seek(netio_t *io, size_t offset, int whence);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 164 - 0
components/fota/netio/flash.c

@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+
+#include <stdio.h>
+#include <aos/kernel.h>
+#include <yoc/netio.h>
+
+#define TAG "fota"
+
+#ifndef __linux__
+#include <aos/aos.h>
+#include <yoc/partition.h>
+static int flash_open(netio_t *io, const char *path)
+{
+    partition_t handle = partition_open(path + sizeof("flash://") - 1);
+
+    if (handle >= 0) {
+        partition_info_t *lp = hal_flash_get_info(handle);
+        aos_assert(lp);
+
+        io->size = lp->length;
+        io->block_size = lp->sector_size;
+
+        io->private = (void *)handle;
+
+        return 0;
+    }
+
+    return -1;
+}
+
+static int flash_close(netio_t *io)
+{
+    partition_t handle = (partition_t)io->private;
+    partition_close(handle);
+
+    return 0;
+}
+
+static int flash_read(netio_t *io, uint8_t *buffer, int length, int timeoutms)
+{
+    partition_t handle = (partition_t)io->private;
+
+    if (io->size - io->offset < length)
+        length = io->size - io->offset;
+
+    if (partition_read(handle, io->offset, buffer, length) >= 0) {
+        io->offset += length;
+        return length;
+    }
+
+    return -1;
+}
+
+static int fota_flash_erase(partition_t partition, off_t off_set, int block_size, uint32_t block_count)
+{
+    if (off_set % block_size == 0) {
+        if (partition_erase(partition, off_set, block_count) < 0) {
+            LOGD(TAG, "0 erase addr:%x length:%x\n", off_set, block_count);
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+static int flash_write(netio_t *io, uint8_t *buffer, int length, int timeoutms)
+{
+    partition_t handle = (partition_t)io->private;
+    // LOGD(TAG, "%d %d %d\n", io->size, io->offset, length);
+    if (io->size - io->offset < length)
+        length = io->size - io->offset;
+    // LOGD(TAG, "length %d\n", length);
+    if (fota_flash_erase(handle, io->offset + (io->block_size << 1), io->block_size, (length + io->block_size - 1) / io->block_size) < 0) {
+        LOGE(TAG, "erase addr:%x length:%x\n", io->offset + (io->block_size << 1), (length + io->block_size - 1) / io->block_size);
+
+        return -1;
+    }
+
+    if (partition_write(handle, io->offset + (io->block_size << 1), buffer, length) >= 0) {
+        // LOGD(TAG, "write addr:%x length:%x\n", io->offset + (io->block_size << 1), length);
+        io->offset += length;
+        return length;
+    }
+
+    LOGD(TAG, "write fail addr:0x%x length:0x%x\n", io->offset + (io->block_size << 1), length);
+    return -1;
+}
+
+static int flash_seek(netio_t *io, size_t offset, int whence)
+{
+    // partition_t handle = (partition_t)io->private;
+
+    switch (whence) {
+        case SEEK_SET:
+            io->offset = offset;
+            return 0;
+        case SEEK_CUR:
+            io->offset += offset;
+            return 0;
+        case SEEK_END:
+            io->offset = io->size - offset;
+            return 0;
+    }
+
+    return -1;
+}
+#else
+typedef int partition_t;
+
+static int flash_open(netio_t *io, const char *path)
+{
+    return 0;
+}
+
+static int flash_close(netio_t *io)
+{
+    return 0;
+}
+
+static int flash_read(netio_t *io, uint8_t *buffer, int length, int timeoutms)
+{
+    return 0;
+}
+
+static int flash_write(netio_t *io, uint8_t *buffer, int length, int timeoutms)
+{
+    return 0;
+}
+
+static int flash_seek(netio_t *io, size_t offset, int whence)
+{
+    // partition_t handle = (partition_t)io->private;
+
+    switch (whence) {
+        case SEEK_SET:
+            io->offset = offset;
+            return 0;
+        case SEEK_CUR:
+            io->offset += offset;
+            return 0;
+        case SEEK_END:
+            io->offset = io->size - offset;
+            return 0;
+    }
+
+    return -1;
+}
+#endif /* __linux__ */
+
+const netio_cls_t flash = {
+    .name = "flash",
+    .open = flash_open,
+    .close = flash_close,
+    .write = flash_write,
+    .read = flash_read,
+    .seek = flash_seek,
+};
+
+int netio_register_flash(void)
+{
+    return netio_register(&flash);
+}

+ 144 - 0
components/fota/netio/http.c

@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "../http/http.h"
+#include <yoc/fota.h>
+#include <yoc/netio.h>
+#include <aos/kernel.h>
+#include "util/network.h"
+#include "ulog/ulog.h"
+
+#define TAG "fota"
+
+static int http_read(netio_t *io, uint8_t *buffer, int length, int timeoutms)
+{
+    int content_len;
+    char *head_end;
+    http_t *http = (http_t*)io->private;
+
+    if (io->offset >= io->size) {
+        LOGD(TAG, "http_read done: %d %d", io->size, io->offset);
+        return 0;
+    }
+
+    memset(http->buffer, 0, BUFFER_SIZE);
+    http->buffer_offset = 0;
+    http_head_sets(http, "Host", http->host);
+
+    int range_end = io->offset + io->block_size - 1;
+    char *range;
+    range = aos_zalloc(56);
+    if (io->size > 0) {
+        range_end = range_end < (io->size - 1)? range_end : (io->size - 1);
+    }
+    snprintf(range, 56, "bytes=%d-%d", io->offset, range_end);
+    http_head_sets(http, "Range", range);
+    aos_free(range);
+    http_head_sets(http, "Connection", "keep-alive");
+    http_head_sets(http, "Cache-Control", "no-cache");
+
+    http_get(http, 10000);
+
+    if ((content_len = http_wait_resp(http, &head_end, 10000)) < 0) {
+        LOGE(TAG, "recv failed: %d", content_len);
+        return content_len;
+    }
+
+    if (content_len != (range_end - io->offset + 1)) {
+        LOGE(TAG, "content_len overflow :%d", content_len);
+        return -1;
+    }
+    // LOGD(TAG, "recv sucess: %d", content_len);
+    io->offset += content_len;
+
+    memcpy(buffer, head_end, content_len);
+
+    return content_len;
+}
+
+static int http_open(netio_t *io, const char *path)
+{
+    http_t *http;
+    if ((http = http_init(path)) == NULL) {
+        LOGD(TAG, "e http init");
+        return -1;
+    }
+
+    io->offset = 0;
+    io->block_size = CONFIG_FOTA_BUFFER_SIZE;// 1024
+    // io->private = http;
+
+    int content_len;
+    char *range;
+    char *head_end;
+    memset(http->buffer, 0, BUFFER_SIZE);
+    http->buffer_offset = 0;
+    http_head_sets(http, "Host", http->host);
+
+    int range_end = io->offset + io->block_size - 1;
+
+    range = (void *)aos_zalloc(56);
+    if (io->size > 0) {
+        range_end = range_end < (io->size - 1)? range_end : (io->size - 1);
+    }
+    snprintf(range, 56, "bytes=%d-%d", io->offset, range_end);
+    http_head_sets(http, "Range", range);
+    aos_free(range);
+    http_head_sets(http, "Connection", "keep-alive");
+    http_head_sets(http, "Cache-Control", "no-cache");
+
+    http_get(http, 10000);
+
+    if ((content_len = http_wait_resp(http, &head_end, 10000)) < 0) {
+        LOGD(TAG, "recv failed: %d", content_len);
+        return -1;
+    }
+
+    if ((range = strstr((char *)http->buffer, "Content-Range: bytes ")) == NULL) {
+        LOGD(TAG, "no Content-Range");
+        return -1;
+    }
+
+    io->size = atoi(strchr(range, '/') + 1);
+
+    LOGD(TAG, "range_len: %d", io->size);
+
+    io->private = http;
+
+    return 0;
+}
+
+static int http_seek(netio_t *io, size_t offset, int whence)
+{
+    // http_t *http = (http_t*)io->private;
+
+    io->offset = offset;
+
+    return 0;
+}
+
+static int http_close(netio_t *io)
+{
+    http_t *http = (http_t*)io->private;
+
+    return http_deinit(http);
+}
+
+const netio_cls_t http_cls = {
+    .name = "http",
+    .read = http_read,
+    .seek = http_seek,
+    .open = http_open,
+    .close = http_close,
+    // .private = http,
+    // .getinfo = http_getinfo,
+};
+
+int netio_register_http(void)
+{
+    return netio_register(&http_cls);
+}

+ 328 - 0
components/fota/netio/httpc.c

@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+
+#include <yoc/fota.h>
+#if CONFIG_FOTA_USE_HTTPC == 1
+#include <yoc/netio.h>
+#include <ulog/ulog.h>
+#include <aos/kernel.h>
+#include <errno.h>
+#include <http_client.h>
+#include "util/network.h"
+
+#define TAG "fota-httpc"
+
+typedef struct {
+    http_client_handle_t http_client;
+    const char *cert;
+    const char *path;
+} httpc_priv_t;
+
+static int _http_event_handler(http_client_event_t *evt)
+{
+    switch(evt->event_id) {
+        case HTTP_EVENT_ERROR:
+            LOGD(TAG, "HTTP_EVENT_ERROR");
+            break;
+        case HTTP_EVENT_ON_CONNECTED:
+            LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
+            break;
+        case HTTP_EVENT_HEADER_SENT:
+            LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
+            break;
+        case HTTP_EVENT_ON_HEADER:
+            // LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
+            break;
+        case HTTP_EVENT_ON_DATA:
+            // LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
+            break;
+        case HTTP_EVENT_ON_FINISH:
+            LOGD(TAG, "HTTP_EVENT_ON_FINISH");
+            break;
+        case HTTP_EVENT_DISCONNECTED:
+            LOGD(TAG, "HTTP_EVENT_DISCONNECTED");
+            break;
+    }
+    return 0;
+}
+
+static bool process_again(int status_code)
+{
+    switch (status_code) {
+        case HttpStatus_MovedPermanently:
+        case HttpStatus_Found:
+        case HttpStatus_TemporaryRedirect:
+        case HttpStatus_Unauthorized:
+            return true;
+        default:
+            return false;
+    }
+    return false;
+}
+
+static http_errors_t _http_handle_response_code(http_client_handle_t http_client, int status_code, char *buffer, int buf_size, int data_size)
+{
+    http_errors_t err;
+    if (status_code == HttpStatus_MovedPermanently || status_code == HttpStatus_Found || status_code == HttpStatus_TemporaryRedirect) {
+        err = http_client_set_redirection(http_client);
+        if (err != HTTP_CLI_OK) {
+            LOGE(TAG, "URL redirection Failed");
+            return err;
+        }
+    } else if (status_code == HttpStatus_Unauthorized) {
+        return HTTP_CLI_FAIL;
+    } else if(status_code == HttpStatus_NotFound || status_code == HttpStatus_Forbidden) {
+        LOGE(TAG, "File not found(%d)", status_code);
+        return HTTP_CLI_FAIL;
+    } else if (status_code == HttpStatus_InternalError) {
+        LOGE(TAG, "Server error occurred(%d)", status_code);
+        return HTTP_CLI_FAIL;
+    }
+
+    // process_again() returns true only in case of redirection.
+    if (data_size > 0 && process_again(status_code)) {
+        /*
+        *  In case of redirection, http_client_read() is called
+        *  to clear the response buffer of http_client.
+        */
+        int data_read;
+        while (data_size > buf_size) {
+            data_read = http_client_read(http_client, buffer, buf_size);
+            if (data_read <= 0) {
+                return HTTP_CLI_OK;
+            }
+            data_size -= buf_size;
+        }
+        data_read = http_client_read(http_client, buffer, data_size);
+        if (data_read <= 0) {
+            return HTTP_CLI_OK;
+        }
+    }
+    return HTTP_CLI_OK;
+}
+
+static http_errors_t _http_connect(http_client_handle_t http_client, char *buffer, int buf_size)
+{
+#define MAX_REDIRECTION_COUNT 10
+    http_errors_t err = HTTP_CLI_FAIL;
+    int status_code = 0, header_ret;
+    int redirect_counter = 0;
+
+    do {
+        if (redirect_counter++ > MAX_REDIRECTION_COUNT) {
+            LOGE(TAG, "redirect_counter is max");
+            return HTTP_CLI_FAIL;
+        }
+        if (process_again(status_code)) {
+            LOGD(TAG, "process again,status code:%d", status_code);
+        }
+        err = http_client_open(http_client, 0);
+        if (err != HTTP_CLI_OK) {
+            LOGE(TAG, "Failed to open HTTP connection");
+            return err;
+        }
+        header_ret = http_client_fetch_headers(http_client);
+        if (header_ret < 0) {
+            LOGE(TAG, "header_ret:%d", header_ret);
+            return header_ret;
+        }
+        LOGD(TAG, "header_ret:%d", header_ret);
+        status_code = http_client_get_status_code(http_client);
+        LOGD(TAG, "status code:%d", status_code);
+        err = _http_handle_response_code(http_client, status_code, buffer, buf_size, header_ret);
+        if (err != HTTP_CLI_OK) {
+            LOGE(TAG, "e handle resp code:%d", err);
+            return err;
+        }
+    } while (process_again(status_code));
+    return err;
+}
+
+static void _http_cleanup(http_client_handle_t client)
+{
+    LOGD(TAG, "httpc cleanup...");
+    if (client) {
+        http_client_cleanup(client);
+    }
+}
+
+static int http_read(netio_t *io, uint8_t *buffer, int length, int timeoutms)
+{
+#define RANGE_BUF_SIZE 56
+    int read_len;
+    long long time1ms;
+    httpc_priv_t *priv = (httpc_priv_t *)io->private;
+    http_client_handle_t client = priv->http_client;
+
+    if (client == NULL) {
+        int ret = 0;
+        int statuscode;
+        char *range = NULL;
+        char *buffer = NULL;
+        http_errors_t err;
+        http_client_config_t config = {0};
+
+        config.method = HTTP_METHOD_GET;
+        config.url = priv->path;
+        config.timeout_ms = timeoutms;
+        config.buffer_size = BUFFER_SIZE;
+        config.cert_pem = priv->cert;
+        config.event_handler = _http_event_handler;
+        client = http_client_init(&config);
+        if (!client) {
+            LOGE(TAG, "Client init e");
+            ret = -1;
+            goto exit;
+        }
+        LOGD(TAG, "http client init ok.[%s]", config.url);
+        LOGD(TAG, "http read connecting........");
+        buffer = aos_zalloc(BUFFER_SIZE + 1);
+        if (!buffer) {
+            LOGE(TAG, "http open nomem.");
+            ret = -ENOMEM;
+            goto exit;
+        }
+        range = (void *)aos_zalloc(RANGE_BUF_SIZE);
+        if (!range) {
+            LOGE(TAG, "range malloc e");
+            ret = -ENOMEM;
+            goto exit;
+        }
+        snprintf(range, RANGE_BUF_SIZE, "bytes=%d-", io->offset);
+        LOGD(TAG, "range:%s", range);
+        err = http_client_set_header(client, "Range", range);
+        if (err != HTTP_CLI_OK) {
+            LOGE(TAG, "set header Range e");
+            ret = -1;
+            goto exit;
+        }
+        err = http_client_set_header(client, "Connection", "keep-alive");
+        if (err != HTTP_CLI_OK) {
+            LOGE(TAG, "set header Connection e");
+            ret = -1;
+            goto exit;
+        }
+        err = http_client_set_header(client, "Cache-Control", "no-cache");
+        if (err != HTTP_CLI_OK) {
+            LOGE(TAG, "set header Cache-Control e");
+            ret = -1;
+            goto exit;
+        }
+
+        err = _http_connect(client, buffer, BUFFER_SIZE);
+        if (err != HTTP_CLI_OK) {
+            LOGE(TAG, "Client connect e");
+            ret = -1;
+            goto exit;
+        }
+        statuscode = http_client_get_status_code(client);
+        if (statuscode != 206) {
+            LOGE(TAG, "not 206 Partial Content");
+            ret = -1;
+            goto exit;
+        }
+        io->size = http_client_get_content_length(client);
+        io->size += io->offset;
+        LOGD(TAG, "range_len: %d", io->size);
+        priv->http_client = client;
+exit:
+        if (buffer) aos_free(buffer);
+        if (range) aos_free(range);
+        if (ret != 0) {
+            _http_cleanup(client);
+            return ret;
+        }
+    }
+    if (io->offset >= io->size) {
+        LOGW(TAG, "http_read done: offset:%d tsize:%d", io->offset, io->size);
+        return 0;
+    }
+
+    time1ms = aos_now_ms();
+    read_len = 0;
+    while (read_len < length) {
+        if (aos_now_ms() - time1ms > timeoutms) {
+            break;
+        }
+        int data_read = http_client_read(client, (char *)buffer + read_len, length - read_len);
+        if (data_read < 0) {
+            LOGW(TAG, "http client read error:%d, errno:%d", data_read, errno);
+            return data_read;
+        } else if (data_read == 0) {
+            LOGD(TAG, "http client read 0 size");
+            break;
+        }
+        read_len += data_read;
+    }
+    io->offset += read_len;
+    return read_len;
+}
+
+static int http_open(netio_t *io, const char *path)
+{
+    const char *cert;
+
+    LOGD(TAG, "http open:%s", path);
+    cert = (const char *)io->cls->private;
+
+    io->size = 0;
+    io->offset = 0;
+    io->block_size = CONFIG_FOTA_BUFFER_SIZE;
+
+    httpc_priv_t *priv = aos_zalloc_check(sizeof(httpc_priv_t));
+    if (!priv) {
+        LOGE(TAG, "http open nomem e");
+        return -1;
+    }
+    priv->http_client = NULL;
+    priv->path = path;
+    priv->cert = cert;
+    io->private = priv;
+#if 0 // just for test, mem leak...
+    char *tempbuffer = (char *)aos_malloc(400);
+    if (tempbuffer == NULL) {
+        LOGE(TAG, "malloc tempbuffer failed");
+        return -1;
+    }
+    memset(tempbuffer, 0, 400);
+    if (strstr(priv->path, "https://")) {
+        snprintf(tempbuffer, 400, "http://%s", priv->path + strlen("https://"));
+    }
+    priv->path = tempbuffer;
+#endif
+#if 0 //just for test redirection
+    priv->path = "http://192.168.1.102:8889";
+#endif
+    return 0;
+}
+
+static int http_seek(netio_t *io, size_t offset, int whence)
+{
+    io->offset = offset;
+    return 0;
+}
+
+static int http_close(netio_t *io)
+{
+    httpc_priv_t *priv = (httpc_priv_t *)io->private;
+    http_client_handle_t client = priv->http_client;
+    _http_cleanup(client);
+    aos_free(priv);
+    return 0;
+}
+
+netio_cls_t httpc_cls = {
+    .name = "http",
+    .read = http_read,
+    .seek = http_seek,
+    .open = http_open,
+    .close = http_close,
+};
+
+int netio_register_httpc(const char *cert)
+{
+    httpc_cls.private = (void *)cert;
+    return netio_register(&httpc_cls);
+}
+#endif

+ 98 - 0
components/fota/netio/netio.c

@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+#include <stdio.h>
+#include <string.h>
+#include <aos/list.h>
+#include <aos/kernel.h>
+#include <yoc/netio.h>
+#include <ulog/ulog.h>
+
+#define TAG "fota"
+typedef struct fota_netio_list {
+    slist_t next;
+    const netio_cls_t *cls;
+} netio_node_t;
+
+static AOS_SLIST_HEAD(netio_cls_list);
+
+int netio_register(const netio_cls_t *cls)
+{
+    netio_node_t *node = aos_malloc(sizeof(netio_node_t));
+
+    if (node) {
+        node->cls = cls;
+        slist_add_tail(&node->next, &netio_cls_list);
+        return 0;
+    }
+
+    return -1;
+}
+
+netio_t *netio_open(const char *path)
+{
+    netio_t *io = NULL;
+    char *delim = strstr(path, "://");
+    LOGD(TAG, "path:%s delim:%s\n", path, delim);
+    if (delim) {
+        int len = delim - path;
+        netio_node_t *node;
+
+        if (strstr(path, "https")) {
+            len -= 1;
+        }
+
+        slist_for_each_entry(&netio_cls_list, node, netio_node_t, next) {
+            if (strncmp(node->cls->name, path, len) == 0) {
+                if (node->cls->open) {
+                    io = aos_zalloc(sizeof(netio_t));
+                    io->cls = node->cls;
+                    if (io->cls->open(io, path) < 0) {
+                        aos_free(io);
+                        LOGD(TAG, "open fail\n");
+                        return NULL;
+                    }
+                }
+                LOGD(TAG, "open break,%s\n", node->cls->name);
+                break;
+            }
+        }
+    }
+
+    return io;
+}
+
+int netio_close(netio_t *io)
+{
+    int ret = -1;
+    if (io->cls->close) {
+        ret = io->cls->close(io);
+        aos_free(io);
+    }
+
+    return ret;
+}
+
+int netio_read(netio_t *io, uint8_t *buffer, size_t lenght, int timeoutms)
+{
+    if (io->cls->read)
+        return io->cls->read(io, buffer, lenght, timeoutms);
+
+    return -1;
+}
+
+int netio_write(netio_t *io, uint8_t *buffer, size_t lenght, int timeoutms)
+{
+    if (io->cls->write)
+        return io->cls->write(io, buffer, lenght, timeoutms);
+
+    return -1;
+}
+
+int netio_seek(netio_t *io, size_t offset, int whence)
+{
+    if (io->cls->seek)
+        return io->cls->seek(io, offset, whence);
+
+    return -1;
+}

+ 83 - 0
components/fota/package.yaml

@@ -0,0 +1,83 @@
+
+## 第一部分: 基础信息
+name: fota                                          # <必选项> 包名称 (符合C语言变量命名规则),长度少于等于64字节
+version: v7.5.0                                     # <必选项> 组件版本号
+description: 空中下载软件服务                       # <必选项> 建议至少20字以上
+type: common                                        # <必选项> 组件类型,为:solution, chip, board, common, sdk
+tag: 系统升级                                       # <可选项> 组件分类,缺省值: ''
+keywords:                                           # <可选项> 标签,会影响到组件被搜索的效果,合理的标签很重要
+  - base
+license: Apache license v2.0                        # <可选项> 源代码的许可证,要确保所有代码、文件的许可证不冲突。如:MIT,Apache license v2.0,BSD
+
+## 第二部分:依赖信息
+#           指定该组件依赖的组件及版本
+# sdk_chip:                                # <可选项> 该组件依赖sdk组件,合理的依赖才能保证组件能编译、使用
+#   - sdk_chip_csky_dummy: v7.4.0
+#   - sdk_chip_riscv_dummy: v7.4.0
+# depends:                                 # <可选项> 该组件依赖其他的组件,合理的依赖才能保证组件能编译、使用
+#   - minilibc: v7.4.0
+#   - aos: v7.4.0
+
+## 第四部分:编译连接信息
+# build_config:                            # <可选项> 编译配置项
+#   include:                               # <可选项> 编译时,影响编译器的-I 参数 ,全局有效
+#     - src                                #        include 只能是该软件包下的目录,不能使用外部目录
+#   internal_include:                      # <可选项> 编译时,影响编译器的-I 参数 ,组件内有效
+#     - include
+#   cflag: ''                              # <可选项> C 编译器所需要要的编译参数
+#   cxxflag: ''                            # <可选项> CXX 编译器所需要要的编译参数
+#   asmflag: ''                            # <可选项> 汇编器所需要要参数
+#   define:                                # <可选项> 宏定义, 增加编译器的-D 选项,如:
+#     XXX: 1                               #   -DXXX=1
+#     AAA: 1                               #   -DAAA
+#     STR: "abc"                           #   -DSTR="abc"
+#   libs:                                  # 该组件中支持的二进制静态库,如:libxxx.a, libyyy.a
+#     - xxx                                #   -lxxx
+#     - yyy                                #   -lyyy
+#   libpath:                               # 指定静态库所在的路径(相对于该组件路径)
+#     - libs                               #   -Llibs
+build_config:
+  include:
+    - include
+  internal_include:
+    - .
+
+# source_file:                             # <可选项> 指定参与编译的源代码文件,支持通配符,采用相对路径
+#   - src/*.c                              # 例:组件 src 目录下所有的扩展名为 c 的源代码文件
+source_file:
+  - "netio/flash.c"
+  - "netio/netio.c"
+  - "netio/http.c"
+  - "netio/httpc.c"
+  - "fota/fota.c"
+  - "fota/fota_cop.c"
+  - "fota/fota_verify.c"
+  - "http/http.c"
+  - "util/network.c"
+
+## 第五部分:配置信息
+# def_config:                              # 组件的可配置项
+#   CONFIG_DEBUG: y
+#   CONFIG_PARAM_NOT_CHECK: y
+#   CONFIG_CLI: y
+def_config:
+  CONFIG_FOTA_IMG_AUTHENTICITY_NOT_CHECK: 1
+
+## 第六部分:安装信息
+# install:
+#   - dest: include/                       # 安装的目的路径 dest是相对路径,通常是相对于YoC SDK 安装目录
+#     source:                              # 安装源列表
+#      - src/*.h                           # 支持通配符,相对路径
+install:
+  - dest: "include/yoc"
+    source:
+      - "include/yoc/*.h"
+
+## 第七部分:导出部分
+# export:
+#   - dest: "<SOLUTION_PATH>/generated/data" # 安装的目的路径 dest是相对路径
+#     source:                                # 安装源列表
+#       - "bootimgs/boot"
+#       - "bootimgs/tee"
+#       - "bootimgs/mtb"
+#       - "configs/config.yaml"

+ 216 - 0
components/fota/util/network.c

@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include "aos/kernel.h"
+
+#include "network.h"
+
+static int net_read(network_t *n, unsigned char *buf, int count, int timeout_ms)
+{
+    fd_set fds;
+    int lfirst = 1;
+    int rc, len = 0, fd;
+    struct timeval tv;
+    unsigned long long delta;
+    struct timeval t1 = {0}, t2 = {0};
+
+    if (!(n && buf && count)) {
+        return -1;
+    }
+
+    fd = n->fd;
+    tv.tv_sec = timeout_ms / 1000;
+    tv.tv_usec = (timeout_ms % 1000) * 1000;
+
+    gettimeofday(&t1, NULL);
+    while (len < count) {
+        FD_ZERO(&fds);
+        FD_SET(fd, &fds);
+        /* FIXME: patch for select. */
+        if (lfirst) {
+            lfirst = 0;
+        } else {
+            gettimeofday(&t2, NULL);
+            delta = (t2.tv_sec  - t1.tv_sec) * 1000 + (t2.tv_usec - t1.tv_usec) / 1000;
+            if (delta >= timeout_ms)
+                break;
+        }
+
+        rc = select(fd + 1, &fds, NULL, NULL, &tv);
+        if (rc < 0) {
+            if ((errno == EINTR) || (errno == EAGAIN)) {
+                aos_msleep(20);
+                continue;
+            }
+            return -1;
+        } else if (rc == 0) {
+            /* time out */
+            break;
+        }
+
+        if (!FD_ISSET(fd, &fds)) {
+            aos_msleep(20);
+            continue;
+        }
+
+        rc = recv(fd, buf + len, count - len, 0);
+        if (rc < 0) {
+            if ((errno == EINTR) || (errno == EAGAIN)) {
+                aos_msleep(20);
+                continue;
+            }
+            return -1;
+        } else if (rc == 0) {
+            /* the sockfd may be closed */
+            break;
+        }
+
+        len += rc;
+    }
+
+    return len;
+}
+
+/**
+ * @brief  write n bytes from a sockfd with timeout
+ * @param  [in] fd
+ * @param  [in] buf
+ * @param  [in] count
+ * @param  [in] timeout_ms
+ * @return -1 on err
+ */
+static int net_write(network_t *n, unsigned char *buf, int count, int timeout_ms)
+{
+    fd_set fds;
+    int lfirst = 1;
+    int rc, len = 0, fd;
+    struct timeval tv;
+    unsigned long long delta;
+    struct timeval t1 = {0}, t2 = {0};
+
+    if (!(n && buf && count)) {
+        return -1;
+    }
+
+    fd = n->fd;
+    tv.tv_sec = timeout_ms / 1000;
+    tv.tv_usec = (timeout_ms % 1000) * 1000;
+
+    gettimeofday(&t1, NULL);
+    while (len < count) {
+        FD_ZERO(&fds);
+        FD_SET(fd, &fds);
+        /* FIXME: patch for select. */
+        if (lfirst) {
+            lfirst = 0;
+        } else {
+            gettimeofday(&t2, NULL);
+            delta = (t2.tv_sec  - t1.tv_sec) * 1000 + (t2.tv_usec - t1.tv_usec) / 1000;
+            if (delta >= timeout_ms)
+                break;
+        }
+
+        rc = select(fd + 1, NULL, &fds, NULL, &tv);
+        if (rc < 0) {
+            if ((errno == EINTR) || (errno == EAGAIN)) {
+                aos_msleep(20);
+                continue;
+            }
+            return -1;
+        } else if (rc == 0) {
+            /* time out */
+            break;
+        }
+
+        if (!FD_ISSET(fd, &fds)) {
+            aos_msleep(20);
+            continue;
+        }
+
+        rc = sendto(fd, buf + len, count - len, 0,
+                    (struct sockaddr *)&(n->address),
+                    sizeof(n->address));
+        if (rc < 0) {
+            if ((errno == EINTR) || (errno == EAGAIN)) {
+                aos_msleep(20);
+                continue;
+            }
+            return -1;
+        } else if (rc == 0) {
+            /* the sockfd may be closed */
+            break;
+        }
+
+        len += rc;
+    }
+
+    return len;
+}
+
+static int net_connect(network_t *n, char *addr, int port, int net_type)
+{
+    int rc = -1;
+    sa_family_t family = AF_INET;
+    struct addrinfo *result = NULL;
+    struct addrinfo hints = {0, AF_UNSPEC, net_type, 0, 0, NULL, NULL, NULL};
+
+    if ((rc = getaddrinfo(addr, NULL, &hints, &result)) == 0) {
+        struct addrinfo *res = result;
+
+        /* prefer ip4 addresses */
+        while (res) {
+            if (res->ai_family == AF_INET) {
+                result = res;
+                break;
+            }
+
+            res = res->ai_next;
+        }
+
+        if (result->ai_family == AF_INET) {
+            n->address.sin_port = htons(port);
+            n->address.sin_family = family = AF_INET;
+            n->address.sin_addr = ((struct sockaddr_in *)(result->ai_addr))->sin_addr;
+        } else {
+            rc = -1;
+        }
+
+        freeaddrinfo(result);
+    }
+
+    if (rc == 0) {
+        n->fd = socket(family, net_type, 0);
+
+        if (n->fd != -1) {
+            rc = connect(n->fd, (struct sockaddr *)&n->address, sizeof(n->address));
+        }
+
+        if (rc < 0) {
+            close(n->fd);
+            n->fd = 0;
+        }
+    }
+
+    return rc;
+}
+
+static void net_disconnect(network_t *n)
+{
+    close(n->fd);
+    n->fd = 0;
+}
+
+void network_init(network_t *n)
+{
+    n->fd = 0;
+    n->net_read       = net_read;
+    n->net_write      = net_write;
+    n->net_connect    = net_connect;
+    n->net_disconncet = net_disconnect;
+}

+ 34 - 0
components/fota/util/network.h

@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+
+#ifndef __NETWORK_H__
+#define __NETWORK_H__
+
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#ifndef __linux__
+#include <lwip/netdb.h>
+#else
+#include <netdb.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct network {
+    int fd;
+    struct sockaddr_in address;
+    int (*net_connect)(struct network *n, char *addr, int port, int net_type);
+    int (*net_read)(struct network *, unsigned char *, int, int);
+    int (*net_write)(struct network *, unsigned char *, int, int);
+    void (*net_disconncet)(struct network *n);
+} network_t;
+
+void network_init(network_t *n);
+
+#ifdef __cplusplus
+}
+#endif
+#endif

+ 7 - 0
components/fota_service/CMakeLists.txt

@@ -0,0 +1,7 @@
+file(GLOB_RECURSE LIBSOURCE "${COMPONENTS_DIR}/fota_service/*.c")
+
+include_directories(${COMPONENTS_DIR}/fota_service/include)
+
+add_library(fota_service STATIC ${LIBSOURCE})
+
+target_link_libraries(fota_service PUBLIC ${LIBS_LIBRARIES})

+ 338 - 0
components/fota_service/dbus/fota-introspect.c

@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2018-2021 Alibaba Group Holding Limited
+ */
+
+/*
+ * fota_supplicant - D-Bus introspection
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+ * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/list.h"
+#include "utils/fota_buf.h"
+#include "dbus_new_helpers.h"
+
+#include <fota.h>
+
+#define fota_printf(...)
+
+struct interfaces {
+    struct dl_list list;
+    char *dbus_interface;
+    struct fotabuf *xml;
+};
+
+static void *zalloc(size_t size)
+{
+    void *n = malloc(size);
+
+    if (n) {
+        memset(n, 0, size);
+    }
+
+    return n;
+}
+
+static struct interfaces *add_interface(struct dl_list *list,
+                                        const char *dbus_interface)
+{
+    struct interfaces *iface;
+
+    dl_list_for_each(iface, list, struct interfaces, list) {
+        if (os_strcmp(iface->dbus_interface, dbus_interface) == 0) {
+            return iface; /* already in the list */
+        }
+    }
+
+    iface = zalloc(sizeof(struct interfaces));
+
+    if (!iface) {
+        return NULL;
+    }
+
+    iface->dbus_interface = os_strdup(dbus_interface);
+    iface->xml = fotabuf_alloc(15000);
+
+    if (iface->dbus_interface == NULL || iface->xml == NULL) {
+        os_free(iface->dbus_interface);
+        fotabuf_free(iface->xml);
+        os_free(iface);
+        return NULL;
+    }
+
+    fotabuf_printf(iface->xml, "<interface name=\"%s\">", dbus_interface);
+    dl_list_add_tail(list, &iface->list);
+    return iface;
+}
+
+
+static void add_arg(struct fotabuf *xml, const char *name, const char *type,
+                    const char *direction)
+{
+    fotabuf_printf(xml, "<arg name=\"%s\"", name);
+
+    if (type) {
+        fotabuf_printf(xml, " type=\"%s\"", type);
+    }
+
+    if (direction) {
+        fotabuf_printf(xml, " direction=\"%s\"", direction);
+    }
+
+    fotabuf_put_str(xml, "/>");
+}
+
+
+static void add_entry(struct fotabuf *xml, const char *type, const char *name,
+                      const struct fota_dbus_argument *args, int include_dir)
+{
+    const struct fota_dbus_argument *arg;
+
+    if (args == NULL || args->name == NULL) {
+        fotabuf_printf(xml, "<%s name=\"%s\"/>", type, name);
+        return;
+    }
+
+    fotabuf_printf(xml, "<%s name=\"%s\">", type, name);
+
+    for (arg = args; arg && arg->name; arg++) {
+        add_arg(xml, arg->name, arg->type,
+                include_dir ? (arg->dir == ARG_IN ? "in" : "out") :
+                NULL);
+    }
+
+    fotabuf_printf(xml, "</%s>", type);
+}
+
+
+static void add_property(struct fotabuf *xml,
+                         const struct fota_dbus_property_desc *dsc)
+{
+    fotabuf_printf(xml, "<property name=\"%s\" type=\"%s\" "
+                    "access=\"%s%s\"/>",
+                    dsc->dbus_property, dsc->type,
+                    dsc->getter ? "read" : "",
+                    dsc->setter ? "write" : "");
+}
+
+
+static void extract_interfaces_methods(
+    struct dl_list *list, const struct fota_dbus_method_desc *methods)
+{
+    const struct fota_dbus_method_desc *dsc;
+    struct interfaces *iface;
+
+    for (dsc = methods; dsc && dsc->dbus_method; dsc++) {
+        iface = add_interface(list, dsc->dbus_interface);
+
+        if (iface) {
+            add_entry(iface->xml, "method", dsc->dbus_method,
+                      dsc->args, 1);
+        }
+    }
+}
+
+
+static void extract_interfaces_signals(
+    struct dl_list *list, const struct fota_dbus_signal_desc *signals)
+{
+    const struct fota_dbus_signal_desc *dsc;
+    struct interfaces *iface;
+
+    for (dsc = signals; dsc && dsc->dbus_signal; dsc++) {
+        iface = add_interface(list, dsc->dbus_interface);
+
+        if (iface)
+            add_entry(iface->xml, "signal", dsc->dbus_signal,
+                      dsc->args, 0);
+    }
+}
+
+
+static void extract_interfaces_properties(
+    struct dl_list *list, const struct fota_dbus_property_desc *properties)
+{
+    const struct fota_dbus_property_desc *dsc;
+    struct interfaces *iface;
+
+    for (dsc = properties; dsc && dsc->dbus_property; dsc++) {
+        iface = add_interface(list, dsc->dbus_interface);
+
+        if (iface) {
+            add_property(iface->xml, dsc);
+        }
+    }
+}
+
+
+/**
+ * extract_interfaces - Extract interfaces from methods, signals and props
+ * @list: Interface list to be filled
+ * @obj_dsc: Description of object from which interfaces will be extracted
+ *
+ * Iterates over all methods, signals, and properties registered with an
+ * object and collects all declared DBus interfaces and create interfaces'
+ * node in XML root node for each. Returned list elements contain interface
+ * name and XML node of corresponding interface.
+ */
+static void extract_interfaces(struct dl_list *list,
+                               struct fota_dbus_object_desc *obj_dsc)
+{
+    extract_interfaces_methods(list, obj_dsc->methods);
+    extract_interfaces_signals(list, obj_dsc->signals);
+    extract_interfaces_properties(list, obj_dsc->properties);
+}
+
+
+static void add_interfaces(struct dl_list *list, struct fotabuf *xml)
+{
+    struct interfaces *iface, *n;
+
+    dl_list_for_each_safe(iface, n, list, struct interfaces, list) {
+        if (fotabuf_len(iface->xml) + 20 < fotabuf_tailroom(xml)) {
+            fotabuf_put_buf(xml, iface->xml);
+            fotabuf_put_str(xml, "</interface>");
+        } else {
+            fota_printf(MSG_DEBUG,
+                         "dbus: Not enough room for add_interfaces inspect data: tailroom %u, add %u",
+                         (unsigned int) fotabuf_tailroom(xml),
+                         (unsigned int) fotabuf_len(iface->xml));
+        }
+
+        dl_list_del(&iface->list);
+        fotabuf_free(iface->xml);
+        os_free(iface->dbus_interface);
+        os_free(iface);
+    }
+}
+
+
+static void add_child_nodes(struct fotabuf *xml, DBusConnection *con,
+                            const char *path)
+{
+    char **children;
+    int i;
+
+    /* add child nodes to introspection tree */
+    dbus_connection_list_registered(con, path, &children);
+
+    for (i = 0; children[i]; i++) {
+        fotabuf_printf(xml, "<node name=\"%s\"/>", children[i]);
+    }
+
+    dbus_free_string_array(children);
+}
+
+
+static void add_introspectable_interface(struct fotabuf *xml)
+{
+    fotabuf_printf(xml, "<interface name=\"%s\">"
+                    "<method name=\"%s\">"
+                    "<arg name=\"data\" type=\"s\" direction=\"out\"/>"
+                    "</method>"
+                    "</interface>",
+                    FOTA_DBUS_INTROSPECTION_INTERFACE,
+                    FOTA_DBUS_INTROSPECTION_METHOD);
+}
+
+
+static void add_properties_interface(struct fotabuf *xml)
+{
+    fotabuf_printf(xml, "<interface name=\"%s\">",
+                    FOTA_DBUS_PROPERTIES_INTERFACE);
+
+    fotabuf_printf(xml, "<method name=\"%s\">", FOTA_DBUS_PROPERTIES_GET);
+    add_arg(xml, "interface", "s", "in");
+    add_arg(xml, "propname", "s", "in");
+    add_arg(xml, "value", "v", "out");
+    fotabuf_put_str(xml, "</method>");
+
+    fotabuf_printf(xml, "<method name=\"%s\">", FOTA_DBUS_PROPERTIES_GETALL);
+    add_arg(xml, "interface", "s", "in");
+    add_arg(xml, "props", "a{sv}", "out");
+    fotabuf_put_str(xml, "</method>");
+
+    fotabuf_printf(xml, "<method name=\"%s\">", FOTA_DBUS_PROPERTIES_SET);
+    add_arg(xml, "interface", "s", "in");
+    add_arg(xml, "propname", "s", "in");
+    add_arg(xml, "value", "v", "in");
+    fotabuf_put_str(xml, "</method>");
+
+    fotabuf_put_str(xml, "</interface>");
+}
+
+
+static void add_fotas_interfaces(struct fotabuf *xml,
+                                  struct fota_dbus_object_desc *obj_dsc)
+{
+    struct dl_list ifaces;
+
+    dl_list_init(&ifaces);
+    extract_interfaces(&ifaces, obj_dsc);
+    add_interfaces(&ifaces, xml);
+}
+
+
+/**
+ * fota_dbus_introspect - Responds for Introspect calls on object
+ * @message: Message with Introspect call
+ * @obj_dsc: Object description on which Introspect was called
+ * Returns: Message with introspection result XML string as only argument
+ *
+ * Iterates over all methods, signals and properties registered with
+ * object and generates introspection data for the object as XML string.
+ */
+DBusMessage *fota_dbus_introspect(DBusMessage *msg,
+                                   struct fota_dbus_object_desc *obj_dsc,
+                                   fota_server_t *fota)
+{
+
+    DBusMessage *reply;
+    struct fotabuf *xml;
+    DBusConnection *conn = fota->conn;
+
+    xml = fotabuf_alloc(20000);
+
+    if (xml == NULL) {
+        return NULL;
+    }
+
+    fotabuf_put_str(xml, "<?xml version=\"1.0\"?>\n");
+    fotabuf_put_str(xml, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE);
+    fotabuf_put_str(xml, "<node>");
+
+    add_introspectable_interface(xml);
+    add_properties_interface(xml);
+    add_fotas_interfaces(xml, obj_dsc);
+    add_child_nodes(xml, obj_dsc->connection,
+                    dbus_message_get_path(msg));
+
+    fotabuf_put_str(xml, "</node>\n");
+
+    reply = dbus_message_new_method_return(msg);
+
+    if (reply) {
+        const char *intro_str = fotabuf_head(xml);
+
+        dbus_message_append_args(reply, DBUS_TYPE_STRING, &intro_str,
+                                 DBUS_TYPE_INVALID);
+
+        if (!dbus_message_get_no_reply(msg)) {
+            dbus_connection_send(conn, reply, NULL);
+        }
+
+        dbus_message_unref(reply);
+        dbus_connection_flush(conn);
+    }
+
+    fotabuf_free(xml);
+
+    return reply;
+}

+ 103 - 0
components/fota_service/dbus/fota-new.c

@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2018-2021 Alibaba Group Holding Limited
+ */
+
+#include <dbus_new_helpers.h>
+#include <fota_server.h>
+#include <fota.h>
+
+extern struct fota_dbus_object_desc obj_dsc;
+
+const struct fota_dbus_method_desc fota_dbus_global_methods[] = {
+    {
+        FOTA_DBUS_METHOD_CALL_START, FOTA_DBUS_INTERFACE,
+        (method_function) fota_dbus_method_start,
+        {
+            END_ARGS
+        }
+    },
+    {
+        FOTA_DBUS_METHOD_CALL_STOP, FOTA_DBUS_INTERFACE,
+        (method_function) fota_dbus_method_stop,
+        {
+            END_ARGS
+        }
+    },
+    {
+        FOTA_DBUS_METHOD_CALL_GET_STATE, FOTA_DBUS_INTERFACE,
+        (method_function) fota_dbus_method_get_state,
+        {
+            END_ARGS
+        }
+    },
+    {
+        FOTA_DBUS_METHOD_CALL_VERSION_CHECK, FOTA_DBUS_INTERFACE,
+        (method_function) fota_dbus_method_version_check,
+        {
+            END_ARGS
+        }
+    },
+    {
+        FOTA_DBUS_METHOD_CALL_DOWNLOAD, FOTA_DBUS_INTERFACE,
+        (method_function) fota_dbus_method_download,
+        {
+            END_ARGS
+        }
+    },
+    {
+        FOTA_DBUS_METHOD_CALL_RESTART, FOTA_DBUS_INTERFACE,
+        (method_function) fota_dbus_method_restart,
+        {
+            { "sec", "d", ARG_IN },
+            END_ARGS
+        }
+    },
+    {
+        FOTA_DBUS_METHOD_CALL_SIZE, FOTA_DBUS_INTERFACE,
+        (method_function) fota_dbus_method_size,
+        {
+            { "name", "s", ARG_IN },
+            END_ARGS
+        }
+    },
+    { NULL, NULL, NULL, { END_ARGS } }
+};
+
+static const struct fota_dbus_signal_desc fota_dbus_global_signals[] = {
+    {
+        FOTA_DBUS_SIGNAL_VERSION, FOTA_DBUS_INTERFACE,
+        {
+            { "str", "s", ARG_OUT },
+            END_ARGS
+        }
+    },
+    {
+        FOTA_DBUS_SIGNAL_DOWNLOAD, FOTA_DBUS_INTERFACE,
+        {
+            {"str", "s", ARG_OUT},
+            END_ARGS
+        }
+    },
+    {
+        FOTA_DBUS_SIGNAL_END, FOTA_DBUS_INTERFACE,
+        {
+            {"str", "s", ARG_OUT},
+            END_ARGS
+        }
+    },
+    {
+        FOTA_DBUS_SIGNAL_RESTART, FOTA_DBUS_INTERFACE,
+        {
+            {"str", "s", ARG_OUT},
+            END_ARGS
+        }
+    },
+    { NULL, NULL, { END_ARGS } }
+
+};
+
+void fota_dbus_register_desc(void)
+{
+    obj_dsc.methods = fota_dbus_global_methods;
+    obj_dsc.signals = fota_dbus_global_signals;
+}

+ 681 - 0
components/fota_service/dbus/fota-server.c

@@ -0,0 +1,681 @@
+/*
+ * Copyright (C) 2018-2021 Alibaba Group Holding Limited
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <pthread.h>
+
+#include <dbus/dbus.h>
+
+#include <dbus_new_helpers.h>
+
+#include <fota.h>
+#include <fota_debug.h>
+
+static int should_dispatch;
+struct fota_dbus_object_desc obj_dsc;
+
+int fota_dbus_signal_version(fota_server_t *fota, const char *str)
+{
+    DBusMessage *msg;
+    DBusMessageIter iter;
+    dbus_uint32_t serial = 0;
+    DBusConnection *conn = fota->conn;
+    char *_str = (char *)str;
+
+    fota_log(LOG_DEBUG, "Enter %s\n", __func__);
+
+    msg = dbus_message_new_signal(FOTA_DBUS_PATH,
+                                  FOTA_DBUS_INTERFACE, FOTA_DBUS_SIGNAL_VERSION);
+
+    if (NULL == msg) {
+        fota_log(LOG_ERR, "Message Null\n");
+        return -1;
+    }
+
+    dbus_message_iter_init_append(msg, &iter);
+
+    if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &_str)) {
+        fota_log(LOG_ERR, "Out Of Memory!\n");
+        return -1;
+    }
+
+    if (!dbus_connection_send(conn, msg, &serial)) {
+        fota_log(LOG_ERR, "Out Of Memory!\n");
+        return -1;
+    }
+
+    dbus_connection_flush(conn);
+
+    dbus_message_unref(msg);
+
+    return 0;
+}
+
+int fota_dbus_signal_download(fota_server_t *fota, const char *str)
+{
+    DBusMessage *msg;
+    DBusMessageIter iter;
+    dbus_uint32_t serial = 0;
+    DBusConnection *conn = fota->conn;
+    char *_str = (char *)str;
+
+    fota_log(LOG_DEBUG, "Enter %s\n", __func__);
+
+    msg = dbus_message_new_signal(FOTA_DBUS_PATH,
+                                  FOTA_DBUS_INTERFACE, FOTA_DBUS_SIGNAL_DOWNLOAD);
+
+    if (NULL == msg) {
+        fota_log(LOG_ERR, "Message Null\n");
+        return -1;
+    }
+
+    dbus_message_iter_init_append(msg, &iter);
+
+    if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &_str)) {
+        fota_log(LOG_ERR, "Out Of Memory!\n");
+        return -1;
+    }
+
+    if (!dbus_connection_send(conn, msg, &serial)) {
+        fota_log(LOG_ERR, "Out Of Memory!\n");
+        return -1;
+    }
+
+    dbus_connection_flush(conn);
+
+    dbus_message_unref(msg);
+
+    return 0;
+}
+
+int fota_dbus_signal_end(fota_server_t *fota, const char *str)
+{
+    DBusMessage *msg;
+    DBusMessageIter iter;
+    dbus_uint32_t serial = 0;
+    DBusConnection *conn = fota->conn;
+    char *_str = (char *)str;
+
+    fota_log(LOG_DEBUG, "Enter %s\n", __func__);
+
+    msg = dbus_message_new_signal(FOTA_DBUS_PATH,
+                                  FOTA_DBUS_INTERFACE, FOTA_DBUS_SIGNAL_END);
+
+    if (NULL == msg) {
+        fota_log(LOG_ERR, "Message Null\n");
+        return -1;
+    }
+
+    dbus_message_iter_init_append(msg, &iter);
+
+    if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &_str)) {
+        fota_log(LOG_ERR, "Out Of Memory!\n");
+        return -1;
+    }
+
+    if (!dbus_connection_send(conn, msg, &serial)) {
+        fota_log(LOG_ERR, "Out Of Memory!\n");
+        return -1;
+    }
+
+    dbus_connection_flush(conn);
+
+    dbus_message_unref(msg);
+
+    return 0;
+}
+
+int fota_dbus_signal_restart(fota_server_t *fota, const char *str)
+{
+    DBusMessage *msg;
+    DBusMessageIter iter;
+    dbus_uint32_t serial = 0;
+    DBusConnection *conn = fota->conn;
+    char *_str = (char *)str;
+
+    fota_log(LOG_DEBUG, "Enter %s\n", __func__);
+
+    msg = dbus_message_new_signal(FOTA_DBUS_PATH,
+                                  FOTA_DBUS_INTERFACE, FOTA_DBUS_SIGNAL_RESTART);
+
+    if (NULL == msg) {
+        fota_log(LOG_ERR, "Message Null\n");
+        return -1;
+    }
+
+    dbus_message_iter_init_append(msg, &iter);
+
+    if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &_str)) {
+        fota_log(LOG_ERR, "Out Of Memory!\n");
+        return -1;
+    }
+
+    if (!dbus_connection_send(conn, msg, &serial)) {
+        fota_log(LOG_ERR, "Out Of Memory!\n");
+        return -1;
+    }
+
+    dbus_connection_flush(conn);
+
+    dbus_message_unref(msg);
+
+    return 0;
+}
+
+void fotax_event(fotax_t *fotax, fotax_event_e event, const char *json)
+{
+    fota_server_t *fota_server = (fota_server_t *)fotax->private;
+
+    switch (event) {
+        case FOTAX_EVENT_VERSION:
+            fota_log(LOG_DEBUG, "%s,%d,%s\n", __func__, __LINE__, json);
+            fota_dbus_signal_version(fota_server, json);
+            break;
+
+        case FOTAX_EVENT_DOWNLOAD:
+            fota_log(LOG_DEBUG, "%s,%d,%s\n", __func__, __LINE__, json);
+            fota_dbus_signal_download(fota_server, json);
+            break;
+
+        case FOTAX_EVENT_END:
+            fota_log(LOG_DEBUG, "%s,%d,%s\n", __func__, __LINE__, json);
+            fota_dbus_signal_end(fota_server, json);
+            break;
+
+        case FOTAX_EVENT_RESTART:
+            fota_log(LOG_DEBUG, "%s,%d,%s\n", __func__, __LINE__, json);
+            fota_dbus_signal_restart(fota_server, json);
+            break;
+
+        default: break;
+    }
+}
+
+int fota_dbus_method_start(DBusMessage *msg, fota_server_t *fota)
+{
+    int ret_val;
+    DBusMessage *reply;
+    DBusMessageIter iter;
+    dbus_uint32_t serial = 0;
+    DBusConnection *conn = fota->conn;
+
+    fota_log(LOG_DEBUG, "Enter %s\n", __func__);
+
+    ret_val = fotax_start(&fota->fotax);
+    if (ret_val == 0) {
+        fota->fotax.fotax_event_cb = fotax_event;
+        fota->fotax.private = fota;
+    }
+
+    reply = dbus_message_new_method_return(msg);
+
+    dbus_message_iter_init_append(reply, &iter);
+
+    if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret_val)) {
+        fota_log(LOG_ERR, "Out Of Memory!\n");
+        return -1;
+    }
+
+    if (!dbus_connection_send(conn, reply, &serial)) {
+        fota_log(LOG_ERR, "Out Of Memory!\n");
+        return -1;
+    }
+
+    dbus_connection_flush(conn);
+
+    dbus_message_unref(reply);
+
+    return 0;
+}
+
+int fota_dbus_method_stop(DBusMessage *msg, fota_server_t *fota)
+{
+    int ret_val;
+    DBusMessage *reply;
+    DBusMessageIter iter;
+    dbus_uint32_t serial = 0;
+    DBusConnection *conn = fota->conn;
+
+    fota_log(LOG_DEBUG, "Enter %s\n", __func__);
+    ret_val = fotax_stop(&fota->fotax);
+
+    reply = dbus_message_new_method_return(msg);
+
+    dbus_message_iter_init_append(reply, &iter);
+
+    if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret_val)) {
+        fota_log(LOG_ERR, "Out Of Memory!\n");
+        return -1;
+    }
+
+    if (!dbus_connection_send(conn, reply, &serial)) {
+        fota_log(LOG_ERR, "Out Of Memory!\n");
+        return -1;
+    }
+
+    dbus_connection_flush(conn);
+
+    dbus_message_unref(reply);
+
+    return 0;
+}
+
+int fota_dbus_method_get_state(DBusMessage *msg, fota_server_t *fota)
+{
+    DBusMessage *reply;
+    DBusMessageIter iter;
+    dbus_uint32_t serial = 0;
+    DBusConnection *conn = fota->conn;
+    char ret_str[10];
+    char *str = (char *)&ret_str;
+
+    fota_log(LOG_DEBUG, "Enter %s\n", __func__);
+    memset(&ret_str, 0, sizeof(ret_str));
+    int ret = fotax_get_state(&fota->fotax);
+    // 版本检测,下载状态,停止状态,空闲状态
+    if (ret == FOTAX_INIT) {
+        strncpy(ret_str, "idle", sizeof(ret_str) - 1);
+    } else if (ret == FOTAX_DOWNLOAD ) {
+        strncpy(ret_str, "download", sizeof(ret_str) - 1);
+    } else if (ret == FOTAX_ABORT ) {
+        strncpy(ret_str, "abort", sizeof(ret_str) - 1);
+    } else if (ret == FOTAX_FINISH ) {
+        strncpy(ret_str, "finish", sizeof(ret_str) - 1);
+    } else {
+        strncpy(ret_str, "", sizeof(ret_str) - 1);
+    }
+
+    reply = dbus_message_new_method_return(msg);
+
+    dbus_message_iter_init_append(reply, &iter);
+
+    if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &str)) {
+        fota_log(LOG_ERR, "Out Of Memory!\n");
+        return -1;
+    }
+
+    if (!dbus_connection_send(conn, reply, &serial)) {
+        fota_log(LOG_ERR, "Out Of Memory!\n");
+        return -1;
+    }
+
+    dbus_connection_flush(conn);
+
+    dbus_message_unref(reply);
+
+    return 0;
+}
+
+int fota_dbus_method_version_check(DBusMessage *msg, fota_server_t *fota)
+{
+    int ret_val;
+    DBusMessage *reply;
+    DBusMessageIter iter;
+    dbus_uint32_t serial = 0;
+    DBusConnection *conn = fota->conn;
+
+    fota_log(LOG_DEBUG, "Enter %s\n", __func__);
+    ret_val = fotax_version_check(&fota->fotax);
+    reply = dbus_message_new_method_return(msg);
+
+    dbus_message_iter_init_append(reply, &iter);
+    if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret_val)) {
+        fota_log(LOG_ERR, "Out Of Memory!\n");
+        return -1;
+    }
+
+    if (!dbus_connection_send(conn, reply, &serial)) {
+        fota_log(LOG_ERR, "Out Of Memory!\n");
+        return -1;
+    }
+
+    dbus_connection_flush(conn);
+
+    dbus_message_unref(reply);
+
+    return 0;
+}
+
+int fota_dbus_method_download(DBusMessage *msg, fota_server_t *fota)
+{
+    int ret_val;
+    DBusMessage *reply;
+    DBusMessageIter iter;
+    dbus_uint32_t serial = 0;
+    DBusConnection *conn = fota->conn;
+
+    fota_log(LOG_DEBUG, "Enter %s\n", __func__);
+    ret_val = fotax_download(&fota->fotax);
+    reply = dbus_message_new_method_return(msg);
+
+    dbus_message_iter_init_append(reply, &iter);
+    if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret_val)) {
+        fota_log(LOG_ERR, "Out Of Memory!\n");
+        return -1;
+    }
+
+    if (!dbus_connection_send(conn, reply, &serial)) {
+        fota_log(LOG_ERR, "Out Of Memory!\n");
+        return -1;
+    }
+
+    dbus_connection_flush(conn);
+
+    dbus_message_unref(reply);
+
+    return 0;
+}
+
+int fota_dbus_method_restart(DBusMessage *msg, fota_server_t *fota)
+{
+    int ret_val;
+    DBusMessage *reply;
+    DBusMessageIter iter;
+    dbus_uint32_t serial = 0;
+    DBusConnection *conn = fota->conn;
+    double _millisecond;
+    int millisecond;
+
+    fota_log(LOG_DEBUG, "Enter %s\n", __func__);
+
+    dbus_message_iter_init(msg, &iter);
+
+//    printf("type = %d\n", dbus_message_iter_get_arg_type(&iter));
+//    if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INT32) {
+//        fota_log(LOG_ERR, "not int\n");
+//    }
+
+    dbus_message_iter_get_basic(&iter, &_millisecond);
+    millisecond = (int)_millisecond;
+
+    printf("millisecond = %d\n", millisecond);
+    ret_val = fotax_restart(&fota->fotax, millisecond);
+
+    reply = dbus_message_new_method_return(msg);
+
+    dbus_message_iter_init_append(reply, &iter);
+
+    if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret_val)) {
+        fota_log(LOG_ERR, "Out Of Memory!\n");
+        return -1;
+    }
+
+    if (!dbus_connection_send(conn, reply, &serial)) {
+        fota_log(LOG_ERR, "Out Of Memory!\n");
+        return -1;
+    }
+
+    dbus_connection_flush(conn);
+
+    dbus_message_unref(reply);
+
+    return 0;
+}
+
+int fota_dbus_method_size(DBusMessage *msg, fota_server_t *fota)
+{
+    int64_t ret_val;
+    DBusMessage *reply;
+    DBusMessageIter iter;
+    dbus_uint32_t serial = 0;
+    DBusConnection *conn = fota->conn;
+    char *name;
+
+    fota_log(LOG_DEBUG, "Enter %s\n", __func__);
+
+    dbus_message_iter_init(msg, &iter);
+
+    dbus_message_iter_get_basic(&iter, &name);
+
+    printf("name = %s\n", name);
+    ret_val = fotax_get_size(&fota->fotax, name);
+
+    reply = dbus_message_new_method_return(msg);
+
+    dbus_message_iter_init_append(reply, &iter);
+
+    if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT64, &ret_val)) {
+        fota_log(LOG_ERR, "Out Of Memory!\n");
+        return -1;
+    }
+
+    if (!dbus_connection_send(conn, reply, &serial)) {
+        fota_log(LOG_ERR, "Out Of Memory!\n");
+        return -1;
+    }
+
+    dbus_connection_flush(conn);
+
+    dbus_message_unref(reply);
+
+    return 0;
+}
+
+static void msg_method_handler(DBusMessage *msg, fota_server_t *fota)
+{
+    const char *member;
+
+    fota_log(LOG_INFO, "Enter %s\n", __func__);
+
+    member = dbus_message_get_member(msg);
+
+    fota_log(LOG_INFO, "method call: %s\n", member);
+
+    for (int i = 0; ; i++) {
+        if (fota_dbus_global_methods[i].dbus_method == NULL) {
+            fota_log(LOG_ERR, "not method call\n");
+            break;
+        }
+
+        if (!strcmp(fota_dbus_global_methods[i].dbus_method, member)) {
+            fota_dbus_global_methods[i].function(msg, fota);
+            break;
+        }
+    }
+}
+
+static DBusHandlerResult message_handler(DBusConnection *connection,
+                                         DBusMessage *message, void *user_data)
+{
+    const char *method;
+    const char *path;
+    const char *interface;
+
+    method = dbus_message_get_member(message);
+    path = dbus_message_get_path(message);
+    interface = dbus_message_get_interface(message);
+
+    if (!method || !path || !interface) {
+        return -1;
+    }
+
+    if (!strncmp(FOTA_DBUS_INTROSPECTION_METHOD, method, 50) &&
+        !strncmp(FOTA_DBUS_INTROSPECTION_INTERFACE, interface, 50)) {
+        fota_dbus_introspect(message, &obj_dsc, (fota_server_t *)user_data);
+    } else if(!strncmp(FOTA_DBUS_INTERFACE, interface, 50)) {
+        msg_method_handler(message, (fota_server_t *)user_data);
+    } else {
+        fota_log(LOG_ERR, "message error\n");
+    }
+
+    return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+int fota_dbus_ctrl_iface_init(fota_server_t *fota)
+{
+    DBusObjectPathVTable vtable = {
+        NULL, &message_handler,
+        NULL, NULL, NULL, NULL
+    };
+
+    if (!dbus_connection_register_object_path(fota->conn, FOTA_DBUS_PATH, &vtable, fota)) {
+        fota_log(LOG_ERR, "dbus_connection_register_object_path error\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+static dbus_bool_t add_watch(DBusWatch *watch, void *data)
+{
+    int fd;
+    unsigned int flags;
+    fota_server_t *fota = (fota_server_t *)data;
+
+    if (!dbus_watch_get_enabled(watch)) {
+        return TRUE;
+    }
+
+    flags = dbus_watch_get_flags(watch);
+    fd = dbus_watch_get_unix_fd(watch);
+
+    if (flags & DBUS_WATCH_READABLE) {
+        fota->watch_rfd = fd;
+        fota->watch = watch;
+    }
+
+    return TRUE;
+}
+
+static void wakeup_main(void *data)
+{
+    should_dispatch = 1;
+}
+
+
+static int integrate_dbus_watch(fota_server_t *fota)
+{
+    if (!dbus_connection_set_watch_functions(fota->conn, add_watch, NULL, NULL, fota, NULL)) {
+        fota_log(LOG_ERR, "dbus_connection_set_watch_functions error\n");
+        return -1;
+    }
+
+    dbus_connection_set_wakeup_main_function(fota->conn, wakeup_main,
+                                             fota, NULL);
+
+
+    return 0;
+}
+
+static void dispatch_data(DBusConnection *con)
+{
+    while (dbus_connection_get_dispatch_status(con) ==
+       DBUS_DISPATCH_DATA_REMAINS)
+        dbus_connection_dispatch(con);
+}
+
+static void process_watch(fota_server_t *fota, DBusWatch *watch)
+{
+    dbus_connection_ref(fota->conn);
+
+    should_dispatch = 0;
+
+    dbus_watch_handle(watch, DBUS_WATCH_READABLE);
+
+    if (should_dispatch) {
+        dispatch_data(fota->conn);
+        should_dispatch = 0;
+    }
+
+    dbus_connection_unref(fota->conn);
+}
+
+static void process_watch_read(fota_server_t *fota, DBusWatch *watch)
+{
+    process_watch(fota, watch);
+}
+
+void fota_loop_run(fota_server_t *fota)
+{
+    int    sockfd;
+    fd_set rset;
+    int    retval;
+
+    integrate_dbus_watch(fota);
+
+    sockfd = fota->watch_rfd;
+
+    FD_ZERO(&rset);
+    FD_SET(sockfd, &rset);
+
+    while (1) {
+        retval = select(sockfd + 1, &rset, NULL, NULL, NULL);
+
+        if (retval == -1) {
+            fota_log(LOG_ERR, "select error\n");
+        } else if (retval) {
+             process_watch_read(fota, fota->watch);
+        } else {
+            fota_log(LOG_ERR, "select timeout\n");
+            // timeout
+        }
+    }
+}
+
+int fota_dbus_init(fota_server_t *fota)
+{
+    int ret;
+    DBusError err;
+    DBusConnection *conn;
+
+    dbus_error_init(&err);
+
+    conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
+
+    obj_dsc.connection = conn;
+
+    fota->conn = conn;
+
+    fota_dbus_register_desc();
+
+    fota_dbus_ctrl_iface_init(fota);
+
+    if (dbus_error_is_set(&err)) {
+        fota_log(LOG_ERR, "Connection Error (%s)\n", err.message);
+        dbus_error_free(&err);
+    }
+
+    if (conn == NULL) {
+        exit(1);
+    }
+
+    ret = dbus_bus_request_name(conn, FOTA_DBUS_SERVER, DBUS_NAME_FLAG_DO_NOT_QUEUE, &err);
+
+    if (dbus_error_is_set(&err)) {
+        fota_log(LOG_ERR, "Name Error (%s)\n", err.message);
+        dbus_error_free(&err);
+    }
+
+    if (ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+	fota_log(LOG_ERR, "Error: Service has been started already\n");
+        exit(1);
+    }
+
+    /////////////////////////////////////////////////////////////////////
+    dbus_bus_add_match(conn,
+                       "type='method_call',interface='org.fota.interface'",
+                       &err);
+    dbus_connection_flush(conn);
+
+    if (dbus_error_is_set(&err)) {
+        fota_log(LOG_ERR, "Match Error (%s)\n", err.message);
+        exit(1);
+    }
+
+    return 0;
+}
+
+void fota_dbus_deinit(fota_server_t *fota)
+{
+    DBusError err;
+
+    dbus_error_init(&err);
+
+    dbus_bus_release_name(fota->conn, FOTA_DBUS_SERVER, &err);
+}

+ 341 - 0
components/fota_service/fotax.c

@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <cJSON.h>
+#include <yoc/fota.h>
+#include <ulog/ulog.h>
+#include <aos/kv.h>
+#include <yoc/sysinfo.h>
+#include <aos/version.h>
+#include "fotax.h"
+
+#define TAG "fotax"
+
+struct timespec diff_timespec(struct timespec start, struct timespec end)
+{
+     struct timespec result;
+ 
+     if (end.tv_nsec < start.tv_nsec)
+     { 
+        result.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec;        
+        result.tv_sec = end.tv_sec - 1 - start.tv_sec;
+     }
+     else
+     {
+        result.tv_nsec = end.tv_nsec - start.tv_nsec;        
+        result.tv_sec = end.tv_sec - start.tv_sec;
+     }
+ 
+    return result;
+}
+
+static int fota_event_cb(void *arg, fota_event_e event)
+{
+    static int data_offset;
+    static struct timespec tv;
+    fota_t *fota = (fota_t *)arg;
+    fotax_t *fotax = (fotax_t *)fota->private;
+    if (!fotax->fotax_event_cb) {
+        LOGE(TAG, "fotax event callback is NULL.");
+        return -1;
+    }
+
+    switch (event) {
+        case FOTA_EVENT_START:
+            LOGD(TAG, "FOTA START :%x", fota->status);
+            break;
+        case FOTA_EVENT_VERSION:
+        {
+            LOGD(TAG, "FOTA VERSION CHECK :%x", fota->status);
+            data_offset = 0;
+            memset(&tv, 0, sizeof(struct timespec));
+            cJSON *root = cJSON_CreateObject();
+            if (fota->error_code != FOTA_ERROR_NULL) {
+                cJSON_AddNumberToObject(root, "code", 1);
+                cJSON_AddStringToObject(root, "msg", "version check failed!");
+            } else {
+                cJSON_AddNumberToObject(root, "code", 0);
+                cJSON_AddStringToObject(root, "curversion", fota->info.cur_version);
+                cJSON_AddStringToObject(root, "newversion", fota->info.new_version);
+                cJSON_AddNumberToObject(root, "timestamp", fota->info.timestamp);
+                cJSON_AddStringToObject(root, "changelog", fota->info.changelog);
+                cJSON_AddStringToObject(root, "local_changelog", fota->info.local_changelog);
+            }
+            char *out = cJSON_PrintUnformatted(root);
+            cJSON_Delete(root);
+            if (out != NULL) {
+                fotax->fotax_event_cb(fotax, FOTAX_EVENT_VERSION, out);
+                cJSON_free(out);
+            } else {
+                fotax->fotax_event_cb(fotax, FOTAX_EVENT_VERSION, "{\"code\": 1, \"msg\": \"(version)JSON print error!\"}");
+            }
+            break;
+        }
+        case FOTA_EVENT_PROGRESS:
+        {
+            LOGD(TAG, "FOTA PROGRESS :%x, %d, %d", fota->status, fota->offset, fota->total_size);
+            int64_t cur_size = fota->offset;
+            int64_t total_size = fota->total_size;
+            int speed = 0; //kbps
+            int percent = 0;
+            if (total_size > 0) {
+                percent = (int)(cur_size * 100 / total_size);
+            }
+            cJSON *root = cJSON_CreateObject();
+            if (fota->error_code != FOTA_ERROR_NULL) {
+                cJSON_AddNumberToObject(root, "code", 1);
+                if (fota->error_code == FOTA_ERROR_VERIFY) {
+                    cJSON_AddStringToObject(root, "msg", "fota image verify failed!");
+                } else {
+                    cJSON_AddStringToObject(root, "msg", "download failed!");
+                }
+                cJSON_AddNumberToObject(root, "total_size", total_size);
+                cJSON_AddNumberToObject(root, "cur_size", cur_size);
+                cJSON_AddNumberToObject(root, "percent", percent);
+            } else {
+                // current_size, total_size, percent, speed
+                struct timespec now;
+                clock_gettime(CLOCK_MONOTONIC, &now);
+                if (tv.tv_sec > 0 && tv.tv_nsec > 0 && data_offset > 0) {
+                    struct timespec diff = diff_timespec(tv, now);
+                    int millisecond = diff.tv_sec * 1000 + diff.tv_nsec / 1000000;
+                    LOGD(TAG, "interval time: %d ms", millisecond);
+                    speed = ((fota->offset - data_offset) / 1024) * 1000 / millisecond;
+                }
+                cJSON_AddNumberToObject(root, "code", 0);
+                cJSON_AddNumberToObject(root, "total_size", total_size);
+                cJSON_AddNumberToObject(root, "cur_size", cur_size);
+                cJSON_AddNumberToObject(root, "percent", percent);
+                cJSON_AddNumberToObject(root, "speed", speed);
+                tv.tv_sec = now.tv_sec;
+                tv.tv_nsec = now.tv_nsec;
+                data_offset = fota->offset;
+            }
+            char *out = cJSON_PrintUnformatted(root);
+            cJSON_Delete(root);
+            if (out != NULL) {
+                fotax->fotax_event_cb(fotax, FOTAX_EVENT_DOWNLOAD, out);
+                cJSON_free(out);
+            } else {
+                fotax->fotax_event_cb(fotax, FOTAX_EVENT_DOWNLOAD, "{\"code\": 1, \"msg\": \"(download)JSON print error!\"}");
+            }
+            break;            
+        }
+            
+        case FOTA_EVENT_FAIL:
+            LOGD(TAG, "FOTA FAIL :%x, %d", fota->status, fota->error_code);
+            break;
+        case FOTA_EVENT_VERIFY:
+            LOGD(TAG, "FOTA VERIFY :%x", fota->status);
+            break;
+        case FOTA_EVENT_FINISH:
+            LOGD(TAG, "FOTA FINISH :%x", fota->status);
+            fotax->fotax_event_cb(fotax, FOTAX_EVENT_END, "{\"code\": 0, \"msg\": \"fota finish\"}");
+            break;
+        case FOTA_EVENT_RESTART:
+            LOGD(TAG, "FOTA RESTART :%x", fota->status);
+            fotax->fotax_event_cb(fotax, FOTAX_EVENT_RESTART, "{\"code\": 0, \"msg\": \"fota restart event\"}");
+            break;
+        case FOTA_EVENT_QUIT:
+            LOGD(TAG, "FOTA QUIT :%x", fota->status);
+            break;
+        default:
+            break;
+    }
+    return 0;
+}
+
+int fotax_start(fotax_t *fotax)
+{
+    int ret;
+    char to_buf[32];
+    char cloud_buf[32];
+    char *cloud = "cop";
+    char *to = "flash://misc";
+    int read_timeoutms;         /*!< read timeout, millisecond */
+    int write_timeoutms;        /*!< write timeout, millisecond */
+    int retry_count;            /*!< when download abort, it will retry to download again in retry_count times */
+    int sleep_time;             /*!< the sleep time for auto-check task */
+    int auto_check_en;          /*!< whether check version automatic */
+    fota_config_t config;
+
+    if (fotax == NULL) {
+        LOGE(TAG, "fotax start args e");
+        return -EINVAL;
+    }
+    if (fotax->state != 0) {
+        LOGE(TAG, "fotax already started.");
+        return -1;
+    }
+    fotax->fota_handle = NULL;
+    LOGD(TAG, "%s, %d", __func__, __LINE__);
+    if (fotax->register_ops && fotax->register_ops->fota_register_cloud) {
+        ret = fotax->register_ops->fota_register_cloud();
+    } else {
+        ret = fota_register_cop();
+        if (ret != 0) {
+            return -1;
+        }
+        ret = netio_register_http();        
+    }
+    if (ret != 0) {
+        return -1;
+    }
+    if (fotax->register_ops && fotax->register_ops->netio_register_from) {
+        ret = fotax->register_ops->netio_register_from(fotax->register_ops->cert);
+    } else {
+        ret = netio_register_httpc(NULL);
+    }
+    if (ret != 0) {
+        return -1;
+    }
+    if (fotax->register_ops && fotax->register_ops->netio_register_to) {
+        ret = fotax->register_ops->netio_register_to();
+    } else {
+        ret = netio_register_flash();
+    }
+    if (ret != 0) {
+        return -1;
+    }
+    memset(cloud_buf, 0 ,sizeof(cloud_buf));
+    ret = aos_kv_getstring(KV_FOTA_CLOUD_PLATFORM, cloud_buf, sizeof(cloud_buf));
+    if (ret >= 0) {
+        cloud = cloud_buf;
+    }
+    LOGD(TAG, "cloud: %s", cloud);
+    memset(to_buf, 0, sizeof(to_buf));
+    ret = aos_kv_getstring(KV_FOTA_TO_URL, to_buf, sizeof(to_buf));
+    if (ret >= 0) {
+        to = to_buf;
+    }
+    LOGD(TAG, "to: %s", to);
+    fotax->fota_handle = fota_open(cloud, to, fota_event_cb);
+    if (fotax->fota_handle == NULL) {
+        return -1;
+    }
+    ((fota_t *)(fotax->fota_handle))->private = fotax;
+    if (aos_kv_getint(KV_FOTA_READ_TIMEOUTMS, &read_timeoutms) < 0) {
+        read_timeoutms = 3000;
+    }
+    if (aos_kv_getint(KV_FOTA_WRITE_TIMEOUTMS, &write_timeoutms) < 0) {
+        write_timeoutms = 3000;
+    }
+    if (aos_kv_getint(KV_FOTA_RETRY_COUNT, &retry_count) < 0) {
+        retry_count = 0;
+    }
+    if (aos_kv_getint(KV_FOTA_AUTO_CHECK, &auto_check_en) < 0) {
+        auto_check_en = 0;
+    }
+    if (aos_kv_getint(KV_FOTA_SLEEP_TIMEMS, &sleep_time) < 0) {
+        sleep_time = 30000;
+    }
+    config.read_timeoutms = read_timeoutms;
+    config.write_timeoutms = write_timeoutms;
+    config.retry_count = retry_count;
+    config.auto_check_en = auto_check_en;
+    config.sleep_time = sleep_time;
+    LOGD(TAG, "read_timeoutms: %d", read_timeoutms);
+    LOGD(TAG, "write_timeoutms: %d", write_timeoutms);
+    LOGD(TAG, "retry_count: %d", retry_count);
+    LOGD(TAG, "auto_check_en: %d", auto_check_en);
+    LOGD(TAG, "sleep_time: %d", sleep_time);
+    fota_config(fotax->fota_handle, &config);
+    ret = fota_start(fotax->fota_handle);
+    fotax->state = FOTAX_INIT;
+    return ret;
+}
+
+int fotax_stop(fotax_t *fotax)
+{
+    if (fotax == NULL || fotax->fota_handle == NULL) {
+        LOGE(TAG, "fotax stop args e");
+        return -EINVAL;
+    }
+    LOGD(TAG, "%s, %d", __func__, __LINE__);
+    fota_stop(fotax->fota_handle);
+    fota_close(fotax->fota_handle);
+    fotax->state = 0;
+    fotax->fota_handle = NULL;
+    LOGD(TAG, "fotax stop finish. %s, %d", __func__, __LINE__);
+    return 0;
+}
+
+int fotax_get_state(fotax_t *fotax)
+{
+    int state;
+    if (fotax == NULL || fotax->fota_handle == NULL) {
+        LOGE(TAG, "fotax get state args e");
+        return -EINVAL;
+    }
+    state = fota_get_status(fotax->fota_handle);
+    LOGD(TAG, "%s, %d, state:%d", __func__, __LINE__, state);
+    return state;
+}
+
+int fotax_version_check(fotax_t *fotax)
+{
+    char *device_id, *model, *app_version, *changelog;
+
+    if (fotax == NULL || fotax->fota_handle == NULL) {
+        LOGE(TAG, "fotax version check args e");
+        return -EINVAL;
+    }
+    device_id = aos_get_device_id();
+    app_version = aos_get_app_version();
+    model = (char *)aos_get_product_model();
+    changelog = aos_get_changelog();
+    cJSON *root = cJSON_CreateObject();
+    cJSON_AddNumberToObject(root, "code", 0);
+    cJSON_AddStringToObject(root, "curversion", app_version ? app_version : "null");
+    cJSON_AddStringToObject(root, "deviceid", device_id ? device_id : "null");
+    cJSON_AddStringToObject(root, "model", model ? model : "null");
+    cJSON_AddStringToObject(root, "local_changelog", changelog ? changelog : "null");
+    char *out = cJSON_PrintUnformatted(root);
+    cJSON_Delete(root);
+    if (out != NULL) {
+        if (fotax->fotax_event_cb) {
+            fotax->fotax_event_cb(fotax, FOTAX_EVENT_VERSION, out);
+        }
+        cJSON_free(out);
+    } else {
+        fotax->fotax_event_cb(fotax, FOTAX_EVENT_VERSION, "{\"code\": 1, \"msg\": \"(version)JSON print error!\"}");
+    }
+    LOGD(TAG, "%s, %d", __func__, __LINE__);
+    fota_do_check(fotax->fota_handle);
+    return 0;
+}
+
+int fotax_download(fotax_t *fotax)
+{
+    if (fotax == NULL || fotax->fota_handle == NULL) {
+        LOGE(TAG, "fotax download args e");
+        return -EINVAL;
+    }
+    LOGD(TAG, "%s, %d", __func__, __LINE__);
+    fotax->state = FOTAX_DOWNLOAD;
+    return fota_download(fotax->fota_handle);
+}
+
+int fotax_restart(fotax_t *fotax, int delay_ms)
+{
+    if (fotax == NULL || fotax->fota_handle == NULL) {
+        LOGE(TAG, "fotax restart e");
+        return -EINVAL;
+    }
+    LOGD(TAG, "%s, %d", __func__, __LINE__);
+    return fota_restart(fotax->fota_handle, delay_ms);
+}
+
+int64_t fotax_get_size(fotax_t *fotax, const char *name)
+{
+    if (fotax == NULL || fotax->fota_handle == NULL) {
+        LOGE(TAG, "fotax get size e");
+        return -EINVAL;
+    }
+    LOGD(TAG, "%s, %d", __func__, __LINE__);
+    return fota_get_size(fotax->fota_handle, name);
+}

+ 124 - 0
components/fota_service/include/dbus_new_helpers.h

@@ -0,0 +1,124 @@
+/*
+ * FOTA Supplicant / dbus-based control interface
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+ * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef _DBUS_NEW_HELPER_H_
+#define _DBUS_NEW_HELPER_H_
+
+#include <stdint.h>
+#include <dbus/dbus.h>
+#include <fota.h>
+
+typedef int (*method_function)(DBusMessage *message, fota_server_t *fota);
+typedef void (*FOTADBusArgumentFreeFunction)(void *handler_arg);
+
+struct fota_dbus_property_desc;
+typedef dbus_bool_t (*FOTADBusPropertyAccessor)(
+    const struct fota_dbus_property_desc *property_desc,
+    DBusMessageIter *iter, DBusError *error, void *user_data);
+#define DECLARE_ACCESSOR(f) \
+    dbus_bool_t f(const struct fota_dbus_property_desc *property_desc, \
+                  DBusMessageIter *iter, DBusError *error, void *user_data)
+
+struct fota_dbus_object_desc {
+    DBusConnection *connection;
+    char *path;
+
+    /* list of methods, properties and signals registered with object */
+    const struct fota_dbus_method_desc *methods;
+    const struct fota_dbus_signal_desc *signals;
+    const struct fota_dbus_property_desc *properties;
+
+    /* property changed flags */
+    uint8_t *prop_changed_flags;
+
+    /* argument for method handlers and properties
+     * getter and setter functions */
+    void *user_data;
+    /* function used to free above argument */
+    FOTADBusArgumentFreeFunction user_data_free_func;
+};
+
+enum dbus_arg_direction { ARG_IN, ARG_OUT };
+
+/**
+ * type Definition:
+ *
+ * 'Auto':  'v'
+ * String:  's'
+ * Number:  'd'
+ * Boolean: 'b'
+ * Array:   'av'
+ * Object:  'a{sv}'
+ */
+struct fota_dbus_argument {
+    char *name;
+    char *type;
+    enum dbus_arg_direction dir;
+};
+
+#define END_ARGS { NULL, NULL, ARG_IN }
+
+/**
+ * struct fota_dbus_method_desc - DBus method description
+ */
+struct fota_dbus_method_desc {
+    /* method name */
+    const char *dbus_method;
+    /* method interface */
+    const char *dbus_interface;
+    /* method handling function */
+    method_function function;
+    /* array of arguments */
+    struct fota_dbus_argument args[4];
+};
+
+/**
+ * struct fota_dbus_signal_desc - DBus signal description
+ */
+struct fota_dbus_signal_desc {
+    /* signal name */
+    const char *dbus_signal;
+    /* signal interface */
+    const char *dbus_interface;
+    /* array of arguments */
+    struct fota_dbus_argument args[4];
+};
+
+/**
+ * struct fota_dbus_property_desc - DBus property description
+ */
+
+struct fota_dbus_property_desc {
+    /* property name */
+    const char *dbus_property;
+    /* property interface */
+    const char *dbus_interface;
+    /* property type signature in DBus type notation */
+    const char *type;
+    /* property getter function */
+    FOTADBusPropertyAccessor getter;
+    /* property setter function */
+    FOTADBusPropertyAccessor setter;
+    /* other data */
+    const char *data;
+};
+
+#define FOTA_DBUS_INTROSPECTION_INTERFACE "org.freedesktop.DBus.Introspectable"
+#define FOTA_DBUS_INTROSPECTION_METHOD "Introspect"
+#define FOTA_DBUS_PROPERTIES_INTERFACE "org.freedesktop.DBus.Properties"
+#define FOTA_DBUS_PROPERTIES_GET "Get"
+#define FOTA_DBUS_PROPERTIES_SET "Set"
+#define FOTA_DBUS_PROPERTIES_GETALL "GetAll"
+
+extern const struct fota_dbus_method_desc fota_dbus_global_methods[];
+DBusMessage *fota_dbus_introspect(DBusMessage *message,
+                                   struct fota_dbus_object_desc *obj_dsc,
+                                   fota_server_t *fota);
+
+#endif /* _DBUS_NEW_HELPER_H_ */

+ 43 - 0
components/fota_service/include/fota.h

@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018-2021 Alibaba Group Holding Limited
+ */
+
+#ifndef __FOTA_H__
+#define __FOTA_H__
+
+#include <dbus/dbus.h>
+#include <fotax.h>
+
+#define FOTA_DBUS_SERVER    "org.fota.server"
+#define FOTA_DBUS_PATH      "/org/fota/path"
+#define FOTA_DBUS_INTERFACE "org.fota.interface"
+
+#define FOTA_DBUS_SIGNAL_VERSION              "version"   // 带字符串
+#define FOTA_DBUS_SIGNAL_DOWNLOAD             "download"  // 带字符串
+#define FOTA_DBUS_SIGNAL_END                  "end"       // 带字符串
+#define FOTA_DBUS_SIGNAL_RESTART              "restart"   // 带字符串
+
+#define FOTA_DBUS_METHOD_CALL_START           "start"
+#define FOTA_DBUS_METHOD_CALL_STOP            "stop"
+#define FOTA_DBUS_METHOD_CALL_GET_STATE       "getState"
+#define FOTA_DBUS_METHOD_CALL_VERSION_CHECK   "versionCheck"
+#define FOTA_DBUS_METHOD_CALL_DOWNLOAD        "download"
+#define FOTA_DBUS_METHOD_CALL_RESTART         "restart"
+#define FOTA_DBUS_METHOD_CALL_SIZE            "availableSize"
+
+typedef struct fota {
+    DBusConnection *conn;      /* DBus connection handle */
+    int             watch_rfd;
+    void           *watch;
+    fotax_t fotax;
+    void *priv;
+} fota_server_t;
+
+fota_server_t *fota_init(void);
+void fota_deinit(fota_server_t *fota);
+int fota_dbus_init(fota_server_t *fota);
+void fota_dbus_deinit(fota_server_t *fota);
+void fota_dbus_register_desc(void);
+void fota_loop_run(fota_server_t *fota);
+
+#endif

+ 37 - 0
components/fota_service/include/fota_debug.h

@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018-2021 Alibaba Group Holding Limited
+ */
+
+#ifndef _FOTA_DEBUG_H_
+#define _FOTA_DEBUG_H_
+
+#include <stdio.h>
+#include <errno.h>
+#include <syslog.h>
+
+/*
+ * priority, defined in syslog.h
+ *
+ * #define LOG_EMERG       0       // system is unusable
+ * #define LOG_ALERT       1       // action must be taken immediately
+ * #define LOG_CRIT        2       // critical conditions
+ * #define LOG_ERR         3       // error conditions
+ * #define LOG_WARNING     4       // warning conditions
+ * #define LOG_NOTICE      5       // normal but significant condition
+ * #define LOG_INFO        6       // informational
+ * #define LOG_DEBUG       7       // debug-level messages
+ */
+
+void fota_log(int priority, const char *format, ...);
+
+#define fota_check(X, errno)                                                                     \
+    do {                                                                                          \
+        if (!(X)) {                                                                               \
+            printf("func: %s, errno: %d\n", __func__, errno);                                     \
+            while(1);                                                                             \
+        }                                                                                         \
+    } while (0)
+
+#define fota_check_param(X) fota_check(X, EINVAL)
+
+#endif /* _FOTA_DEBUG_H_ */

+ 34 - 0
components/fota_service/include/fota_server.h

@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018-2021 Alibaba Group Holding Limited
+ */
+
+#ifndef __FOTA_SERVER_H__
+#define __FOTA_SERVER_H__
+
+#include <stddef.h>
+#include <pthread.h>
+#include <dbus/dbus.h>
+
+#include <fota.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int fota_dbus_signal_version(fota_server_t *fota, const char *str);
+int fota_dbus_signal_download(fota_server_t *fota, const char *str);
+int fota_dbus_signal_end(fota_server_t *fota, const char *str);
+int fota_dbus_signal_restart(fota_server_t *fota, const char *str);
+int fota_dbus_method_start(DBusMessage *msg, fota_server_t *fota);
+int fota_dbus_method_stop(DBusMessage *msg, fota_server_t *fota);
+int fota_dbus_method_get_state(DBusMessage *msg, fota_server_t *fota);
+int fota_dbus_method_version_check(DBusMessage *msg, fota_server_t *fota);
+int fota_dbus_method_download(DBusMessage *msg, fota_server_t *fota);
+int fota_dbus_method_restart(DBusMessage *msg, fota_server_t *fota);
+int fota_dbus_method_size(DBusMessage *msg, fota_server_t *fota);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __FOTA_SERVER_H__ */

+ 61 - 0
components/fota_service/include/fotax.h

@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+#ifndef __FOTAX_H__
+#define __FOTAX_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+    FOTAX_EVENT_VERSION = 0,     /*!< Check version from server ok */
+    FOTAX_EVENT_DOWNLOAD,        /*!< Downloading the fota data */
+    FOTAX_EVENT_END,             /*!< This event occurs when there are any errors during execution */
+    FOTAX_EVENT_RESTART          /*!< real want to restart */
+} fotax_event_e;
+
+typedef enum {
+    FOTAX_INIT = 1,          /*!< create fota task, wait for version check */
+    FOTAX_DOWNLOAD = 2,      /*!< start to download fota data */
+    FOTAX_ABORT = 3,         /*!< read or write exception */
+    FOTAX_FINISH = 4,        /*!< download finish */
+} fotax_status_e;
+
+typedef struct {
+    void *cert;
+    int (*fota_register_cloud)(void);
+    int (*netio_register_from)(const char *cert);
+    int (*netio_register_to)(void);
+} fota_register_ops_t;
+
+typedef struct fotax fotax_t;
+
+typedef void (*fotax_event_cb_t)(fotax_t *fotax, fotax_event_e event, const char *json);   ///< fota Event call back.
+
+struct fotax {
+    void *fota_handle;
+    fotax_status_e state;
+    fotax_event_cb_t fotax_event_cb;
+    fota_register_ops_t *register_ops;
+    void *private;
+};
+
+int fotax_start(fotax_t *fotax);
+
+int fotax_stop(fotax_t *fotax);
+
+int fotax_get_state(fotax_t *fotax);
+
+int fotax_version_check(fotax_t *fotax);
+
+int fotax_download(fotax_t *fotax);
+
+int fotax_restart(fotax_t *fotax, int delay_ms);
+
+int64_t fotax_get_size(fotax_t *fotax, const char *name);
+
+#ifdef __cplusplus
+}
+#endif
+#endif

+ 594 - 0
components/fota_service/include/utils/common.h

@@ -0,0 +1,594 @@
+/*
+ * fota_supplicant/hostapd / common helper functions, etc.
+ * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef COMMON_H
+#define COMMON_H
+
+#include "os.h"
+
+#if defined(__linux__) || defined(__GLIBC__)
+#include <endian.h>
+#include <byteswap.h>
+#endif /* __linux__ */
+
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || \
+    defined(__OpenBSD__)
+#include <sys/types.h>
+#include <sys/endian.h>
+#define __BYTE_ORDER	_BYTE_ORDER
+#define	__LITTLE_ENDIAN	_LITTLE_ENDIAN
+#define	__BIG_ENDIAN	_BIG_ENDIAN
+#ifdef __OpenBSD__
+#define bswap_16 swap16
+#define bswap_32 swap32
+#define bswap_64 swap64
+#else /* __OpenBSD__ */
+#define bswap_16 bswap16
+#define bswap_32 bswap32
+#define bswap_64 bswap64
+#endif /* __OpenBSD__ */
+#endif /* defined(__FreeBSD__) || defined(__NetBSD__) ||
+	* defined(__DragonFly__) || defined(__OpenBSD__) */
+
+#ifdef __APPLE__
+#include <sys/types.h>
+#include <machine/endian.h>
+#define __BYTE_ORDER	_BYTE_ORDER
+#define __LITTLE_ENDIAN	_LITTLE_ENDIAN
+#define __BIG_ENDIAN	_BIG_ENDIAN
+static inline unsigned short bswap_16(unsigned short v)
+{
+    return ((v & 0xff) << 8) | (v >> 8);
+}
+
+static inline unsigned int bswap_32(unsigned int v)
+{
+    return ((v & 0xff) << 24) | ((v & 0xff00) << 8) |
+           ((v & 0xff0000) >> 8) | (v >> 24);
+}
+#endif /* __APPLE__ */
+
+#ifdef __rtems__
+#include <rtems/endian.h>
+#define __BYTE_ORDER BYTE_ORDER
+#define __LITTLE_ENDIAN LITTLE_ENDIAN
+#define __BIG_ENDIAN BIG_ENDIAN
+#define bswap_16 CPU_swap_u16
+#define bswap_32 CPU_swap_u32
+#endif /* __rtems__ */
+
+#ifdef CONFIG_NATIVE_WINDOWS
+#include <winsock.h>
+
+typedef int socklen_t;
+
+#ifndef MSG_DONTWAIT
+#define MSG_DONTWAIT 0 /* not supported */
+#endif
+
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+#ifdef _MSC_VER
+#define inline __inline
+
+#undef vsnprintf
+#define vsnprintf _vsnprintf
+#undef close
+#define close closesocket
+#endif /* _MSC_VER */
+
+
+/* Define platform specific integer types */
+
+#ifdef _MSC_VER
+typedef UINT64 u64;
+typedef UINT32 u32;
+typedef UINT16 u16;
+typedef UINT8 u8;
+typedef INT64 s64;
+typedef INT32 s32;
+typedef INT16 s16;
+typedef INT8 s8;
+#define FOTA_TYPES_DEFINED
+#endif /* _MSC_VER */
+
+#ifdef __vxworks
+typedef unsigned long long u64;
+typedef UINT32 u32;
+typedef UINT16 u16;
+typedef UINT8 u8;
+typedef long long s64;
+typedef INT32 s32;
+typedef INT16 s16;
+typedef INT8 s8;
+#define FOTA_TYPES_DEFINED
+#endif /* __vxworks */
+
+#ifndef FOTA_TYPES_DEFINED
+#ifdef CONFIG_USE_INTTYPES_H
+#include <inttypes.h>
+#else
+#include <stdint.h>
+#endif
+typedef uint64_t u64;
+typedef uint32_t u32;
+typedef uint16_t u16;
+typedef uint8_t u8;
+typedef int64_t s64;
+typedef int32_t s32;
+typedef int16_t s16;
+typedef int8_t s8;
+#define FOTA_TYPES_DEFINED
+#endif /* !FOTA_TYPES_DEFINED */
+
+
+/* Define platform specific byte swapping macros */
+
+#if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS)
+
+static inline unsigned short fota_swap_16(unsigned short v)
+{
+    return ((v & 0xff) << 8) | (v >> 8);
+}
+
+static inline unsigned int fota_swap_32(unsigned int v)
+{
+    return ((v & 0xff) << 24) | ((v & 0xff00) << 8) |
+           ((v & 0xff0000) >> 8) | (v >> 24);
+}
+
+#define le_to_host16(n) (n)
+#define host_to_le16(n) (n)
+#define be_to_host16(n) fota_swap_16(n)
+#define host_to_be16(n) fota_swap_16(n)
+#define le_to_host32(n) (n)
+#define host_to_le32(n) (n)
+#define be_to_host32(n) fota_swap_32(n)
+#define host_to_be32(n) fota_swap_32(n)
+#define host_to_le64(n) (n)
+
+#define FOTA_BYTE_SWAP_DEFINED
+
+#endif /* __CYGWIN__ || CONFIG_NATIVE_WINDOWS */
+
+
+#ifndef FOTA_BYTE_SWAP_DEFINED
+
+#ifndef __BYTE_ORDER
+#ifndef __LITTLE_ENDIAN
+#ifndef __BIG_ENDIAN
+#define __LITTLE_ENDIAN 1234
+#define __BIG_ENDIAN 4321
+#if defined(sparc)
+#define __BYTE_ORDER __BIG_ENDIAN
+#endif
+#endif /* __BIG_ENDIAN */
+#endif /* __LITTLE_ENDIAN */
+#endif /* __BYTE_ORDER */
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define le_to_host16(n) ((__force u16) (le16) (n))
+#define host_to_le16(n) ((__force le16) (u16) (n))
+#define be_to_host16(n) bswap_16((__force u16) (be16) (n))
+#define host_to_be16(n) ((__force be16) bswap_16((n)))
+#define le_to_host32(n) ((__force u32) (le32) (n))
+#define host_to_le32(n) ((__force le32) (u32) (n))
+#define be_to_host32(n) bswap_32((__force u32) (be32) (n))
+#define host_to_be32(n) ((__force be32) bswap_32((n)))
+#define le_to_host64(n) ((__force u64) (le64) (n))
+#define host_to_le64(n) ((__force le64) (u64) (n))
+#define be_to_host64(n) bswap_64((__force u64) (be64) (n))
+#define host_to_be64(n) ((__force be64) bswap_64((n)))
+#elif __BYTE_ORDER == __BIG_ENDIAN
+#define le_to_host16(n) bswap_16(n)
+#define host_to_le16(n) bswap_16(n)
+#define be_to_host16(n) (n)
+#define host_to_be16(n) (n)
+#define le_to_host32(n) bswap_32(n)
+#define host_to_le32(n) bswap_32(n)
+#define be_to_host32(n) (n)
+#define host_to_be32(n) (n)
+#define le_to_host64(n) bswap_64(n)
+#define host_to_le64(n) bswap_64(n)
+#define be_to_host64(n) (n)
+#define host_to_be64(n) (n)
+#ifndef WORDS_BIGENDIAN
+#define WORDS_BIGENDIAN
+#endif
+#else
+#error Could not determine CPU byte order
+#endif
+
+#define FOTA_BYTE_SWAP_DEFINED
+#endif /* !FOTA_BYTE_SWAP_DEFINED */
+
+
+/* Macros for handling unaligned memory accesses */
+
+static inline u16 FOTA_GET_BE16(const u8 *a)
+{
+    return (a[0] << 8) | a[1];
+}
+
+static inline void FOTA_PUT_BE16(u8 *a, u16 val)
+{
+    a[0] = val >> 8;
+    a[1] = val & 0xff;
+}
+
+static inline u16 FOTA_GET_LE16(const u8 *a)
+{
+    return (a[1] << 8) | a[0];
+}
+
+static inline void FOTA_PUT_LE16(u8 *a, u16 val)
+{
+    a[1] = val >> 8;
+    a[0] = val & 0xff;
+}
+
+static inline u32 FOTA_GET_BE24(const u8 *a)
+{
+    return (a[0] << 16) | (a[1] << 8) | a[2];
+}
+
+static inline void FOTA_PUT_BE24(u8 *a, u32 val)
+{
+    a[0] = (val >> 16) & 0xff;
+    a[1] = (val >> 8) & 0xff;
+    a[2] = val & 0xff;
+}
+
+static inline u32 FOTA_GET_BE32(const u8 *a)
+{
+    return ((u32) a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3];
+}
+
+static inline void FOTA_PUT_BE32(u8 *a, u32 val)
+{
+    a[0] = (val >> 24) & 0xff;
+    a[1] = (val >> 16) & 0xff;
+    a[2] = (val >> 8) & 0xff;
+    a[3] = val & 0xff;
+}
+
+static inline u32 FOTA_GET_LE32(const u8 *a)
+{
+    return ((u32) a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0];
+}
+
+static inline void FOTA_PUT_LE32(u8 *a, u32 val)
+{
+    a[3] = (val >> 24) & 0xff;
+    a[2] = (val >> 16) & 0xff;
+    a[1] = (val >> 8) & 0xff;
+    a[0] = val & 0xff;
+}
+
+static inline u64 FOTA_GET_BE64(const u8 *a)
+{
+    return (((u64) a[0]) << 56) | (((u64) a[1]) << 48) |
+           (((u64) a[2]) << 40) | (((u64) a[3]) << 32) |
+           (((u64) a[4]) << 24) | (((u64) a[5]) << 16) |
+           (((u64) a[6]) << 8) | ((u64) a[7]);
+}
+
+static inline void FOTA_PUT_BE64(u8 *a, u64 val)
+{
+    a[0] = val >> 56;
+    a[1] = val >> 48;
+    a[2] = val >> 40;
+    a[3] = val >> 32;
+    a[4] = val >> 24;
+    a[5] = val >> 16;
+    a[6] = val >> 8;
+    a[7] = val & 0xff;
+}
+
+static inline u64 FOTA_GET_LE64(const u8 *a)
+{
+    return (((u64) a[7]) << 56) | (((u64) a[6]) << 48) |
+           (((u64) a[5]) << 40) | (((u64) a[4]) << 32) |
+           (((u64) a[3]) << 24) | (((u64) a[2]) << 16) |
+           (((u64) a[1]) << 8) | ((u64) a[0]);
+}
+
+static inline void FOTA_PUT_LE64(u8 *a, u64 val)
+{
+    a[7] = val >> 56;
+    a[6] = val >> 48;
+    a[5] = val >> 40;
+    a[4] = val >> 32;
+    a[3] = val >> 24;
+    a[2] = val >> 16;
+    a[1] = val >> 8;
+    a[0] = val & 0xff;
+}
+
+
+#ifndef ETH_ALEN
+#define ETH_ALEN 6
+#endif
+#ifndef ETH_HLEN
+#define ETH_HLEN 14
+#endif
+#ifndef IFNAMSIZ
+#define IFNAMSIZ 16
+#endif
+#ifndef ETH_P_ALL
+#define ETH_P_ALL 0x0003
+#endif
+#ifndef ETH_P_IP
+#define ETH_P_IP 0x0800
+#endif
+#ifndef ETH_P_80211_ENCAP
+#define ETH_P_80211_ENCAP 0x890d /* TDLS comes under this category */
+#endif
+#ifndef ETH_P_PAE
+#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */
+#endif /* ETH_P_PAE */
+#ifndef ETH_P_EAPOL
+#define ETH_P_EAPOL ETH_P_PAE
+#endif /* ETH_P_EAPOL */
+#ifndef ETH_P_RSN_PREAUTH
+#define ETH_P_RSN_PREAUTH 0x88c7
+#endif /* ETH_P_RSN_PREAUTH */
+#ifndef ETH_P_RRB
+#define ETH_P_RRB 0x890D
+#endif /* ETH_P_RRB */
+#ifndef ETH_P_OUI
+#define ETH_P_OUI 0x88B7
+#endif /* ETH_P_OUI */
+
+
+#ifdef __GNUC__
+#define PRINTF_FORMAT(a,b) __attribute__ ((format (printf, (a), (b))))
+#define STRUCT_PACKED __attribute__ ((packed))
+#else
+#define PRINTF_FORMAT(a,b)
+#define STRUCT_PACKED
+#endif
+
+
+#ifdef CONFIG_ANSI_C_EXTRA
+
+#if !defined(_MSC_VER) || _MSC_VER < 1400
+/* snprintf - used in number of places; sprintf() is _not_ a good replacement
+ * due to possible buffer overflow; see, e.g.,
+ * http://www.ijs.si/software/snprintf/ for portable implementation of
+ * snprintf. */
+int snprintf(char *str, size_t size, const char *format, ...);
+
+/* vsnprintf - only used for fota_msg() in fota_supplicant.c */
+int vsnprintf(char *str, size_t size, const char *format, va_list ap);
+#endif /* !defined(_MSC_VER) || _MSC_VER < 1400 */
+
+/* getopt - only used in main.c */
+int getopt(int argc, char *const argv[], const char *optstring);
+extern char *optarg;
+extern int optind;
+
+#ifndef CONFIG_NO_SOCKLEN_T_TYPEDEF
+#ifndef __socklen_t_defined
+typedef int socklen_t;
+#endif
+#endif
+
+/* inline - define as __inline or just define it to be empty, if needed */
+#ifdef CONFIG_NO_INLINE
+#define inline
+#else
+#define inline __inline
+#endif
+
+#ifndef __func__
+#define __func__ "__func__ not defined"
+#endif
+
+#ifndef bswap_16
+#define bswap_16(a) ((((u16) (a) << 8) & 0xff00) | (((u16) (a) >> 8) & 0xff))
+#endif
+
+#ifndef bswap_32
+#define bswap_32(a) ((((u32) (a) << 24) & 0xff000000) | \
+                     (((u32) (a) << 8) & 0xff0000) | \
+                     (((u32) (a) >> 8) & 0xff00) | \
+                     (((u32) (a) >> 24) & 0xff))
+#endif
+
+#ifndef MSG_DONTWAIT
+#define MSG_DONTWAIT 0
+#endif
+
+#ifdef _WIN32_WCE
+void perror(const char *s);
+#endif /* _WIN32_WCE */
+
+#endif /* CONFIG_ANSI_C_EXTRA */
+
+#ifndef MAC2STR
+#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
+#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
+
+/*
+ * Compact form for string representation of MAC address
+ * To be used, e.g., for constructing dbus paths for P2P Devices
+ */
+#define COMPACT_MACSTR "%02x%02x%02x%02x%02x%02x"
+#endif
+
+#ifndef BIT
+#define BIT(x) (1U << (x))
+#endif
+
+/*
+ * Definitions for sparse validation
+ * (http://kernel.org/pub/linux/kernel/people/josh/sparse/)
+ */
+#ifdef __CHECKER__
+#define __force __attribute__((force))
+#undef __bitwise
+#define __bitwise __attribute__((bitwise))
+#else
+#define __force
+#undef __bitwise
+#define __bitwise
+#endif
+
+typedef u16 __bitwise be16;
+typedef u16 __bitwise le16;
+typedef u32 __bitwise be32;
+typedef u32 __bitwise le32;
+typedef u64 __bitwise be64;
+typedef u64 __bitwise le64;
+
+#ifndef __must_check
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+#define __must_check __attribute__((__warn_unused_result__))
+#else
+#define __must_check
+#endif /* __GNUC__ */
+#endif /* __must_check */
+
+#ifndef __maybe_unused
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+#define __maybe_unused __attribute__((unused))
+#else
+#define __maybe_unused
+#endif /* __GNUC__ */
+#endif /* __must_check */
+
+#define SSID_MAX_LEN 32
+
+struct fota_ssid_value {
+    u8 ssid[SSID_MAX_LEN];
+    size_t ssid_len;
+};
+
+int hwaddr_aton(const char *txt, u8 *addr);
+int hwaddr_masked_aton(const char *txt, u8 *addr, u8 *mask, u8 maskable);
+int hwaddr_compact_aton(const char *txt, u8 *addr);
+int hwaddr_aton2(const char *txt, u8 *addr);
+int hex2byte(const char *hex);
+int hexstr2bin(const char *hex, u8 *buf, size_t len);
+void inc_byte_array(u8 *counter, size_t len);
+void buf_shift_right(u8 *buf, size_t len, size_t bits);
+void fota_get_ntp_timestamp(u8 *buf);
+int fota_scnprintf(char *buf, size_t size, const char *fmt, ...);
+int fota_snprintf_hex_sep(char *buf, size_t buf_size, const u8 *data, size_t len,
+                           char sep);
+int fota_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len);
+int fota_snprintf_hex_uppercase(char *buf, size_t buf_size, const u8 *data,
+                                 size_t len);
+
+int hwaddr_mask_txt(char *buf, size_t len, const u8 *addr, const u8 *mask);
+int ssid_parse(const char *buf, struct fota_ssid_value *ssid);
+
+#ifdef CONFIG_NATIVE_WINDOWS
+void fota_unicode2ascii_inplace(TCHAR *str);
+TCHAR *fota_strdup_tchar(const char *str);
+#else /* CONFIG_NATIVE_WINDOWS */
+#define fota_unicode2ascii_inplace(s) do { } while (0)
+#define fota_strdup_tchar(s) strdup((s))
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len);
+size_t printf_decode(u8 *buf, size_t maxlen, const char *str);
+
+const char *fota_ssid_txt(const u8 *ssid, size_t ssid_len);
+
+char *fota_config_parse_string(const char *value, size_t *len);
+int is_hex(const u8 *data, size_t len);
+int has_ctrl_char(const u8 *data, size_t len);
+int has_newline(const char *str);
+size_t merge_byte_arrays(u8 *res, size_t res_len,
+                         const u8 *src1, size_t src1_len,
+                         const u8 *src2, size_t src2_len);
+char *dup_binstr(const void *src, size_t len);
+
+static inline int is_zero_ether_addr(const u8 *a)
+{
+    return !(a[0] | a[1] | a[2] | a[3] | a[4] | a[5]);
+}
+
+static inline int is_broadcast_ether_addr(const u8 *a)
+{
+    return (a[0] & a[1] & a[2] & a[3] & a[4] & a[5]) == 0xff;
+}
+
+static inline int is_multicast_ether_addr(const u8 *a)
+{
+    return a[0] & 0x01;
+}
+
+#define broadcast_ether_addr (const u8 *) "\xff\xff\xff\xff\xff\xff"
+
+#include "fota_debug.h"
+
+
+struct fota_freq_range_list {
+    struct fota_freq_range {
+        unsigned int min;
+        unsigned int max;
+    } *range;
+    unsigned int num;
+};
+
+int freq_range_list_parse(struct fota_freq_range_list *res, const char *value);
+int freq_range_list_includes(const struct fota_freq_range_list *list,
+                             unsigned int freq);
+char *freq_range_list_str(const struct fota_freq_range_list *list);
+
+int int_array_len(const int *a);
+void int_array_concat(int **res, const int *a);
+void int_array_sort_unique(int *a);
+void int_array_add_unique(int **res, int a);
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+void str_clear_free(char *str);
+void bin_clear_free(void *bin, size_t len);
+
+int random_mac_addr(u8 *addr);
+int random_mac_addr_keep_oui(u8 *addr);
+
+const char *cstr_token(const char *str, const char *delim, const char **last);
+char *str_token(char *str, const char *delim, char **context);
+size_t utf8_escape(const char *inp, size_t in_size,
+                   char *outp, size_t out_size);
+size_t utf8_unescape(const char *inp, size_t in_size,
+                     char *outp, size_t out_size);
+int is_ctrl_char(char c);
+
+int str_starts(const char *str, const char *start);
+
+u8 rssi_to_rcpi(int rssi);
+char *get_param(const char *cmd, const char *param);
+
+void forced_memzero(void *ptr, size_t len);
+
+/*
+ * gcc 4.4 ends up generating strict-aliasing warnings about some very common
+ * networking socket uses that do not really result in a real problem and
+ * cannot be easily avoided with union-based type-punning due to struct
+ * definitions including another struct in system header files. To avoid having
+ * to fully disable strict-aliasing warnings, provide a mechanism to hide the
+ * typecast from aliasing for now. A cleaner solution will hopefully be found
+ * in the future to handle these cases.
+ */
+void *__hide_aliasing_typecast(void *foo);
+#define aliasing_hide_typecast(a,t) (t *) __hide_aliasing_typecast((a))
+
+#ifdef CONFIG_VALGRIND
+#include <valgrind/memcheck.h>
+#define FOTA_MEM_DEFINED(ptr, len) VALGRIND_MAKE_MEM_DEFINED((ptr), (len))
+#else /* CONFIG_VALGRIND */
+#define FOTA_MEM_DEFINED(ptr, len) do { } while (0)
+#endif /* CONFIG_VALGRIND */
+
+#endif /* COMMON_H */

+ 167 - 0
components/fota_service/include/utils/fota_buf.h

@@ -0,0 +1,167 @@
+/*
+ * Dynamic data buffer
+ * Copyright (c) 2007-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef FOTABUF_H
+#define FOTABUF_H
+
+#include <stdint.h>
+
+/* fotabuf::buf is a pointer to external data */
+#define FOTABUF_FLAG_EXT_DATA BIT(0)
+
+/*
+ * Internal data structure for fotabuf. Please do not touch this directly from
+ * elsewhere. This is only defined in header file to allow inline functions
+ * from this file to access data.
+ */
+struct fotabuf {
+    size_t size; /* total size of the allocated buffer */
+    size_t used; /* length of data in the buffer */
+    uint8_t *buf; /* pointer to the head of the buffer */
+    unsigned int flags;
+    /* optionally followed by the allocated buffer */
+};
+
+
+int fotabuf_resize(struct fotabuf **buf, size_t add_len);
+struct fotabuf *fotabuf_alloc(size_t len);
+struct fotabuf *fotabuf_alloc_ext_data(uint8_t *data, size_t len);
+struct fotabuf *fotabuf_alloc_copy(const void *data, size_t len);
+struct fotabuf *fotabuf_dup(const struct fotabuf *src);
+void fotabuf_free(struct fotabuf *buf);
+void fotabuf_clear_free(struct fotabuf *buf);
+void *fotabuf_put(struct fotabuf *buf, size_t len);
+struct fotabuf *fotabuf_concat(struct fotabuf *a, struct fotabuf *b);
+struct fotabuf *fotabuf_zeropad(struct fotabuf *buf, size_t len);
+void fotabuf_printf(struct fotabuf *buf, char *fmt, ...) PRINTF_FORMAT(2, 3);
+struct fotabuf *fotabuf_parse_bin(const char *buf);
+
+
+/**
+ * fotabuf_size - Get the currently allocated size of a fotabuf buffer
+ * @buf: fotabuf buffer
+ * Returns: Currently allocated size of the buffer
+ */
+static inline size_t fotabuf_size(const struct fotabuf *buf)
+{
+    return buf->size;
+}
+
+/**
+ * fotabuf_len - Get the current length of a fotabuf buffer data
+ * @buf: fotabuf buffer
+ * Returns: Currently used length of the buffer
+ */
+static inline size_t fotabuf_len(const struct fotabuf *buf)
+{
+    return buf->used;
+}
+
+/**
+ * fotabuf_tailroom - Get size of available tail room in the end of the buffer
+ * @buf: fotabuf buffer
+ * Returns: Tail room (in bytes) of available space in the end of the buffer
+ */
+static inline size_t fotabuf_tailroom(const struct fotabuf *buf)
+{
+    return buf->size - buf->used;
+}
+
+/**
+ * fotabuf_head - Get pointer to the head of the buffer data
+ * @buf: fotabuf buffer
+ * Returns: Pointer to the head of the buffer data
+ */
+static inline const void *fotabuf_head(const struct fotabuf *buf)
+{
+    return buf->buf;
+}
+
+static inline const u8 *fotabuf_head_u8(const struct fotabuf *buf)
+{
+    return (const u8 *) fotabuf_head(buf);
+}
+
+/**
+ * fotabuf_mhead - Get modifiable pointer to the head of the buffer data
+ * @buf: fotabuf buffer
+ * Returns: Pointer to the head of the buffer data
+ */
+static inline void *fotabuf_mhead(struct fotabuf *buf)
+{
+    return buf->buf;
+}
+
+static inline u8 *fotabuf_mhead_u8(struct fotabuf *buf)
+{
+    return (u8 *) fotabuf_mhead(buf);
+}
+
+static inline void fotabuf_put_u8(struct fotabuf *buf, u8 data)
+{
+    u8 *pos = (u8 *) fotabuf_put(buf, 1);
+    *pos = data;
+}
+
+static inline void fotabuf_put_le16(struct fotabuf *buf, u16 data)
+{
+    u8 *pos = (u8 *) fotabuf_put(buf, 2);
+    FOTA_PUT_LE16(pos, data);
+}
+
+static inline void fotabuf_put_le32(struct fotabuf *buf, u32 data)
+{
+    u8 *pos = (u8 *) fotabuf_put(buf, 4);
+    FOTA_PUT_LE32(pos, data);
+}
+
+static inline void fotabuf_put_be16(struct fotabuf *buf, u16 data)
+{
+    u8 *pos = (u8 *) fotabuf_put(buf, 2);
+    FOTA_PUT_BE16(pos, data);
+}
+
+static inline void fotabuf_put_be24(struct fotabuf *buf, u32 data)
+{
+    u8 *pos = (u8 *) fotabuf_put(buf, 3);
+    FOTA_PUT_BE24(pos, data);
+}
+
+static inline void fotabuf_put_be32(struct fotabuf *buf, u32 data)
+{
+    u8 *pos = (u8 *) fotabuf_put(buf, 4);
+    FOTA_PUT_BE32(pos, data);
+}
+
+static inline void fotabuf_put_data(struct fotabuf *buf, const void *data,
+                                     size_t len)
+{
+    if (data) {
+        os_memcpy(fotabuf_put(buf, len), data, len);
+    }
+}
+
+static inline void fotabuf_put_buf(struct fotabuf *dst,
+                                    const struct fotabuf *src)
+{
+    fotabuf_put_data(dst, fotabuf_head(src), fotabuf_len(src));
+}
+
+static inline void fotabuf_set(struct fotabuf *buf, const void *data, size_t len)
+{
+    buf->buf = (u8 *) data;
+    buf->flags = FOTABUF_FLAG_EXT_DATA;
+    buf->size = buf->used = len;
+}
+
+static inline void fotabuf_put_str(struct fotabuf *dst, const char *str)
+{
+    fotabuf_put_data(dst, str, os_strlen(str));
+}
+
+#endif /* FOTABUF_H */

+ 42 - 0
components/fota_service/include/utils/includes.h

@@ -0,0 +1,42 @@
+/*
+ * hostapd - Default include files
+ * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This header file is included into all C files so that commonly used header
+ * files can be selected with OS specific ifdef blocks in one place instead of
+ * having to have OS/C library specific selection in many files.
+ */
+
+#ifndef INCLUDES_H
+#define INCLUDES_H
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#ifndef _WIN32_WCE
+#include <signal.h>
+#include <sys/types.h>
+#include <errno.h>
+#endif /* _WIN32_WCE */
+#include <ctype.h>
+
+#ifndef _MSC_VER
+#include <unistd.h>
+#endif /* _MSC_VER */
+
+#ifndef CONFIG_NATIVE_WINDOWS
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#ifndef __vxworks
+#include <sys/uio.h>
+#include <sys/time.h>
+#endif /* __vxworks */
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+#endif /* INCLUDES_H */

+ 100 - 0
components/fota_service/include/utils/list.h

@@ -0,0 +1,100 @@
+/*
+ * Doubly-linked list
+ * Copyright (c) 2009-2019, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef LIST_H
+#define LIST_H
+
+/**
+ * struct dl_list - Doubly-linked list
+ */
+struct dl_list {
+    struct dl_list *next;
+    struct dl_list *prev;
+};
+
+#define DL_LIST_HEAD_INIT(l) { &(l), &(l) }
+
+static inline void dl_list_init(struct dl_list *list)
+{
+    list->next = list;
+    list->prev = list;
+}
+
+static inline void dl_list_add(struct dl_list *list, struct dl_list *item)
+{
+    item->next = list->next;
+    item->prev = list;
+    list->next->prev = item;
+    list->next = item;
+}
+
+static inline void dl_list_add_tail(struct dl_list *list, struct dl_list *item)
+{
+    dl_list_add(list->prev, item);
+}
+
+static inline void dl_list_del(struct dl_list *item)
+{
+    item->next->prev = item->prev;
+    item->prev->next = item->next;
+    item->next = NULL;
+    item->prev = NULL;
+}
+
+static inline int dl_list_empty(struct dl_list *list)
+{
+    return list->next == list;
+}
+
+static inline unsigned int dl_list_len(struct dl_list *list)
+{
+    struct dl_list *item;
+    int count = 0;
+
+    for (item = list->next; item != list; item = item->next) {
+        count++;
+    }
+
+    return count;
+}
+
+#ifndef offsetof
+#define offsetof(type, member) ((long) &((type *) 0)->member)
+#endif
+
+#define dl_list_entry(item, type, member) \
+    ((type *) ((char *) item - offsetof(type, member)))
+
+#define dl_list_first(list, type, member) \
+    (dl_list_empty((list)) ? NULL : \
+     dl_list_entry((list)->next, type, member))
+
+#define dl_list_last(list, type, member) \
+    (dl_list_empty((list)) ? NULL : \
+     dl_list_entry((list)->prev, type, member))
+
+#define dl_list_for_each(item, list, type, member) \
+    for (item = dl_list_first((list), type, member); \
+         item && item != dl_list_entry((list), type, member); \
+         item = dl_list_entry(item->member.next, type, member))
+
+#define dl_list_for_each_safe(item, n, list, type, member) \
+    for (item = dl_list_entry((list)->next, type, member), \
+         n = dl_list_entry(item->member.next, type, member); \
+         &item->member != (list); \
+         item = n, n = dl_list_entry(n->member.next, type, member))
+
+#define dl_list_for_each_reverse(item, list, type, member) \
+    for (item = dl_list_entry((list)->prev, type, member); \
+         &item->member != (list); \
+         item = dl_list_entry(item->member.prev, type, member))
+
+#define DEFINE_DL_LIST(name) \
+    struct dl_list name = { &(name), &(name) }
+
+#endif /* LIST_H */

+ 688 - 0
components/fota_service/include/utils/os.h

@@ -0,0 +1,688 @@
+/*
+ * OS specific functions
+ * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef OS_H
+#define OS_H
+
+#include <stdlib.h>
+
+typedef long os_time_t;
+
+/**
+ * os_sleep - Sleep (sec, usec)
+ * @sec: Number of seconds to sleep
+ * @usec: Number of microseconds to sleep
+ */
+void os_sleep(os_time_t sec, os_time_t usec);
+
+struct os_time {
+    os_time_t sec;
+    os_time_t usec;
+};
+
+struct os_reltime {
+    os_time_t sec;
+    os_time_t usec;
+};
+
+/**
+ * os_get_time - Get current time (sec, usec)
+ * @t: Pointer to buffer for the time
+ * Returns: 0 on success, -1 on failure
+ */
+int os_get_time(struct os_time *t);
+
+/**
+ * os_get_reltime - Get relative time (sec, usec)
+ * @t: Pointer to buffer for the time
+ * Returns: 0 on success, -1 on failure
+ */
+int os_get_reltime(struct os_reltime *t);
+
+
+/* Helpers for handling struct os_time */
+
+static inline int os_time_before(struct os_time *a, struct os_time *b)
+{
+    return (a->sec < b->sec) ||
+           (a->sec == b->sec && a->usec < b->usec);
+}
+
+
+static inline void os_time_sub(struct os_time *a, struct os_time *b,
+                               struct os_time *res)
+{
+    res->sec = a->sec - b->sec;
+    res->usec = a->usec - b->usec;
+
+    if (res->usec < 0) {
+        res->sec--;
+        res->usec += 1000000;
+    }
+}
+
+
+/* Helpers for handling struct os_reltime */
+
+static inline int os_reltime_before(struct os_reltime *a,
+                                    struct os_reltime *b)
+{
+    return (a->sec < b->sec) ||
+           (a->sec == b->sec && a->usec < b->usec);
+}
+
+
+static inline void os_reltime_sub(struct os_reltime *a, struct os_reltime *b,
+                                  struct os_reltime *res)
+{
+    res->sec = a->sec - b->sec;
+    res->usec = a->usec - b->usec;
+
+    if (res->usec < 0) {
+        res->sec--;
+        res->usec += 1000000;
+    }
+}
+
+
+static inline void os_reltime_age(struct os_reltime *start,
+                                  struct os_reltime *age)
+{
+    struct os_reltime now;
+
+    os_get_reltime(&now);
+    os_reltime_sub(&now, start, age);
+}
+
+
+static inline int os_reltime_expired(struct os_reltime *now,
+                                     struct os_reltime *ts,
+                                     os_time_t timeout_secs)
+{
+    struct os_reltime age;
+
+    os_reltime_sub(now, ts, &age);
+    return (age.sec > timeout_secs) ||
+           (age.sec == timeout_secs && age.usec > 0);
+}
+
+
+static inline int os_reltime_initialized(struct os_reltime *t)
+{
+    return t->sec != 0 || t->usec != 0;
+}
+
+
+/**
+ * os_mktime - Convert broken-down time into seconds since 1970-01-01
+ * @year: Four digit year
+ * @month: Month (1 .. 12)
+ * @day: Day of month (1 .. 31)
+ * @hour: Hour (0 .. 23)
+ * @min: Minute (0 .. 59)
+ * @sec: Second (0 .. 60)
+ * @t: Buffer for returning calendar time representation (seconds since
+ * 1970-01-01 00:00:00)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Note: The result is in seconds from Epoch, i.e., in UTC, not in local time
+ * which is used by POSIX mktime().
+ */
+int os_mktime(int year, int month, int day, int hour, int min, int sec,
+              os_time_t *t);
+
+struct os_tm {
+    int sec; /* 0..59 or 60 for leap seconds */
+    int min; /* 0..59 */
+    int hour; /* 0..23 */
+    int day; /* 1..31 */
+    int month; /* 1..12 */
+    int year; /* Four digit year */
+};
+
+int os_gmtime(os_time_t t, struct os_tm *tm);
+
+/**
+ * os_daemonize - Run in the background (detach from the controlling terminal)
+ * @pid_file: File name to write the process ID to or %NULL to skip this
+ * Returns: 0 on success, -1 on failure
+ */
+int os_daemonize(const char *pid_file);
+
+/**
+ * os_daemonize_terminate - Stop running in the background (remove pid file)
+ * @pid_file: File name to write the process ID to or %NULL to skip this
+ */
+void os_daemonize_terminate(const char *pid_file);
+
+/**
+ * os_get_random - Get cryptographically strong pseudo random data
+ * @buf: Buffer for pseudo random data
+ * @len: Length of the buffer
+ * Returns: 0 on success, -1 on failure
+ */
+int os_get_random(unsigned char *buf, size_t len);
+
+/**
+ * os_random - Get pseudo random value (not necessarily very strong)
+ * Returns: Pseudo random value
+ */
+unsigned long os_random(void);
+
+/**
+ * os_rel2abs_path - Get an absolute path for a file
+ * @rel_path: Relative path to a file
+ * Returns: Absolute path for the file or %NULL on failure
+ *
+ * This function tries to convert a relative path of a file to an absolute path
+ * in order for the file to be found even if current working directory has
+ * changed. The returned value is allocated and caller is responsible for
+ * freeing it. It is acceptable to just return the same path in an allocated
+ * buffer, e.g., return strdup(rel_path). This function is only used to find
+ * configuration files when os_daemonize() may have changed the current working
+ * directory and relative path would be pointing to a different location.
+ */
+char *os_rel2abs_path(const char *rel_path);
+
+/**
+ * os_program_init - Program initialization (called at start)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called when a programs starts. If there are any OS specific
+ * processing that is needed, it can be placed here. It is also acceptable to
+ * just return 0 if not special processing is needed.
+ */
+int os_program_init(void);
+
+/**
+ * os_program_deinit - Program deinitialization (called just before exit)
+ *
+ * This function is called just before a program exists. If there are any OS
+ * specific processing, e.g., freeing resourced allocated in os_program_init(),
+ * it should be done here. It is also acceptable for this function to do
+ * nothing.
+ */
+void os_program_deinit(void);
+
+/**
+ * os_setenv - Set environment variable
+ * @name: Name of the variable
+ * @value: Value to set to the variable
+ * @overwrite: Whether existing variable should be overwritten
+ * Returns: 0 on success, -1 on error
+ *
+ * This function is only used for fota_cli action scripts. OS wrapper does not
+ * need to implement this if such functionality is not needed.
+ */
+int os_setenv(const char *name, const char *value, int overwrite);
+
+/**
+ * os_unsetenv - Delete environent variable
+ * @name: Name of the variable
+ * Returns: 0 on success, -1 on error
+ *
+ * This function is only used for fota_cli action scripts. OS wrapper does not
+ * need to implement this if such functionality is not needed.
+ */
+int os_unsetenv(const char *name);
+
+/**
+ * os_readfile - Read a file to an allocated memory buffer
+ * @name: Name of the file to read
+ * @len: For returning the length of the allocated buffer
+ * Returns: Pointer to the allocated buffer or %NULL on failure
+ *
+ * This function allocates memory and reads the given file to this buffer. Both
+ * binary and text files can be read with this function. The caller is
+ * responsible for freeing the returned buffer with os_free().
+ */
+char *os_readfile(const char *name, size_t *len);
+
+/**
+ * os_file_exists - Check whether the specified file exists
+ * @fname: Path and name of the file
+ * Returns: 1 if the file exists or 0 if not
+ */
+int os_file_exists(const char *fname);
+
+/**
+ * os_fdatasync - Sync a file's (for a given stream) state with storage device
+ * @stream: the stream to be flushed
+ * Returns: 0 if the operation succeeded or -1 on failure
+ */
+int os_fdatasync(FILE *stream);
+
+/**
+ * os_zalloc - Allocate and zero memory
+ * @size: Number of bytes to allocate
+ * Returns: Pointer to allocated and zeroed memory or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer with os_free().
+ */
+void *os_zalloc(size_t size);
+
+/**
+ * os_calloc - Allocate and zero memory for an array
+ * @nmemb: Number of members in the array
+ * @size: Number of bytes in each member
+ * Returns: Pointer to allocated and zeroed memory or %NULL on failure
+ *
+ * This function can be used as a wrapper for os_zalloc(nmemb * size) when an
+ * allocation is used for an array. The main benefit over os_zalloc() is in
+ * having an extra check to catch integer overflows in multiplication.
+ *
+ * Caller is responsible for freeing the returned buffer with os_free().
+ */
+static inline void *os_calloc(size_t nmemb, size_t size)
+{
+    if (size && nmemb > (~(size_t) 0) / size) {
+        return NULL;
+    }
+
+    return os_zalloc(nmemb * size);
+}
+
+
+/*
+ * The following functions are wrapper for standard ANSI C or POSIX functions.
+ * By default, they are just defined to use the standard function name and no
+ * os_*.c implementation is needed for them. This avoids extra function calls
+ * by allowing the C pre-processor take care of the function name mapping.
+ *
+ * If the target system uses a C library that does not provide these functions,
+ * build_config.h can be used to define the wrappers to use a different
+ * function name. This can be done on function-by-function basis since the
+ * defines here are only used if build_config.h does not define the os_* name.
+ * If needed, os_*.c file can be used to implement the functions that are not
+ * included in the C library on the target system. Alternatively,
+ * OS_NO_C_LIB_DEFINES can be defined to skip all defines here in which case
+ * these functions need to be implemented in os_*.c file for the target system.
+ */
+
+#ifdef OS_NO_C_LIB_DEFINES
+
+/**
+ * os_malloc - Allocate dynamic memory
+ * @size: Size of the buffer to allocate
+ * Returns: Allocated buffer or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer with os_free().
+ */
+void *os_malloc(size_t size);
+
+/**
+ * os_realloc - Re-allocate dynamic memory
+ * @ptr: Old buffer from os_malloc() or os_realloc()
+ * @size: Size of the new buffer
+ * Returns: Allocated buffer or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer with os_free().
+ * If re-allocation fails, %NULL is returned and the original buffer (ptr) is
+ * not freed and caller is still responsible for freeing it.
+ */
+void *os_realloc(void *ptr, size_t size);
+
+/**
+ * os_free - Free dynamic memory
+ * @ptr: Old buffer from os_malloc() or os_realloc(); can be %NULL
+ */
+void os_free(void *ptr);
+
+/**
+ * os_memcpy - Copy memory area
+ * @dest: Destination
+ * @src: Source
+ * @n: Number of bytes to copy
+ * Returns: dest
+ *
+ * The memory areas src and dst must not overlap. os_memmove() can be used with
+ * overlapping memory.
+ */
+void *os_memcpy(void *dest, const void *src, size_t n);
+
+/**
+ * os_memmove - Copy memory area
+ * @dest: Destination
+ * @src: Source
+ * @n: Number of bytes to copy
+ * Returns: dest
+ *
+ * The memory areas src and dst may overlap.
+ */
+void *os_memmove(void *dest, const void *src, size_t n);
+
+/**
+ * os_memset - Fill memory with a constant byte
+ * @s: Memory area to be filled
+ * @c: Constant byte
+ * @n: Number of bytes started from s to fill with c
+ * Returns: s
+ */
+void *os_memset(void *s, int c, size_t n);
+
+/**
+ * os_memcmp - Compare memory areas
+ * @s1: First buffer
+ * @s2: Second buffer
+ * @n: Maximum numbers of octets to compare
+ * Returns: An integer less than, equal to, or greater than zero if s1 is
+ * found to be less than, to match, or be greater than s2. Only first n
+ * characters will be compared.
+ */
+int os_memcmp(const void *s1, const void *s2, size_t n);
+
+/**
+ * os_strdup - Duplicate a string
+ * @s: Source string
+ * Returns: Allocated buffer with the string copied into it or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer with os_free().
+ */
+char *os_strdup(const char *s);
+
+/**
+ * os_strlen - Calculate the length of a string
+ * @s: '\0' terminated string
+ * Returns: Number of characters in s (not counting the '\0' terminator)
+ */
+size_t os_strlen(const char *s);
+
+/**
+ * os_strcasecmp - Compare two strings ignoring case
+ * @s1: First string
+ * @s2: Second string
+ * Returns: An integer less than, equal to, or greater than zero if s1 is
+ * found to be less than, to match, or be greatred than s2
+ */
+int os_strcasecmp(const char *s1, const char *s2);
+
+/**
+ * os_strncasecmp - Compare two strings ignoring case
+ * @s1: First string
+ * @s2: Second string
+ * @n: Maximum numbers of characters to compare
+ * Returns: An integer less than, equal to, or greater than zero if s1 is
+ * found to be less than, to match, or be greater than s2. Only first n
+ * characters will be compared.
+ */
+int os_strncasecmp(const char *s1, const char *s2, size_t n);
+
+/**
+ * os_strchr - Locate the first occurrence of a character in string
+ * @s: String
+ * @c: Character to search for
+ * Returns: Pointer to the matched character or %NULL if not found
+ */
+char *os_strchr(const char *s, int c);
+
+/**
+ * os_strrchr - Locate the last occurrence of a character in string
+ * @s: String
+ * @c: Character to search for
+ * Returns: Pointer to the matched character or %NULL if not found
+ */
+char *os_strrchr(const char *s, int c);
+
+/**
+ * os_strcmp - Compare two strings
+ * @s1: First string
+ * @s2: Second string
+ * Returns: An integer less than, equal to, or greater than zero if s1 is
+ * found to be less than, to match, or be greatred than s2
+ */
+int os_strcmp(const char *s1, const char *s2);
+
+/**
+ * os_strncmp - Compare two strings
+ * @s1: First string
+ * @s2: Second string
+ * @n: Maximum numbers of characters to compare
+ * Returns: An integer less than, equal to, or greater than zero if s1 is
+ * found to be less than, to match, or be greater than s2. Only first n
+ * characters will be compared.
+ */
+int os_strncmp(const char *s1, const char *s2, size_t n);
+
+/**
+ * os_strstr - Locate a substring
+ * @haystack: String (haystack) to search from
+ * @needle: Needle to search from haystack
+ * Returns: Pointer to the beginning of the substring or %NULL if not found
+ */
+char *os_strstr(const char *haystack, const char *needle);
+
+/**
+ * os_snprintf - Print to a memory buffer
+ * @str: Memory buffer to print into
+ * @size: Maximum length of the str buffer
+ * @format: printf format
+ * Returns: Number of characters printed (not including trailing '\0').
+ *
+ * If the output buffer is truncated, number of characters which would have
+ * been written is returned. Since some C libraries return -1 in such a case,
+ * the caller must be prepared on that value, too, to indicate truncation.
+ *
+ * Note: Some C library implementations of snprintf() may not guarantee null
+ * termination in case the output is truncated. The OS wrapper function of
+ * os_snprintf() should provide this guarantee, i.e., to null terminate the
+ * output buffer if a C library version of the function is used and if that
+ * function does not guarantee null termination.
+ *
+ * If the target system does not include snprintf(), see, e.g.,
+ * http://www.ijs.si/software/snprintf/ for an example of a portable
+ * implementation of snprintf.
+ */
+int os_snprintf(char *str, size_t size, const char *format, ...);
+
+#else /* OS_NO_C_LIB_DEFINES */
+
+#ifdef FOTA_TRACE
+void *os_malloc(size_t size);
+void *os_realloc(void *ptr, size_t size);
+void os_free(void *ptr);
+char *os_strdup(const char *s);
+#else /* FOTA_TRACE */
+#ifndef os_malloc
+#define os_malloc(s) malloc((s))
+#endif
+#ifndef os_realloc
+#define os_realloc(p, s) realloc((p), (s))
+#endif
+#ifndef os_free
+#define os_free(p) free((p))
+#endif
+#ifndef os_strdup
+#ifdef _MSC_VER
+#define os_strdup(s) _strdup(s)
+#else
+#define os_strdup(s) strdup(s)
+#endif
+#endif
+#endif /* FOTA_TRACE */
+
+#ifndef os_memcpy
+#define os_memcpy(d, s, n) memcpy((d), (s), (n))
+#endif
+#ifndef os_memmove
+#define os_memmove(d, s, n) memmove((d), (s), (n))
+#endif
+#ifndef os_memset
+#define os_memset(s, c, n) memset(s, c, n)
+#endif
+#ifndef os_memcmp
+#define os_memcmp(s1, s2, n) memcmp((s1), (s2), (n))
+#endif
+
+#ifndef os_strlen
+#define os_strlen(s) strlen(s)
+#endif
+#ifndef os_strcasecmp
+#ifdef _MSC_VER
+#define os_strcasecmp(s1, s2) _stricmp((s1), (s2))
+#else
+#define os_strcasecmp(s1, s2) strcasecmp((s1), (s2))
+#endif
+#endif
+#ifndef os_strncasecmp
+#ifdef _MSC_VER
+#define os_strncasecmp(s1, s2, n) _strnicmp((s1), (s2), (n))
+#else
+#define os_strncasecmp(s1, s2, n) strncasecmp((s1), (s2), (n))
+#endif
+#endif
+#ifndef os_strchr
+#define os_strchr(s, c) strchr((s), (c))
+#endif
+#ifndef os_strcmp
+#define os_strcmp(s1, s2) strcmp((s1), (s2))
+#endif
+#ifndef os_strncmp
+#define os_strncmp(s1, s2, n) strncmp((s1), (s2), (n))
+#endif
+#ifndef os_strrchr
+#define os_strrchr(s, c) strrchr((s), (c))
+#endif
+#ifndef os_strstr
+#define os_strstr(h, n) strstr((h), (n))
+#endif
+
+#ifndef os_snprintf
+#ifdef _MSC_VER
+#define os_snprintf _snprintf
+#else
+#define os_snprintf snprintf
+#endif
+#endif
+
+#endif /* OS_NO_C_LIB_DEFINES */
+
+
+static inline int os_snprintf_error(size_t size, int res)
+{
+    return res < 0 || (unsigned int) res >= size;
+}
+
+
+static inline void *os_realloc_array(void *ptr, size_t nmemb, size_t size)
+{
+    if (size && nmemb > (~(size_t) 0) / size) {
+        return NULL;
+    }
+
+    return os_realloc(ptr, nmemb * size);
+}
+
+/**
+ * os_remove_in_array - Remove a member from an array by index
+ * @ptr: Pointer to the array
+ * @nmemb: Current member count of the array
+ * @size: The size per member of the array
+ * @idx: Index of the member to be removed
+ */
+static inline void os_remove_in_array(void *ptr, size_t nmemb, size_t size,
+                                      size_t idx)
+{
+    if (idx < nmemb - 1)
+        os_memmove(((unsigned char *) ptr) + idx * size,
+                   ((unsigned char *) ptr) + (idx + 1) * size,
+                   (nmemb - idx - 1) * size);
+}
+
+/**
+ * os_strlcpy - Copy a string with size bound and NUL-termination
+ * @dest: Destination
+ * @src: Source
+ * @siz: Size of the target buffer
+ * Returns: Total length of the target string (length of src) (not including
+ * NUL-termination)
+ *
+ * This function matches in behavior with the strlcpy(3) function in OpenBSD.
+ */
+size_t os_strlcpy(char *dest, const char *src, size_t siz);
+
+/**
+ * os_memcmp_const - Constant time memory comparison
+ * @a: First buffer to compare
+ * @b: Second buffer to compare
+ * @len: Number of octets to compare
+ * Returns: 0 if buffers are equal, non-zero if not
+ *
+ * This function is meant for comparing passwords or hash values where
+ * difference in execution time could provide external observer information
+ * about the location of the difference in the memory buffers. The return value
+ * does not behave like os_memcmp(), i.e., os_memcmp_const() cannot be used to
+ * sort items into a defined order. Unlike os_memcmp(), execution time of
+ * os_memcmp_const() does not depend on the contents of the compared memory
+ * buffers, but only on the total compared length.
+ */
+int os_memcmp_const(const void *a, const void *b, size_t len);
+
+
+/**
+ * os_memdup - Allocate duplicate of passed memory chunk
+ * @src: Source buffer to duplicate
+ * @len: Length of source buffer
+ * Returns: %NULL if allocation failed, copy of src buffer otherwise
+ *
+ * This function allocates a memory block like os_malloc() would, and
+ * copies the given source buffer into it.
+ */
+void *os_memdup(const void *src, size_t len);
+
+/**
+ * os_exec - Execute an external program
+ * @program: Path to the program
+ * @arg: Command line argument string
+ * @wait_completion: Whether to wait until the program execution completes
+ * Returns: 0 on success, -1 on error
+ */
+int os_exec(const char *program, const char *arg, int wait_completion);
+
+
+#ifdef OS_REJECT_C_LIB_FUNCTIONS
+#define malloc OS_DO_NOT_USE_malloc
+#define realloc OS_DO_NOT_USE_realloc
+#define free OS_DO_NOT_USE_free
+#define memcpy OS_DO_NOT_USE_memcpy
+#define memmove OS_DO_NOT_USE_memmove
+#define memset OS_DO_NOT_USE_memset
+#define memcmp OS_DO_NOT_USE_memcmp
+#undef strdup
+#define strdup OS_DO_NOT_USE_strdup
+#define strlen OS_DO_NOT_USE_strlen
+#define strcasecmp OS_DO_NOT_USE_strcasecmp
+#define strncasecmp OS_DO_NOT_USE_strncasecmp
+#undef strchr
+#define strchr OS_DO_NOT_USE_strchr
+#undef strcmp
+#define strcmp OS_DO_NOT_USE_strcmp
+#undef strncmp
+#define strncmp OS_DO_NOT_USE_strncmp
+#undef strncpy
+#define strncpy OS_DO_NOT_USE_strncpy
+#define strrchr OS_DO_NOT_USE_strrchr
+#define strstr OS_DO_NOT_USE_strstr
+#undef snprintf
+#define snprintf OS_DO_NOT_USE_snprintf
+
+#define strcpy OS_DO_NOT_USE_strcpy
+#endif /* OS_REJECT_C_LIB_FUNCTIONS */
+
+
+#if defined(FOTA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS)
+#define TEST_FAIL() testing_test_fail()
+int testing_test_fail(void);
+extern char fota_trace_fail_func[256];
+extern unsigned int fota_trace_fail_after;
+extern char fota_trace_test_fail_func[256];
+extern unsigned int fota_trace_test_fail_after;
+#else
+#define TEST_FAIL() 0
+#endif
+
+#endif /* OS_H */

+ 71 - 0
components/fota_service/include/utils/trace.h

@@ -0,0 +1,71 @@
+/*
+ * Backtrace debugging
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef TRACE_H
+#define TRACE_H
+
+#define FOTA_TRACE_LEN 16
+
+#ifdef FOTA_TRACE
+#include <execinfo.h>
+
+#include "list.h"
+
+#define FOTA_TRACE_INFO void *btrace[FOTA_TRACE_LEN]; int btrace_num;
+
+struct fota_trace_ref {
+    struct dl_list list;
+    const void *addr;
+    FOTA_TRACE_INFO
+};
+#define FOTA_TRACE_REF(name) struct fota_trace_ref fota_trace_ref_##name
+
+#define fota_trace_dump(title, ptr) \
+    fota_trace_dump_func((title), (ptr)->btrace, (ptr)->btrace_num)
+void fota_trace_dump_func(const char *title, void **btrace, int btrace_num);
+#define fota_trace_record(ptr) \
+    (ptr)->btrace_num = backtrace((ptr)->btrace, FOTA_TRACE_LEN)
+void fota_trace_show(const char *title);
+#define fota_trace_add_ref(ptr, name, addr) \
+    fota_trace_add_ref_func(&(ptr)->fota_trace_ref_##name, (addr))
+void fota_trace_add_ref_func(struct fota_trace_ref *ref, const void *addr);
+#define fota_trace_remove_ref(ptr, name, addr)	\
+    do { \
+        if ((addr)) \
+            dl_list_del(&(ptr)->fota_trace_ref_##name.list); \
+    } while (0)
+void fota_trace_check_ref(const void *addr);
+size_t fota_trace_calling_func(const char *buf[], size_t len);
+
+#else /* FOTA_TRACE */
+
+#define FOTA_TRACE_INFO
+#define FOTA_TRACE_REF(n)
+#define fota_trace_dump(title, ptr) do { } while (0)
+#define fota_trace_record(ptr) do { } while (0)
+#define fota_trace_show(title) do { } while (0)
+#define fota_trace_add_ref(ptr, name, addr) do { } while (0)
+#define fota_trace_remove_ref(ptr, name, addr) do { } while (0)
+#define fota_trace_check_ref(addr) do { } while (0)
+
+#endif /* FOTA_TRACE */
+
+
+#ifdef FOTA_TRACE_BFD
+
+void fota_trace_dump_funcname(const char *title, void *pc);
+
+#else /* FOTA_TRACE_BFD */
+
+#define fota_trace_dump_funcname(title, pc) do { } while (0)
+
+#endif /* FOTA_TRACE_BFD */
+
+void fota_trace_deinit(void);
+
+#endif /* TRACE_H */

+ 477 - 0
components/fota_service/lib/fota_buf.c

@@ -0,0 +1,477 @@
+/*
+ * Dynamic data buffer
+ * Copyright (c) 2007-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/trace.h"
+#include "utils/fota_buf.h"
+
+#define fota_printf(...)
+
+static void *zalloc(size_t size)
+{
+    void *n = malloc(size);
+
+    if (n) {
+        memset(n, 0, size);
+    }
+
+    return n;
+}
+
+static int hex2num(char c)
+{
+    if (c >= '0' && c <= '9') {
+        return c - '0';
+    }
+
+    if (c >= 'a' && c <= 'f') {
+        return c - 'a' + 10;
+    }
+
+    if (c >= 'A' && c <= 'F') {
+        return c - 'A' + 10;
+    }
+
+    return -1;
+}
+
+
+int hex2byte(const char *hex)
+{
+    int a, b;
+    a = hex2num(*hex++);
+
+    if (a < 0) {
+        return -1;
+    }
+
+    b = hex2num(*hex++);
+
+    if (b < 0) {
+        return -1;
+    }
+
+    return (a << 4) | b;
+}
+
+
+int hexstr2bin(const char *hex, u8 *buf, size_t len)
+{
+    size_t i;
+    int a;
+    const char *ipos = hex;
+    u8 *opos = buf;
+
+    for (i = 0; i < len; i++) {
+        a = hex2byte(ipos);
+
+        if (a < 0) {
+            return -1;
+        }
+
+        *opos++ = a;
+        ipos += 2;
+    }
+
+    return 0;
+}
+
+#ifdef FOTA_TRACE
+#define FOTABUF_MAGIC 0x51a974e3
+
+struct fotabuf_trace {
+    unsigned int magic;
+} __attribute__((aligned(8)));
+
+static struct fotabuf_trace *fotabuf_get_trace(const struct fotabuf *buf)
+{
+    return (struct fotabuf_trace *)
+           ((const u8 *) buf - sizeof(struct fotabuf_trace));
+}
+#endif /* FOTA_TRACE */
+
+
+static void fotabuf_overflow(const struct fotabuf *buf, size_t len)
+{
+#ifdef FOTA_TRACE
+    struct fotabuf_trace *trace = fotabuf_get_trace(buf);
+
+    if (trace->magic != FOTABUF_MAGIC) {
+        fota_printf(MSG_ERROR, "fotabuf: invalid magic %x",
+                     trace->magic);
+    }
+
+#endif /* FOTA_TRACE */
+    fota_printf(MSG_ERROR, "fotabuf %p (size=%lu used=%lu) overflow len=%lu",
+                 buf, (unsigned long) buf->size, (unsigned long) buf->used,
+                 (unsigned long) len);
+    fota_trace_show("fotabuf overflow");
+    abort();
+}
+
+
+int fotabuf_resize(struct fotabuf **_buf, size_t add_len)
+{
+    struct fotabuf *buf = *_buf;
+#ifdef FOTA_TRACE
+    struct fotabuf_trace *trace;
+#endif /* FOTA_TRACE */
+
+    if (buf == NULL) {
+        *_buf = fotabuf_alloc(add_len);
+        return *_buf == NULL ? -1 : 0;
+    }
+
+#ifdef FOTA_TRACE
+    trace = fotabuf_get_trace(buf);
+
+    if (trace->magic != FOTABUF_MAGIC) {
+        fota_printf(MSG_ERROR, "fotabuf: invalid magic %x",
+                     trace->magic);
+        fota_trace_show("fotabuf_resize invalid magic");
+        abort();
+    }
+
+#endif /* FOTA_TRACE */
+
+    if (buf->used + add_len > buf->size) {
+        unsigned char *nbuf;
+
+        if (buf->flags & FOTABUF_FLAG_EXT_DATA) {
+            nbuf = os_realloc(buf->buf, buf->used + add_len);
+
+            if (nbuf == NULL) {
+                return -1;
+            }
+
+            os_memset(nbuf + buf->used, 0, add_len);
+            buf->buf = nbuf;
+        } else {
+#ifdef FOTA_TRACE
+            nbuf = os_realloc(trace, sizeof(struct fotabuf_trace) +
+                              sizeof(struct fotabuf) +
+                              buf->used + add_len);
+
+            if (nbuf == NULL) {
+                return -1;
+            }
+
+            trace = (struct fotabuf_trace *) nbuf;
+            buf = (struct fotabuf *)(trace + 1);
+            os_memset(nbuf + sizeof(struct fotabuf_trace) +
+                      sizeof(struct fotabuf) + buf->used, 0,
+                      add_len);
+#else /* FOTA_TRACE */
+            nbuf = realloc(buf, sizeof(struct fotabuf) +
+                           buf->used + add_len);
+
+            if (nbuf == NULL) {
+                return -1;
+            }
+
+            buf = (struct fotabuf *) nbuf;
+            os_memset(nbuf + sizeof(struct fotabuf) + buf->used, 0,
+                      add_len);
+#endif /* FOTA_TRACE */
+            buf->buf = (u8 *)(buf + 1);
+            *_buf = buf;
+        }
+
+        buf->size = buf->used + add_len;
+    }
+
+    return 0;
+}
+
+
+/**
+ * fotabuf_alloc - Allocate a fotabuf of the given size
+ * @len: Length for the allocated buffer
+ * Returns: Buffer to the allocated fotabuf or %NULL on failure
+ */
+struct fotabuf *fotabuf_alloc(size_t len)
+{
+#ifdef FOTA_TRACE
+    struct fotabuf_trace *trace = zalloc(sizeof(struct fotabuf_trace) +
+                                          sizeof(struct fotabuf) + len);
+    struct fotabuf *buf;
+
+    if (trace == NULL) {
+        return NULL;
+    }
+
+    trace->magic = FOTABUF_MAGIC;
+    buf = (struct fotabuf *)(trace + 1);
+#else /* FOTA_TRACE */
+    struct fotabuf *buf = zalloc(sizeof(struct fotabuf) + len);
+
+    if (buf == NULL) {
+        return NULL;
+    }
+
+#endif /* FOTA_TRACE */
+
+    buf->size = len;
+    buf->buf = (u8 *)(buf + 1);
+    return buf;
+}
+
+
+struct fotabuf *fotabuf_alloc_ext_data(u8 *data, size_t len)
+{
+#ifdef FOTA_TRACE
+    struct fotabuf_trace *trace = zalloc(sizeof(struct fotabuf_trace) +
+                                          sizeof(struct fotabuf));
+    struct fotabuf *buf;
+
+    if (trace == NULL) {
+        return NULL;
+    }
+
+    trace->magic = FOTABUF_MAGIC;
+    buf = (struct fotabuf *)(trace + 1);
+#else /* FOTA_TRACE */
+    struct fotabuf *buf = zalloc(sizeof(struct fotabuf));
+
+    if (buf == NULL) {
+        return NULL;
+    }
+
+#endif /* FOTA_TRACE */
+
+    buf->size = len;
+    buf->used = len;
+    buf->buf = data;
+    buf->flags |= FOTABUF_FLAG_EXT_DATA;
+
+    return buf;
+}
+
+
+struct fotabuf *fotabuf_alloc_copy(const void *data, size_t len)
+{
+    struct fotabuf *buf = fotabuf_alloc(len);
+
+    if (buf) {
+        fotabuf_put_data(buf, data, len);
+    }
+
+    return buf;
+}
+
+
+struct fotabuf *fotabuf_dup(const struct fotabuf *src)
+{
+    struct fotabuf *buf = fotabuf_alloc(fotabuf_len(src));
+
+    if (buf) {
+        fotabuf_put_data(buf, fotabuf_head(src), fotabuf_len(src));
+    }
+
+    return buf;
+}
+
+
+/**
+ * fotabuf_free - Free a fotabuf
+ * @buf: fotabuf buffer
+ */
+void fotabuf_free(struct fotabuf *buf)
+{
+#ifdef FOTA_TRACE
+    struct fotabuf_trace *trace;
+
+    if (buf == NULL) {
+        return;
+    }
+
+    trace = fotabuf_get_trace(buf);
+
+    if (trace->magic != FOTABUF_MAGIC) {
+        fota_printf(MSG_ERROR, "fotabuf_free: invalid magic %x",
+                     trace->magic);
+        fota_trace_show("fotabuf_free magic mismatch");
+        abort();
+    }
+
+    if (buf->flags & FOTABUF_FLAG_EXT_DATA) {
+        os_free(buf->buf);
+    }
+
+    os_free(trace);
+#else /* FOTA_TRACE */
+
+    if (buf == NULL) {
+        return;
+    }
+
+    if (buf->flags & FOTABUF_FLAG_EXT_DATA) {
+        os_free(buf->buf);
+    }
+
+    os_free(buf);
+#endif /* FOTA_TRACE */
+}
+
+
+void fotabuf_clear_free(struct fotabuf *buf)
+{
+    if (buf) {
+        os_memset(fotabuf_mhead(buf), 0, fotabuf_len(buf));
+        fotabuf_free(buf);
+    }
+}
+
+
+void *fotabuf_put(struct fotabuf *buf, size_t len)
+{
+    void *tmp = fotabuf_mhead_u8(buf) + fotabuf_len(buf);
+    buf->used += len;
+
+    if (buf->used > buf->size) {
+        fotabuf_overflow(buf, len);
+    }
+
+    return tmp;
+}
+
+
+/**
+ * fotabuf_concat - Concatenate two buffers into a newly allocated one
+ * @a: First buffer
+ * @b: Second buffer
+ * Returns: fotabuf with concatenated a + b data or %NULL on failure
+ *
+ * Both buffers a and b will be freed regardless of the return value. Input
+ * buffers can be %NULL which is interpreted as an empty buffer.
+ */
+struct fotabuf *fotabuf_concat(struct fotabuf *a, struct fotabuf *b)
+{
+    struct fotabuf *n = NULL;
+    size_t len = 0;
+
+    if (b == NULL) {
+        return a;
+    }
+
+    if (a) {
+        len += fotabuf_len(a);
+    }
+
+    len += fotabuf_len(b);
+
+    n = fotabuf_alloc(len);
+
+    if (n) {
+        if (a) {
+            fotabuf_put_buf(n, a);
+        }
+
+        fotabuf_put_buf(n, b);
+    }
+
+    fotabuf_free(a);
+    fotabuf_free(b);
+
+    return n;
+}
+
+
+/**
+ * fotabuf_zeropad - Pad buffer with 0x00 octets (prefix) to specified length
+ * @buf: Buffer to be padded
+ * @len: Length for the padded buffer
+ * Returns: fotabuf padded to len octets or %NULL on failure
+ *
+ * If buf is longer than len octets or of same size, it will be returned as-is.
+ * Otherwise a new buffer is allocated and prefixed with 0x00 octets followed
+ * by the source data. The source buffer will be freed on error, i.e., caller
+ * will only be responsible on freeing the returned buffer. If buf is %NULL,
+ * %NULL will be returned.
+ */
+struct fotabuf *fotabuf_zeropad(struct fotabuf *buf, size_t len)
+{
+    struct fotabuf *ret;
+    size_t blen;
+
+    if (buf == NULL) {
+        return NULL;
+    }
+
+    blen = fotabuf_len(buf);
+
+    if (blen >= len) {
+        return buf;
+    }
+
+    ret = fotabuf_alloc(len);
+
+    if (ret) {
+        os_memset(fotabuf_put(ret, len - blen), 0, len - blen);
+        fotabuf_put_buf(ret, buf);
+    }
+
+    fotabuf_free(buf);
+
+    return ret;
+}
+
+
+void fotabuf_printf(struct fotabuf *buf, char *fmt, ...)
+{
+    va_list ap;
+    void *tmp = fotabuf_mhead_u8(buf) + fotabuf_len(buf);
+    int res;
+
+    va_start(ap, fmt);
+    res = vsnprintf(tmp, buf->size - buf->used, fmt, ap);
+    va_end(ap);
+
+    if (res < 0 || (size_t) res >= buf->size - buf->used) {
+        fotabuf_overflow(buf, res);
+    }
+
+    buf->used += res;
+}
+
+
+/**
+ * fotabuf_parse_bin - Parse a null terminated string of binary data to a fotabuf
+ * @buf: Buffer with null terminated string (hexdump) of binary data
+ * Returns: fotabuf or %NULL on failure
+ *
+ * The string len must be a multiple of two and contain only hexadecimal digits.
+ */
+struct fotabuf *fotabuf_parse_bin(const char *buf)
+{
+    size_t len;
+    struct fotabuf *ret;
+
+    len = os_strlen(buf);
+
+    if (len & 0x01) {
+        return NULL;
+    }
+
+    len /= 2;
+
+    ret = fotabuf_alloc(len);
+
+    if (ret == NULL) {
+        return NULL;
+    }
+
+    if (hexstr2bin(buf, fotabuf_put(ret, len), len)) {
+        fotabuf_free(ret);
+        return NULL;
+    }
+
+    return ret;
+}

+ 29 - 0
components/fota_service/lib/fota_debug.c

@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018-2021 Alibaba Group Holding Limited
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <stdarg.h>
+
+int daemonize = 0;
+
+void fota_log(int priority, const char *format, ...)
+{
+    va_list arg;
+
+    if (daemonize == 1) {
+        openlog("fota", LOG_CONS | LOG_PID, LOG_LOCAL0);
+        va_list args;
+        va_start(args, format);
+        vsyslog(priority, format, args);
+        va_end(args);
+        closelog();
+    } else {
+        va_start(arg, format);
+        printf("[FotaService] ");
+        vfprintf(stdout, format, arg);
+        va_end(arg);
+    }
+}

+ 5 - 0
components/httpclient/CMakeLists.txt

@@ -0,0 +1,5 @@
+file(GLOB_RECURSE LIBSOURCE "${COMPONENTS_DIR}/httpclient/src/*.c")
+
+include_directories(${COMPONENTS_DIR}/httpclient/include)
+
+add_library(httpclient STATIC ${LIBSOURCE})

+ 167 - 0
components/httpclient/README.md

@@ -0,0 +1,167 @@
+# 概述
+
+Http(s) client模块为http/https客户端组件,为用户提供一组简洁的调用接口。
+
+
+# HTTPS配置
+
+HTTP(s) client组件同时支持HTTP及HTTPS请求,两者接口调用方式及流程完全一样。模块会通过请求的URL协议头自动匹配HTTP/HTTPS请求操作。
+
+但为了节省硬件资源,该模块没有内置所有的CA机构根证书,需要由用户在http_client_init配置参数中设置,如下:
+
+```c
+static char ca_crt_rsa[] = {
+"-----BEGIN CERTIFICATE-----\r\n"
+"MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/\r\n"
+"MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\r\n"
+"DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow\r\n"
+"SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT\r\n"
+"GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC\r\n"
+"AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF\r\n"
+"q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8\r\n"
+"SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0\r\n"
+"Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA\r\n"
+"a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj\r\n"
+"/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T\r\n"
+"AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG\r\n"
+"CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv\r\n"
+"bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k\r\n"
+"c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw\r\n"
+"VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC\r\n"
+"ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz\r\n"
+"MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu\r\n"
+"Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF\r\n"
+"AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo\r\n"
+"uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/\r\n"
+"wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu\r\n"
+"X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG\r\n"
+"PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6\r\n"
+"KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==\r\n"
+"-----END CERTIFICATE-----\r\n"
+" "
+};
+
+http_client_config_t config = {
+    .url = "https://www.howsmyssl.com",
+    .event_handler = _http_event_handler,
+    .cert_pem = ca_crt_rsa,
+};
+http_client_handle_t client = http_client_init(&config);
+```
+
+## 根证书获取方式
+
+每个网站的CA认证机构不同,所以需要事先取得其对应的根证书。可以通过Linux中的openssl程序获取,以百度为例,获取命令如下:
+
+```bash
+openssl s_client -showcerts -connect www.baidu.com:443 -servername www.baidu.com
+```
+
+其执行结果如下,其中第二个GlobalSign Root CA证书为我们所需要的CA根证书。将其完整拷贝下来(包括“-----BEGIN CERTIFICATE-----”及“-----END CERTIFICATE-----”),放入程序中的证书字符串中即可。
+
+```bash
+yohn@L:/mnt/$ openssl s_client -showcerts -connect www.baidu.com:443 -servername www.baidu.com
+CONNECTED(00000003)
+depth=2 C = BE, O = GlobalSign nv-sa, OU = Root CA, CN = GlobalSign Root CA
+verify return:1
+depth=1 C = BE, O = GlobalSign nv-sa, CN = GlobalSign Organization Validation CA - SHA256 - G2
+verify return:1
+depth=0 C = CN, ST = beijing, L = beijing, OU = service operation department, O = "Beijing Baidu Netcom Science Technology Co., Ltd", CN = baidu.com
+verify return:1
+---
+Certificate chain
+ 0 s:/C=CN/ST=beijing/L=beijing/OU=service operation department/O=Beijing Baidu Netcom Science Technology Co., Ltd/CN=baidu.com
+   i:/C=BE/O=GlobalSign nv-sa/CN=GlobalSign Organization Validation CA - SHA256 - G2
+-----BEGIN CERTIFICATE-----
+MIIJrzCCCJegAwIBAgIMLO4ZPBiCeOo+Q3VzMA0GCSqGSIb3DQEBCwUAMGYxCzAJ
+BgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMTwwOgYDVQQDEzNH
+....
+....
+XVdqyUHEJSsyGKpiqB5JgXMcgV9e+uSUMsNQbY6qzGxMUwz6j040eZ+lYMD4UHW4
+oZ0B5qslIww7JAJAWCT/NAKLlGEQaC+2gOPQX0oKpwLSwJg+HegCyCdxJrKoh7bb
+nRBHS8ITYjTG0Dw5CTklj/6i9PP735snPfzQKOht3N0X0x8=
+-----END CERTIFICATE-----
+ 1 s:/C=BE/O=GlobalSign nv-sa/CN=GlobalSign Organization Validation CA - SHA256 - G2
+   i:/C=BE/O=GlobalSign nv-sa/OU=Root CA/CN=GlobalSign Root CA
+-----BEGIN CERTIFICATE-----
+MIIEaTCCA1GgAwIBAgILBAAAAAABRE7wQkcwDQYJKoZIhvcNAQELBQAwVzELMAkG
+A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
+....
+....
+30JAZGSGvip2CTFvHST0mdCF/vIhCPnG9vHQWe3WVjwIKANnuvD58ZAWR65n5ryA
+SOlCdjSXVWkkDoPWoC209fN5ikkodBpBocLTJIg1MGCUF7ThBCIxPTsvFwayuJ2G
+K1pp74P1S8SqtCr4fKGxhZSM9AyHDPSsQPhZSZg=
+-----END CERTIFICATE-----
+....
+....
+```
+
+# 使用示例
+
+头文件:
+
+```c
+#include <http_client.h>
+```
+
+使用http_client_perform进行HTTPS交互:
+
+```c
+void https_with_url()
+{
+    http_client_config_t config = {
+        .url = "https://www.howsmyssl.com",
+        .event_handler = _http_event_handler,
+        .cert_pem = ca_crt_rsa,
+    };
+    http_client_handle_t client = http_client_init(&config);
+    http_errors_t err = http_client_perform(client);
+
+    if (err == HTTP_CLI_OK) {
+        LOGI(TAG, "HTTPS Status = %d, content_length = %d \r\n",
+                http_client_get_status_code(client),
+                http_client_get_content_length(client));
+    } else {
+        LOGE(TAG, "Error perform http request 0x%x @#@@@@@@", (err));
+        e_count ++;
+    }
+    http_client_cleanup(client);
+}
+```
+
+用户通过event_handler获取事件及HTTP应答数据:
+
+```c
+int _http_event_handler(http_client_event_t *evt)
+{
+    switch(evt->event_id) {
+        case HTTP_EVENT_ERROR:
+            LOGD(TAG, "HTTP_EVENT_ERROR");
+            break;
+        case HTTP_EVENT_ON_CONNECTED:
+            LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
+            break;
+        case HTTP_EVENT_HEADER_SENT:
+            LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
+            break;
+        case HTTP_EVENT_ON_HEADER:
+            LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
+            break;
+        case HTTP_EVENT_ON_DATA:
+            LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
+            if (!http_client_is_chunked_response(evt->client)) {
+                // Write out data
+                // printf("%.*s", evt->data_len, (char*)evt->data);
+            }
+
+            break;
+        case HTTP_EVENT_ON_FINISH:
+            LOGD(TAG, "HTTP_EVENT_ON_FINISH");
+            break;
+        case HTTP_EVENT_DISCONNECTED:
+            LOGD(TAG, "HTTP_EVENT_DISCONNECTED");
+            break;
+    }
+    return 0;
+}
+```

+ 41 - 0
components/httpclient/build.mk

@@ -0,0 +1,41 @@
+##
+ # Copyright (C) 2017 C-SKY Microsystems Co., All rights reserved.
+ #
+ # Licensed under the Apache License, Version 2.0 (the "License");
+ # you may not use this file except in compliance with the License.
+ # You may obtain a copy of the License at
+ #
+ #   http://www.apache.org/licenses/LICENSE-2.0
+ #
+ # Unless required by applicable law or agreed to in writing, software
+ # distributed under the License is distributed on an "AS IS" BASIS,
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ # See the License for the specific language governing permissions and
+ # limitations under the License.
+##
+
+L_PATH := $(call cur-dir)
+
+include $(DEFINE_LOCAL)
+
+L_MODULE := libhttpclient
+
+#L_CFLAGS := -Wall
+
+L_SRCS_PATH :=
+
+L_INCS += include/yoc
+L_INCS += libs/include
+L_INCS += libs/transport/include
+L_INCS += libs/httpclient
+L_INCS += libs/httpclient/include
+
+L_SRCS += \
+    $(L_SRCS_PATH)http_utils.c      	\
+    $(L_SRCS_PATH)http_parser.c        	\
+    $(L_SRCS_PATH)http_header.c     	\
+	$(L_SRCS_PATH)http_auth.c     		\
+    $(L_SRCS_PATH)http_client.c
+
+
+include $(BUILD_MODULE)

+ 533 - 0
components/httpclient/examples/http_examples.c

@@ -0,0 +1,533 @@
+#include <http_client.h>
+#include <aos/kernel.h>
+#include <aos/debug.h>
+
+#define TAG "https_example"
+
+static char ca_crt_rsa[] = {
+"-----BEGIN CERTIFICATE-----\r\n"
+"MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/\r\n"
+"MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\r\n"
+"DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow\r\n"
+"SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT\r\n"
+"GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC\r\n"
+"AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF\r\n"
+"q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8\r\n"
+"SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0\r\n"
+"Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA\r\n"
+"a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj\r\n"
+"/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T\r\n"
+"AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG\r\n"
+"CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv\r\n"
+"bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k\r\n"
+"c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw\r\n"
+"VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC\r\n"
+"ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz\r\n"
+"MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu\r\n"
+"Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF\r\n"
+"AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo\r\n"
+"uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/\r\n"
+"wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu\r\n"
+"X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG\r\n"
+"PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6\r\n"
+"KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==\r\n"
+"-----END CERTIFICATE-----\r\n"
+" "
+};
+
+
+#define MAX_HTTP_RECV_BUFFER 512
+
+static int e_count = 0;
+
+int _http_event_handler(http_client_event_t *evt)
+{
+    switch(evt->event_id) {
+        case HTTP_EVENT_ERROR:
+            LOGD(TAG, "HTTP_EVENT_ERROR");
+            break;
+        case HTTP_EVENT_ON_CONNECTED:
+            LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
+            break;
+        case HTTP_EVENT_HEADER_SENT:
+            LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
+            break;
+        case HTTP_EVENT_ON_HEADER:
+            LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
+            break;
+        case HTTP_EVENT_ON_DATA:
+            LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
+            if (!http_client_is_chunked_response(evt->client)) {
+                // Write out data
+                // printf("%.*s", evt->data_len, (char*)evt->data);
+            }
+
+            break;
+        case HTTP_EVENT_ON_FINISH:
+            LOGD(TAG, "HTTP_EVENT_ON_FINISH");
+            break;
+        case HTTP_EVENT_DISCONNECTED:
+            LOGD(TAG, "HTTP_EVENT_DISCONNECTED");
+            break;
+    }
+    return 0;
+}
+
+static void http_rest_with_url()
+{
+    http_client_config_t config = {
+        .url = "http://httpbin.org/get",
+        .event_handler = _http_event_handler,
+    };
+    http_client_handle_t client = http_client_init(&config);
+
+    // GET
+    http_errors_t err = http_client_perform(client);
+    if (err == HTTP_CLI_OK) {
+        LOGI(TAG, "HTTP GET Status = %d, content_length = %d \r\n",
+                http_client_get_status_code(client),
+                http_client_get_content_length(client));
+    } else {
+        LOGE(TAG, "HTTP GET request failed: 0x%x @#@@@@@@", (err));
+        e_count ++;
+    }
+
+    // POST
+    const char *post_data = "field1=value1&field2=value2";
+    http_client_set_url(client, "http://httpbin.org/post");
+    http_client_set_method(client, HTTP_METHOD_POST);
+    http_client_set_post_field(client, post_data, strlen(post_data));
+    err = http_client_perform(client);
+    if (err == HTTP_CLI_OK) {
+        LOGI(TAG, "HTTP POST Status = %d, content_length = %d \r\n",
+                http_client_get_status_code(client),
+                http_client_get_content_length(client));
+    } else {
+        LOGE(TAG, "HTTP POST request failed: 0x%x @#@@@@@@", (err));
+        e_count ++;
+    }
+
+    //PUT
+    http_client_set_url(client, "http://httpbin.org/put");
+    http_client_set_method(client, HTTP_METHOD_PUT);
+    err = http_client_perform(client);
+    if (err == HTTP_CLI_OK) {
+        LOGI(TAG, "HTTP PUT Status = %d, content_length = %d \r\n",
+                http_client_get_status_code(client),
+                http_client_get_content_length(client));
+    } else {
+        LOGE(TAG, "HTTP PUT request failed: 0x%x @#@@@@@@", (err));
+        e_count ++;
+    }
+
+    //PATCH
+    http_client_set_url(client, "http://httpbin.org/patch");
+    http_client_set_method(client, HTTP_METHOD_PATCH);
+    http_client_set_post_field(client, NULL, 0);
+    err = http_client_perform(client);
+    if (err == HTTP_CLI_OK) {
+        LOGI(TAG, "HTTP PATCH Status = %d, content_length = %d \r\n",
+                http_client_get_status_code(client),
+                http_client_get_content_length(client));
+    } else {
+        LOGE(TAG, "HTTP PATCH request failed: 0x%x @#@@@@@@", (err));
+        e_count ++;
+    }
+
+    //DELETE
+    http_client_set_url(client, "http://httpbin.org/delete");
+    http_client_set_method(client, HTTP_METHOD_DELETE);
+    err = http_client_perform(client);
+    if (err == HTTP_CLI_OK) {
+        LOGI(TAG, "HTTP DELETE Status = %d, content_length = %d \r\n",
+                http_client_get_status_code(client),
+                http_client_get_content_length(client));
+    } else {
+        LOGE(TAG, "HTTP DELETE request failed: 0x%x @#@@@@@@", (err));
+        e_count ++;
+    }
+
+    //HEAD
+    http_client_set_url(client, "http://httpbin.org/head");
+    http_client_set_method(client, HTTP_METHOD_HEAD);
+    err = http_client_perform(client);
+    if (err == HTTP_CLI_OK) {
+        LOGI(TAG, "HTTP HEAD Status = %d, content_length = %d \r\n",
+                http_client_get_status_code(client),
+                http_client_get_content_length(client));
+    } else {
+        LOGE(TAG, "HTTP HEAD request failed: 0x%x @#@@@@@@", (err));
+        e_count ++;
+    }
+
+    http_client_cleanup(client);
+}
+
+static void http_rest_with_hostname_path()
+{
+    http_client_config_t config = {
+        .host = "httpbin.org",
+        .path = "/get",
+        .transport_type = HTTP_TRANSPORT_OVER_TCP,
+        .event_handler = _http_event_handler,
+    };
+    http_client_handle_t client = http_client_init(&config);
+
+    // GET
+    http_errors_t err = http_client_perform(client);
+    if (err == HTTP_CLI_OK) {
+        LOGI(TAG, "HTTP GET Status = %d, content_length = %d \r\n",
+                http_client_get_status_code(client),
+                http_client_get_content_length(client));
+    } else {
+        LOGE(TAG, "HTTP GET request failed: 0x%x @#@@@@@@", (err));
+        e_count ++;
+    }
+
+    // POST
+    const char *post_data = "field1=value1&field2=value2";
+    http_client_set_url(client, "/post");
+    http_client_set_method(client, HTTP_METHOD_POST);
+    http_client_set_post_field(client, post_data, strlen(post_data));
+    err = http_client_perform(client);
+    if (err == HTTP_CLI_OK) {
+        LOGI(TAG, "HTTP POST Status = %d, content_length = %d \r\n",
+                http_client_get_status_code(client),
+                http_client_get_content_length(client));
+    } else {
+        LOGE(TAG, "HTTP POST request failed: 0x%x @#@@@@@@", (err));
+        e_count ++;
+    }
+
+    //PUT
+    http_client_set_url(client, "/put");
+    http_client_set_method(client, HTTP_METHOD_PUT);
+    err = http_client_perform(client);
+    if (err == HTTP_CLI_OK) {
+        LOGI(TAG, "HTTP PUT Status = %d, content_length = %d \r\n",
+                http_client_get_status_code(client),
+                http_client_get_content_length(client));
+    } else {
+        LOGE(TAG, "HTTP PUT request failed: 0x%x @#@@@@@@", (err));
+        e_count ++;
+    }
+
+    //PATCH
+    http_client_set_url(client, "/patch");
+    http_client_set_method(client, HTTP_METHOD_PATCH);
+    http_client_set_post_field(client, NULL, 0);
+    err = http_client_perform(client);
+    if (err == HTTP_CLI_OK) {
+        LOGI(TAG, "HTTP PATCH Status = %d, content_length = %d \r\n",
+                http_client_get_status_code(client),
+                http_client_get_content_length(client));
+    } else {
+        LOGE(TAG, "HTTP PATCH request failed: 0x%x @#@@@@@@", (err));
+        e_count ++;
+    }
+
+    //DELETE
+    http_client_set_url(client, "/delete");
+    http_client_set_method(client, HTTP_METHOD_DELETE);
+    err = http_client_perform(client);
+    if (err == HTTP_CLI_OK) {
+        LOGI(TAG, "HTTP DELETE Status = %d, content_length = %d \r\n",
+                http_client_get_status_code(client),
+                http_client_get_content_length(client));
+    } else {
+        LOGE(TAG, "HTTP DELETE request failed: 0x%x @#@@@@@@", (err));
+        e_count ++;
+    }
+
+    //HEAD
+    http_client_set_url(client, "/get");
+    http_client_set_method(client, HTTP_METHOD_HEAD);
+    err = http_client_perform(client);
+    if (err == HTTP_CLI_OK) {
+        LOGI(TAG, "HTTP HEAD Status = %d, content_length = %d \r\n",
+                http_client_get_status_code(client),
+                http_client_get_content_length(client));
+    } else {
+        LOGE(TAG, "HTTP HEAD request failed: 0x%x @#@@@@@@", (err));
+        e_count ++;
+    }
+
+    http_client_cleanup(client);
+}
+
+
+static void http_auth_basic()
+{
+    http_client_config_t config = {
+        .url = "http://user:passwd@httpbin.org/basic-auth/user/passwd",
+        .event_handler = _http_event_handler,
+        .auth_type = HTTP_AUTH_TYPE_BASIC,
+    };
+    http_client_handle_t client = http_client_init(&config);
+    http_errors_t err = http_client_perform(client);
+
+    if (err == HTTP_CLI_OK) {
+        LOGI(TAG, "HTTP Basic Auth Status = %d, content_length = %d \r\n",
+                http_client_get_status_code(client),
+                http_client_get_content_length(client));
+    } else {
+        LOGE(TAG, "Error perform http request 0x%x @#@@@@@@", (err));
+        e_count ++;
+    }
+    http_client_cleanup(client);
+}
+
+static void http_auth_basic_redirect()
+{
+    http_client_config_t config = {
+        .url = "http://user:passwd@httpbin.org/basic-auth/user/passwd",
+        .event_handler = _http_event_handler,
+    };
+    http_client_handle_t client = http_client_init(&config);
+    http_errors_t err = http_client_perform(client);
+
+    if (err == HTTP_CLI_OK) {
+        LOGI(TAG, "HTTP Basic Auth redirect Status = %d, content_length = %d \r\n",
+                http_client_get_status_code(client),
+                http_client_get_content_length(client));
+    } else {
+        LOGE(TAG, "Error perform http request 0x%x @#@@@@@@", (err));
+        e_count ++;
+    }
+    http_client_cleanup(client);
+}
+
+static void http_auth_digest()
+{
+    http_client_config_t config = {
+        .url = "http://user:passwd@httpbin.org/digest-auth/auth/user/passwd/MD5/never",
+        .event_handler = _http_event_handler,
+    };
+    http_client_handle_t client = http_client_init(&config);
+    http_errors_t err = http_client_perform(client);
+
+    if (err == HTTP_CLI_OK) {
+        LOGI(TAG, "HTTP Digest Auth Status = %d, content_length = %d \r\n",
+                http_client_get_status_code(client),
+                http_client_get_content_length(client));
+    } else {
+        LOGE(TAG, "Error perform http request 0x%x @#@@@@@@", (err));
+        e_count ++;
+    }
+    http_client_cleanup(client);
+}
+
+static void https_with_url()
+{
+    http_client_config_t config = {
+        .url = "https://www.howsmyssl.com",
+        .event_handler = _http_event_handler,
+        .cert_pem = ca_crt_rsa,
+    };
+    http_client_handle_t client = http_client_init(&config);
+    http_errors_t err = http_client_perform(client);
+
+    if (err == HTTP_CLI_OK) {
+        LOGI(TAG, "HTTPS Status = %d, content_length = %d \r\n",
+                http_client_get_status_code(client),
+                http_client_get_content_length(client));
+    } else {
+        LOGE(TAG, "Error perform http request 0x%x @#@@@@@@", (err));
+        e_count ++;
+    }
+    http_client_cleanup(client);
+}
+
+static void https_with_hostname_path()
+{
+    http_client_config_t config = {
+        .host = "www.howsmyssl.com",
+        .path = "/",
+        .transport_type = HTTP_TRANSPORT_OVER_SSL,
+        .event_handler = _http_event_handler,
+        .cert_pem = ca_crt_rsa,
+    };
+    http_client_handle_t client = http_client_init(&config);
+    http_errors_t err = http_client_perform(client);
+
+    if (err == HTTP_CLI_OK) {
+        LOGI(TAG, "HTTPS Status = %d, content_length = %d \r\n",
+                http_client_get_status_code(client),
+                http_client_get_content_length(client));
+    } else {
+        LOGE(TAG, "Error perform http request 0x%x @#@@@@@@", (err));
+        e_count ++;
+    }
+    http_client_cleanup(client);
+}
+
+static void http_relative_redirect()
+{
+    http_client_config_t config = {
+        .url = "http://httpbin.org/relative-redirect/3",
+        .event_handler = _http_event_handler,
+    };
+    http_client_handle_t client = http_client_init(&config);
+    http_errors_t err = http_client_perform(client);
+
+    if (err == HTTP_CLI_OK) {
+        LOGI(TAG, "HTTP Relative path redirect Status = %d, content_length = %d \r\n",
+                http_client_get_status_code(client),
+                http_client_get_content_length(client));
+    } else {
+        LOGE(TAG, "Error perform http request 0x%x @#@@@@@@", (err));
+        e_count ++;
+    }
+    http_client_cleanup(client);
+}
+
+static void http_absolute_redirect()
+{
+    http_client_config_t config = {
+        .url = "http://httpbin.org/absolute-redirect/3",
+        .event_handler = _http_event_handler,
+    };
+    http_client_handle_t client = http_client_init(&config);
+    http_errors_t err = http_client_perform(client);
+
+    if (err == HTTP_CLI_OK) {
+        LOGI(TAG, "HTTP Absolute path redirect Status = %d, content_length = %d \r\n",
+                http_client_get_status_code(client),
+                http_client_get_content_length(client));
+    } else {
+        LOGE(TAG, "Error perform http request 0x%x @#@@@@@@", (err));
+        e_count ++;
+    }
+    http_client_cleanup(client);
+}
+
+static void http_redirect_to_https()
+{
+    http_client_config_t config = {
+        .url = "http://httpbin.org/redirect-to?url=https%3A%2F%2Fwww.howsmyssl.com",
+        .event_handler = _http_event_handler,
+    };
+    http_client_handle_t client = http_client_init(&config);
+    http_errors_t err = http_client_perform(client);
+
+    if (err == HTTP_CLI_OK) {
+        LOGI(TAG, "HTTP redirect to HTTPS Status = %d, content_length = %d \r\n",
+                http_client_get_status_code(client),
+                http_client_get_content_length(client));
+    } else {
+        LOGE(TAG, "Error perform http request 0x%x @#@@@@@@", (err));
+        e_count ++;
+    }
+    http_client_cleanup(client);
+}
+
+
+static void http_download_chunk()
+{
+    http_client_config_t config = {
+        .url = "http://httpbin.org/stream-bytes/8912",
+        .event_handler = _http_event_handler,
+    };
+    http_client_handle_t client = http_client_init(&config);
+    http_errors_t err = http_client_perform(client);
+
+    if (err == HTTP_CLI_OK) {
+        LOGI(TAG, "HTTP chunk encoding Status = %d, content_length = %d \r\n",
+                http_client_get_status_code(client),
+                http_client_get_content_length(client));
+    } else {
+        LOGE(TAG, "Error perform http request 0x%x @#@@@@@@", (err));
+        e_count ++;
+    }
+    http_client_cleanup(client);
+}
+
+static void http_perform_as_stream_reader()
+{
+    char *buffer = aos_malloc(MAX_HTTP_RECV_BUFFER + 1);
+    if (buffer == NULL) {
+        LOGE(TAG, "Cannot aos_malloc http receive buffer");
+        return;
+    }
+    http_client_config_t config = {
+        .url = "http://httpbin.org/get",
+        .event_handler = _http_event_handler,
+    };
+    http_client_handle_t client = http_client_init(&config);
+    http_errors_t err;
+    if ((err = http_client_open(client, 0)) != HTTP_CLI_OK) {
+        LOGE(TAG, "Failed to open HTTP connection: 0x%x @#@@@@@@", (err));
+        aos_free(buffer);
+        return;
+    }
+    int content_length =  http_client_fetch_headers(client);
+    int total_read_len = 0, read_len;
+    if (total_read_len < content_length && content_length <= MAX_HTTP_RECV_BUFFER) {
+        read_len = http_client_read(client, buffer, content_length);
+        if (read_len <= 0) {
+            LOGE(TAG, "Error read data");
+        }
+        buffer[read_len] = 0;
+        LOGD(TAG, "read_len = %d", read_len);
+    }
+    LOGI(TAG, "HTTP Stream reader Status = %d, content_length = %d \r\n",
+                    http_client_get_status_code(client),
+                    http_client_get_content_length(client));
+    http_client_close(client);
+    http_client_cleanup(client);
+    aos_free(buffer);
+}
+
+static void https_async()
+{
+    http_client_config_t config = {
+        .url = "https://postman-echo.com/post",
+        .event_handler = _http_event_handler,
+        .is_async = true,
+        .timeout_ms = 5000,
+    };
+    http_client_handle_t client = http_client_init(&config);
+    http_errors_t err;
+    const char *post_data = "Using a Palantír requires a person with great strength of will and wisdom. The Palantíri were meant to "
+                            "be used by the Dúnedain to communicate throughout the Realms in Exile. During the War of the Ring, "
+                            "the Palantíri were used by many individuals. Sauron used the Ithil-stone to take advantage of the users "
+                            "of the other two stones, the Orthanc-stone and Anor-stone, but was also susceptible to deception himself.";
+    http_client_set_method(client, HTTP_METHOD_POST);
+    http_client_set_post_field(client, post_data, strlen(post_data));
+    while (1) {
+        err = http_client_perform(client);
+        if (err != EAGAIN) {
+            break;
+        }
+    }
+    if (err == HTTP_CLI_OK) {
+        LOGI(TAG, "HTTPS Status = %d, content_length = %d \r\n",
+                http_client_get_status_code(client),
+                http_client_get_content_length(client));
+    } else {
+        LOGE(TAG, "Error perform http request 0x%x @#@@@@@@", (err));
+        e_count ++;
+    }
+    http_client_cleanup(client);
+}
+
+
+void test_https(void)
+{
+    ca_crt_rsa[sizeof(ca_crt_rsa) - 1] = 0;
+    http_rest_with_url();
+    http_rest_with_hostname_path();
+    http_auth_basic();
+    http_auth_basic_redirect();
+    http_auth_digest();
+    http_relative_redirect();
+    http_absolute_redirect();
+    https_with_url();
+    https_with_hostname_path();
+    http_redirect_to_https();
+    http_download_chunk();
+    http_perform_as_stream_reader();
+    https_async();
+
+    LOGI(TAG, "Finish http example [%d]", e_count);
+}

+ 68 - 0
components/httpclient/include/http_auth.h

@@ -0,0 +1,68 @@
+// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+#ifndef _HTTP_BASIC_AUTH_H_
+#define _HTTP_BASIC_AUTH_H_
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * HTTP Digest authentication data
+ */
+typedef struct {
+    char *method;       /*!< Request method, example: GET */
+    char *algorithm;    /*!< Authentication algorithm */
+    char *uri;          /*!< URI of request example: /path/to/file.html */
+    char *realm;        /*!< Authentication realm */
+    char *nonce;        /*!< Authentication nonce */
+    char *qop;          /*!< Authentication qop */
+    char *opaque;       /*!< Authentication opaque */
+    uint64_t cnonce;    /*!< Authentication cnonce */
+    int nc;             /*!< Authentication nc */
+} http_auth_data_t;
+
+/**
+ * @brief      This use for Http digest authentication method, create http header for digest authentication.
+ *             The returned string need to free after use
+ *
+ * @param[in]  username   The username
+ * @param[in]  password   The password
+ * @param      auth_data  The auth data
+ *
+ * @return
+ *     - HTTP Header value of Authorization, valid for digest authentication, must be freed after usage
+ *     - NULL
+ */
+char *http_auth_digest(const char *username, const char *password, http_auth_data_t *auth_data);
+
+/**
+ * @brief      This use for Http basic authentication method, create header value for basic http authentication
+ *            The returned string need to free after use
+ *
+ * @param[in]  username  The username
+ * @param[in]  password  The password
+ *
+ * @return
+ *     - HTTP Header value of Authorization, valid for basic authentication, must be free after use
+ *     - NULL
+ */
+char *http_auth_basic(const char *username, const char *password);
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 475 - 0
components/httpclient/include/http_client.h

@@ -0,0 +1,475 @@
+// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef __HTTP_CLIENT_H__
+#define __HTTP_CLIENT_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define DEFAULT_HTTP_BUF_SIZE (512)
+
+typedef struct http_client *http_client_handle_t;
+typedef struct http_client_event *http_client_event_handle_t;
+
+
+typedef enum  {
+    HTTP_CLI_OK     = 0,
+    HTTP_CLI_FAIL   = -1,
+    HTTP_CLI_ERR_NO_MEM              = 0x101,   /*!< Out of memory */
+    HTTP_CLI_ERR_INVALID_ARG         = 0x102,   /*!< Invalid argument */
+    HTTP_CLI_ERR_INVALID_STATE       = 0x103,   /*!< Invalid state */
+    HTTP_CLI_ERR_INVALID_SIZE        = 0x104,   /*!< Invalid size */
+    HTTP_CLI_ERR_NOT_FOUND           = 0x105,   /*!< Requested resource not found */
+    HTTP_CLI_ERR_NOT_SUPPORTED       = 0x106,   /*!< Operation or feature not supported */
+    HTTP_CLI_ERR_TIMEOUT             = 0x107,   /*!< Operation timed out */
+    HTTP_CLI_ERR_INVALID_RESPONSE    = 0x108,   /*!< Received response was invalid */
+    HTTP_CLI_ERR_INVALID_CRC         = 0x109,   /*!< CRC or checksum was invalid */
+    HTTP_CLI_ERR_INVALID_VERSION     = 0x10A,   /*!< Version was invalid */
+    HTTP_CLI_ERR_INVALID_MAC         = 0x10B    /*!< MAC address was invalid */
+} http_errors_t;
+
+/**
+ * @brief   HTTP Client events id
+ */
+typedef enum {
+    HTTP_EVENT_ERROR = 0,       /*!< This event occurs when there are any errors during execution */
+    HTTP_EVENT_ON_CONNECTED,    /*!< Once the HTTP has been connected to the server, no data exchange has been performed */
+    HTTP_EVENT_HEADER_SENT,     /*!< After sending all the headers to the server */
+    HTTP_EVENT_ON_HEADER,       /*!< Occurs when receiving each header sent from the server */
+    HTTP_EVENT_ON_DATA,         /*!< Occurs when receiving data from the server, possibly multiple portions of the packet */
+    HTTP_EVENT_ON_FINISH,       /*!< Occurs when finish a HTTP session */
+    HTTP_EVENT_DISCONNECTED,    /*!< The connection has been disconnected */
+} http_client_event_id_t;
+
+/**
+ * @brief      HTTP Client events data
+ */
+typedef struct http_client_event {
+    http_client_event_id_t event_id;        /*!< event_id, to know the cause of the event */
+    http_client_handle_t client;            /*!< http_client_handle_t context */
+    void *data;                             /*!< data of the event */
+    int data_len;                           /*!< data length of data */
+    void *user_data;                        /*!< user_data context, from http_client_config_t user_data */
+    char *header_key;                       /*!< For HTTP_EVENT_ON_HEADER event_id, it's store current http header key */
+    char *header_value;                     /*!< For HTTP_EVENT_ON_HEADER event_id, it's store current http header value */
+} http_client_event_t;
+
+
+/**
+ * @brief      HTTP Client transport
+ */
+typedef enum {
+    HTTP_TRANSPORT_UNKNOWN = 0x0,   /*!< Unknown */
+    HTTP_TRANSPORT_OVER_TCP,        /*!< Transport over tcp */
+    HTTP_TRANSPORT_OVER_SSL,        /*!< Transport over ssl */
+} http_client_transport_t;
+
+typedef int (*http_event_handle_cb)(http_client_event_t *evt);
+
+/**
+ * @brief HTTP method
+ */
+typedef enum {
+    HTTP_METHOD_GET = 0,    /*!< HTTP GET Method */
+    HTTP_METHOD_POST,       /*!< HTTP POST Method */
+    HTTP_METHOD_PUT,        /*!< HTTP PUT Method */
+    HTTP_METHOD_PATCH,      /*!< HTTP PATCH Method */
+    HTTP_METHOD_DELETE,     /*!< HTTP DELETE Method */
+    HTTP_METHOD_HEAD,       /*!< HTTP HEAD Method */
+    HTTP_METHOD_NOTIFY,     /*!< HTTP NOTIFY Method */
+    HTTP_METHOD_SUBSCRIBE,  /*!< HTTP SUBSCRIBE Method */
+    HTTP_METHOD_UNSUBSCRIBE,/*!< HTTP UNSUBSCRIBE Method */
+    HTTP_METHOD_OPTIONS,    /*!< HTTP OPTIONS Method */
+    HTTP_METHOD_MAX,
+} http_client_method_t;
+
+/**
+ * @brief HTTP Authentication type
+ */
+typedef enum {
+    HTTP_AUTH_TYPE_NONE = 0,    /*!< No authention */
+    HTTP_AUTH_TYPE_BASIC,       /*!< HTTP Basic authentication */
+    HTTP_AUTH_TYPE_DIGEST,      /*!< HTTP Disgest authentication */
+} http_client_auth_type_t;
+
+/**
+ * @brief HTTP configuration
+ */
+typedef struct {
+    const char                  *url;                /*!< HTTP URL, the information on the URL is most important, it overrides the other fields below, if any */
+    const char                  *host;               /*!< Domain or IP as string */
+    int                         port;                /*!< Port to connect, default depend on http_client_transport_t (80 or 443) */
+    const char                  *username;           /*!< Using for Http authentication */
+    const char                  *password;           /*!< Using for Http authentication */
+    http_client_auth_type_t     auth_type;           /*!< Http authentication type, see `http_client_auth_type_t` */
+    const char                  *path;               /*!< HTTP Path, if not set, default is `/` */
+    const char                  *query;              /*!< HTTP query */
+    const char                  *cert_pem;           /*!< SSL server certification, PEM format as string, if the client requires to verify server */
+    const char                  *client_cert_pem;    /*!< SSL client certification, PEM format as string, if the server requires to verify client */
+    const char                  *client_key_pem;     /*!< SSL client key, PEM format as string, if the server requires to verify client */
+    http_client_method_t        method;                   /*!< HTTP Method */
+    int                         timeout_ms;               /*!< Network timeout in milliseconds */
+    bool                        disable_auto_redirect;    /*!< Disable HTTP automatic redirects */
+    int                         max_redirection_count;    /*!< Max redirection number, using default value if zero*/
+    http_event_handle_cb        event_handler;             /*!< HTTP Event Handle */
+    http_client_transport_t     transport_type;           /*!< HTTP transport type, see `http_client_transport_t` */
+    int                         buffer_size;              /*!< HTTP buffer size (both send and receive) */
+    void                        *user_data;               /*!< HTTP user_data context */
+    bool                        is_async;                 /*!< Set asynchronous mode, only supported with HTTPS for now */
+    bool                        use_global_ca_store;      /*!< Use a global ca_store for all the connections in which this bool is set. */
+} http_client_config_t;
+
+/**
+ * Enum for the HTTP status codes.
+ */
+typedef enum {
+    /* 3xx - Redirection */
+    HttpStatus_MovedPermanently  = 301,
+    HttpStatus_Found             = 302,
+    HttpStatus_TemporaryRedirect = 307,
+
+    /* 4xx - Client Error */
+    HttpStatus_Unauthorized      = 401,
+    HttpStatus_Forbidden         = 403,
+    HttpStatus_NotFound          = 404,
+
+    /* 5xx - Server Error */
+    HttpStatus_InternalError     = 500
+} HttpStatus_Code;
+
+/**
+ * @brief      Start a HTTP session
+ *             This function must be the first function to call,
+ *             and it returns a http_client_handle_t that you must use as input to other functions in the interface.
+ *             This call MUST have a corresponding call to http_client_cleanup when the operation is complete.
+ *
+ * @param[in]  config   The configurations, see `http_client_config_t`
+ *
+ * @return
+ *     - `http_client_handle_t`
+ *     - NULL if any errors
+ */
+http_client_handle_t http_client_init(const http_client_config_t *config);
+
+/**
+ * @brief      Invoke this function after `http_client_init` and all the options calls are made, and will perform the
+ *             transfer as described in the options. It must be called with the same http_client_handle_t as input as the http_client_init call returned.
+ *             http_client_perform performs the entire request in either blocking or non-blocking manner. By default, the API performs request in a blocking manner and returns when done,
+ *             or if it failed, and in non-blocking manner, it returns if EAGAIN/EWOULDBLOCK or EINPROGRESS is encountered, or if it failed. And in case of non-blocking request,
+ *             the user may call this API multiple times unless request & response is complete or there is a failure. To enable non-blocking http_client_perform(), `is_async` member of http_client_config_t
+ *             must be set while making a call to http_client_init() API.
+ *             You can do any amount of calls to http_client_perform while using the same http_client_handle_t. The underlying connection may be kept open if the server allows it.
+ *             If you intend to transfer more than one file, you are even encouraged to do so.
+ *             http_client will then attempt to re-use the same connection for the following transfers, thus making the operations faster, less CPU intense and using less network resources.
+ *             Just note that you will have to use `http_client_set_**` between the invokes to set options for the following http_client_perform.
+ *
+ * @note       You must never call this function simultaneously from two places using the same client handle.
+ *             Let the function return first before invoking it another time.
+ *             If you want parallel transfers, you must use several http_client_handle_t.
+ *             This function include `http_client_open` -> `http_client_write` -> `http_client_fetch_headers` -> `http_client_read` (and option) `http_client_close`.
+ *
+ * @param      client  The http_client handle
+ *
+ * @return
+ *  - HTTP_CLI_OK on successful
+ *  - HTTP_CLI_FAIL on error
+ */
+http_errors_t http_client_perform(http_client_handle_t client);
+
+/**
+ * @brief      Set URL for client, when performing this behavior, the options in the URL will replace the old ones
+ *
+ * @param[in]  client  The http_client handle
+ * @param[in]  url     The url
+ *
+ * @return
+ *  - HTTP_CLI_OK
+ *  - HTTP_CLI_FAIL
+ */
+http_errors_t http_client_set_url(http_client_handle_t client, const char *url);
+
+/**
+ * @brief      Set post data, this function must be called before `http_client_perform`.
+ *             Note: The data parameter passed to this function is a pointer and this function will not copy the data
+ *
+ * @param[in]  client  The http_client handle
+ * @param[in]  data    post data pointer
+ * @param[in]  len     post length
+ *
+ * @return
+ *  - HTTP_CLI_OK
+ *  - HTTP_CLI_FAIL
+ */
+http_errors_t http_client_set_post_field(http_client_handle_t client, const char *data, int len);
+
+/**
+ * @brief      Get current post field information
+ *
+ * @param[in]  client  The client
+ * @param[out] data    Point to post data pointer
+ *
+ * @return     Size of post data
+ */
+int http_client_get_post_field(http_client_handle_t client, char **data);
+
+/**
+ * @brief      Set http request header, this function must be called after http_client_init and before any
+ *             perform function
+ *
+ * @param[in]  client  The http_client handle
+ * @param[in]  key     The header key
+ * @param[in]  value   The header value
+ *
+ * @return
+ *  - HTTP_CLI_OK
+ *  - HTTP_CLI_FAIL
+ */
+http_errors_t http_client_set_header(http_client_handle_t client, const char *key, const char *value);
+
+/**
+ * @brief      Get http request header.
+ *             The value parameter will be set to NULL if there is no header which is same as
+ *             the key specified, otherwise the address of header value will be assigned to value parameter.
+ *             This function must be called after `http_client_init`.
+ *
+ * @param[in]  client  The http_client handle
+ * @param[in]  key     The header key
+ * @param[out] value   The header value
+ *
+ * @return
+ *     - HTTP_CLI_OK
+ *     - HTTP_CLI_FAIL
+ */
+http_errors_t http_client_get_header(http_client_handle_t client, const char *key, char **value);
+
+/**
+ * @brief      Get http request username.
+ *             The address of username buffer will be assigned to value parameter.
+ *             This function must be called after `http_client_init`.
+ *
+ * @param[in]  client  The http_client handle
+ * @param[out] value   The username value
+ *
+ * @return
+ *     - HTTP_CLI_OK
+ *     - HTTP_CLI_ERR_INVALID_ARG
+ */
+http_errors_t http_client_get_username(http_client_handle_t client, char **value);
+
+/**
+ * @brief      Get http request password.
+ *             The address of password buffer will be assigned to value parameter.
+ *             This function must be called after `http_client_init`.
+ *
+ * @param[in]  client  The http_client handle
+ * @param[out] value   The password value
+ *
+ * @return
+ *     - HTTP_CLI_OK
+ *     - HTTP_CLI_ERR_INVALID_ARG
+ */
+http_errors_t http_client_get_password(http_client_handle_t client, char **value);
+
+/**
+ * @brief      Set http request method
+ *
+ * @param[in]  client  The http_client handle
+ * @param[in]  method  The method
+ *
+ * @return
+ *     - HTTP_CLI_OK
+ *     - HTTP_CLI_ERR_INVALID_ARG
+ */
+http_errors_t http_client_set_method(http_client_handle_t client, http_client_method_t method);
+
+/**
+ * @brief      Delete http request header
+ *
+ * @param[in]  client  The http_client handle
+ * @param[in]  key     The key
+ *
+ * @return
+ *  - HTTP_CLI_OK
+ *  - HTTP_CLI_FAIL
+ */
+http_errors_t http_client_delete_header(http_client_handle_t client, const char *key);
+
+/**
+ * @brief      This function will be open the connection, write all header strings and return
+ *
+ * @param[in]  client     The http_client handle
+ * @param[in]  write_len  HTTP Content length need to write to the server
+ *
+ * @return
+ *  - HTTP_CLI_OK
+ *  - HTTP_CLI_FAIL
+ */
+http_errors_t http_client_open(http_client_handle_t client, int write_len);
+
+/**
+ * @brief     This function will write data to the HTTP connection previously opened by http_client_open()
+ *
+ * @param[in]  client  The http_client handle
+ * @param      buffer  The buffer
+ * @param[in]  len     This value must not be larger than the write_len parameter provided to http_client_open()
+ *
+ * @return
+ *     - (-1) if any errors
+ *     - Length of data written
+ */
+int http_client_write(http_client_handle_t client, const char *buffer, int len);
+
+/**
+ * @brief      This function need to call after http_client_open, it will read from http stream, process all receive headers
+ *
+ * @param[in]  client  The http_client handle
+ *
+ * @return
+ *     - (0) if stream doesn't contain content-length header, or chunked encoding (checked by `http_client_is_chunked` response)
+ *     - (-1: HTTP_CLI_FAIL) if any errors
+ *     - Download data length defined by content-length header
+ */
+int http_client_fetch_headers(http_client_handle_t client);
+
+
+/**
+ * @brief      Check response data is chunked
+ *
+ * @param[in]  client  The http_client handle
+ *
+ * @return     true or false
+ */
+bool http_client_is_chunked_response(http_client_handle_t client);
+
+/**
+ * @brief      Read data from http stream
+ *
+ * @param[in]  client  The http_client handle
+ * @param      buffer  The buffer
+ * @param[in]  len     The length
+ *
+ * @return
+ *     - (-1) if any errors
+ *     - Length of data was read
+ */
+int http_client_read(http_client_handle_t client, char *buffer, int len);
+
+
+/**
+ * @brief      Get http response status code, the valid value if this function invoke after `http_client_perform`
+ *
+ * @param[in]  client  The http_client handle
+ *
+ * @return     Status code
+ */
+int http_client_get_status_code(http_client_handle_t client);
+
+/**
+ * @brief      Get http response content length (from header Content-Length)
+ *             the valid value if this function invoke after `http_client_perform`
+ *
+ * @param[in]  client  The http_client handle
+ *
+ * @return
+ *     - (-1) Chunked transfer
+ *     - Content-Length value as bytes
+ */
+int http_client_get_content_length(http_client_handle_t client);
+
+/**
+ * @brief      Close http connection, still kept all http request resources
+ *
+ * @param[in]  client  The http_client handle
+ *
+ * @return
+ *     - HTTP_CLI_OK
+ *     - HTTP_CLI_FAIL
+ */
+http_errors_t http_client_close(http_client_handle_t client);
+
+/**
+ * @brief      This function must be the last function to call for an session.
+ *             It is the opposite of the http_client_init function and must be called with the same handle as input that a http_client_init call returned.
+ *             This might close all connections this handle has used and possibly has kept open until now.
+ *             Don't call this function if you intend to transfer more files, re-using handles is a key to good performance with http_client.
+ *
+ * @param[in]  client  The http_client handle
+ *
+ * @return
+ *     - HTTP_CLI_OK
+ *     - HTTP_CLI_FAIL
+ */
+http_errors_t http_client_cleanup(http_client_handle_t client);
+
+/**
+ * @brief      Get transport type
+ *
+ * @param[in]  client   The http_client handle
+ *
+ * @return
+ *     - HTTP_TRANSPORT_UNKNOWN
+ *     - HTTP_TRANSPORT_OVER_TCP
+ *     - HTTP_TRANSPORT_OVER_SSL
+ */
+http_client_transport_t http_client_get_transport_type(http_client_handle_t client);
+
+/**
+ * @brief      Set redirection URL.
+ *             When received the 30x code from the server, the client stores the redirect URL provided by the server.
+ *             This function will set the current URL to redirect to enable client to execute the redirection request.
+ *
+ * @param[in]  client  The http_client handle
+ *
+ * @return
+ *     - HTTP_CLI_OK
+ *     - HTTP_CLI_FAIL
+ */
+http_errors_t http_client_set_redirection(http_client_handle_t client);
+
+/**
+ * @brief      Checks if entire data in the response has been read without any error.
+ *
+ * @param[in]  client   The http_client handle
+ *
+ * @return
+ *     - true
+ *     - false
+ */
+bool http_client_is_complete_data_received(http_client_handle_t client);
+
+/**
+ * @brief      Helper API to read larger data chunks
+ *             This is a helper API which internally calls `http_client_read` multiple times till the end of data is reached or till the buffer gets full.
+ *
+ * @param[in]  client   The http_client handle
+ * @param      buffer   The buffer
+ * @param[in]  len      The buffer length
+ *
+ * @return
+ *     - Length of data was read
+ */
+
+int http_client_read_response(http_client_handle_t client, char *buffer, int len);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif

+ 129 - 0
components/httpclient/include/http_header.h

@@ -0,0 +1,129 @@
+// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef _HTTP_HEADER_H_
+#define _HTTP_HEADER_H_
+
+
+#include "aos/queue.h"
+#include "transport/tperrors.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct http_header *http_header_handle_t;
+typedef struct http_header_item *http_header_item_handle_t;
+
+/**
+ * @brief      initialize and allocate the memory for the header object
+ *
+ * @return
+ *     - http_header_handle_t
+ *     - NULL if any errors
+ */
+http_header_handle_t http_header_init();
+
+/**
+ * @brief      Cleanup and free all http header pairs
+ *
+ * @param[in]  header  The header
+ *
+ * @return
+ *     - WEB_OK
+ *     - WEB_FAIL
+ */
+web_err_t http_header_clean(http_header_handle_t header);
+
+/**
+ * @brief      Cleanup with http_header_clean and destroy http header handle object
+ *
+ * @param[in]  header  The header
+ *
+ * @return
+ *     - WEB_OK
+ *     - WEB_FAIL
+ */
+web_err_t http_header_destroy(http_header_handle_t header);
+
+/**
+ * @brief      Add a key-value pair of http header to the list,
+ *             note that with value = NULL, this function will remove the header with `key` already exists in the list.
+ *
+ * @param[in]  header  The header
+ * @param[in]  key     The key
+ * @param[in]  value   The value
+ *
+ * @return
+ *     - WEB_OK
+ *     - WEB_FAIL
+ */
+web_err_t http_header_set(http_header_handle_t header, const char *key, const char *value);
+
+/**
+ * @brief      Sample as `http_header_set` but the value can be formated
+ *
+ * @param[in]  header     The header
+ * @param[in]  key        The key
+ * @param[in]  format     The format
+ * @param[in]  ...        format parameters
+ *
+ * @return     Total length of value
+ */
+int http_header_set_format(http_header_handle_t header, const char *key, const char *format, ...);
+
+/**
+ * @brief      Get a value of header in header list
+ *             The address of the value will be assign set to `value` parameter or NULL if no header with the key exists in the list
+ *
+ * @param[in]  header  The header
+ * @param[in]  key     The key
+ * @param[out] value   The value
+ *
+ * @return
+ *     - WEB_OK
+ *     - WEB_FAIL
+ */
+web_err_t http_header_get(http_header_handle_t header, const char *key, char **value);
+
+/**
+ * @brief      Create HTTP header string from the header with index, output string to buffer with buffer_len
+ *             Also return the last index of header was generated
+ *
+ * @param[in]  header      The header
+ * @param[in]  index       The index
+ * @param      buffer      The buffer
+ * @param      buffer_len  The buffer length
+ *
+ * @return     The last index of header was generated
+ */
+int http_header_generate_string(http_header_handle_t header, int index, char *buffer, int *buffer_len);
+
+/**
+ * @brief      Remove the header with key from the headers list
+ *
+ * @param[in]  header  The header
+ * @param[in]  key     The key
+ *
+ * @return
+ *     - WEB_OK
+ *     - WEB_FAIL
+ */
+web_err_t http_header_delete(http_header_handle_t header, const char *key);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 348 - 0
components/httpclient/include/http_parser.h

@@ -0,0 +1,348 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+#ifndef http_parser_h
+#define http_parser_h
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Also update SONAME in the Makefile whenever you change these. */
+#define HTTP_PARSER_VERSION_MAJOR 2
+#define HTTP_PARSER_VERSION_MINOR 7
+#define HTTP_PARSER_VERSION_PATCH 0
+
+#include <stddef.h>
+#include <stdint.h>
+
+/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
+ * faster
+ */
+#ifndef HTTP_PARSER_STRICT
+# define HTTP_PARSER_STRICT 1
+#endif
+
+/* Maximium header size allowed. If the macro is not defined
+ * before including this header then the default is used. To
+ * change the maximum header size, define the macro in the build
+ * environment (e.g. -DHTTP_MAX_HEADER_SIZE=<value>). To remove
+ * the effective limit on the size of the header, define the macro
+ * to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff)
+ */
+#ifndef HTTP_MAX_HEADER_SIZE
+# define HTTP_MAX_HEADER_SIZE (2*1024)
+#endif
+
+typedef struct http_parser http_parser;
+typedef struct http_parser_settings http_parser_settings;
+
+
+/* Callbacks should return non-zero to indicate an error. The parser will
+ * then halt execution.
+ *
+ * The one exception is on_headers_complete. In a HTTP_RESPONSE parser
+ * returning '1' from on_headers_complete will tell the parser that it
+ * should not expect a body. This is used when receiving a response to a
+ * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
+ * chunked' headers that indicate the presence of a body.
+ *
+ * Returning `2` from on_headers_complete will tell parser that it should not
+ * expect neither a body nor any futher responses on this connection. This is
+ * useful for handling responses to a CONNECT request which may not contain
+ * `Upgrade` or `Connection: upgrade` headers.
+ *
+ * http_data_cb does not return data chunks. It will be called arbitrarily
+ * many times for each string. E.G. you might get 10 callbacks for "on_url"
+ * each providing just a few characters more data.
+ */
+typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
+typedef int (*http_cb) (http_parser*);
+
+
+/* Request Methods */
+#define HTTP_METHOD_MAP(XX)         \
+  XX(0,  DELETE,      DELETE)       \
+  XX(1,  GET,         GET)          \
+  XX(2,  HEAD,        HEAD)         \
+  XX(3,  POST,        POST)         \
+  XX(4,  PUT,         PUT)          \
+  /* pathological */                \
+  XX(5,  CONNECT,     CONNECT)      \
+  XX(6,  OPTIONS,     OPTIONS)      \
+  XX(7,  TRACE,       TRACE)        \
+  /* WebDAV */                      \
+  XX(8,  COPY,        COPY)         \
+  XX(9,  LOCK,        LOCK)         \
+  XX(10, MKCOL,       MKCOL)        \
+  XX(11, MOVE,        MOVE)         \
+  XX(12, PROPFIND,    PROPFIND)     \
+  XX(13, PROPPATCH,   PROPPATCH)    \
+  XX(14, SEARCH,      SEARCH)       \
+  XX(15, UNLOCK,      UNLOCK)       \
+  XX(16, BIND,        BIND)         \
+  XX(17, REBIND,      REBIND)       \
+  XX(18, UNBIND,      UNBIND)       \
+  XX(19, ACL,         ACL)          \
+  /* subversion */                  \
+  XX(20, REPORT,      REPORT)       \
+  XX(21, MKACTIVITY,  MKACTIVITY)   \
+  XX(22, CHECKOUT,    CHECKOUT)     \
+  XX(23, MERGE,       MERGE)        \
+  /* upnp */                        \
+  XX(24, MSEARCH,     M-SEARCH)     \
+  XX(25, NOTIFY,      NOTIFY)       \
+  XX(26, SUBSCRIBE,   SUBSCRIBE)    \
+  XX(27, UNSUBSCRIBE, UNSUBSCRIBE)  \
+  /* RFC-5789 */                    \
+  XX(28, PATCH,       PATCH)        \
+  XX(29, PURGE,       PURGE)        \
+  /* CalDAV */                      \
+  XX(30, MKCALENDAR,  MKCALENDAR)   \
+  /* RFC-2068, section 19.6.1.2 */  \
+  XX(31, LINK,        LINK)         \
+  XX(32, UNLINK,      UNLINK)       \
+
+enum http_method
+  {
+#define XX(num, name, string) HTTP_##name = num,
+  HTTP_METHOD_MAP(XX)
+#undef XX
+  };
+
+
+enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
+
+
+/* Flag values for http_parser.flags field */
+enum flags
+  { F_CHUNKED               = 1 << 0
+  , F_CONNECTION_KEEP_ALIVE = 1 << 1
+  , F_CONNECTION_CLOSE      = 1 << 2
+  , F_CONNECTION_UPGRADE    = 1 << 3
+  , F_TRAILING              = 1 << 4
+  , F_UPGRADE               = 1 << 5
+  , F_SKIPBODY              = 1 << 6
+  , F_CONTENTLENGTH         = 1 << 7
+  };
+
+
+/* Map for errno-related constants
+ *
+ * The provided argument should be a macro that takes 2 arguments.
+ */
+#define HTTP_ERRNO_MAP(XX)                                           \
+  /* No error */                                                     \
+  XX(OK, "success")                                                  \
+                                                                     \
+  /* Callback-related errors */                                      \
+  XX(CB_message_begin, "the on_message_begin callback failed")       \
+  XX(CB_url, "the on_url callback failed")                           \
+  XX(CB_header_field, "the on_header_field callback failed")         \
+  XX(CB_header_value, "the on_header_value callback failed")         \
+  XX(CB_headers_complete, "the on_headers_complete callback failed") \
+  XX(CB_body, "the on_body callback failed")                         \
+  XX(CB_message_complete, "the on_message_complete callback failed") \
+  XX(CB_status, "the on_status callback failed")                     \
+  XX(CB_chunk_header, "the on_chunk_header callback failed")         \
+  XX(CB_chunk_complete, "the on_chunk_complete callback failed")     \
+                                                                     \
+  /* Parsing-related errors */                                       \
+  XX(INVALID_EOF_STATE, "stream ended at an unexpected time")        \
+  XX(HEADER_OVERFLOW,                                                \
+     "too many header bytes seen; overflow detected")                \
+  XX(CLOSED_CONNECTION,                                              \
+     "data received after completed connection: close message")      \
+  XX(INVALID_VERSION, "invalid HTTP version")                        \
+  XX(INVALID_STATUS, "invalid HTTP status code")                     \
+  XX(INVALID_METHOD, "invalid HTTP method")                          \
+  XX(INVALID_URL, "invalid URL")                                     \
+  XX(INVALID_HOST, "invalid host")                                   \
+  XX(INVALID_PORT, "invalid port")                                   \
+  XX(INVALID_PATH, "invalid path")                                   \
+  XX(INVALID_QUERY_STRING, "invalid query string")                   \
+  XX(INVALID_FRAGMENT, "invalid fragment")                           \
+  XX(LF_EXPECTED, "LF character expected")                           \
+  XX(INVALID_HEADER_TOKEN, "invalid character in header")            \
+  XX(INVALID_CONTENT_LENGTH,                                         \
+     "invalid character in content-length header")                   \
+  XX(UNEXPECTED_CONTENT_LENGTH,                                      \
+     "unexpected content-length header")                             \
+  XX(INVALID_CHUNK_SIZE,                                             \
+     "invalid character in chunk size header")                       \
+  XX(INVALID_CONSTANT, "invalid constant string")                    \
+  XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
+  XX(STRICT, "strict mode assertion failed")                         \
+  XX(PAUSED, "parser is paused")                                     \
+  XX(UNKNOWN, "an unknown error occurred")
+
+
+/* Define HPE_* values for each errno value above */
+#define HTTP_ERRNO_GEN(n, s) HPE_##n,
+enum http_errno {
+  HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
+};
+#undef HTTP_ERRNO_GEN
+
+
+/* Get an http_errno value from an http_parser */
+#define HTTP_PARSER_ERRNO(p)            ((enum http_errno) (p)->http_errno)
+
+
+struct http_parser {
+  /** PRIVATE **/
+  unsigned int type : 2;         /* enum http_parser_type */
+  unsigned int flags : 8;        /* F_* values from 'flags' enum; semi-public */
+  unsigned int state : 7;        /* enum state from http_parser.c */
+  unsigned int header_state : 7; /* enum header_state from http_parser.c */
+  unsigned int index : 7;        /* index into current matcher */
+  unsigned int lenient_http_headers : 1;
+
+  uint32_t nread;          /* # bytes read in various scenarios */
+  uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
+
+  /** READ-ONLY **/
+  unsigned short http_major;
+  unsigned short http_minor;
+  unsigned int status_code : 16; /* responses only */
+  unsigned int method : 8;       /* requests only */
+  unsigned int http_errno : 7;
+
+  /* 1 = Upgrade header was present and the parser has exited because of that.
+   * 0 = No upgrade header present.
+   * Should be checked when http_parser_execute() returns in addition to
+   * error checking.
+   */
+  unsigned int upgrade : 1;
+
+  /** PUBLIC **/
+  void *data; /* A pointer to get hook to the "connection" or "socket" object */
+};
+
+
+struct http_parser_settings {
+  http_cb      on_message_begin;
+  http_data_cb on_url;
+  http_data_cb on_status;
+  http_data_cb on_header_field;
+  http_data_cb on_header_value;
+  http_cb      on_headers_complete;
+  http_data_cb on_body;
+  http_cb      on_message_complete;
+  /* When on_chunk_header is called, the current chunk length is stored
+   * in parser->content_length.
+   */
+  http_cb      on_chunk_header;
+  http_cb      on_chunk_complete;
+};
+
+
+enum http_parser_url_fields
+  { UF_SCHEMA           = 0
+  , UF_HOST             = 1
+  , UF_PORT             = 2
+  , UF_PATH             = 3
+  , UF_QUERY            = 4
+  , UF_FRAGMENT         = 5
+  , UF_USERINFO         = 6
+  , UF_MAX              = 7
+  };
+
+
+/* Result structure for http_parser_parse_url().
+ *
+ * Callers should index into field_data[] with UF_* values iff field_set
+ * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
+ * because we probably have padding left over), we convert any port to
+ * a uint16_t.
+ */
+struct http_parser_url {
+  uint16_t field_set;           /* Bitmask of (1 << UF_*) values */
+  uint16_t port;                /* Converted UF_PORT string */
+
+  struct {
+    uint16_t off;               /* Offset into buffer in which field starts */
+    uint16_t len;               /* Length of run in buffer */
+  } field_data[UF_MAX];
+};
+
+
+/* Returns the library version. Bits 16-23 contain the major version number,
+ * bits 8-15 the minor version number and bits 0-7 the patch level.
+ * Usage example:
+ *
+ *   unsigned long version = http_parser_version();
+ *   unsigned major = (version >> 16) & 255;
+ *   unsigned minor = (version >> 8) & 255;
+ *   unsigned patch = version & 255;
+ *   printf("http_parser v%u.%u.%u\n", major, minor, patch);
+ */
+unsigned long http_parser_version(void);
+
+void http_parser_init(http_parser *parser, enum http_parser_type type);
+
+
+/* Initialize http_parser_settings members to 0
+ */
+void http_parser_settings_init(http_parser_settings *settings);
+
+
+/* Executes the parser. Returns number of parsed bytes. Sets
+ * `parser->http_errno` on error. */
+size_t http_parser_execute(http_parser *parser,
+                           const http_parser_settings *settings,
+                           const char *data,
+                           size_t len);
+
+
+/* If http_should_keep_alive() in the on_headers_complete or
+ * on_message_complete callback returns 0, then this should be
+ * the last message on the connection.
+ * If you are the server, respond with the "Connection: close" header.
+ * If you are the client, close the connection.
+ */
+int http_should_keep_alive(const http_parser *parser);
+
+/* Returns a string version of the HTTP method. */
+const char *http_method_str(enum http_method m);
+
+/* Return a string name of the given error */
+const char *http_errno_name(enum http_errno err);
+
+/* Return a string description of the given error */
+const char *http_errno_description(enum http_errno err);
+
+/* Initialize all http_parser_url members to 0 */
+void http_parser_url_init(struct http_parser_url *u);
+
+/* Parse a URL; return nonzero on failure */
+int http_parser_parse_url(const char *buf, size_t buflen,
+                          int is_connect,
+                          struct http_parser_url *u);
+
+/* Pause or un-pause the parser; a nonzero value pauses */
+void http_parser_pause(http_parser *parser, int paused);
+
+/* Checks if this is the final chunk of the body. */
+int http_body_is_final(const http_parser *parser);
+
+#ifdef __cplusplus
+}
+#endif
+#endif

+ 46 - 0
components/httpclient/include/http_tls.h

@@ -0,0 +1,46 @@
+#ifndef __HTTP_TLS_H__
+#define __HTTP_TLS_H__
+
+#if defined(CONFIG_USING_TLS)
+#include <transport/tls.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * @brief      Create a new blocking TLS/SSL connection with a given "HTTP" url
+ *
+ * The behaviour is same as tls_conn_new() API. However this API accepts host's url.
+ *
+ * @param[in]  url  url of host.
+ * @param[in]  cfg  TLS configuration as tls_cfg_t. If you wish to open
+ *                  non-TLS connection, keep this NULL. For TLS connection,
+ *                  a pass pointer to 'tls_cfg_t'. At a minimum, this
+ *                  structure should be zero-initialized.
+ * @return pointer to tls_t, or NULL if connection couldn't be opened.
+ */
+tls_t *tls_conn_http_new(const char *url, const tls_cfg_t *cfg);
+
+/**
+ * @brief      Create a new non-blocking TLS/SSL connection with a given "HTTP" url
+ *
+ * The behaviour is same as tls_conn_new() API. However this API accepts host's url.
+ *
+ * @param[in]  url     url of host.
+ * @param[in]  cfg     TLS configuration as tls_cfg_t.
+ * @param[in]  tls     pointer to tls as tls handle.
+ *
+ * @return
+ *             - -1     If connection establishment fails.
+ *             -  0     If connection establishment is in progress.
+ *             -  1     If connection establishment is successful.
+ */
+int tls_conn_http_new_async(const char *url, const tls_cfg_t *cfg, tls_t *tls);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
+#endif

+ 98 - 0
components/httpclient/include/http_utils.h

@@ -0,0 +1,98 @@
+// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+#ifndef _HTTP_UTILS_H_
+#define _HTTP_UTILS_H_
+#include <sys/time.h>
+#include <ulog/ulog.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief      Assign new_str to *str pointer, and realloc *str if it not NULL
+ *
+ * @param      str      pointer to string pointer
+ * @param      new_str  assign this tring to str
+ * @param      len      length of string, 0 if new_str is zero terminated
+ *
+ * @return
+ *  - new_str pointer
+ *  - NULL
+ */
+char *http_utils_assign_string(char **str, const char *new_str, int len);
+
+/**
+ * @brief      Remove white space at begin and end of string
+ *
+ * @param[in]  str   The string
+ *
+ * @return     New strings have been trimmed
+ */
+void http_utils_trim_whitespace(char **str);
+
+/**
+ * @brief      Gets the string between 2 string.
+ *             It will allocate a new memory space for this string, so you need to free it when no longer use
+ *
+ * @param[in]  str    The source string
+ * @param[in]  begin  The begin string
+ * @param[in]  end    The end string
+ *
+ * @return     The string between begin and end
+ */
+char *http_utils_get_string_between(const char *str, const char *begin, const char *end);
+
+/**
+ * @brief      Join 2 strings to one
+ *             It will allocate a new memory space for this string, so you need to free it when no longer use
+ *
+ * @param[in]  first_str   The first string
+ * @param[in]  len_first   The length first
+ * @param[in]  second_str  The second string
+ * @param[in]  len_second  The length second
+ *
+ * @return
+ * - New string pointer
+ * - NULL: Invalid input
+ */
+char *http_utils_join_string(const char *first_str, int len_first, const char *second_str, int len_second);
+
+/**
+ * @brief      Check if ``str`` is start with ``start``
+ *
+ * @param[in]  str    The string
+ * @param[in]  start  The start
+ *
+ * @return
+ *     - (-1) if length of ``start`` larger than length of ``str``
+ *     - (1) if ``start`` NOT starts with ``start``
+ *     - (0) if ``str`` starts with ``start``
+ */
+int http_utils_str_starts_with(const char *str, const char *start);
+
+
+// #define HTTP_MEM_CHECK(TAG, a, action) //TRANSPORT_MEM_CHECK(TAG, a, action)
+#define HTTP_MEM_CHECK(TAG, a, action) if (!(a)) {                                                      \
+        LOGE(TAG,"%s:%d (%s): %s", __FILE__, __LINE__, __FUNCTION__, "Memory exhausted");       \
+        action;                                                                                         \
+        }
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 77 - 0
components/httpclient/package.yaml

@@ -0,0 +1,77 @@
+
+## 第一部分: 基础信息
+name: httpclient                                    # <必选项> 包名称 (符合C语言变量命名规则),长度少于等于64字节
+version: v7.4.4                                     # <必选项> 组件版本号
+description: http/https客户端组件                   # <必选项> 建议至少20字以上
+type: common                                        # <必选项> 组件类型,为:solution, chip, board, common, sdk
+tag: 网络组件                                       # <可选项> 组件分类,缺省值: ''
+keywords:                                           # <可选项> 标签,会影响到组件被搜索的效果,合理的标签很重要
+  - network
+license: Apache license v2.0                        # <可选项> 源代码的许可证,要确保所有代码、文件的许可证不冲突。如:MIT,Apache license v2.0,BSD
+
+## 第二部分:依赖信息
+#           指定该组件依赖的组件及版本,版本支持条件比较,支持:>=v1.0, >v1.0, ==v1.0, <=v1.0, <v1.0, v1.0
+#           未指定条件时,默认为 ==,如 v1.0 与 ==v1.0
+# sdk_chip:                                # <可选项> 该组件依赖sdk组件,合理的依赖才能保证组件能编译、使用
+#   - sdk_chip_csky_dummy: v7.4.y
+#   - sdk_chip_riscv_dummy: v7.4.y
+# depends:                                 # <可选项> 该组件依赖其他的组件,合理的依赖才能保证组件能编译、使用
+#   - minilibc: v7.2.0
+#   - aos: >=v7.2.0
+depends:
+  - transport: v7.4.4
+  - mbedtls: v7.4.4
+
+## 第四部分:编译连接信息
+# build_config:                            # <可选项> 编译配置项
+#   include:                               # <可选项> 编译时,影响编译器的-I 参数 ,全局有效
+#     - src                                #        include 只能是该软件包下的目录,不能使用外部目录
+#   internal_include:                      # <可选项> 编译时,影响编译器的-I 参数 ,组件内有效
+#     - include
+#   cflag: ''                              # <可选项> C 编译器所需要要的编译参数
+#   cxxflag: ''                            # <可选项> CXX 编译器所需要要的编译参数
+#   asmflag: ''                            # <可选项> 汇编器所需要要参数
+#   define:                                # <可选项> 宏定义, 增加编译器的-D 选项,如:
+#     XXX: 1                               #   -DXXX=1
+#     AAA: 1                               #   -DAAA
+#     STR: "abc"                           #   -DSTR="abc"
+#   libs:                                  # 该组件中支持的二进制静态库,如:libxxx.a, libyyy.a
+#     - xxx                                #   -lxxx
+#     - yyy                                #   -lyyy
+#   libpath:                               # 指定静态库所在的路径(相对于该组件路径)
+#     - libs                               #   -Llibs
+build_config:
+  define:
+    - CONFIG_USING_TLS=1
+  include:
+    - include
+
+# source_file:                             # <可选项> 指定参与编译的源代码文件,支持通配符,采用相对路径
+#   - src/*.c                              # 例:组件 src 目录下所有的扩展名为 c 的源代码文件
+source_file:
+  - "src/*.c"
+
+## 第五部分:配置信息
+# def_config:                              # 组件的可配置项
+#   CONFIG_DEBUG: y
+#   CONFIG_PARAM_NOT_CHECK: y
+#   CONFIG_CLI: y
+
+## 第六部分:安装信息
+# install:
+#   - dest: include/                       # 安装的目的路径 dest是相对路径,通常是相对于YoC SDK 安装目录
+#     source:                              # 安装源列表
+#      - src/*.h                           # 支持通配符,相对路径
+install:
+  - dest: "include"
+    source:
+      - "include/http_client.h"
+
+## 第七部分:导出部分
+# export:
+#   - dest: "<SOLUTION_PATH>/generated/data" # 安装的目的路径 dest是相对路径
+#     source:                                # 安装源列表
+#       - "bootimgs/boot"
+#       - "bootimgs/tee"
+#       - "bootimgs/mtb"
+#       - "configs/config.yaml"

+ 157 - 0
components/httpclient/src/http_auth.c

@@ -0,0 +1,157 @@
+// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include <sys/socket.h>
+#include "mbedtls/base64.h"
+#include "mbedtls/md5.h"
+#include "transport/tperrors.h"
+
+#include "http_utils.h"
+#include "http_auth.h"
+
+
+#define MD5_MAX_LEN (33)
+#define HTTP_AUTH_BUF_LEN (1024)
+
+static const char *TAG = "HTTP_AUTH";
+
+/**
+ * @brief      This function hash a formatted string with MD5 and format the result as ascii characters
+ *
+ * @param      md         The buffer will hold the ascii result
+ * @param[in]  fmt        The format
+ *
+ * @return     Length of the result
+ */
+static int md5_printf(char *md, const char *fmt, ...)
+{
+    unsigned char *buf;
+    unsigned char digest[MD5_MAX_LEN];
+    int len, i;
+    // struct MD5Context md5_ctx;
+    mbedtls_md5_context md5_ctx;
+    va_list ap;
+    va_start(ap, fmt);
+    len = vasprintf((char **)&buf, fmt, ap);
+    if (buf == NULL) {
+        va_end(ap);
+        return WEB_FAIL;
+    }
+
+    mbedtls_md5_init(&md5_ctx);
+    mbedtls_md5_starts(&md5_ctx);
+    mbedtls_md5_update(&md5_ctx, buf, len);
+    mbedtls_md5_finish(&md5_ctx, digest);
+    mbedtls_md5_free(&md5_ctx);
+
+    // MD5Init(&md5_ctx);
+    // MD5Update(&md5_ctx, buf, len);
+    // MD5Final(digest, &md5_ctx);
+
+    for (i = 0; i < 16; ++i) {
+        sprintf(&md[i * 2], "%02x", (unsigned int)digest[i]);
+    }
+    va_end(ap);
+
+    free(buf);
+    return MD5_MAX_LEN;
+}
+
+char *http_auth_digest(const char *username, const char *password, http_auth_data_t *auth_data)
+{
+    char *ha1, *ha2 = NULL;
+    char *digest = NULL;
+    char *auth_str = NULL;
+
+    if (username == NULL ||
+        password == NULL ||
+        auth_data->nonce == NULL ||
+        auth_data->uri == NULL ||
+        auth_data->realm == NULL) {
+        return NULL;
+    }
+
+    ha1 = calloc(1, MD5_MAX_LEN);
+    HTTP_MEM_CHECK(TAG, ha1, goto _digest_exit);
+
+    ha2 = calloc(1, MD5_MAX_LEN);
+    HTTP_MEM_CHECK(TAG, ha2, goto _digest_exit);
+
+    digest = calloc(1, MD5_MAX_LEN);
+    HTTP_MEM_CHECK(TAG, digest, goto _digest_exit);
+
+    if (md5_printf(ha1, "%s:%s:%s", username, auth_data->realm, password) <= 0) {
+        goto _digest_exit;
+    }
+
+    LOGD(TAG, "%s %s %s %s\r\n", "Digest", username, auth_data->realm, password);
+    if (strcasecmp(auth_data->algorithm, "md5-sess") == 0) {
+        if (md5_printf(ha1, "%s:%s:%016llx", ha1, auth_data->nonce, auth_data->cnonce) <= 0) {
+            goto _digest_exit;
+        }
+    }
+    if (md5_printf(ha2, "%s:%s", auth_data->method, auth_data->uri) <= 0) {
+        goto _digest_exit;
+    }
+
+    //support qop = auth
+    if (auth_data->qop && strcasecmp(auth_data->qop, "auth-int") == 0) {
+        if (md5_printf(ha2, "%s:%s", ha2, "entity") <= 0) {
+            goto _digest_exit;
+        }
+    }
+
+    if (auth_data->qop) {
+        // response=MD5(HA1:nonce:nonceCount:cnonce:qop:HA2)
+        if (md5_printf(digest, "%s:%s:%08x:%016llx:%s:%s", ha1, auth_data->nonce, auth_data->nc, auth_data->cnonce, auth_data->qop, ha2) <= 0) {
+            goto _digest_exit;
+        }
+    } else {
+        // response=MD5(HA1:nonce:HA2)
+        if (md5_printf(digest, "%s:%s:%s", ha1, auth_data->nonce, ha2) <= 0) {
+            goto _digest_exit;
+        }
+    }
+    asprintf(&auth_str, "Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", algorithm=\"MD5\", "
+             "response=\"%s\", opaque=\"%s\", qop=%s, nc=%08x, cnonce=\"%016llx\"",
+             username, auth_data->realm, auth_data->nonce, auth_data->uri, digest, auth_data->opaque, auth_data->qop, auth_data->nc, auth_data->cnonce);
+_digest_exit:
+    free(ha1);
+    free(ha2);
+    free(digest);
+    return auth_str;
+}
+
+char *http_auth_basic(const char *username, const char *password)
+{
+    int out;
+    char *user_info = NULL;
+    char *digest = NULL;
+    size_t n = 0;
+    asprintf(&user_info, "%s:%s", username, password);
+    HTTP_MEM_CHECK(TAG, user_info, return NULL);
+    mbedtls_base64_encode(NULL, 0, &n, (const unsigned char *)user_info, strlen(user_info));
+    digest = calloc(1, 6 + n + 1);
+    HTTP_MEM_CHECK(TAG, digest, goto _basic_exit);
+    strcpy(digest, "Basic ");
+    mbedtls_base64_encode((unsigned char *)digest + 6, n, (size_t *)&out, (const unsigned char *)user_info, strlen(user_info));
+_basic_exit:
+    free(user_info);
+    return digest;
+}

+ 1321 - 0
components/httpclient/src/http_client.c

@@ -0,0 +1,1321 @@
+// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#include <stdlib.h>
+#include "http_client.h"
+#include "http_header.h"
+#include "http_utils.h"
+#include "http_parser.h"
+#include "http_auth.h"
+#include "transport/transport_tcp.h"
+#include "transport/tperrors.h"
+#include "ulog/ulog.h"
+#include "aos/errno.h"
+#include <errno.h>
+
+#include <string.h>
+#include <stdio.h>
+
+#ifdef CONFIG_USING_TLS
+#include "transport/transport_ssl.h"
+#endif
+
+static const char *TAG = "HTTP_CLIENT";
+
+#define ERR_HTTP_BASE               (0x3000)                /*!< Starting number of HTTP error codes */
+#define ERR_HTTP_MAX_REDIRECT       (ERR_HTTP_BASE + 1)     /*!< The error exceeds the number of HTTP redirects */
+#define ERR_HTTP_CONNECT            (ERR_HTTP_BASE + 2)     /*!< Error open the HTTP connection */
+#define ERR_HTTP_WRITE_DATA         (ERR_HTTP_BASE + 3)     /*!< Error write HTTP data */
+#define ERR_HTTP_FETCH_HEADER       (ERR_HTTP_BASE + 4)     /*!< Error read HTTP header from server */
+#define ERR_HTTP_INVALID_TRANSPORT  (ERR_HTTP_BASE + 5)     /*!< There are no transport support for the input scheme */
+#define ERR_HTTP_CONNECTING         (ERR_HTTP_BASE + 6)     /*!< HTTP connection hasn't been established yet */
+#define ERR_HTTP_EAGAIN             EAGAIN//(ERR_HTTP_BASE + 7)     /*!< Mapping of errno EAGAIN to web_err_t */
+
+/**
+ * HTTP Buffer
+ */
+typedef struct {
+    char *data;         /*!< The HTTP data received from the server */
+    int len;            /*!< The HTTP data len received from the server */
+    char *raw_data;     /*!< The HTTP data after decoding */
+    int raw_len;        /*!< The HTTP data len after decoding */
+    char *output_ptr;   /*!< The destination address of the data to be copied to after decoding */
+} http_buffer_t;
+
+/**
+ * private HTTP Data structure
+ */
+typedef struct {
+    http_header_handle_t headers;       /*!< http header */
+    http_buffer_t       *buffer;        /*!< data buffer as linked list */
+    int                 status_code;    /*!< status code (integer) */
+    int                 content_length; /*!< data length */
+    int                 data_offset;    /*!< offset to http data (Skip header) */
+    int                 data_process;   /*!< data processed */
+    int                 method;         /*!< http method */
+    bool                is_chunked;
+} http_data_t;
+
+typedef struct {
+    char                         *url;
+    char                         *scheme;
+    char                         *host;
+    int                          port;
+    char                         *username;
+    char                         *password;
+    char                         *path;
+    char                         *query;
+    char                         *cert_pem;
+    http_client_method_t         method;
+    http_client_auth_type_t      auth_type;
+    http_client_transport_t      transport_type;
+    int                          max_store_header_size;
+} connection_info_t;
+
+typedef enum {
+    HTTP_STATE_UNINIT = 0,
+    HTTP_STATE_INIT,
+    HTTP_STATE_CONNECTED,
+    HTTP_STATE_REQ_COMPLETE_HEADER,
+    HTTP_STATE_REQ_COMPLETE_DATA,
+    HTTP_STATE_RES_COMPLETE_HEADER,
+    HTTP_STATE_RES_COMPLETE_DATA,
+    HTTP_STATE_CLOSE
+} http_state_t;
+/**
+ * HTTP client class
+ */
+struct http_client {
+    int                         redirect_counter;
+    int                         max_redirection_count;
+    int                         process_again;
+    struct http_parser          *parser;
+    struct http_parser_settings *parser_settings;
+    transport_list_handle_t     transport_list;
+    transport_handle_t          transport;
+    http_data_t                 *request;
+    http_data_t                 *response;
+    void                        *user_data;
+    http_auth_data_t            *auth_data;
+    char                        *post_data;
+    char                        *location;
+    char                        *auth_header;
+    char                        *current_header_key;
+    char                        *current_header_value;
+    int                         post_len;
+    connection_info_t           connection_info;
+    bool                        is_chunk_complete;
+    http_state_t                state;
+    http_event_handle_cb        event_handler;
+    int                         timeout_ms;
+    int                         buffer_size;
+    bool                        disable_auto_redirect;
+    http_client_event_t         event;
+    int                         data_written_index;
+    int                         data_write_left;
+    bool                        first_line_prepared;
+    int                         header_index;
+    bool                        is_async;
+};
+
+typedef struct http_client http_client_t;
+
+static web_err_t _clear_connection_info(http_client_handle_t client);
+/**
+ * Default settings
+ */
+#define DEFAULT_HTTP_PORT (80)
+#define DEFAULT_HTTPS_PORT (443)
+
+#define ASYNC_TRANS_CONNECT_FAIL -1
+#define ASYNC_TRANS_CONNECTING 0
+#define ASYNC_TRANS_CONNECT_PASS 1
+
+static const char *DEFAULT_HTTP_USER_AGENT = "CK HTTP Client/1.0";
+static const char *DEFAULT_HTTP_PROTOCOL = "HTTP/1.1";
+static const char *DEFAULT_HTTP_PATH = "/";
+static int DEFAULT_MAX_REDIRECT = 10;
+static int DEFAULT_TIMEOUT_MS = 5000;
+
+static const char *HTTP_METHOD_MAPPING[] = {
+    "GET",
+    "POST",
+    "PUT",
+    "PATCH",
+    "DELETE",
+    "HEAD",
+    "NOTIFY",
+    "SUBSCRIBE",
+    "UNSUBSCRIBE",
+    "OPTIONS"
+};
+
+static web_err_t http_client_request_send(http_client_handle_t client, int write_len);
+static web_err_t http_client_connect(http_client_handle_t client);
+static web_err_t http_client_send_post_data(http_client_handle_t client);
+
+static web_err_t http_dispatch_event(http_client_t *client, http_client_event_id_t event_id, void *data, int len)
+{
+    http_client_event_t *event = &client->event;
+
+    if (client->event_handler) {
+        event->event_id = event_id;
+        event->user_data = client->user_data;
+        event->data = data;
+        event->data_len = len;
+        return client->event_handler(event);
+    }
+    return WEB_OK;
+}
+
+static int http_on_message_begin(http_parser *parser)
+{
+    http_client_t *client = parser->data;
+    LOGD(TAG, "on_message_begin");
+
+    client->response->is_chunked = false;
+    client->is_chunk_complete = false;
+    return 0;
+}
+
+static int http_on_url(http_parser *parser, const char *at, size_t length)
+{
+    LOGD(TAG, "http_on_url");
+    return 0;
+}
+
+static int http_on_status(http_parser *parser, const char *at, size_t length)
+{
+    return 0;
+}
+
+static int http_on_header_field(http_parser *parser, const char *at, size_t length)
+{
+    http_client_t *client = parser->data;
+    http_utils_assign_string(&client->current_header_key, at, length);
+
+    return 0;
+}
+
+static int http_on_header_value(http_parser *parser, const char *at, size_t length)
+{
+    http_client_handle_t client = parser->data;
+    if (client->current_header_key == NULL) {
+        return 0;
+    }
+    if (strcasecmp(client->current_header_key, "Location") == 0) {
+        http_utils_assign_string(&client->location, at, length);
+    } else if (strcasecmp(client->current_header_key, "Transfer-Encoding") == 0
+               && memcmp(at, "chunked", length) == 0) {
+        client->response->is_chunked = true;
+    } else if (strcasecmp(client->current_header_key, "WWW-Authenticate") == 0) {
+        http_utils_assign_string(&client->auth_header, at, length);
+    }
+    http_utils_assign_string(&client->current_header_value, at, length);
+
+    LOGD(TAG, "HEADER=%s:%s", client->current_header_key, client->current_header_value);
+    client->event.header_key = client->current_header_key;
+    client->event.header_value = client->current_header_value;
+    http_dispatch_event(client, HTTP_EVENT_ON_HEADER, NULL, 0);
+    free(client->current_header_key);
+    free(client->current_header_value);
+    client->current_header_key = NULL;
+    client->current_header_value = NULL;
+    return 0;
+}
+
+static int http_on_headers_complete(http_parser *parser)
+{
+    http_client_handle_t client = parser->data;
+    client->response->status_code = parser->status_code;
+    client->response->data_offset = parser->nread;
+    client->response->content_length = parser->content_length;
+    client->response->data_process = 0;
+    LOGD(TAG, "http_on_headers_complete, status=%d, offset=%d, nread=%d", parser->status_code, client->response->data_offset, parser->nread);
+    client->state = HTTP_STATE_RES_COMPLETE_HEADER;
+    return 0;
+}
+
+static int http_on_body(http_parser *parser, const char *at, size_t length)
+{
+    http_client_t *client = parser->data;
+    // LOGD(TAG, "http_on_body %d", length);
+    client->response->buffer->raw_data = (char *)at;
+    if (client->response->buffer->output_ptr) {
+        memcpy(client->response->buffer->output_ptr, (char *)at, length);
+        client->response->buffer->output_ptr += length;
+    }
+
+    client->response->data_process += length;
+    client->response->buffer->raw_len += length;
+    http_dispatch_event(client, HTTP_EVENT_ON_DATA, (void *)at, length);
+    return 0;
+}
+
+static int http_on_message_complete(http_parser *parser)
+{
+    LOGD(TAG, "http_on_message_complete, parser=0x%lx", (unsigned long)parser);
+    http_client_handle_t client = parser->data;
+    client->is_chunk_complete = true;
+    return 0;
+}
+
+static int http_on_chunk_complete(http_parser *parser)
+{
+    LOGD(TAG, "http_on_chunk_complete");
+    return 0;
+}
+
+web_err_t http_client_set_header(http_client_handle_t client, const char *key, const char *value)
+{
+    return http_header_set(client->request->headers, key, value);
+}
+
+web_err_t http_client_get_header(http_client_handle_t client, const char *key, char **value)
+{
+    return http_header_get(client->request->headers, key, value);
+}
+
+web_err_t http_client_delete_header(http_client_handle_t client, const char *key)
+{
+    return http_header_delete(client->request->headers, key);
+}
+
+web_err_t http_client_get_username(http_client_handle_t client, char **value)
+{
+    if (client == NULL || value == NULL) {
+        LOGE(TAG, "client or value must not be NULL");
+        return WEB_ERR_INVALID_ARG;
+    }
+    *value = client->connection_info.username;
+    return WEB_OK;
+}
+
+web_err_t http_client_get_password(http_client_handle_t client, char **value)
+{
+    if (client == NULL || value == NULL) {
+        LOGE(TAG, "client or value must not be NULL");
+        return WEB_ERR_INVALID_ARG;
+    }
+    *value = client->connection_info.password;
+    return WEB_OK;
+}
+
+static web_err_t _set_config(http_client_handle_t client, const http_client_config_t *config)
+{
+    client->connection_info.method = config->method;
+    client->connection_info.port = config->port;
+    client->connection_info.auth_type = config->auth_type;
+    client->event_handler = config->event_handler;
+    client->timeout_ms = config->timeout_ms;
+    client->max_redirection_count = config->max_redirection_count;
+    client->user_data = config->user_data;
+    client->buffer_size = config->buffer_size;
+    client->disable_auto_redirect = config->disable_auto_redirect;
+
+    if (config->buffer_size == 0) {
+        client->buffer_size = DEFAULT_HTTP_BUF_SIZE;
+    }
+
+    if (client->max_redirection_count == 0) {
+        client->max_redirection_count = DEFAULT_MAX_REDIRECT;
+    }
+
+    if (config->path) {
+        client->connection_info.path = strdup(config->path);
+    } else {
+        client->connection_info.path = strdup(DEFAULT_HTTP_PATH);
+    }
+
+    HTTP_MEM_CHECK(TAG, client->connection_info.path, {
+        return WEB_ERR_NO_MEM;
+    });
+
+    if (config->host) {
+        client->connection_info.host = strdup(config->host);
+
+        HTTP_MEM_CHECK(TAG, client->connection_info.host, {
+            _clear_connection_info(client);
+            return WEB_ERR_NO_MEM;
+        });
+    }
+
+    if (config->query) {
+        client->connection_info.query = strdup(config->query);
+        HTTP_MEM_CHECK(TAG, client->connection_info.query, {
+            _clear_connection_info(client);
+            return WEB_ERR_NO_MEM;
+        });
+    }
+
+    if (config->username) {
+        client->connection_info.username = strdup(config->username);
+        HTTP_MEM_CHECK(TAG, client->connection_info.username, {
+            _clear_connection_info(client);
+            return WEB_ERR_NO_MEM;
+        });
+    }
+
+    if (config->password) {
+        client->connection_info.password = strdup(config->password);
+        HTTP_MEM_CHECK(TAG, client->connection_info.password, {
+            _clear_connection_info(client);
+            return WEB_ERR_NO_MEM;
+        });
+    }
+
+    if (config->transport_type == HTTP_TRANSPORT_OVER_SSL) {
+        http_utils_assign_string(&client->connection_info.scheme, "https", 0);
+        if (client->connection_info.port == 0) {
+            client->connection_info.port = DEFAULT_HTTPS_PORT;
+        }
+    } else {
+        http_utils_assign_string(&client->connection_info.scheme, "http", 0);
+        if (client->connection_info.port == 0) {
+            client->connection_info.port = DEFAULT_HTTP_PORT;
+        }
+    }
+    if (client->timeout_ms == 0) {
+        client->timeout_ms = DEFAULT_TIMEOUT_MS;
+    }
+    if (config->is_async) {
+        client->is_async = true;
+    }
+
+    return WEB_OK;
+}
+
+static web_err_t _clear_connection_info(http_client_handle_t client)
+{
+    free(client->connection_info.path);
+    free(client->connection_info.host);
+    free(client->connection_info.query);
+    free(client->connection_info.username);
+    if (client->connection_info.password) {
+        memset(client->connection_info.password, 0, strlen(client->connection_info.password));
+        free(client->connection_info.password);
+    }
+    free(client->connection_info.scheme);
+    free(client->connection_info.url);
+    memset(&client->connection_info, 0, sizeof(connection_info_t));
+    return WEB_OK;
+}
+
+static web_err_t _clear_auth_data(http_client_handle_t client)
+{
+    if (client->auth_data == NULL) {
+        return WEB_FAIL;
+    }
+
+    free(client->auth_data->method);
+    free(client->auth_data->realm);
+    free(client->auth_data->algorithm);
+    free(client->auth_data->qop);
+    free(client->auth_data->nonce);
+    free(client->auth_data->opaque);
+    memset(client->auth_data, 0, sizeof(http_auth_data_t));
+    return WEB_OK;
+}
+
+static web_err_t http_client_prepare(http_client_handle_t client)
+{
+    client->process_again = 0;
+    client->response->data_process = 0;
+    client->first_line_prepared = false;
+    http_parser_init(client->parser, HTTP_RESPONSE);
+    if (client->connection_info.username) {
+        char *auth_response = NULL;
+
+        if (client->connection_info.auth_type == HTTP_AUTH_TYPE_BASIC) {
+            auth_response = http_auth_basic(client->connection_info.username, client->connection_info.password);
+        }
+        else if (client->connection_info.auth_type == HTTP_AUTH_TYPE_DIGEST && client->auth_data) {
+            client->auth_data->uri = client->connection_info.path;
+            client->auth_data->cnonce = ((uint64_t)rand() << 32) + rand();
+            auth_response = http_auth_digest(client->connection_info.username, client->connection_info.password, client->auth_data);
+            client->auth_data->nc ++;
+        }
+
+        if (auth_response) {
+            LOGD(TAG, "auth_response=%s", auth_response);
+            http_client_set_header(client, "Authorization", auth_response);
+            free(auth_response);
+        }
+    }
+    return WEB_OK;
+}
+
+http_client_handle_t http_client_init(const http_client_config_t *config)
+{
+
+    http_client_handle_t client;
+    transport_handle_t tcp;
+    bool _success;
+
+    _success = (
+                   (client                         = calloc(1, sizeof(http_client_t)))           &&
+                   (client->parser                 = calloc(1, sizeof(struct http_parser)))          &&
+                   (client->parser_settings        = calloc(1, sizeof(struct http_parser_settings))) &&
+                   (client->auth_data              = calloc(1, sizeof(http_auth_data_t)))        &&
+                   (client->request                = calloc(1, sizeof(http_data_t)))             &&
+                   (client->request->headers       = http_header_init())                             &&
+                   (client->request->buffer        = calloc(1, sizeof(http_buffer_t)))           &&
+                   (client->response               = calloc(1, sizeof(http_data_t)))             &&
+                   (client->response->headers      = http_header_init())                             &&
+                   (client->response->buffer       = calloc(1, sizeof(http_buffer_t)))
+               );
+
+    if (!_success) {
+        LOGE(TAG, "Error allocate memory");
+        goto error;
+    }
+
+    _success = (
+                   (client->transport_list = transport_list_init()) &&
+                   (tcp = transport_tcp_init()) &&
+                   (transport_set_default_port(tcp, DEFAULT_HTTP_PORT) == WEB_OK) &&
+                   (transport_list_add(client->transport_list, tcp, "http") == WEB_OK)
+               );
+    if (!_success) {
+        LOGE(TAG, "Error initialize transport");
+        goto error;
+    }
+#ifdef CONFIG_USING_TLS
+    transport_handle_t ssl;
+    _success = (
+                   (ssl = transport_ssl_init()) &&
+                   (transport_set_default_port(ssl, DEFAULT_HTTPS_PORT) == WEB_OK) &&
+                   (transport_list_add(client->transport_list, ssl, "https") == WEB_OK)
+               );
+
+    if (!_success) {
+        LOGE(TAG, "Error initialize SSL Transport");
+        goto error;
+    }
+
+    if (config->use_global_ca_store == true) {
+        transport_ssl_enable_global_ca_store(ssl);
+    } else if (config->cert_pem) {
+        transport_ssl_set_cert_data(ssl, config->cert_pem, strlen(config->cert_pem));
+    }
+
+    if (config->client_cert_pem) {
+        transport_ssl_set_client_cert_data(ssl, config->client_cert_pem, strlen(config->client_cert_pem));
+    }
+
+    if (config->client_key_pem) {
+        transport_ssl_set_client_key_data(ssl, config->client_key_pem, strlen(config->client_key_pem));
+    }
+#endif
+
+    if (_set_config(client, config) != WEB_OK) {
+        LOGE(TAG, "Error set configurations");
+        goto error;
+    }
+    _success = (
+                   (client->request->buffer->data  = malloc(client->buffer_size))  &&
+                   (client->response->buffer->data = malloc(client->buffer_size))
+               );
+
+    if (!_success) {
+        LOGE(TAG, "Allocation failed");
+        goto error;
+    }
+
+    if (config->host != NULL && config->path != NULL) {
+        _success = (
+            (http_client_set_header(client, "User-Agent", DEFAULT_HTTP_USER_AGENT) == WEB_OK) &&
+            (http_client_set_header(client, "Host", client->connection_info.host) == WEB_OK)
+        );
+
+        if (!_success) {
+            LOGE(TAG, "Error while setting default configurations");
+            goto error;
+        }
+    } else if (config->url != NULL) {
+        _success = (
+                    (http_client_set_url(client, config->url) == WEB_OK) &&
+                    (http_client_set_header(client, "User-Agent", DEFAULT_HTTP_USER_AGENT) == WEB_OK) &&
+                    (http_client_set_header(client, "Host", client->connection_info.host) == WEB_OK)
+                );
+
+        if (!_success) {
+            LOGE(TAG, "Error while setting default configurations");
+            goto error;
+        }
+    } else {
+        LOGE(TAG, "config should have either URL or host & path");
+        goto error;
+    }
+
+    client->parser_settings->on_message_begin = http_on_message_begin;
+    client->parser_settings->on_url = http_on_url;
+    client->parser_settings->on_status = http_on_status;
+    client->parser_settings->on_header_field = http_on_header_field;
+    client->parser_settings->on_header_value = http_on_header_value;
+    client->parser_settings->on_headers_complete = http_on_headers_complete;
+    client->parser_settings->on_body = http_on_body;
+    client->parser_settings->on_message_complete = http_on_message_complete;
+    client->parser_settings->on_chunk_complete = http_on_chunk_complete;
+    client->parser->data = client;
+    client->event.client = client;
+
+    client->state = HTTP_STATE_INIT;
+    return client;
+error:
+    http_client_cleanup(client);
+    return NULL;
+}
+
+web_err_t http_client_cleanup(http_client_handle_t client)
+{
+    if (client == NULL) {
+        return WEB_FAIL;
+    }
+    http_client_close(client);
+    transport_list_destroy(client->transport_list);
+    http_header_destroy(client->request->headers);
+    free(client->request->buffer->data);
+    free(client->request->buffer);
+    free(client->request);
+    http_header_destroy(client->response->headers);
+    free(client->response->buffer->data);
+    free(client->response->buffer);
+    free(client->response);
+
+    free(client->parser);
+    free(client->parser_settings);
+    _clear_connection_info(client);
+    _clear_auth_data(client);
+    free(client->auth_data);
+    free(client->current_header_key);
+    free(client->location);
+    free(client->auth_header);
+    free(client);
+    return WEB_OK;
+}
+
+http_errors_t http_client_set_redirection(http_client_handle_t client)
+{
+    if (client == NULL) {
+        return HTTP_CLI_ERR_INVALID_ARG;
+    }
+    if (client->location == NULL) {
+        return HTTP_CLI_ERR_INVALID_ARG;
+    }
+    LOGD(TAG, "Redirect to %s", client->location);
+    return http_client_set_url(client, client->location);
+}
+
+static web_err_t http_check_response(http_client_handle_t client)
+{
+    char *auth_header = NULL;
+
+    if (client->redirect_counter >= client->max_redirection_count || client->disable_auto_redirect) {
+        LOGE(TAG, "Error, reach max_redirection_count count=%d", client->redirect_counter);
+        return ERR_HTTP_MAX_REDIRECT;
+    }
+    switch (client->response->status_code) {
+        case HttpStatus_MovedPermanently:
+        case HttpStatus_Found:
+        case HttpStatus_TemporaryRedirect:
+            LOGI(TAG, "Redirect to %s", client->location);
+            http_client_set_url(client, client->location);
+            client->redirect_counter ++;
+            client->process_again = 1;
+            break;
+        case HttpStatus_Unauthorized:
+            auth_header = client->auth_header;
+            if (auth_header) {
+                http_utils_trim_whitespace(&auth_header);
+                LOGD(TAG, "UNAUTHORIZED: %s", auth_header);
+                client->redirect_counter ++;
+                if (http_utils_str_starts_with(auth_header, "Digest") == 0) {
+                    LOGD(TAG, "type = Digest");
+                    client->connection_info.auth_type = HTTP_AUTH_TYPE_DIGEST;
+                } else if (http_utils_str_starts_with(auth_header, "Basic") == 0) {
+                    LOGD(TAG, "type = Basic");
+                    client->connection_info.auth_type = HTTP_AUTH_TYPE_BASIC;
+                } else {
+                    client->connection_info.auth_type = HTTP_AUTH_TYPE_NONE;
+                    LOGE(TAG, "This authentication method is not supported: %s", auth_header);
+                    break;
+                }
+
+                _clear_auth_data(client);
+
+                client->auth_data->method = strdup(HTTP_METHOD_MAPPING[client->connection_info.method]);
+
+                client->auth_data->nc = 1;
+                client->auth_data->realm = http_utils_get_string_between(auth_header, "realm=\"", "\"");
+                client->auth_data->algorithm = http_utils_get_string_between(auth_header, "algorithm=", ",");
+                client->auth_data->qop = http_utils_get_string_between(auth_header, "qop=\"", "\"");
+                client->auth_data->nonce = http_utils_get_string_between(auth_header, "nonce=\"", "\"");
+                client->auth_data->opaque = http_utils_get_string_between(auth_header, "opaque=\"", "\"");
+                client->process_again = 1;
+            } else {
+                client->connection_info.auth_type = HTTP_AUTH_TYPE_NONE;
+                LOGW(TAG, "This request requires authentication, but does not provide header information for that");
+            }
+    }
+    return WEB_OK;
+}
+
+web_err_t http_client_set_url(http_client_handle_t client, const char *url)
+{
+    char *old_host = NULL;
+    char *old_path = NULL;
+    struct http_parser_url purl;
+    int old_port;
+
+    if (client == NULL || url == NULL) {
+        LOGE(TAG, "client or url must not NULL");
+        return WEB_ERR_INVALID_ARG;
+    }
+
+    http_parser_url_init(&purl);
+
+    int parser_status = http_parser_parse_url(url, strlen(url), 0, &purl);
+
+    if (parser_status != 0) {
+        LOGE(TAG, "Error parse url %s", url);
+        return WEB_ERR_INVALID_ARG;
+    }
+    if (client->connection_info.host) {
+        old_host = strdup(client->connection_info.host);
+    }
+    old_port = client->connection_info.port;
+
+    // Whether the passed url is absolute or is just a path
+    bool is_absolute_url = (bool) purl.field_data[UF_HOST].len;
+
+    if (is_absolute_url) {
+        http_utils_assign_string(&client->connection_info.host, url + purl.field_data[UF_HOST].off, purl.field_data[UF_HOST].len);
+        HTTP_MEM_CHECK(TAG, client->connection_info.host, {
+            if (old_host) free(old_host);
+            return WEB_ERR_NO_MEM;
+        });
+    }
+    // Close the connection if host was changed
+    if (old_host && client->connection_info.host
+            && strcasecmp(old_host, (const void *)client->connection_info.host) != 0) {
+        LOGD(TAG, "New host assign = %s", client->connection_info.host);
+        if (http_client_set_header(client, "Host", client->connection_info.host) != WEB_OK) {
+            free(old_host);
+            return WEB_ERR_NO_MEM;
+        }
+        http_client_close(client);
+    }
+
+    if (old_host) {
+        free(old_host);
+        old_host = NULL;
+    }
+
+    if (purl.field_data[UF_SCHEMA].len) {
+        http_utils_assign_string(&client->connection_info.scheme, url + purl.field_data[UF_SCHEMA].off, purl.field_data[UF_SCHEMA].len);
+        HTTP_MEM_CHECK(TAG, client->connection_info.scheme, return WEB_ERR_NO_MEM);
+
+        if (strcasecmp(client->connection_info.scheme, "http") == 0) {
+            client->connection_info.port = DEFAULT_HTTP_PORT;
+        } else if (strcasecmp(client->connection_info.scheme, "https") == 0) {
+            client->connection_info.port = DEFAULT_HTTPS_PORT;
+        }
+    }
+
+    if (purl.field_data[UF_PORT].len) {
+        client->connection_info.port = strtol((const char*)(url + purl.field_data[UF_PORT].off), NULL, 10);
+    }
+
+    if (old_port != client->connection_info.port) {
+        http_client_close(client);
+    }
+
+    if (purl.field_data[UF_USERINFO].len) {
+        char *user_info = NULL;
+        http_utils_assign_string(&user_info, url + purl.field_data[UF_USERINFO].off, purl.field_data[UF_USERINFO].len);
+        if (user_info) {
+            char *username = user_info;
+            char *password = strchr(user_info, ':');
+            if (password) {
+                *password = 0;
+                password ++;
+                http_utils_assign_string(&client->connection_info.password, password, 0);
+                HTTP_MEM_CHECK(TAG, client->connection_info.password, return WEB_ERR_NO_MEM);
+            }
+            http_utils_assign_string(&client->connection_info.username, username, 0);
+            HTTP_MEM_CHECK(TAG, client->connection_info.username, return WEB_ERR_NO_MEM);
+            free(user_info);
+        } else {
+            return WEB_ERR_NO_MEM;
+        }
+    } else if (is_absolute_url) {
+        // Only reset authentication info if the passed URL is full
+        free(client->connection_info.username);
+        free(client->connection_info.password);
+        client->connection_info.username = NULL;
+        client->connection_info.password = NULL;
+    }
+
+    if (client->connection_info.path) {
+        old_path = strdup(client->connection_info.path);
+    }
+    //Reset path and query if there are no information
+    if (purl.field_data[UF_PATH].len) {
+        http_utils_assign_string(&client->connection_info.path, url + purl.field_data[UF_PATH].off, purl.field_data[UF_PATH].len);
+    } else {
+        http_utils_assign_string(&client->connection_info.path, "/", 0);
+    }
+    LOGD(TAG, "###path:%s", client->connection_info.path);
+    HTTP_MEM_CHECK(TAG, client->connection_info.path, {
+        if (old_path) free(old_path);
+        return WEB_ERR_NO_MEM;
+    });
+
+    // Close the connection if path was changed
+    if (old_path && client->connection_info.path
+            && strcasecmp(old_path, (const void *)client->connection_info.path) != 0) {
+        LOGD(TAG, "New path assign = %s", client->connection_info.path);
+        http_client_close(client);
+    }
+    if (old_path) {
+        free(old_path);
+        old_path = NULL;
+    }
+
+    if (purl.field_data[UF_QUERY].len) {
+        http_utils_assign_string(&client->connection_info.query, url + purl.field_data[UF_QUERY].off, purl.field_data[UF_QUERY].len);
+        HTTP_MEM_CHECK(TAG, client->connection_info.query, return WEB_ERR_NO_MEM);
+    } else if (client->connection_info.query) {
+        free(client->connection_info.query);
+        client->connection_info.query = NULL;
+    }
+
+    return WEB_OK;
+}
+
+web_err_t http_client_set_method(http_client_handle_t client, http_client_method_t method)
+{
+    client->connection_info.method = method;
+    return WEB_OK;
+}
+
+static int http_client_get_data(http_client_handle_t client)
+{
+    if (client->state < HTTP_STATE_RES_COMPLETE_HEADER) {
+        return WEB_FAIL;
+    }
+
+    if (client->connection_info.method == HTTP_METHOD_HEAD) {
+        return 0;
+    }
+
+    http_buffer_t *res_buffer = client->response->buffer;
+
+    LOGD(TAG, "data_process=%d, content_length=%d", client->response->data_process, client->response->content_length);
+
+    int rlen = transport_read(client->transport, res_buffer->data, client->buffer_size, client->timeout_ms);
+    if (rlen >= 0) {
+        http_parser_execute(client->parser, client->parser_settings, res_buffer->data, rlen);
+    }
+    return rlen;
+}
+
+bool http_client_is_complete_data_received(http_client_handle_t client)
+{
+    if (client->response->is_chunked) {
+        if (!client->is_chunk_complete) {
+            LOGD(TAG, "Chunks were not completely read");
+            return false;
+        }
+    } else {
+        if (client->response->data_process != client->response->content_length) {
+            LOGD(TAG, "Data processed %d != Data specified in content length %d", client->response->data_process, client->response->content_length);
+            return false;
+        }
+    }
+    return true;
+}
+
+int http_client_read(http_client_handle_t client, char *buffer, int len)
+{
+    http_buffer_t *res_buffer = client->response->buffer;
+
+    int rlen = WEB_FAIL, ridx = 0;
+    if (res_buffer->raw_len) {
+        int remain_len = client->response->buffer->raw_len;
+        if (remain_len > len) {
+            remain_len = len;
+        }
+        memcpy(buffer, res_buffer->raw_data, remain_len);
+        res_buffer->raw_len -= remain_len;
+        res_buffer->raw_data += remain_len;
+        ridx = remain_len;
+    }
+    int need_read = len - ridx;
+    bool is_data_remain = true;
+    while (need_read > 0 && is_data_remain) {
+        if (client->response->is_chunked) {
+            is_data_remain = !client->is_chunk_complete;
+        } else {
+            is_data_remain = client->response->data_process < client->response->content_length;
+        }
+        // LOGD(TAG, "is_data_remain=%d, is_chunked=%d", is_data_remain, client->response->is_chunked);
+        if (!is_data_remain) {
+            break;
+        }
+        int byte_to_read = need_read;
+        if (byte_to_read > client->buffer_size) {
+            byte_to_read = client->buffer_size;
+        }
+        errno = 0;
+        rlen = transport_read(client->transport, res_buffer->data, byte_to_read, client->timeout_ms);
+        // LOGD(TAG, "need_read=%d, byte_to_read=%d, rlen=%d, ridx=%d", need_read, byte_to_read, rlen, ridx);
+
+        if (rlen <= 0) {
+            if (errno != 0) {
+                /* On connection close from server, recv should ideally return 0 but we have error conversion
+                 * in `tcp_transport` SSL layer which translates it `-1` and hence below additional checks */
+                if (rlen == -1 && errno == ENOTCONN && client->response->is_chunked) {
+                    /* Explicit call to parser for invoking `message_complete` callback */
+                    http_parser_execute(client->parser, client->parser_settings, res_buffer->data, 0);
+                    /* ...and lowering the message severity, as closed connection from server side is expected in chunked transport */
+                }
+                LOGW(TAG, "transport_read returned:%d and errno:%d ", rlen, errno);
+            }
+            if (rlen < 0 && ridx == 0 && !http_client_is_complete_data_received(client)) {
+                return -1;
+            } else {
+                return ridx;
+            }
+        }
+        res_buffer->output_ptr = buffer + ridx;
+        http_parser_execute(client->parser, client->parser_settings, res_buffer->data, rlen);
+        ridx += res_buffer->raw_len;
+        need_read -= res_buffer->raw_len;
+
+        res_buffer->raw_len = 0; //clear
+        res_buffer->output_ptr = NULL;
+    }
+
+    return ridx;
+}
+
+web_err_t http_client_perform(http_client_handle_t client)
+{
+    web_err_t err;
+    do {
+        LOGD(TAG, "client->state: %d, client->process_again: %d", client->state, client->process_again);
+        if (client->process_again) {
+            http_client_prepare(client);
+        }
+        switch (client->state) {
+        /* In case of blocking http_client_perform(), the following states will fall through one after the after;
+           in case of non-blocking http_client_perform(), if there is an error condition, like EINPROGRESS or EAGAIN,
+           then the http_client_perform() API will return WEB_ERR_HTTP_EAGAIN error. The user may call
+           http_client_perform API again, and for this reason, we maintain the states */
+            case HTTP_STATE_INIT:
+                if ((err = http_client_connect(client)) != WEB_OK) {
+                    if (client->is_async && err == ERR_HTTP_CONNECTING) {
+                        return ERR_HTTP_EAGAIN;
+                    }
+                    return err;
+                }
+                LOGD(TAG, "HTTP_STATE_INIT");
+                /* falls through */
+            case HTTP_STATE_CONNECTED:
+                if ((err = http_client_request_send(client, client->post_len)) != WEB_OK) {
+                    if (client->is_async && errno == EAGAIN) {
+                        return ERR_HTTP_EAGAIN;
+                    }
+                    return err;
+                }
+                LOGD(TAG, "HTTP_STATE_CONNECTED");
+                /* falls through */
+            case HTTP_STATE_REQ_COMPLETE_HEADER:
+                if ((err = http_client_send_post_data(client)) != WEB_OK) {
+                    if (client->is_async && errno == EAGAIN) {
+                        return ERR_HTTP_EAGAIN;
+                    }
+                    return err;
+                }
+                LOGD(TAG, "HTTP_STATE_REQ_COMPLETE_HEADER");
+                /* falls through */
+            case HTTP_STATE_REQ_COMPLETE_DATA:
+                if (http_client_fetch_headers(client) < 0) {
+                    if (client->is_async && errno == EAGAIN) {
+                        return ERR_HTTP_EAGAIN;
+                    }
+                    return ERR_HTTP_FETCH_HEADER;
+                }
+                LOGD(TAG, "HTTP_STATE_REQ_COMPLETE_DATA");
+                /* falls through */
+            case HTTP_STATE_RES_COMPLETE_HEADER:
+                if ((err = http_check_response(client)) != WEB_OK) {
+                    LOGE(TAG, "Error response");
+                    return err;
+                }
+                while (client->response->is_chunked && !client->is_chunk_complete) {
+                    if (http_client_get_data(client) <= 0) {
+                        if (client->is_async && errno == EAGAIN) {
+                            return ERR_HTTP_EAGAIN;
+                        }
+                        LOGD(TAG, "Read finish or server requests close");
+                        break;
+                    }
+                }
+                while (client->response->data_process < client->response->content_length) {
+                    if (http_client_get_data(client) <= 0) {
+                        if (client->is_async && errno == EAGAIN) {
+                            return ERR_HTTP_EAGAIN;
+                        }
+                        LOGD(TAG, "Read finish or server requests close");
+                        break;
+                    }
+                }
+                http_dispatch_event(client, HTTP_EVENT_ON_FINISH, NULL, 0);
+
+                if (!http_should_keep_alive(client->parser)) {
+                    LOGD(TAG, "Close connection");
+                    http_client_close(client);
+                } else {
+                    if (client->state > HTTP_STATE_CONNECTED) {
+                        client->state = HTTP_STATE_CONNECTED;
+                        client->first_line_prepared = false;
+                    }
+                }
+                LOGD(TAG, "HTTP_STATE_RES_COMPLETE_HEADER");
+                break;
+                default:
+                break;
+        }
+    } while (client->process_again);
+    return WEB_OK;
+}
+
+int http_client_fetch_headers(http_client_handle_t client)
+{
+    if (client->state < HTTP_STATE_REQ_COMPLETE_HEADER) {
+        return WEB_FAIL;
+    }
+
+    client->state = HTTP_STATE_REQ_COMPLETE_DATA;
+    http_buffer_t *buffer = client->response->buffer;
+    client->response->status_code = -1;
+
+    while (client->state < HTTP_STATE_RES_COMPLETE_HEADER) {
+        buffer->len = transport_read(client->transport, buffer->data, client->buffer_size, client->timeout_ms);
+        if (buffer->len <= 0) {
+            return WEB_FAIL;
+        }
+        http_parser_execute(client->parser, client->parser_settings, buffer->data, buffer->len);
+    }
+    LOGD(TAG, "content_length = %d", client->response->content_length);
+    if (client->response->content_length <= 0) {
+        client->response->is_chunked = true;
+        return 0;
+    }
+    return client->response->content_length;
+}
+
+static web_err_t http_client_connect(http_client_handle_t client)
+{
+    web_err_t err;
+
+    if (client->state == HTTP_STATE_UNINIT) {
+        LOGE(TAG, "Client has not been initialized");
+        return WEB_ERR_INVALID_STATE;
+    }
+
+    if ((err = http_client_prepare(client)) != WEB_OK) {
+        LOGE(TAG, "Failed to initialize request data");
+        http_client_close(client);
+        return err;
+    }
+
+    if (client->state < HTTP_STATE_CONNECTED) {
+        LOGD(TAG, "Begin connect to: %s://%s:%d", client->connection_info.scheme, client->connection_info.host, client->connection_info.port);
+        client->transport = transport_list_get_transport(client->transport_list, client->connection_info.scheme);
+        if (client->transport == NULL) {
+            LOGE(TAG, "No transport found");
+#ifndef CONFIG_USING_TLS
+            if (strcasecmp(client->connection_info.scheme, "https") == 0) {
+                LOGE(TAG, "Please enable HTTPS at menuconfig to allow requesting via https");
+            }
+#endif
+            return ERR_HTTP_INVALID_TRANSPORT;
+        }
+        if (!client->is_async) {
+            if (transport_connect(client->transport, client->connection_info.host, client->connection_info.port, client->timeout_ms) < 0) {
+                LOGE(TAG, "Connection failed, sock < 0");
+                return ERR_HTTP_CONNECT;
+            }
+        } else {
+            int ret = transport_connect_async(client->transport, client->connection_info.host, client->connection_info.port, client->timeout_ms);
+            if (ret == ASYNC_TRANS_CONNECT_FAIL) {
+                LOGE(TAG, "Connection failed");
+                if (strcasecmp(client->connection_info.scheme, "http") == 0) {
+                    LOGE(TAG, "Asynchronous mode doesn't work for HTTP based connection");
+                    return WEB_ERR_INVALID_ARG;
+                }
+                return ERR_HTTP_CONNECT;
+            } else if (ret == ASYNC_TRANS_CONNECTING) {
+                LOGD(TAG, "Connection not yet established");
+                return ERR_HTTP_CONNECTING;
+            }
+        }
+        client->state = HTTP_STATE_CONNECTED;
+        http_dispatch_event(client, HTTP_EVENT_ON_CONNECTED, NULL, 0);
+    }
+    return WEB_OK;
+}
+
+static int http_client_prepare_first_line(http_client_handle_t client, int write_len)
+{
+    if (write_len >= 0) {
+        http_header_set_format(client->request->headers, "Content-Length", "%d", write_len);
+    } else {
+        http_client_set_header(client, "Transfer-Encoding", "chunked");
+        http_client_set_method(client, HTTP_METHOD_POST);
+    }
+
+    const char *method = HTTP_METHOD_MAPPING[client->connection_info.method];
+
+    int first_line_len = snprintf(client->request->buffer->data,
+                                  client->buffer_size, "%s %s",
+                                  method,
+                                  client->connection_info.path);
+    if (first_line_len >= client->buffer_size) {
+        LOGE(TAG, "Out of buffer");
+        return -1;
+    }
+
+    if (client->connection_info.query) {
+        first_line_len += snprintf(client->request->buffer->data + first_line_len,
+                                   client->buffer_size - first_line_len, "?%s", client->connection_info.query);
+        if (first_line_len >= client->buffer_size) {
+            LOGE(TAG, "Out of buffer");
+            return -1;
+
+        }
+    }
+    first_line_len += snprintf(client->request->buffer->data + first_line_len,
+                               client->buffer_size - first_line_len, " %s\r\n", DEFAULT_HTTP_PROTOCOL);
+    if (first_line_len >= client->buffer_size) {
+        LOGE(TAG, "Out of buffer");
+        return -1;
+    }
+    return first_line_len;
+}
+
+static web_err_t http_client_request_send(http_client_handle_t client, int write_len)
+{
+    int first_line_len = 0;
+    if (!client->first_line_prepared) {
+        if ((first_line_len = http_client_prepare_first_line(client, write_len)) < 0) {
+            return first_line_len;
+        }
+        client->first_line_prepared = true;
+        client->header_index = 0;
+        client->data_written_index = 0;
+        client->data_write_left = 0;
+    }
+
+    if (client->data_write_left > 0) {
+        /* sending leftover data from previous call to http_client_request_send() API */
+        int wret = 0;
+        if (((wret = http_client_write(client, client->request->buffer->data + client->data_written_index, client->data_write_left)) < 0)) {
+            LOGE(TAG, "Error write request");
+            return ERR_HTTP_WRITE_DATA;
+        }
+        client->data_write_left -= wret;
+        client->data_written_index += wret;
+        if (client->is_async && client->data_write_left > 0) {
+            return ERR_HTTP_WRITE_DATA;      /* In case of EAGAIN error, we return WEB_ERR_HTTP_WRITE_DATA,
+                                                 and the handling of EAGAIN should be done in the higher level APIs. */
+        }
+    }
+
+    int wlen = client->buffer_size - first_line_len;
+    while ((client->header_index = http_header_generate_string(client->request->headers, client->header_index, client->request->buffer->data + first_line_len, &wlen))) {
+        if (wlen <= 0) {
+            break;
+        }
+        if (first_line_len) {
+            wlen += first_line_len;
+            first_line_len = 0;
+        }
+        client->request->buffer->data[wlen] = 0;
+        LOGD(TAG, "Write header[%d]: %s", client->header_index, client->request->buffer->data);
+
+        client->data_write_left = wlen;
+        client->data_written_index = 0;
+        while (client->data_write_left > 0) {
+            int wret = transport_write(client->transport, client->request->buffer->data + client->data_written_index, client->data_write_left, client->timeout_ms);
+            if (wret <= 0) {
+                LOGE(TAG, "Error write request");
+                http_client_close(client);
+                return ERR_HTTP_WRITE_DATA;
+            }
+            client->data_write_left -= wret;
+            client->data_written_index += wret;
+        }
+        wlen = client->buffer_size;
+    }
+
+    client->data_written_index = 0;
+    client->data_write_left = client->post_len;
+    http_dispatch_event(client, HTTP_EVENT_HEADER_SENT, NULL, 0);
+    client->state = HTTP_STATE_REQ_COMPLETE_HEADER;
+    return WEB_OK;
+}
+
+static web_err_t http_client_send_post_data(http_client_handle_t client)
+{
+    if (client->state != HTTP_STATE_REQ_COMPLETE_HEADER) {
+        LOGE(TAG, "Invalid state");
+        return WEB_ERR_INVALID_STATE;
+    }
+    if (!(client->post_data && client->post_len)) {
+        goto success;
+    }
+
+    int wret = http_client_write(client, client->post_data + client->data_written_index, client->data_write_left);
+    if (wret < 0) {
+        return wret;
+    }
+    client->data_write_left -= wret;
+    client->data_written_index += wret;
+
+    if (client->data_write_left <= 0) {
+        goto success;
+    } else {
+        return ERR_HTTP_WRITE_DATA;
+    }
+
+success:
+    client->state = HTTP_STATE_REQ_COMPLETE_DATA;
+    return WEB_OK;
+}
+
+web_err_t http_client_open(http_client_handle_t client, int write_len)
+{
+    client->post_len = write_len;
+    web_err_t err;
+    if ((err = http_client_connect(client)) != WEB_OK) {
+        return err;
+    }
+    if ((err = http_client_request_send(client, write_len)) != WEB_OK) {
+        return err;
+    }
+    return WEB_OK;
+}
+
+int http_client_write(http_client_handle_t client, const char *buffer, int len)
+{
+    if (client->state < HTTP_STATE_REQ_COMPLETE_HEADER) {
+        return WEB_FAIL;
+    }
+
+    int wlen = 0, widx = 0;
+    while (len > 0) {
+        wlen = transport_write(client->transport, buffer + widx, len, client->timeout_ms);
+        /* client->async_block is initialised in case of non-blocking IO, and in this case we return how
+           much ever data was written by the transport_write() API. */
+        if (client->is_async || wlen <= 0) {
+            return wlen;
+        }
+        widx += wlen;
+        len -= wlen;
+    }
+    return widx;
+}
+
+web_err_t http_client_close(http_client_handle_t client)
+{
+    if (client->state >= HTTP_STATE_INIT) {
+        http_dispatch_event(client, HTTP_EVENT_DISCONNECTED, NULL, 0);
+        client->state = HTTP_STATE_INIT;
+        return transport_close(client->transport);
+    }
+    return WEB_OK;
+}
+
+web_err_t http_client_set_post_field(http_client_handle_t client, const char *data, int len)
+{
+    web_err_t err = WEB_OK;
+    client->post_data = (char *)data;
+    client->post_len = len;
+    LOGD(TAG, "set post file length = %d", len);
+    if (client->post_data) {
+        char *value = NULL;
+        if ((err = http_client_get_header(client, "Content-Type", &value)) != WEB_OK) {
+            return err;
+        }
+        if (value == NULL) {
+            err = http_client_set_header(client, "Content-Type", "application/x-www-form-urlencoded");
+        }
+    } else {
+        client->post_len = 0;
+        err = http_client_set_header(client, "Content-Type", NULL);
+    }
+    return err;
+}
+
+int http_client_get_post_field(http_client_handle_t client, char **data)
+{
+    if (client->post_data) {
+        *data = client->post_data;
+        return client->post_len;
+    }
+    return 0;
+}
+
+int http_client_get_status_code(http_client_handle_t client)
+{
+    return client->response->status_code;
+}
+
+int http_client_get_content_length(http_client_handle_t client)
+{
+    return client->response->content_length;
+}
+
+bool http_client_is_chunked_response(http_client_handle_t client)
+{
+    return client->response->is_chunked;
+}
+
+http_client_transport_t http_client_get_transport_type(http_client_handle_t client)
+{
+    if (!strcasecmp(client->connection_info.scheme, "https") ) {
+        return HTTP_TRANSPORT_OVER_SSL;
+    } else if (!strcasecmp(client->connection_info.scheme, "http")) {
+        return HTTP_TRANSPORT_OVER_TCP;
+    } else {
+        return HTTP_TRANSPORT_UNKNOWN;
+    }
+}
+
+int http_client_read_response(http_client_handle_t client, char *buffer, int len)
+{
+    int read_len = 0;
+    while (read_len < len) {
+        int data_read = http_client_read(client, buffer + read_len, len - read_len);
+        if (data_read <= 0) {
+            return read_len;
+        }
+        read_len += data_read;
+    }
+    return read_len;
+}

+ 242 - 0
components/httpclient/src/http_header.c

@@ -0,0 +1,242 @@
+// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "http_header.h"
+#include "http_utils.h"
+
+static const char *TAG = "HTTP_HEADER";
+#define HEADER_BUFFER (1024)
+
+/**
+ * dictionary item struct, with key-value pair
+ */
+typedef struct http_header_item {
+    char *key;                          /*!< key */
+    char *value;                        /*!< value */
+    STAILQ_ENTRY(http_header_item) next;   /*!< Point to next entry */
+} http_header_item_t;
+
+STAILQ_HEAD(http_header, http_header_item);
+
+http_header_handle_t http_header_init()
+{
+    http_header_handle_t header = calloc(1, sizeof(struct http_header));
+    HTTP_MEM_CHECK(TAG, header, return NULL);
+    STAILQ_INIT(header);
+    return header;
+}
+
+web_err_t http_header_destroy(http_header_handle_t header)
+{
+    web_err_t err = http_header_clean(header);
+    free(header);
+    return err;
+}
+
+http_header_item_handle_t http_header_get_item(http_header_handle_t header, const char *key)
+{
+    http_header_item_handle_t item;
+    if (header == NULL || key == NULL) {
+        return NULL;
+    }
+    STAILQ_FOREACH(item, header, next) {
+        if (strcasecmp(item->key, key) == 0) {
+            return item;
+        }
+    }
+    return NULL;
+}
+
+web_err_t http_header_get(http_header_handle_t header, const char *key, char **value)
+{
+    http_header_item_handle_t item;
+
+    item = http_header_get_item(header, key);
+    if (item) {
+        *value = item->value;
+    } else {
+        *value = NULL;
+    }
+
+    return WEB_OK;
+}
+
+static web_err_t http_header_new_item(http_header_handle_t header, const char *key, const char *value)
+{
+    http_header_item_handle_t item;
+
+    item = calloc(1, sizeof(http_header_item_t));
+    HTTP_MEM_CHECK(TAG, item, return WEB_ERR_NO_MEM);
+    http_utils_assign_string(&item->key, key, 0);
+    HTTP_MEM_CHECK(TAG, item->key, goto _header_new_item_exit);
+    http_utils_trim_whitespace(&item->key);
+    http_utils_assign_string(&item->value, value, 0);
+    HTTP_MEM_CHECK(TAG, item->value, goto _header_new_item_exit);
+    http_utils_trim_whitespace(&item->value);
+    STAILQ_INSERT_TAIL(header, item, next);
+    return WEB_OK;
+_header_new_item_exit:
+    if (item->key)
+        free(item->key);
+    if (item->value)
+        free(item->value);
+    free(item);
+    return WEB_ERR_NO_MEM;
+}
+
+web_err_t http_header_set(http_header_handle_t header, const char *key, const char *value)
+{
+    http_header_item_handle_t item;
+
+    if (value == NULL) {
+        return http_header_delete(header, key);
+    }
+
+    item = http_header_get_item(header, key);
+
+    if (item) {
+        free(item->value);
+        item->value = strdup(value);
+        http_utils_trim_whitespace(&item->value);
+        return WEB_OK;
+    }
+    return http_header_new_item(header, key, value);
+}
+
+web_err_t http_header_set_from_string(http_header_handle_t header, const char *key_value_data)
+{
+    char *eq_ch;
+    char *p_str;
+
+    p_str = strdup(key_value_data);
+    HTTP_MEM_CHECK(TAG, p_str, return WEB_ERR_NO_MEM);
+    eq_ch = strchr(p_str, ':');
+    if (eq_ch == NULL) {
+        free(p_str);
+        return WEB_ERR_INVALID_ARG;
+    }
+    *eq_ch = 0;
+
+    http_header_set(header, p_str, eq_ch + 1);
+    free(p_str);
+    return WEB_OK;
+}
+
+
+web_err_t http_header_delete(http_header_handle_t header, const char *key)
+{
+    http_header_item_handle_t item = http_header_get_item(header, key);
+    if (item) {
+        STAILQ_REMOVE(header, item, http_header_item, next);
+        free(item->key);
+        free(item->value);
+        free(item);
+    } else {
+        return WEB_ERR_NOT_FOUND;
+    }
+    return WEB_OK;
+}
+
+
+int http_header_set_format(http_header_handle_t header, const char *key, const char *format, ...)
+{
+    va_list argptr;
+    int len = 0;
+    char *buf = NULL;
+
+    va_start(argptr, format);
+    len = vasprintf(&buf, format, argptr);
+    va_end(argptr);
+    if (buf == NULL) {
+        return 0;
+    }
+    http_header_set(header, key, buf);
+    free(buf);
+    return len;
+}
+
+int http_header_generate_string(http_header_handle_t header, int index, char *buffer, int *buffer_len)
+{
+    http_header_item_handle_t item;
+    int siz = 0;
+    int idx = 0;
+    int ret_idx = -1;
+    bool is_end = false;
+    STAILQ_FOREACH(item, header, next) {
+        if (item->value && idx >= index) {
+            siz += strlen(item->key);
+            siz += strlen(item->value);
+            siz += 4; //': ' and '\r\n'
+        }
+        idx ++;
+
+        if (siz + 1 > *buffer_len - 2) {
+            ret_idx = idx - 1;
+        }
+    }
+
+    if (siz == 0) {
+        return 0;
+    }
+    if (ret_idx < 0) {
+        ret_idx = idx;
+        is_end = true;
+    }
+
+    int str_len = 0;
+    idx = 0;
+    STAILQ_FOREACH(item, header, next) {
+        if (item->value && idx >= index && idx < ret_idx) {
+            str_len += snprintf(buffer + str_len, *buffer_len - str_len, "%s: %s\r\n", item->key, item->value);
+        }
+        idx ++;
+    }
+    if (is_end) {
+        str_len += snprintf(buffer + str_len, *buffer_len - str_len, "\r\n");
+    }
+    *buffer_len = str_len;
+    return ret_idx;
+}
+
+web_err_t http_header_clean(http_header_handle_t header)
+{
+    http_header_item_handle_t item = STAILQ_FIRST(header), tmp;
+    while (item != NULL) {
+        tmp = STAILQ_NEXT(item, next);
+        free(item->key);
+        free(item->value);
+        free(item);
+        item = tmp;
+    }
+    STAILQ_INIT(header);
+    return WEB_OK;
+}
+
+int http_header_count(http_header_handle_t header)
+{
+    http_header_item_handle_t item;
+    int count = 0;
+    STAILQ_FOREACH(item, header, next) {
+        count ++;
+    }
+    return count;
+}

+ 2469 - 0
components/httpclient/src/http_parser.c

@@ -0,0 +1,2469 @@
+/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev
+ *
+ * Additional changes are licensed under the same terms as NGINX and
+ * copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+#include "http_parser.h"
+#include <assert.h>
+#include <stddef.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#ifndef ULLONG_MAX
+# define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */
+#endif
+
+#ifndef MIN
+# define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef ARRAY_SIZE
+# define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#endif
+
+#ifndef BIT_AT
+# define BIT_AT(a, i)                                                \
+  (!!((unsigned int) (a)[(unsigned int) (i) >> 3] &                  \
+   (1 << ((unsigned int) (i) & 7))))
+#endif
+
+#ifndef ELEM_AT
+# define ELEM_AT(a, i, v) ((unsigned int) (i) < ARRAY_SIZE(a) ? (a)[(i)] : (v))
+#endif
+
+#define SET_ERRNO(e)                                                 \
+do {                                                                 \
+  parser->http_errno = (e);                                          \
+} while(0)
+
+#define CURRENT_STATE() p_state
+#define UPDATE_STATE(V) p_state = (enum state) (V);
+#define RETURN(V)                                                    \
+do {                                                                 \
+  parser->state = CURRENT_STATE();                                   \
+  return (V);                                                        \
+} while (0);
+#define REEXECUTE()                                                  \
+  goto reexecute;                                                    \
+
+
+#ifdef __GNUC__
+# define LIKELY(X) __builtin_expect(!!(X), 1)
+# define UNLIKELY(X) __builtin_expect(!!(X), 0)
+#else
+# define LIKELY(X) (X)
+# define UNLIKELY(X) (X)
+#endif
+
+
+/* Run the notify callback FOR, returning ER if it fails */
+#define CALLBACK_NOTIFY_(FOR, ER)                                    \
+do {                                                                 \
+  assert(HTTP_PARSER_ERRNO(parser) == HPE_OK);                       \
+                                                                     \
+  if (LIKELY(settings->on_##FOR)) {                                  \
+    parser->state = CURRENT_STATE();                                 \
+    if (UNLIKELY(0 != settings->on_##FOR(parser))) {                 \
+      SET_ERRNO(HPE_CB_##FOR);                                       \
+    }                                                                \
+    UPDATE_STATE(parser->state);                                     \
+                                                                     \
+    /* We either errored above or got paused; get out */             \
+    if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) {             \
+      return (ER);                                                   \
+    }                                                                \
+  }                                                                  \
+} while (0)
+
+/* Run the notify callback FOR and consume the current byte */
+#define CALLBACK_NOTIFY(FOR)            CALLBACK_NOTIFY_(FOR, p - data + 1)
+
+/* Run the notify callback FOR and don't consume the current byte */
+#define CALLBACK_NOTIFY_NOADVANCE(FOR)  CALLBACK_NOTIFY_(FOR, p - data)
+
+/* Run data callback FOR with LEN bytes, returning ER if it fails */
+#define CALLBACK_DATA_(FOR, LEN, ER)                                 \
+do {                                                                 \
+  assert(HTTP_PARSER_ERRNO(parser) == HPE_OK);                       \
+                                                                     \
+  if (FOR##_mark) {                                                  \
+    if (LIKELY(settings->on_##FOR)) {                                \
+      parser->state = CURRENT_STATE();                               \
+      if (UNLIKELY(0 !=                                              \
+                   settings->on_##FOR(parser, FOR##_mark, (LEN)))) { \
+        SET_ERRNO(HPE_CB_##FOR);                                     \
+      }                                                              \
+      UPDATE_STATE(parser->state);                                   \
+                                                                     \
+      /* We either errored above or got paused; get out */           \
+      if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) {           \
+        return (ER);                                                 \
+      }                                                              \
+    }                                                                \
+    FOR##_mark = NULL;                                               \
+  }                                                                  \
+} while (0)
+
+/* Run the data callback FOR and consume the current byte */
+#define CALLBACK_DATA(FOR)                                           \
+    CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1)
+
+/* Run the data callback FOR and don't consume the current byte */
+#define CALLBACK_DATA_NOADVANCE(FOR)                                 \
+    CALLBACK_DATA_(FOR, p - FOR##_mark, p - data)
+
+/* Set the mark FOR; non-destructive if mark is already set */
+#define MARK(FOR)                                                    \
+do {                                                                 \
+  if (!FOR##_mark) {                                                 \
+    FOR##_mark = p;                                                  \
+  }                                                                  \
+} while (0)
+
+/* Don't allow the total size of the HTTP headers (including the status
+ * line) to exceed HTTP_MAX_HEADER_SIZE.  This check is here to protect
+ * embedders against denial-of-service attacks where the attacker feeds
+ * us a never-ending header that the embedder keeps buffering.
+ *
+ * This check is arguably the responsibility of embedders but we're doing
+ * it on the embedder's behalf because most won't bother and this way we
+ * make the web a little safer.  HTTP_MAX_HEADER_SIZE is still far bigger
+ * than any reasonable request or response so this should never affect
+ * day-to-day operation.
+ */
+#define COUNT_HEADER_SIZE(V)                                         \
+do {                                                                 \
+  parser->nread += (V);                                              \
+  if (UNLIKELY(parser->nread > (HTTP_MAX_HEADER_SIZE))) {            \
+    SET_ERRNO(HPE_HEADER_OVERFLOW);                                  \
+    goto error;                                                      \
+  }                                                                  \
+} while (0)
+
+
+#define PROXY_CONNECTION "proxy-connection"
+#define CONNECTION "connection"
+#define CONTENT_LENGTH "content-length"
+#define TRANSFER_ENCODING "transfer-encoding"
+#define UPGRADE "upgrade"
+#define CHUNKED "chunked"
+#define KEEP_ALIVE "keep-alive"
+#define CLOSE "close"
+
+
+static const char *method_strings[] =
+  {
+#define XX(num, name, string) #string,
+  HTTP_METHOD_MAP(XX)
+#undef XX
+  };
+
+
+/* Tokens as defined by rfc 2616. Also lowercases them.
+ *        token       = 1*<any CHAR except CTLs or separators>
+ *     separators     = "(" | ")" | "<" | ">" | "@"
+ *                    | "," | ";" | ":" | "\" | <">
+ *                    | "/" | "[" | "]" | "?" | "="
+ *                    | "{" | "}" | SP | HT
+ */
+static const char tokens[256] = {
+/*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */
+        0,       0,       0,       0,       0,       0,       0,       0,
+/*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */
+        0,       0,       0,       0,       0,       0,       0,       0,
+/*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */
+        0,       0,       0,       0,       0,       0,       0,       0,
+/*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */
+        0,       0,       0,       0,       0,       0,       0,       0,
+/*  32 sp    33  !    34  "    35  #    36  $    37  %    38  &    39  '  */
+        0,      '!',      0,      '#',     '$',     '%',     '&',    '\'',
+/*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */
+        0,       0,      '*',     '+',      0,      '-',     '.',      0,
+/*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */
+       '0',     '1',     '2',     '3',     '4',     '5',     '6',     '7',
+/*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */
+       '8',     '9',      0,       0,       0,       0,       0,       0,
+/*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */
+        0,      'a',     'b',     'c',     'd',     'e',     'f',     'g',
+/*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */
+       'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',
+/*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */
+       'p',     'q',     'r',     's',     't',     'u',     'v',     'w',
+/*  88  X    89  Y    90  Z    91  [    92  \    93  ]    94  ^    95  _  */
+       'x',     'y',     'z',      0,       0,       0,      '^',     '_',
+/*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */
+       '`',     'a',     'b',     'c',     'd',     'e',     'f',     'g',
+/* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */
+       'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',
+/* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */
+       'p',     'q',     'r',     's',     't',     'u',     'v',     'w',
+/* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */
+       'x',     'y',     'z',      0,      '|',      0,      '~',       0 };
+
+
+static const int8_t unhex[256] =
+  {-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
+  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+  , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1
+  ,-1,10,11,12,13,14,15,-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,10,11,12,13,14,15,-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
+  };
+
+
+#if HTTP_PARSER_STRICT
+# define T(v) 0
+#else
+# define T(v) v
+#endif
+
+
+static const uint8_t normal_url_char[32] = {
+/*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */
+        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,
+/*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */
+        0    | T(2)   |   0    |   0    | T(16)  |   0    |   0    |   0,
+/*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */
+        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,
+/*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */
+        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,
+/*  32 sp    33  !    34  "    35  #    36  $    37  %    38  &    39  '  */
+        0    |   2    |   4    |   0    |   16   |   32   |   64   |  128,
+/*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
+/*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
+/*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |   0,
+/*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
+/*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
+/*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
+/*  88  X    89  Y    90  Z    91  [    92  \    93  ]    94  ^    95  _  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
+/*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
+/* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
+/* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
+/* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |   0, };
+
+#undef T
+
+enum state
+  { s_dead = 1 /* important that this is > 0 */
+
+  , s_start_req_or_res
+  , s_res_or_resp_H
+  , s_start_res
+  , s_res_H
+  , s_res_HT
+  , s_res_HTT
+  , s_res_HTTP
+  , s_res_first_http_major
+  , s_res_http_major
+  , s_res_first_http_minor
+  , s_res_http_minor
+  , s_res_first_status_code
+  , s_res_status_code
+  , s_res_status_start
+  , s_res_status
+  , s_res_line_almost_done
+
+  , s_start_req
+
+  , s_req_method
+  , s_req_spaces_before_url
+  , s_req_schema
+  , s_req_schema_slash
+  , s_req_schema_slash_slash
+  , s_req_server_start
+  , s_req_server
+  , s_req_server_with_at
+  , s_req_path
+  , s_req_query_string_start
+  , s_req_query_string
+  , s_req_fragment_start
+  , s_req_fragment
+  , s_req_http_start
+  , s_req_http_H
+  , s_req_http_HT
+  , s_req_http_HTT
+  , s_req_http_HTTP
+  , s_req_first_http_major
+  , s_req_http_major
+  , s_req_first_http_minor
+  , s_req_http_minor
+  , s_req_line_almost_done
+
+  , s_header_field_start
+  , s_header_field
+  , s_header_value_discard_ws
+  , s_header_value_discard_ws_almost_done
+  , s_header_value_discard_lws
+  , s_header_value_start
+  , s_header_value
+  , s_header_value_lws
+
+  , s_header_almost_done
+
+  , s_chunk_size_start
+  , s_chunk_size
+  , s_chunk_parameters
+  , s_chunk_size_almost_done
+
+  , s_headers_almost_done
+  , s_headers_done
+
+  /* Important: 's_headers_done' must be the last 'header' state. All
+   * states beyond this must be 'body' states. It is used for overflow
+   * checking. See the PARSING_HEADER() macro.
+   */
+
+  , s_chunk_data
+  , s_chunk_data_almost_done
+  , s_chunk_data_done
+
+  , s_body_identity
+  , s_body_identity_eof
+
+  , s_message_done
+  };
+
+
+#define PARSING_HEADER(state) (state <= s_headers_done)
+
+
+enum header_states
+  { h_general = 0
+  , h_C
+  , h_CO
+  , h_CON
+
+  , h_matching_connection
+  , h_matching_proxy_connection
+  , h_matching_content_length
+  , h_matching_transfer_encoding
+  , h_matching_upgrade
+
+  , h_connection
+  , h_content_length
+  , h_transfer_encoding
+  , h_upgrade
+
+  , h_matching_transfer_encoding_chunked
+  , h_matching_connection_token_start
+  , h_matching_connection_keep_alive
+  , h_matching_connection_close
+  , h_matching_connection_upgrade
+  , h_matching_connection_token
+
+  , h_transfer_encoding_chunked
+  , h_connection_keep_alive
+  , h_connection_close
+  , h_connection_upgrade
+  };
+
+enum http_host_state
+  {
+    s_http_host_dead = 1
+  , s_http_userinfo_start
+  , s_http_userinfo
+  , s_http_host_start
+  , s_http_host_v6_start
+  , s_http_host
+  , s_http_host_v6
+  , s_http_host_v6_end
+  , s_http_host_v6_zone_start
+  , s_http_host_v6_zone
+  , s_http_host_port_start
+  , s_http_host_port
+};
+
+/* Macros for character classes; depends on strict-mode  */
+#define CR                  '\r'
+#define LF                  '\n'
+#define LOWER(c)            (unsigned char)(c | 0x20)
+#define IS_ALPHA(c)         (LOWER(c) >= 'a' && LOWER(c) <= 'z')
+#define IS_NUM(c)           ((c) >= '0' && (c) <= '9')
+#define IS_ALPHANUM(c)      (IS_ALPHA(c) || IS_NUM(c))
+#define IS_HEX(c)           (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f'))
+#define IS_MARK(c)          ((c) == '-' || (c) == '_' || (c) == '.' || \
+  (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \
+  (c) == ')')
+#define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \
+  (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \
+  (c) == '$' || (c) == ',')
+
+#define STRICT_TOKEN(c)     (tokens[(unsigned char)c])
+
+#if HTTP_PARSER_STRICT
+#define TOKEN(c)            (tokens[(unsigned char)c])
+#define IS_URL_CHAR(c)      (BIT_AT(normal_url_char, (unsigned char)c))
+#define IS_HOST_CHAR(c)     (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')
+#else
+#define TOKEN(c)            ((c == ' ') ? ' ' : tokens[(unsigned char)c])
+#define IS_URL_CHAR(c)                                                         \
+  (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80))
+#define IS_HOST_CHAR(c)                                                        \
+  (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')
+#endif
+
+/**
+ * Verify that a char is a valid visible (printable) US-ASCII
+ * character or %x80-FF
+ **/
+#define IS_HEADER_CHAR(ch)                                                     \
+  (ch == CR || ch == LF || ch == 9 || ((unsigned char)ch > 31 && ch != 127))
+
+#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res)
+
+
+#if HTTP_PARSER_STRICT
+# define STRICT_CHECK(cond)                                          \
+do {                                                                 \
+  if (cond) {                                                        \
+    SET_ERRNO(HPE_STRICT);                                           \
+    goto error;                                                      \
+  }                                                                  \
+} while (0)
+# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead)
+#else
+# define STRICT_CHECK(cond)
+# define NEW_MESSAGE() start_state
+#endif
+
+
+/* Map errno values to strings for human-readable output */
+#define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s },
+static struct {
+  const char *name;
+  const char *description;
+} http_strerror_tab[] = {
+  HTTP_ERRNO_MAP(HTTP_STRERROR_GEN)
+};
+#undef HTTP_STRERROR_GEN
+
+int http_message_needs_eof(const http_parser *parser);
+
+/* Our URL parser.
+ *
+ * This is designed to be shared by http_parser_execute() for URL validation,
+ * hence it has a state transition + byte-for-byte interface. In addition, it
+ * is meant to be embedded in http_parser_parse_url(), which does the dirty
+ * work of turning state transitions URL components for its API.
+ *
+ * This function should only be invoked with non-space characters. It is
+ * assumed that the caller cares about (and can detect) the transition between
+ * URL and non-URL states by looking for these.
+ */
+static enum state
+parse_url_char(enum state s, const char ch)
+{
+  if (ch == ' ' || ch == '\r' || ch == '\n') {
+    return s_dead;
+  }
+
+#if HTTP_PARSER_STRICT
+  if (ch == '\t' || ch == '\f') {
+    return s_dead;
+  }
+#endif
+
+  switch (s) {
+    case s_req_spaces_before_url:
+      /* Proxied requests are followed by scheme of an absolute URI (alpha).
+       * All methods except CONNECT are followed by '/' or '*'.
+       */
+
+      if (ch == '/' || ch == '*') {
+        return s_req_path;
+      }
+
+      if (IS_ALPHA(ch)) {
+        return s_req_schema;
+      }
+
+      break;
+
+    case s_req_schema:
+      if (IS_ALPHA(ch)) {
+        return s;
+      }
+
+      if (ch == ':') {
+        return s_req_schema_slash;
+      }
+
+      break;
+
+    case s_req_schema_slash:
+      if (ch == '/') {
+        return s_req_schema_slash_slash;
+      }
+
+      break;
+
+    case s_req_schema_slash_slash:
+      if (ch == '/') {
+        return s_req_server_start;
+      }
+
+      break;
+
+    case s_req_server_with_at:
+      if (ch == '@') {
+        return s_dead;
+      }
+
+    /* FALLTHROUGH */
+    case s_req_server_start:
+    case s_req_server:
+      if (ch == '/') {
+        return s_req_path;
+      }
+
+      if (ch == '?') {
+        return s_req_query_string_start;
+      }
+
+      if (ch == '@') {
+        return s_req_server_with_at;
+      }
+
+      if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') {
+        return s_req_server;
+      }
+
+      break;
+
+    case s_req_path:
+      if (IS_URL_CHAR(ch)) {
+        return s;
+      }
+
+      switch (ch) {
+        case '?':
+          return s_req_query_string_start;
+
+        case '#':
+          return s_req_fragment_start;
+      }
+
+      break;
+
+    case s_req_query_string_start:
+    case s_req_query_string:
+      if (IS_URL_CHAR(ch)) {
+        return s_req_query_string;
+      }
+
+      switch (ch) {
+        case '?':
+          /* allow extra '?' in query string */
+          return s_req_query_string;
+
+        case '#':
+          return s_req_fragment_start;
+      }
+
+      break;
+
+    case s_req_fragment_start:
+      if (IS_URL_CHAR(ch)) {
+        return s_req_fragment;
+      }
+
+      switch (ch) {
+        case '?':
+          return s_req_fragment;
+
+        case '#':
+          return s;
+      }
+
+      break;
+
+    case s_req_fragment:
+      if (IS_URL_CHAR(ch)) {
+        return s;
+      }
+
+      switch (ch) {
+        case '?':
+        case '#':
+          return s;
+      }
+
+      break;
+
+    default:
+      break;
+  }
+
+  /* We should never fall out of the switch above unless there's an error */
+  return s_dead;
+}
+
+size_t http_parser_execute (http_parser *parser,
+                            const http_parser_settings *settings,
+                            const char *data,
+                            size_t len)
+{
+  char c, ch;
+  int8_t unhex_val;
+  const char *p = data;
+  const char *header_field_mark = 0;
+  const char *header_value_mark = 0;
+  const char *url_mark = 0;
+  const char *body_mark = 0;
+  const char *status_mark = 0;
+  enum state p_state = (enum state) parser->state;
+  const unsigned int lenient = parser->lenient_http_headers;
+
+  /* We're in an error state. Don't bother doing anything. */
+  if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
+    return 0;
+  }
+
+  if (len == 0) {
+    switch (CURRENT_STATE()) {
+      case s_body_identity_eof:
+        /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if
+         * we got paused.
+         */
+        CALLBACK_NOTIFY_NOADVANCE(message_complete);
+        return 0;
+
+      case s_dead:
+      case s_start_req_or_res:
+      case s_start_res:
+      case s_start_req:
+        return 0;
+
+      default:
+        SET_ERRNO(HPE_INVALID_EOF_STATE);
+        return 1;
+    }
+  }
+
+
+  if (CURRENT_STATE() == s_header_field)
+    header_field_mark = data;
+  if (CURRENT_STATE() == s_header_value)
+    header_value_mark = data;
+  switch (CURRENT_STATE()) {
+  case s_req_path:
+  case s_req_schema:
+  case s_req_schema_slash:
+  case s_req_schema_slash_slash:
+  case s_req_server_start:
+  case s_req_server:
+  case s_req_server_with_at:
+  case s_req_query_string_start:
+  case s_req_query_string:
+  case s_req_fragment_start:
+  case s_req_fragment:
+    url_mark = data;
+    break;
+  case s_res_status:
+    status_mark = data;
+    break;
+  default:
+    break;
+  }
+
+  for (p=data; p != data + len; p++) {
+    ch = *p;
+
+    if (PARSING_HEADER(CURRENT_STATE()))
+      COUNT_HEADER_SIZE(1);
+
+reexecute:
+    switch (CURRENT_STATE()) {
+
+      case s_dead:
+        /* this state is used after a 'Connection: close' message
+         * the parser will error out if it reads another message
+         */
+        if (LIKELY(ch == CR || ch == LF))
+          break;
+
+        SET_ERRNO(HPE_CLOSED_CONNECTION);
+        goto error;
+
+      case s_start_req_or_res:
+      {
+        if (ch == CR || ch == LF)
+          break;
+        parser->flags = 0;
+        parser->content_length = ULLONG_MAX;
+
+        if (ch == 'H') {
+          UPDATE_STATE(s_res_or_resp_H);
+
+          CALLBACK_NOTIFY(message_begin);
+        } else {
+          parser->type = HTTP_REQUEST;
+          UPDATE_STATE(s_start_req);
+          REEXECUTE();
+        }
+
+        break;
+      }
+
+      case s_res_or_resp_H:
+        if (ch == 'T') {
+          parser->type = HTTP_RESPONSE;
+          UPDATE_STATE(s_res_HT);
+        } else {
+          if (UNLIKELY(ch != 'E')) {
+            SET_ERRNO(HPE_INVALID_CONSTANT);
+            goto error;
+          }
+
+          parser->type = HTTP_REQUEST;
+          parser->method = HTTP_HEAD;
+          parser->index = 2;
+          UPDATE_STATE(s_req_method);
+        }
+        break;
+
+      case s_start_res:
+      {
+        parser->flags = 0;
+        parser->content_length = ULLONG_MAX;
+
+        switch (ch) {
+          case 'H':
+            UPDATE_STATE(s_res_H);
+            break;
+
+          case CR:
+          case LF:
+            break;
+
+          default:
+            SET_ERRNO(HPE_INVALID_CONSTANT);
+            goto error;
+        }
+
+        CALLBACK_NOTIFY(message_begin);
+        break;
+      }
+
+      case s_res_H:
+        STRICT_CHECK(ch != 'T');
+        UPDATE_STATE(s_res_HT);
+        break;
+
+      case s_res_HT:
+        STRICT_CHECK(ch != 'T');
+        UPDATE_STATE(s_res_HTT);
+        break;
+
+      case s_res_HTT:
+        STRICT_CHECK(ch != 'P');
+        UPDATE_STATE(s_res_HTTP);
+        break;
+
+      case s_res_HTTP:
+        STRICT_CHECK(ch != '/');
+        UPDATE_STATE(s_res_first_http_major);
+        break;
+
+      case s_res_first_http_major:
+        if (UNLIKELY(ch < '0' || ch > '9')) {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        parser->http_major = ch - '0';
+        UPDATE_STATE(s_res_http_major);
+        break;
+
+      /* major HTTP version or dot */
+      case s_res_http_major:
+      {
+        if (ch == '.') {
+          UPDATE_STATE(s_res_first_http_minor);
+          break;
+        }
+
+        if (!IS_NUM(ch)) {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        parser->http_major *= 10;
+        parser->http_major += ch - '0';
+
+        if (UNLIKELY(parser->http_major > 999)) {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        break;
+      }
+
+      /* first digit of minor HTTP version */
+      case s_res_first_http_minor:
+        if (UNLIKELY(!IS_NUM(ch))) {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        parser->http_minor = ch - '0';
+        UPDATE_STATE(s_res_http_minor);
+        break;
+
+      /* minor HTTP version or end of request line */
+      case s_res_http_minor:
+      {
+        if (ch == ' ') {
+          UPDATE_STATE(s_res_first_status_code);
+          break;
+        }
+
+        if (UNLIKELY(!IS_NUM(ch))) {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        parser->http_minor *= 10;
+        parser->http_minor += ch - '0';
+
+        if (UNLIKELY(parser->http_minor > 999)) {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        break;
+      }
+
+      case s_res_first_status_code:
+      {
+        if (!IS_NUM(ch)) {
+          if (ch == ' ') {
+            break;
+          }
+
+          SET_ERRNO(HPE_INVALID_STATUS);
+          goto error;
+        }
+        parser->status_code = ch - '0';
+        UPDATE_STATE(s_res_status_code);
+        break;
+      }
+
+      case s_res_status_code:
+      {
+        if (!IS_NUM(ch)) {
+          switch (ch) {
+            case ' ':
+              UPDATE_STATE(s_res_status_start);
+              break;
+            case CR:
+              UPDATE_STATE(s_res_line_almost_done);
+              break;
+            case LF:
+              UPDATE_STATE(s_header_field_start);
+              break;
+            default:
+              SET_ERRNO(HPE_INVALID_STATUS);
+              goto error;
+          }
+          break;
+        }
+
+        parser->status_code *= 10;
+        parser->status_code += ch - '0';
+
+        if (UNLIKELY(parser->status_code > 999)) {
+          SET_ERRNO(HPE_INVALID_STATUS);
+          goto error;
+        }
+
+        break;
+      }
+
+      case s_res_status_start:
+      {
+        if (ch == CR) {
+          UPDATE_STATE(s_res_line_almost_done);
+          break;
+        }
+
+        if (ch == LF) {
+          UPDATE_STATE(s_header_field_start);
+          break;
+        }
+
+        MARK(status);
+        UPDATE_STATE(s_res_status);
+        parser->index = 0;
+        break;
+      }
+
+      case s_res_status:
+        if (ch == CR) {
+          UPDATE_STATE(s_res_line_almost_done);
+          CALLBACK_DATA(status);
+          break;
+        }
+
+        if (ch == LF) {
+          UPDATE_STATE(s_header_field_start);
+          CALLBACK_DATA(status);
+          break;
+        }
+
+        break;
+
+      case s_res_line_almost_done:
+        STRICT_CHECK(ch != LF);
+        UPDATE_STATE(s_header_field_start);
+        break;
+
+      case s_start_req:
+      {
+        if (ch == CR || ch == LF)
+          break;
+        parser->flags = 0;
+        parser->content_length = ULLONG_MAX;
+
+        if (UNLIKELY(!IS_ALPHA(ch))) {
+          SET_ERRNO(HPE_INVALID_METHOD);
+          goto error;
+        }
+
+        parser->method = (enum http_method) 0;
+        parser->index = 1;
+        switch (ch) {
+          case 'A': parser->method = HTTP_ACL; break;
+          case 'B': parser->method = HTTP_BIND; break;
+          case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break;
+          case 'D': parser->method = HTTP_DELETE; break;
+          case 'G': parser->method = HTTP_GET; break;
+          case 'H': parser->method = HTTP_HEAD; break;
+          case 'L': parser->method = HTTP_LOCK; /* or LINK */ break;
+          case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break;
+          case 'N': parser->method = HTTP_NOTIFY; break;
+          case 'O': parser->method = HTTP_OPTIONS; break;
+          case 'P': parser->method = HTTP_POST;
+            /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */
+            break;
+          case 'R': parser->method = HTTP_REPORT; /* or REBIND */ break;
+          case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break;
+          case 'T': parser->method = HTTP_TRACE; break;
+          case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE, UNBIND, UNLINK */ break;
+          default:
+            SET_ERRNO(HPE_INVALID_METHOD);
+            goto error;
+        }
+        UPDATE_STATE(s_req_method);
+
+        CALLBACK_NOTIFY(message_begin);
+
+        break;
+      }
+
+      case s_req_method:
+      {
+        const char *matcher;
+        if (UNLIKELY(ch == '\0')) {
+          SET_ERRNO(HPE_INVALID_METHOD);
+          goto error;
+        }
+
+        matcher = method_strings[parser->method];
+        if (ch == ' ' && matcher[parser->index] == '\0') {
+          UPDATE_STATE(s_req_spaces_before_url);
+        } else if (ch == matcher[parser->index]) {
+          ; /* nada */
+        } else if (IS_ALPHA(ch)) {
+
+          switch (parser->method << 16 | parser->index << 8 | ch) {
+#define XX(meth, pos, ch, new_meth) \
+            case (HTTP_##meth << 16 | pos << 8 | ch): \
+              parser->method = HTTP_##new_meth; break;
+
+            XX(POST,      1, 'U', PUT)
+            XX(POST,      1, 'A', PATCH)
+            XX(CONNECT,   1, 'H', CHECKOUT)
+            XX(CONNECT,   2, 'P', COPY)
+            XX(MKCOL,     1, 'O', MOVE)
+            XX(MKCOL,     1, 'E', MERGE)
+            XX(MKCOL,     2, 'A', MKACTIVITY)
+            XX(MKCOL,     3, 'A', MKCALENDAR)
+            XX(SUBSCRIBE, 1, 'E', SEARCH)
+            XX(REPORT,    2, 'B', REBIND)
+            XX(POST,      1, 'R', PROPFIND)
+            XX(PROPFIND,  4, 'P', PROPPATCH)
+            XX(PUT,       2, 'R', PURGE)
+            XX(LOCK,      1, 'I', LINK)
+            XX(UNLOCK,    2, 'S', UNSUBSCRIBE)
+            XX(UNLOCK,    2, 'B', UNBIND)
+            XX(UNLOCK,    3, 'I', UNLINK)
+#undef XX
+
+            default:
+              SET_ERRNO(HPE_INVALID_METHOD);
+              goto error;
+          }
+        } else if (ch == '-' &&
+                   parser->index == 1 &&
+                   parser->method == HTTP_MKCOL) {
+          parser->method = HTTP_MSEARCH;
+        } else {
+          SET_ERRNO(HPE_INVALID_METHOD);
+          goto error;
+        }
+
+        ++parser->index;
+        break;
+      }
+
+      case s_req_spaces_before_url:
+      {
+        if (ch == ' ') break;
+
+        MARK(url);
+        if (parser->method == HTTP_CONNECT) {
+          UPDATE_STATE(s_req_server_start);
+        }
+
+        UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
+        if (UNLIKELY(CURRENT_STATE() == s_dead)) {
+          SET_ERRNO(HPE_INVALID_URL);
+          goto error;
+        }
+
+        break;
+      }
+
+      case s_req_schema:
+      case s_req_schema_slash:
+      case s_req_schema_slash_slash:
+      case s_req_server_start:
+      {
+        switch (ch) {
+          /* No whitespace allowed here */
+          case ' ':
+          case CR:
+          case LF:
+            SET_ERRNO(HPE_INVALID_URL);
+            goto error;
+          default:
+            UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
+            if (UNLIKELY(CURRENT_STATE() == s_dead)) {
+              SET_ERRNO(HPE_INVALID_URL);
+              goto error;
+            }
+        }
+
+        break;
+      }
+
+      case s_req_server:
+      case s_req_server_with_at:
+      case s_req_path:
+      case s_req_query_string_start:
+      case s_req_query_string:
+      case s_req_fragment_start:
+      case s_req_fragment:
+      {
+        switch (ch) {
+          case ' ':
+            UPDATE_STATE(s_req_http_start);
+            CALLBACK_DATA(url);
+            break;
+          case CR:
+          case LF:
+            parser->http_major = 0;
+            parser->http_minor = 9;
+            UPDATE_STATE((ch == CR) ?
+              s_req_line_almost_done :
+              s_header_field_start);
+            CALLBACK_DATA(url);
+            break;
+          default:
+            UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
+            if (UNLIKELY(CURRENT_STATE() == s_dead)) {
+              SET_ERRNO(HPE_INVALID_URL);
+              goto error;
+            }
+        }
+        break;
+      }
+
+      case s_req_http_start:
+        switch (ch) {
+          case 'H':
+            UPDATE_STATE(s_req_http_H);
+            break;
+          case ' ':
+            break;
+          default:
+            SET_ERRNO(HPE_INVALID_CONSTANT);
+            goto error;
+        }
+        break;
+
+      case s_req_http_H:
+        STRICT_CHECK(ch != 'T');
+        UPDATE_STATE(s_req_http_HT);
+        break;
+
+      case s_req_http_HT:
+        STRICT_CHECK(ch != 'T');
+        UPDATE_STATE(s_req_http_HTT);
+        break;
+
+      case s_req_http_HTT:
+        STRICT_CHECK(ch != 'P');
+        UPDATE_STATE(s_req_http_HTTP);
+        break;
+
+      case s_req_http_HTTP:
+        STRICT_CHECK(ch != '/');
+        UPDATE_STATE(s_req_first_http_major);
+        break;
+
+      /* first digit of major HTTP version */
+      case s_req_first_http_major:
+        if (UNLIKELY(ch < '1' || ch > '9')) {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        parser->http_major = ch - '0';
+        UPDATE_STATE(s_req_http_major);
+        break;
+
+      /* major HTTP version or dot */
+      case s_req_http_major:
+      {
+        if (ch == '.') {
+          UPDATE_STATE(s_req_first_http_minor);
+          break;
+        }
+
+        if (UNLIKELY(!IS_NUM(ch))) {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        parser->http_major *= 10;
+        parser->http_major += ch - '0';
+
+        if (UNLIKELY(parser->http_major > 999)) {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        break;
+      }
+
+      /* first digit of minor HTTP version */
+      case s_req_first_http_minor:
+        if (UNLIKELY(!IS_NUM(ch))) {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        parser->http_minor = ch - '0';
+        UPDATE_STATE(s_req_http_minor);
+        break;
+
+      /* minor HTTP version or end of request line */
+      case s_req_http_minor:
+      {
+        if (ch == CR) {
+          UPDATE_STATE(s_req_line_almost_done);
+          break;
+        }
+
+        if (ch == LF) {
+          UPDATE_STATE(s_header_field_start);
+          break;
+        }
+
+        /* XXX allow spaces after digit? */
+
+        if (UNLIKELY(!IS_NUM(ch))) {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        parser->http_minor *= 10;
+        parser->http_minor += ch - '0';
+
+        if (UNLIKELY(parser->http_minor > 999)) {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        break;
+      }
+
+      /* end of request line */
+      case s_req_line_almost_done:
+      {
+        if (UNLIKELY(ch != LF)) {
+          SET_ERRNO(HPE_LF_EXPECTED);
+          goto error;
+        }
+
+        UPDATE_STATE(s_header_field_start);
+        break;
+      }
+
+      case s_header_field_start:
+      {
+        if (ch == CR) {
+          UPDATE_STATE(s_headers_almost_done);
+          break;
+        }
+
+        if (ch == LF) {
+          /* they might be just sending \n instead of \r\n so this would be
+           * the second \n to denote the end of headers*/
+          UPDATE_STATE(s_headers_almost_done);
+          REEXECUTE();
+        }
+
+        c = TOKEN(ch);
+
+        if (UNLIKELY(!c)) {
+          SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
+          goto error;
+        }
+
+        MARK(header_field);
+
+        parser->index = 0;
+        UPDATE_STATE(s_header_field);
+
+        switch (c) {
+          case 'c':
+            parser->header_state = h_C;
+            break;
+
+          case 'p':
+            parser->header_state = h_matching_proxy_connection;
+            break;
+
+          case 't':
+            parser->header_state = h_matching_transfer_encoding;
+            break;
+
+          case 'u':
+            parser->header_state = h_matching_upgrade;
+            break;
+
+          default:
+            parser->header_state = h_general;
+            break;
+        }
+        break;
+      }
+
+      case s_header_field:
+      {
+        const char* start = p;
+        for (; p != data + len; p++) {
+          ch = *p;
+          c = TOKEN(ch);
+
+          if (!c)
+            break;
+
+          switch (parser->header_state) {
+            case h_general:
+              break;
+
+            case h_C:
+              parser->index++;
+              parser->header_state = (c == 'o' ? h_CO : h_general);
+              break;
+
+            case h_CO:
+              parser->index++;
+              parser->header_state = (c == 'n' ? h_CON : h_general);
+              break;
+
+            case h_CON:
+              parser->index++;
+              switch (c) {
+                case 'n':
+                  parser->header_state = h_matching_connection;
+                  break;
+                case 't':
+                  parser->header_state = h_matching_content_length;
+                  break;
+                default:
+                  parser->header_state = h_general;
+                  break;
+              }
+              break;
+
+            /* connection */
+
+            case h_matching_connection:
+              parser->index++;
+              if (parser->index > sizeof(CONNECTION)-1
+                  || c != CONNECTION[parser->index]) {
+                parser->header_state = h_general;
+              } else if (parser->index == sizeof(CONNECTION)-2) {
+                parser->header_state = h_connection;
+              }
+              break;
+
+            /* proxy-connection */
+
+            case h_matching_proxy_connection:
+              parser->index++;
+              if (parser->index > sizeof(PROXY_CONNECTION)-1
+                  || c != PROXY_CONNECTION[parser->index]) {
+                parser->header_state = h_general;
+              } else if (parser->index == sizeof(PROXY_CONNECTION)-2) {
+                parser->header_state = h_connection;
+              }
+              break;
+
+            /* content-length */
+
+            case h_matching_content_length:
+              parser->index++;
+              if (parser->index > sizeof(CONTENT_LENGTH)-1
+                  || c != CONTENT_LENGTH[parser->index]) {
+                parser->header_state = h_general;
+              } else if (parser->index == sizeof(CONTENT_LENGTH)-2) {
+                if (parser->flags & F_CONTENTLENGTH) {
+                  SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
+                  goto error;
+                }
+                parser->header_state = h_content_length;
+                parser->flags |= F_CONTENTLENGTH;
+              }
+              break;
+
+            /* transfer-encoding */
+
+            case h_matching_transfer_encoding:
+              parser->index++;
+              if (parser->index > sizeof(TRANSFER_ENCODING)-1
+                  || c != TRANSFER_ENCODING[parser->index]) {
+                parser->header_state = h_general;
+              } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) {
+                parser->header_state = h_transfer_encoding;
+              }
+              break;
+
+            /* upgrade */
+
+            case h_matching_upgrade:
+              parser->index++;
+              if (parser->index > sizeof(UPGRADE)-1
+                  || c != UPGRADE[parser->index]) {
+                parser->header_state = h_general;
+              } else if (parser->index == sizeof(UPGRADE)-2) {
+                parser->header_state = h_upgrade;
+              }
+              break;
+
+            case h_connection:
+            case h_content_length:
+            case h_transfer_encoding:
+            case h_upgrade:
+              if (ch != ' ') parser->header_state = h_general;
+              break;
+
+            default:
+              assert(0 && "Unknown header_state");
+              break;
+          }
+        }
+
+        COUNT_HEADER_SIZE(p - start);
+
+        if (p == data + len) {
+          --p;
+          break;
+        }
+
+        if (ch == ':') {
+          UPDATE_STATE(s_header_value_discard_ws);
+          CALLBACK_DATA(header_field);
+          break;
+        }
+
+        SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
+        goto error;
+      }
+
+      case s_header_value_discard_ws:
+        if (ch == ' ' || ch == '\t') break;
+
+        if (ch == CR) {
+          UPDATE_STATE(s_header_value_discard_ws_almost_done);
+          break;
+        }
+
+        if (ch == LF) {
+          UPDATE_STATE(s_header_value_discard_lws);
+          break;
+        }
+
+        /* FALLTHROUGH */
+
+      case s_header_value_start:
+      {
+        MARK(header_value);
+
+        UPDATE_STATE(s_header_value);
+        parser->index = 0;
+
+        c = LOWER(ch);
+
+        switch (parser->header_state) {
+          case h_upgrade:
+            parser->flags |= F_UPGRADE;
+            parser->header_state = h_general;
+            break;
+
+          case h_transfer_encoding:
+            /* looking for 'Transfer-Encoding: chunked' */
+            if ('c' == c) {
+              parser->header_state = h_matching_transfer_encoding_chunked;
+            } else {
+              parser->header_state = h_general;
+            }
+            break;
+
+          case h_content_length:
+            if (UNLIKELY(!IS_NUM(ch))) {
+              SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+              goto error;
+            }
+
+            parser->content_length = ch - '0';
+            break;
+
+          case h_connection:
+            /* looking for 'Connection: keep-alive' */
+            if (c == 'k') {
+              parser->header_state = h_matching_connection_keep_alive;
+            /* looking for 'Connection: close' */
+            } else if (c == 'c') {
+              parser->header_state = h_matching_connection_close;
+            } else if (c == 'u') {
+              parser->header_state = h_matching_connection_upgrade;
+            } else {
+              parser->header_state = h_matching_connection_token;
+            }
+            break;
+
+          /* Multi-value `Connection` header */
+          case h_matching_connection_token_start:
+            break;
+
+          default:
+            parser->header_state = h_general;
+            break;
+        }
+        break;
+      }
+
+      case s_header_value:
+      {
+        const char* start = p;
+        enum header_states h_state = (enum header_states) parser->header_state;
+        for (; p != data + len; p++) {
+          ch = *p;
+          if (ch == CR) {
+            UPDATE_STATE(s_header_almost_done);
+            parser->header_state = h_state;
+            CALLBACK_DATA(header_value);
+            break;
+          }
+
+          if (ch == LF) {
+            UPDATE_STATE(s_header_almost_done);
+            COUNT_HEADER_SIZE(p - start);
+            parser->header_state = h_state;
+            CALLBACK_DATA_NOADVANCE(header_value);
+            REEXECUTE();
+          }
+
+          if (!lenient && !IS_HEADER_CHAR(ch)) {
+            SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
+            goto error;
+          }
+
+          c = LOWER(ch);
+
+          switch (h_state) {
+            case h_general:
+            {
+              const char* p_cr;
+              const char* p_lf;
+              size_t limit = data + len - p;
+
+              limit = MIN(limit, HTTP_MAX_HEADER_SIZE);
+
+              p_cr = (const char*) memchr(p, CR, limit);
+              p_lf = (const char*) memchr(p, LF, limit);
+              if (p_cr != NULL) {
+                if (p_lf != NULL && p_cr >= p_lf)
+                  p = p_lf;
+                else
+                  p = p_cr;
+              } else if (UNLIKELY(p_lf != NULL)) {
+                p = p_lf;
+              } else {
+                p = data + len;
+              }
+              --p;
+
+              break;
+            }
+
+            case h_connection:
+            case h_transfer_encoding:
+              assert(0 && "Shouldn't get here.");
+              break;
+
+            case h_content_length:
+            {
+              uint64_t t;
+
+              if (ch == ' ') break;
+
+              if (UNLIKELY(!IS_NUM(ch))) {
+                SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+                parser->header_state = h_state;
+                goto error;
+              }
+
+              t = parser->content_length;
+              t *= 10;
+              t += ch - '0';
+
+              /* Overflow? Test against a conservative limit for simplicity. */
+              if (UNLIKELY((ULLONG_MAX - 10) / 10 < parser->content_length)) {
+                SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+                parser->header_state = h_state;
+                goto error;
+              }
+
+              parser->content_length = t;
+              break;
+            }
+
+            /* Transfer-Encoding: chunked */
+            case h_matching_transfer_encoding_chunked:
+              parser->index++;
+              if (parser->index > sizeof(CHUNKED)-1
+                  || c != CHUNKED[parser->index]) {
+                h_state = h_general;
+              } else if (parser->index == sizeof(CHUNKED)-2) {
+                h_state = h_transfer_encoding_chunked;
+              }
+              break;
+
+            case h_matching_connection_token_start:
+              /* looking for 'Connection: keep-alive' */
+              if (c == 'k') {
+                h_state = h_matching_connection_keep_alive;
+              /* looking for 'Connection: close' */
+              } else if (c == 'c') {
+                h_state = h_matching_connection_close;
+              } else if (c == 'u') {
+                h_state = h_matching_connection_upgrade;
+              } else if (STRICT_TOKEN(c)) {
+                h_state = h_matching_connection_token;
+              } else if (c == ' ' || c == '\t') {
+                /* Skip lws */
+              } else {
+                h_state = h_general;
+              }
+              break;
+
+            /* looking for 'Connection: keep-alive' */
+            case h_matching_connection_keep_alive:
+              parser->index++;
+              if (parser->index > sizeof(KEEP_ALIVE)-1
+                  || c != KEEP_ALIVE[parser->index]) {
+                h_state = h_matching_connection_token;
+              } else if (parser->index == sizeof(KEEP_ALIVE)-2) {
+                h_state = h_connection_keep_alive;
+              }
+              break;
+
+            /* looking for 'Connection: close' */
+            case h_matching_connection_close:
+              parser->index++;
+              if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) {
+                h_state = h_matching_connection_token;
+              } else if (parser->index == sizeof(CLOSE)-2) {
+                h_state = h_connection_close;
+              }
+              break;
+
+            /* looking for 'Connection: upgrade' */
+            case h_matching_connection_upgrade:
+              parser->index++;
+              if (parser->index > sizeof(UPGRADE) - 1 ||
+                  c != UPGRADE[parser->index]) {
+                h_state = h_matching_connection_token;
+              } else if (parser->index == sizeof(UPGRADE)-2) {
+                h_state = h_connection_upgrade;
+              }
+              break;
+
+            case h_matching_connection_token:
+              if (ch == ',') {
+                h_state = h_matching_connection_token_start;
+                parser->index = 0;
+              }
+              break;
+
+            case h_transfer_encoding_chunked:
+              if (ch != ' ') h_state = h_general;
+              break;
+
+            case h_connection_keep_alive:
+            case h_connection_close:
+            case h_connection_upgrade:
+              if (ch == ',') {
+                if (h_state == h_connection_keep_alive) {
+                  parser->flags |= F_CONNECTION_KEEP_ALIVE;
+                } else if (h_state == h_connection_close) {
+                  parser->flags |= F_CONNECTION_CLOSE;
+                } else if (h_state == h_connection_upgrade) {
+                  parser->flags |= F_CONNECTION_UPGRADE;
+                }
+                h_state = h_matching_connection_token_start;
+                parser->index = 0;
+              } else if (ch != ' ') {
+                h_state = h_matching_connection_token;
+              }
+              break;
+
+            default:
+              UPDATE_STATE(s_header_value);
+              h_state = h_general;
+              break;
+          }
+        }
+        parser->header_state = h_state;
+
+        COUNT_HEADER_SIZE(p - start);
+
+        if (p == data + len)
+          --p;
+        break;
+      }
+
+      case s_header_almost_done:
+      {
+        if (UNLIKELY(ch != LF)) {
+          SET_ERRNO(HPE_LF_EXPECTED);
+          goto error;
+        }
+
+        UPDATE_STATE(s_header_value_lws);
+        break;
+      }
+
+      case s_header_value_lws:
+      {
+        if (ch == ' ' || ch == '\t') {
+          UPDATE_STATE(s_header_value_start);
+          REEXECUTE();
+        }
+
+        /* finished the header */
+        switch (parser->header_state) {
+          case h_connection_keep_alive:
+            parser->flags |= F_CONNECTION_KEEP_ALIVE;
+            break;
+          case h_connection_close:
+            parser->flags |= F_CONNECTION_CLOSE;
+            break;
+          case h_transfer_encoding_chunked:
+            parser->flags |= F_CHUNKED;
+            break;
+          case h_connection_upgrade:
+            parser->flags |= F_CONNECTION_UPGRADE;
+            break;
+          default:
+            break;
+        }
+
+        UPDATE_STATE(s_header_field_start);
+        REEXECUTE();
+      }
+
+      case s_header_value_discard_ws_almost_done:
+      {
+        STRICT_CHECK(ch != LF);
+        UPDATE_STATE(s_header_value_discard_lws);
+        break;
+      }
+
+      case s_header_value_discard_lws:
+      {
+        if (ch == ' ' || ch == '\t') {
+          UPDATE_STATE(s_header_value_discard_ws);
+          break;
+        } else {
+          switch (parser->header_state) {
+            case h_connection_keep_alive:
+              parser->flags |= F_CONNECTION_KEEP_ALIVE;
+              break;
+            case h_connection_close:
+              parser->flags |= F_CONNECTION_CLOSE;
+              break;
+            case h_connection_upgrade:
+              parser->flags |= F_CONNECTION_UPGRADE;
+              break;
+            case h_transfer_encoding_chunked:
+              parser->flags |= F_CHUNKED;
+              break;
+            default:
+              break;
+          }
+
+          /* header value was empty */
+          MARK(header_value);
+          UPDATE_STATE(s_header_field_start);
+          CALLBACK_DATA_NOADVANCE(header_value);
+          REEXECUTE();
+        }
+      }
+
+      case s_headers_almost_done:
+      {
+        STRICT_CHECK(ch != LF);
+
+        if (parser->flags & F_TRAILING) {
+          /* End of a chunked request */
+          UPDATE_STATE(s_message_done);
+          CALLBACK_NOTIFY_NOADVANCE(chunk_complete);
+          REEXECUTE();
+        }
+
+        /* Cannot use chunked encoding and a content-length header together
+           per the HTTP specification. */
+        if ((parser->flags & F_CHUNKED) &&
+            (parser->flags & F_CONTENTLENGTH)) {
+          SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
+          goto error;
+        }
+
+        UPDATE_STATE(s_headers_done);
+
+        /* Set this here so that on_headers_complete() callbacks can see it */
+        parser->upgrade =
+          ((parser->flags & (F_UPGRADE | F_CONNECTION_UPGRADE)) ==
+           (F_UPGRADE | F_CONNECTION_UPGRADE) ||
+           parser->method == HTTP_CONNECT);
+
+        /* Here we call the headers_complete callback. This is somewhat
+         * different than other callbacks because if the user returns 1, we
+         * will interpret that as saying that this message has no body. This
+         * is needed for the annoying case of recieving a response to a HEAD
+         * request.
+         *
+         * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so
+         * we have to simulate it by handling a change in errno below.
+         */
+        if (settings->on_headers_complete) {
+          switch (settings->on_headers_complete(parser)) {
+            case 0:
+              break;
+
+            case 2:
+              parser->upgrade = 1;
+              /* falls through */
+            case 1:
+              parser->flags |= F_SKIPBODY;
+              break;
+
+            default:
+              SET_ERRNO(HPE_CB_headers_complete);
+              RETURN(p - data); /* Error */
+          }
+        }
+
+        if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
+          RETURN(p - data);
+        }
+
+        REEXECUTE();
+      }
+
+      case s_headers_done:
+      {
+        int hasBody;
+        STRICT_CHECK(ch != LF);
+
+        parser->nread = 0;
+
+        hasBody = parser->flags & F_CHUNKED ||
+          (parser->content_length > 0 && parser->content_length != ULLONG_MAX);
+        if (parser->upgrade && (parser->method == HTTP_CONNECT ||
+                                (parser->flags & F_SKIPBODY) || !hasBody)) {
+          /* Exit, the rest of the message is in a different protocol. */
+          UPDATE_STATE(NEW_MESSAGE());
+          CALLBACK_NOTIFY(message_complete);
+          RETURN((p - data) + 1);
+        }
+
+        if (parser->flags & F_SKIPBODY) {
+          UPDATE_STATE(NEW_MESSAGE());
+          CALLBACK_NOTIFY(message_complete);
+        } else if (parser->flags & F_CHUNKED) {
+          /* chunked encoding - ignore Content-Length header */
+          UPDATE_STATE(s_chunk_size_start);
+        } else {
+          if (parser->content_length == 0) {
+            /* Content-Length header given but zero: Content-Length: 0\r\n */
+            UPDATE_STATE(NEW_MESSAGE());
+            CALLBACK_NOTIFY(message_complete);
+          } else if (parser->content_length != ULLONG_MAX) {
+            /* Content-Length header given and non-zero */
+            UPDATE_STATE(s_body_identity);
+          } else {
+            if (!http_message_needs_eof(parser)) {
+              /* Assume content-length 0 - read the next */
+              UPDATE_STATE(NEW_MESSAGE());
+              CALLBACK_NOTIFY(message_complete);
+            } else {
+              /* Read body until EOF */
+              UPDATE_STATE(s_body_identity_eof);
+            }
+          }
+        }
+
+        break;
+      }
+
+      case s_body_identity:
+      {
+        uint64_t to_read = MIN(parser->content_length,
+                               (uint64_t) ((data + len) - p));
+
+        assert(parser->content_length != 0
+            && parser->content_length != ULLONG_MAX);
+
+        /* The difference between advancing content_length and p is because
+         * the latter will automaticaly advance on the next loop iteration.
+         * Further, if content_length ends up at 0, we want to see the last
+         * byte again for our message complete callback.
+         */
+        MARK(body);
+        parser->content_length -= to_read;
+        p += to_read - 1;
+
+        if (parser->content_length == 0) {
+          UPDATE_STATE(s_message_done);
+
+          /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte.
+           *
+           * The alternative to doing this is to wait for the next byte to
+           * trigger the data callback, just as in every other case. The
+           * problem with this is that this makes it difficult for the test
+           * harness to distinguish between complete-on-EOF and
+           * complete-on-length. It's not clear that this distinction is
+           * important for applications, but let's keep it for now.
+           */
+          CALLBACK_DATA_(body, p - body_mark + 1, p - data);
+          REEXECUTE();
+        }
+
+        break;
+      }
+
+      /* read until EOF */
+      case s_body_identity_eof:
+        MARK(body);
+        p = data + len - 1;
+
+        break;
+
+      case s_message_done:
+        UPDATE_STATE(NEW_MESSAGE());
+        CALLBACK_NOTIFY(message_complete);
+        if (parser->upgrade) {
+          /* Exit, the rest of the message is in a different protocol. */
+          RETURN((p - data) + 1);
+        }
+        break;
+
+      case s_chunk_size_start:
+      {
+        assert(parser->nread == 1);
+        assert(parser->flags & F_CHUNKED);
+
+        unhex_val = unhex[(unsigned char)ch];
+        if (UNLIKELY(unhex_val == -1)) {
+          SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
+          goto error;
+        }
+
+        parser->content_length = unhex_val;
+        UPDATE_STATE(s_chunk_size);
+        break;
+      }
+
+      case s_chunk_size:
+      {
+        uint64_t t;
+
+        assert(parser->flags & F_CHUNKED);
+
+        if (ch == CR) {
+          UPDATE_STATE(s_chunk_size_almost_done);
+          break;
+        }
+
+        unhex_val = unhex[(unsigned char)ch];
+
+        if (unhex_val == -1) {
+          if (ch == ';' || ch == ' ') {
+            UPDATE_STATE(s_chunk_parameters);
+            break;
+          }
+
+          SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
+          goto error;
+        }
+
+        t = parser->content_length;
+        t *= 16;
+        t += unhex_val;
+
+        /* Overflow? Test against a conservative limit for simplicity. */
+        if (UNLIKELY((ULLONG_MAX - 16) / 16 < parser->content_length)) {
+          SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+          goto error;
+        }
+
+        parser->content_length = t;
+        break;
+      }
+
+      case s_chunk_parameters:
+      {
+        assert(parser->flags & F_CHUNKED);
+        /* just ignore this shit. TODO check for overflow */
+        if (ch == CR) {
+          UPDATE_STATE(s_chunk_size_almost_done);
+          break;
+        }
+        break;
+      }
+
+      case s_chunk_size_almost_done:
+      {
+        assert(parser->flags & F_CHUNKED);
+        STRICT_CHECK(ch != LF);
+
+        parser->nread = 0;
+
+        if (parser->content_length == 0) {
+          parser->flags |= F_TRAILING;
+          UPDATE_STATE(s_header_field_start);
+        } else {
+          UPDATE_STATE(s_chunk_data);
+        }
+        CALLBACK_NOTIFY(chunk_header);
+        break;
+      }
+
+      case s_chunk_data:
+      {
+        uint64_t to_read = MIN(parser->content_length,
+                               (uint64_t) ((data + len) - p));
+
+        assert(parser->flags & F_CHUNKED);
+        assert(parser->content_length != 0
+            && parser->content_length != ULLONG_MAX);
+
+        /* See the explanation in s_body_identity for why the content
+         * length and data pointers are managed this way.
+         */
+        MARK(body);
+        parser->content_length -= to_read;
+        p += to_read - 1;
+
+        if (parser->content_length == 0) {
+          UPDATE_STATE(s_chunk_data_almost_done);
+        }
+
+        break;
+      }
+
+      case s_chunk_data_almost_done:
+        assert(parser->flags & F_CHUNKED);
+        assert(parser->content_length == 0);
+        STRICT_CHECK(ch != CR);
+        UPDATE_STATE(s_chunk_data_done);
+        CALLBACK_DATA(body);
+        break;
+
+      case s_chunk_data_done:
+        assert(parser->flags & F_CHUNKED);
+        STRICT_CHECK(ch != LF);
+        parser->nread = 0;
+        UPDATE_STATE(s_chunk_size_start);
+        CALLBACK_NOTIFY(chunk_complete);
+        break;
+
+      default:
+        assert(0 && "unhandled state");
+        SET_ERRNO(HPE_INVALID_INTERNAL_STATE);
+        goto error;
+    }
+  }
+
+  /* Run callbacks for any marks that we have leftover after we ran our of
+   * bytes. There should be at most one of these set, so it's OK to invoke
+   * them in series (unset marks will not result in callbacks).
+   *
+   * We use the NOADVANCE() variety of callbacks here because 'p' has already
+   * overflowed 'data' and this allows us to correct for the off-by-one that
+   * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p'
+   * value that's in-bounds).
+   */
+
+  assert(((header_field_mark ? 1 : 0) +
+          (header_value_mark ? 1 : 0) +
+          (url_mark ? 1 : 0)  +
+          (body_mark ? 1 : 0) +
+          (status_mark ? 1 : 0)) <= 1);
+
+  CALLBACK_DATA_NOADVANCE(header_field);
+  CALLBACK_DATA_NOADVANCE(header_value);
+  CALLBACK_DATA_NOADVANCE(url);
+  CALLBACK_DATA_NOADVANCE(body);
+  CALLBACK_DATA_NOADVANCE(status);
+
+  RETURN(len);
+
+error:
+  if (HTTP_PARSER_ERRNO(parser) == HPE_OK) {
+    SET_ERRNO(HPE_UNKNOWN);
+  }
+
+  RETURN(p - data);
+}
+
+
+/* Does the parser need to see an EOF to find the end of the message? */
+int
+http_message_needs_eof (const http_parser *parser)
+{
+  if (parser->type == HTTP_REQUEST) {
+    return 0;
+  }
+
+  /* See RFC 2616 section 4.4 */
+  if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */
+      parser->status_code == 204 ||     /* No Content */
+      parser->status_code == 304 ||     /* Not Modified */
+      parser->flags & F_SKIPBODY) {     /* response to a HEAD request */
+    return 0;
+  }
+
+  if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) {
+    return 0;
+  }
+
+  return 1;
+}
+
+
+int
+http_should_keep_alive (const http_parser *parser)
+{
+  if (parser->http_major > 0 && parser->http_minor > 0) {
+    /* HTTP/1.1 */
+    if (parser->flags & F_CONNECTION_CLOSE) {
+      return 0;
+    }
+  } else {
+    /* HTTP/1.0 or earlier */
+    if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) {
+      return 0;
+    }
+  }
+
+  return !http_message_needs_eof(parser);
+}
+
+
+const char *
+http_method_str (enum http_method m)
+{
+  return ELEM_AT(method_strings, m, "<unknown>");
+}
+
+
+void
+http_parser_init (http_parser *parser, enum http_parser_type t)
+{
+  void *data = parser->data; /* preserve application data */
+  memset(parser, 0, sizeof(*parser));
+  parser->data = data;
+  parser->type = t;
+  parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res));
+  parser->http_errno = HPE_OK;
+}
+
+void
+http_parser_settings_init(http_parser_settings *settings)
+{
+  memset(settings, 0, sizeof(*settings));
+}
+
+const char *
+http_errno_name(enum http_errno err) {
+  assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab));
+  return http_strerror_tab[err].name;
+}
+
+const char *
+http_errno_description(enum http_errno err) {
+  assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab));
+  return http_strerror_tab[err].description;
+}
+
+static enum http_host_state
+http_parse_host_char(enum http_host_state s, const char ch) {
+  switch(s) {
+    case s_http_userinfo:
+    case s_http_userinfo_start:
+      if (ch == '@') {
+        return s_http_host_start;
+      }
+
+      if (IS_USERINFO_CHAR(ch)) {
+        return s_http_userinfo;
+      }
+      break;
+
+    case s_http_host_start:
+      if (ch == '[') {
+        return s_http_host_v6_start;
+      }
+
+      if (IS_HOST_CHAR(ch)) {
+        return s_http_host;
+      }
+
+      break;
+
+    case s_http_host:
+      if (IS_HOST_CHAR(ch)) {
+        return s_http_host;
+      }
+
+    /* FALLTHROUGH */
+    case s_http_host_v6_end:
+      if (ch == ':') {
+        return s_http_host_port_start;
+      }
+
+      break;
+
+    case s_http_host_v6:
+      if (ch == ']') {
+        return s_http_host_v6_end;
+      }
+
+    /* FALLTHROUGH */
+    case s_http_host_v6_start:
+      if (IS_HEX(ch) || ch == ':' || ch == '.') {
+        return s_http_host_v6;
+      }
+
+      if (s == s_http_host_v6 && ch == '%') {
+        return s_http_host_v6_zone_start;
+      }
+      break;
+
+    case s_http_host_v6_zone:
+      if (ch == ']') {
+        return s_http_host_v6_end;
+      }
+
+    /* FALLTHROUGH */
+    case s_http_host_v6_zone_start:
+      /* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */
+      if (IS_ALPHANUM(ch) || ch == '%' || ch == '.' || ch == '-' || ch == '_' ||
+          ch == '~') {
+        return s_http_host_v6_zone;
+      }
+      break;
+
+    case s_http_host_port:
+    case s_http_host_port_start:
+      if (IS_NUM(ch)) {
+        return s_http_host_port;
+      }
+
+      break;
+
+    default:
+      break;
+  }
+  return s_http_host_dead;
+}
+
+static int
+http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
+  enum http_host_state s;
+
+  const char *p;
+  size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len;
+
+  assert(u->field_set & (1 << UF_HOST));
+
+  u->field_data[UF_HOST].len = 0;
+
+  s = found_at ? s_http_userinfo_start : s_http_host_start;
+
+  for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) {
+    enum http_host_state new_s = http_parse_host_char(s, *p);
+
+    if (new_s == s_http_host_dead) {
+      return 1;
+    }
+
+    switch(new_s) {
+      case s_http_host:
+        if (s != s_http_host) {
+          u->field_data[UF_HOST].off = p - buf;
+        }
+        u->field_data[UF_HOST].len++;
+        break;
+
+      case s_http_host_v6:
+        if (s != s_http_host_v6) {
+          u->field_data[UF_HOST].off = p - buf;
+        }
+        u->field_data[UF_HOST].len++;
+        break;
+
+      case s_http_host_v6_zone_start:
+      case s_http_host_v6_zone:
+        u->field_data[UF_HOST].len++;
+        break;
+
+      case s_http_host_port:
+        if (s != s_http_host_port) {
+          u->field_data[UF_PORT].off = p - buf;
+          u->field_data[UF_PORT].len = 0;
+          u->field_set |= (1 << UF_PORT);
+        }
+        u->field_data[UF_PORT].len++;
+        break;
+
+      case s_http_userinfo:
+        if (s != s_http_userinfo) {
+          u->field_data[UF_USERINFO].off = p - buf ;
+          u->field_data[UF_USERINFO].len = 0;
+          u->field_set |= (1 << UF_USERINFO);
+        }
+        u->field_data[UF_USERINFO].len++;
+        break;
+
+      default:
+        break;
+    }
+    s = new_s;
+  }
+
+  /* Make sure we don't end somewhere unexpected */
+  switch (s) {
+    case s_http_host_start:
+    case s_http_host_v6_start:
+    case s_http_host_v6:
+    case s_http_host_v6_zone_start:
+    case s_http_host_v6_zone:
+    case s_http_host_port_start:
+    case s_http_userinfo:
+    case s_http_userinfo_start:
+      return 1;
+    default:
+      break;
+  }
+
+  return 0;
+}
+
+void
+http_parser_url_init(struct http_parser_url *u) {
+  memset(u, 0, sizeof(*u));
+}
+
+int
+http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
+                      struct http_parser_url *u)
+{
+  enum state s;
+  const char *p;
+  enum http_parser_url_fields uf, old_uf;
+  int found_at = 0;
+
+  u->port = u->field_set = 0;
+  s = is_connect ? s_req_server_start : s_req_spaces_before_url;
+  old_uf = UF_MAX;
+
+  for (p = buf; p < buf + buflen; p++) {
+    s = parse_url_char(s, *p);
+
+    /* Figure out the next field that we're operating on */
+    switch (s) {
+      case s_dead:
+        return 1;
+
+      /* Skip delimeters */
+      case s_req_schema_slash:
+      case s_req_schema_slash_slash:
+      case s_req_server_start:
+      case s_req_query_string_start:
+      case s_req_fragment_start:
+        continue;
+
+      case s_req_schema:
+        uf = UF_SCHEMA;
+        break;
+
+      case s_req_server_with_at:
+        found_at = 1;
+
+        /* falls through */
+      case s_req_server:
+        uf = UF_HOST;
+        break;
+
+      case s_req_path:
+        uf = UF_PATH;
+        break;
+
+      case s_req_query_string:
+        uf = UF_QUERY;
+        break;
+
+      case s_req_fragment:
+        uf = UF_FRAGMENT;
+        break;
+
+      default:
+        assert(!"Unexpected state");
+        return 1;
+    }
+
+    /* Nothing's changed; soldier on */
+    if (uf == old_uf) {
+      u->field_data[uf].len++;
+      continue;
+    }
+
+    u->field_data[uf].off = p - buf;
+    u->field_data[uf].len = 1;
+
+    u->field_set |= (1 << uf);
+    old_uf = uf;
+  }
+
+  /* host must be present if there is a schema */
+  /* parsing http:///toto will fail */
+  if ((u->field_set & (1 << UF_SCHEMA)) &&
+      (u->field_set & (1 << UF_HOST)) == 0) {
+    return 1;
+  }
+
+  if (u->field_set & (1 << UF_HOST)) {
+    if (http_parse_host(buf, u, found_at) != 0) {
+      return 1;
+    }
+  }
+
+  /* CONNECT requests can only contain "hostname:port" */
+  if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) {
+    return 1;
+  }
+
+  if (u->field_set & (1 << UF_PORT)) {
+    /* Don't bother with endp; we've already validated the string */
+    unsigned long v = strtoul(buf + u->field_data[UF_PORT].off, NULL, 10);
+
+    /* Ports have a max value of 2^16 */
+    if (v > 0xffff) {
+      return 1;
+    }
+
+    u->port = (uint16_t) v;
+  }
+
+  return 0;
+}
+
+void
+http_parser_pause(http_parser *parser, int paused) {
+  /* Users should only be pausing/unpausing a parser that is not in an error
+   * state. In non-debug builds, there's not much that we can do about this
+   * other than ignore it.
+   */
+  if (HTTP_PARSER_ERRNO(parser) == HPE_OK ||
+      HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) {
+    SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK);
+  } else {
+    assert(0 && "Attempting to pause parser in error state");
+  }
+}
+
+int
+http_body_is_final(const struct http_parser *parser) {
+    return parser->state == s_message_done;
+}
+
+unsigned long
+http_parser_version(void) {
+  return HTTP_PARSER_VERSION_MAJOR * 0x10000 |
+         HTTP_PARSER_VERSION_MINOR * 0x00100 |
+         HTTP_PARSER_VERSION_PATCH * 0x00001;
+}

+ 48 - 0
components/httpclient/src/http_tls.c

@@ -0,0 +1,48 @@
+#if defined(CONFIG_USING_TLS)
+#include "http_tls.h"
+#include "http_parser.h"
+
+static int get_port(const char *url, struct http_parser_url *u)
+{
+    if (u->field_data[UF_PORT].len) {
+        return strtol(&url[u->field_data[UF_PORT].off], NULL, 10);
+    } else {
+        if (strncasecmp(&url[u->field_data[UF_SCHEMA].off], "http", u->field_data[UF_SCHEMA].len) == 0) {
+            return 80;
+        } else if (strncasecmp(&url[u->field_data[UF_SCHEMA].off], "https", u->field_data[UF_SCHEMA].len) == 0) {
+            return 443;
+        }
+    }
+    return 0;
+}
+
+/**
+ * @brief      Create a new TLS/SSL connection with a given "HTTP" url
+ */
+tls_t *tls_conn_http_new(const char *url, const tls_cfg_t *cfg)
+{
+    /* Parse URI */
+    struct http_parser_url u;
+    http_parser_url_init(&u);
+    http_parser_parse_url(url, strlen(url), 0, &u);
+
+    /* Connect to host */
+    return tls_conn_new(&url[u.field_data[UF_HOST].off], u.field_data[UF_HOST].len,
+			    get_port(url, &u), cfg);
+}
+
+/**
+ * @brief      Create a new non-blocking TLS/SSL connection with a given "HTTP" url
+ */
+int tls_conn_http_new_async(const char *url, const tls_cfg_t *cfg, tls_t *tls)
+{
+    /* Parse URI */
+    struct http_parser_url u;
+    http_parser_url_init(&u);
+    http_parser_parse_url(url, strlen(url), 0, &u);
+
+    /* Connect to host */
+    return tls_conn_new_async(&url[u.field_data[UF_HOST].off], u.field_data[UF_HOST].len,
+			    get_port(url, &u), cfg, tls);
+}
+#endif

+ 127 - 0
components/httpclient/src/http_utils.c

@@ -0,0 +1,127 @@
+// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include "http_utils.h"
+
+#ifndef mem_check
+#define mem_check(x) assert(x)
+#endif
+
+char *http_utils_join_string(const char *first_str, int len_first, const char *second_str, int len_second)
+{
+    int first_str_len = len_first > 0 ? len_first : strlen(first_str);
+    int second_str_len = len_second > 0 ? len_second : strlen(second_str);
+    char *ret = NULL;
+    if (first_str_len + second_str_len > 0) {
+        ret = calloc(1, first_str_len + second_str_len + 1);
+        mem_check(ret);
+        memcpy(ret, first_str, first_str_len);
+        memcpy(ret + first_str_len, second_str, second_str_len);
+    }
+    return ret;
+}
+
+char *http_utils_assign_string(char **str, const char *new_str, int len)
+{
+    int l = len;
+    if (new_str == NULL) {
+        return NULL;
+    }
+    char *old_str = *str;
+    if (l <= 0) {
+        l = strlen(new_str);
+    }
+    if (old_str) {
+        old_str = realloc(old_str, l + 1);
+        mem_check(old_str);
+        old_str[l] = 0;
+    } else {
+        old_str = calloc(1, l + 1);
+        mem_check(old_str);
+    }
+    memcpy(old_str, new_str, l);
+    *str = old_str;
+    return old_str;
+}
+
+void http_utils_trim_whitespace(char **str)
+{
+    char *end, *start;
+    if (str == NULL) {
+        return;
+    }
+    start = *str;
+    if (start == NULL) {
+        return;
+    }
+    // Trim leading space
+    while (isspace((unsigned char)*start)) start ++;
+
+    if (*start == 0) {  // All spaces?
+        **str = 0;
+        return;
+    }
+
+    // Trim trailing space
+    end = (char *)(start + strlen(start) - 1);
+    while (end > start && isspace((unsigned char)*end)) {
+        end--;
+    }
+
+    // Write new null terminator
+    *(end + 1) = 0;
+    memmove(*str, start, strlen(start) + 1);
+}
+
+char *http_utils_get_string_between(const char *str, const char *begin, const char *end)
+{
+    char *found = strstr(str, begin);
+    char *ret = NULL;
+    if (found) {
+        found += strlen(begin);
+        char *found_end = strstr(found, end);
+        if (found_end) {
+            ret = calloc(1, found_end - found + 1);
+            mem_check(ret);
+            memcpy(ret, found, found_end - found);
+            return ret;
+        }
+    }
+    return NULL;
+}
+
+int http_utils_str_starts_with(const char *str, const char *start)
+{
+    int i;
+    int match_str_len = strlen(str);
+    int start_len = strlen(start);
+
+    if (start_len > match_str_len) {
+        return -1;
+    }
+    for (i = 0; i < start_len; i++) {
+        if (str[i] != start[i]) {
+            return 1;
+        }
+    }
+    return 0;
+}
+
+

+ 5 - 0
components/kv/CMakeLists.txt

@@ -0,0 +1,5 @@
+file(GLOB_RECURSE LIBSOURCE "${COMPONENTS_DIR}/kv/*.c")
+
+include_directories(${COMPONENTS_DIR}/kv/include)
+
+add_library(kv STATIC ${LIBSOURCE})

+ 99 - 0
components/kv/include/aos/kv.h

@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+
+#ifndef AOS_KV_H
+#define AOS_KV_H
+
+// #include <aos/kernel.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <string.h>
+
+#define KV_PATH_DEFULT "/data/kv/kv"
+#ifndef CONFIG_NV_PATH
+#define NV_PATH_DEFUALT "/mnt/PARAM/nv"
+#else
+#define NV_PATH_DEFUALT CONFIG_NV_PATH
+#endif
+
+/**
+ * This function will init kv system
+ * @return  0 on success, negative error on failure.
+ */
+int aos_kv_init(const char *pathname);
+
+/**
+ * This function will reset all kv, restore factory settting
+ * @return  0 on success, negative error on failure.
+ */
+int aos_kv_reset(void);
+
+/**
+ * Add a new KV pair.
+ *
+ * @param[in]  key    the key of the KV pair.
+ * @param[in]  value  the value of the KV pair.
+ * @param[in]  len    the length of the value.
+ * @param[in]  sync   save the KV pair to flash right now (should always be 1).
+ *
+ * @return  0 on success, negative error on failure.
+ */
+int aos_kv_set(const char *key, void *value, int len, int sync);
+
+int aos_kv_setfloat(const char *key, float v);
+
+int aos_kv_setint(const char *key, int v);
+
+/**
+ * Get the KV pair's value stored in buffer by its key.
+ *
+ * @note: the buffer_len should be larger than the real length of the value,
+ *        otherwise buffer would be NULL.
+ *
+ * @param[in]      key         the key of the KV pair to get.
+ * @param[out]     buffer      the memory to store the value.
+ * @param[in-out]  buffer_len  in: the length of the input buffer.
+ *                             out: the real length of the value.
+ *
+ * @return  0 on success, negative error on failure.
+ */
+int aos_kv_get(const char *key, void *buffer, int *buffer_len);
+
+int aos_kv_getfloat(const char *key, float *value);
+
+int aos_kv_getint(const char *key, int *value);
+
+/**
+ *
+ * @param[in]  key    the key of the KV pair.
+ * @param[in]  value  the value of the KV pair.
+ * @param[in]  len    the length of the value.
+ * @param[in]  sync   save the KV pair to flash right now (should always be 1).
+ *
+ * @setstring return  0 on success, negative error on failure.
+ * @getstring return  >=0 on success(read len), negative error on failure.
+ */
+int aos_kv_setstring(const char *key, const char *v);
+int aos_kv_getstring(const char *key, char *value, int len);
+
+/**
+ * Delete the KV pair by its key.
+ *
+ * @param[in]  key  the key of the KV pair to delete.
+ *
+ * @return  0 on success, negative error on failure.
+ */
+int aos_kv_del(const char *key);
+
+void aos_kv_foreach(void (*func)(char *key, char *val, uint16_t val_size, void *arg), void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AOS_KV_H */

+ 53 - 0
components/kv/include/aos/nvram.h

@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+
+#ifndef YOC_NVRAM_H
+#define YOC_NVRAM_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * This function will init nvram.
+ *
+ * @param[in]   pathname   the data pair of the key, less than 64 bytes
+ * @return  0 on success, negative error on failure.
+ */
+int nvram_init(const char *pathname);
+
+/**
+ * This function will reset nvram, clear all data.
+ *
+ * @return  0 on success, negative error on failure.
+ */
+int nvram_reset();
+
+/**
+ * This function will get data from the factory setting area.
+ *
+ * @param[in]   key   the data pair of the key, less than 64 bytes
+ * @param[in]   len   the length of the buffer
+ * @param[out]  value   the buffer that will store the data
+ * @return  the length of the data value, error code otherwise
+ */
+int nvram_get_val(const char *key, char *value, int len);
+
+/**
+ * This function will set data to the factory setting area.
+ *
+ * @param[in]   key   the data pair of the key, less than 64 bytes
+ * @param[in]   value the data pair of the value, delete the pair if value == NULL
+ * @return  0 on success, negative error on failure.
+ */
+int nvram_set_val(const char *key, char *value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+

+ 169 - 0
components/kv/kv_aos.c

@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+
+#include <stdint.h>
+#include <ulog/ulog.h>
+#include <aos/kv.h>
+#include "kv_linux.h"
+
+#define TAG "KV"
+
+static kv_t        g_kv;
+static aos_kv_mutex_t g_kv_lock;
+
+static int __kv_init(const char *pathname)
+{
+    g_kv_lock.key_file = pathname;
+    int ret = kv_init(&g_kv, pathname);
+
+    /* shmkey ftok need real path, must call after kv_init */
+    aos_kv_mutex_new(&g_kv_lock);
+    return ret;
+}
+
+static int __kv_setdata(char *key, char *buf, int bufsize)
+{
+    if (g_kv.handle < 0) {
+        return -1;
+    }
+
+    aos_kv_mutex_lock(&g_kv_lock, -1);
+    int ret = kv_set(&g_kv, key, buf, bufsize) >= 0 ? 0 : -1;
+    aos_kv_mutex_unlock(&g_kv_lock);
+
+    return ret;
+}
+
+static int __kv_getdata(char *key, char *buf, int bufsize)
+{
+    if (g_kv.handle < 0) {
+        return -1;
+    }
+
+    if (key == NULL || buf == NULL || bufsize <= 0)
+        return -1;
+    aos_kv_mutex_lock(&g_kv_lock, -1);
+    int ret = kv_get(&g_kv, key, buf, bufsize);
+    aos_kv_mutex_unlock(&g_kv_lock);
+
+    return ret;
+}
+
+static int __kv_del(char *key)
+{
+    if (g_kv.handle < 0) {
+        return -1;
+    }
+
+    aos_kv_mutex_lock(&g_kv_lock, -1);
+    int ret = kv_rm(&g_kv, key);
+    aos_kv_mutex_unlock(&g_kv_lock);
+
+    return ret;
+}
+
+static int __kv_reset(void)
+{
+    if (g_kv.handle < 0) {
+        return -1;
+    }
+
+    aos_kv_mutex_lock(&g_kv_lock, -1);
+    int ret = kv_reset(&g_kv);
+    aos_kv_mutex_unlock(&g_kv_lock);
+
+    return ret;
+}
+
+
+void __kv_foreach(void (*func)(char *key, char *val, uint16_t val_size, void *arg), void *arg)
+{
+    if (func == NULL) {
+        LOGE(TAG, "%s: param error", __FUNCTION__);
+        return;
+    }
+
+    aos_kv_mutex_lock(&g_kv_lock, -1);
+    kv_iter(&g_kv, func, arg);
+    aos_kv_mutex_unlock(&g_kv_lock);
+}
+
+int aos_kv_init(const char *pathname)
+{
+    return __kv_init(pathname);
+}
+
+__attribute__((weak)) int aos_kv_set(const char *key, void *value, int len, int sync)
+{
+    return __kv_setdata((char *)key, value, len);
+}
+
+__attribute__((weak)) int aos_kv_setstring(const char *key, const char *v)
+{
+    return __kv_setdata((char *)key, (void *)v, strlen(v));
+}
+
+__attribute__((weak)) int aos_kv_setfloat(const char *key, float v)
+{
+    return __kv_setdata((char *)key, (void *)&v, sizeof(v));
+}
+
+__attribute__((weak)) int aos_kv_setint(const char *key, int v)
+{
+    return __kv_setdata((char *)key, (void *)&v, sizeof(v));
+}
+
+__attribute__((weak)) int aos_kv_get(const char *key, void *buffer, int *buffer_len)
+{
+    if (buffer_len) {
+        int ret = __kv_getdata((char *)key, buffer, *buffer_len);
+
+        if (ret > 0) {
+            *buffer_len = ret;
+        }
+
+        return (ret <= 0) ? -1 : 0;
+    }
+
+    return -1;
+}
+
+__attribute__((weak)) int aos_kv_getstring(const char *key, char *value, int len)
+{
+    int ret;
+    ret = __kv_getdata((char *)key, (char *)value, len - 1);
+
+    if (ret > 0) {
+        value[ret < len ? ret : len - 1] = '\0';
+    }
+
+    return ret;
+}
+
+__attribute__((weak)) int aos_kv_getfloat(const char *key, float *value)
+{
+    int ret = __kv_getdata((char *)key, (char *)value, sizeof(float));
+    return (ret == sizeof(float)) ? 0 : -1;
+}
+
+__attribute__((weak)) int aos_kv_getint(const char *key, int *value)
+{
+    int ret = __kv_getdata((char *)key, (char *)value, sizeof(int));
+    return (ret == sizeof(int)) ? 0 : -1;
+}
+
+__attribute__((weak)) int aos_kv_del(const char *key)
+{
+    return __kv_del((char *)key);
+}
+
+__attribute__((weak)) int aos_kv_reset(void)
+{
+    return __kv_reset();
+}
+
+__attribute__((weak)) void aos_kv_foreach(void (*func)(char *key, char *val, uint16_t val_size, void *arg), void *arg)
+{
+    __kv_foreach(func, arg);
+}

+ 111 - 0
components/kv/kv_fct.c

@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+
+#include <ulog/ulog.h>
+#include <aos/kv.h>
+#include <aos/nvram.h>
+#include "kv_linux.h"
+
+#define TAG "NV"
+
+static kv_t        g_kv;
+static aos_kv_mutex_t g_kv_lock;
+
+static int __kv_init(const char *pathname)
+{
+    g_kv_lock.key_file = pathname;
+    int ret = kv_init(&g_kv, pathname);
+
+    /* shmkey ftok need real path, must call after kv_init */
+    aos_kv_mutex_new(&g_kv_lock);
+    return ret;
+}
+
+static int __kv_setdata(char *key, char *buf, int bufsize)
+{
+    if (g_kv.handle < 0) {
+        return -1;
+    }
+
+    aos_kv_mutex_lock(&g_kv_lock, -1);
+    int ret = kv_set(&g_kv, key, buf, bufsize) >= 0 ? 0 : -1;
+    aos_kv_mutex_unlock(&g_kv_lock);
+
+    return ret;
+}
+
+static int __kv_getdata(char *key, char *buf, int bufsize)
+{
+    if (g_kv.handle < 0) {
+        return -1;
+    }
+
+    if (key == NULL || buf == NULL || bufsize <= 0)
+        return -1;
+    aos_kv_mutex_lock(&g_kv_lock, -1);
+    int ret = kv_get(&g_kv, key, buf, bufsize);
+    aos_kv_mutex_unlock(&g_kv_lock);
+
+    return ret;
+}
+
+static int __kv_del(char *key)
+{
+    if (g_kv.handle < 0) {
+        return -1;
+    }
+
+    aos_kv_mutex_lock(&g_kv_lock, -1);
+    int ret = kv_rm(&g_kv, key);
+    aos_kv_mutex_unlock(&g_kv_lock);
+
+    return ret;
+}
+
+static int __kv_reset(void)
+{
+    if (g_kv.handle < 0) {
+        return -1;
+    }
+
+    aos_kv_mutex_lock(&g_kv_lock, -1);
+    int ret = kv_reset(&g_kv);
+    aos_kv_mutex_unlock(&g_kv_lock);
+
+    return ret;
+}
+
+/*************************
+ * Set Get API
+ *************************/
+int nvram_init(const char *pathname)
+{
+    return __kv_init(pathname);
+}
+int nvram_get_val(const char *key, char *value, int len)
+{
+    int ret;
+    ret = __kv_getdata((char *)key, (char *)value, len - 1);
+
+    if(ret > 0) {
+        value[ret < len ? ret : len - 1] = '\0';
+    }
+
+    return ret;
+}
+
+int nvram_set_val(const char *key, char *value)
+{
+    return __kv_setdata((char *)key, (void *)value, strlen(value));
+}
+
+int nvram_del(const char *key)
+{
+    return __kv_del((char *)key);
+}
+
+int nvram_reset(void)
+{
+    return __kv_reset();
+}

+ 421 - 0
components/kv/kv_linux.c

@@ -0,0 +1,421 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include <ulog/ulog.h>
+#include "kv_linux.h"
+
+#define TAG "kvlinux"
+
+
+static int char2hex(char c, uint8_t *x)
+{
+	if (c >= '0' && c <= '9') {
+		*x = c - '0';
+	} else if (c >= 'a' && c <= 'f') {
+		*x = c - 'a' + 10;
+	} else if (c >= 'A' && c <= 'F') {
+		*x = c - 'A' + 10;
+	} else {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int hex2char(uint8_t x, char *c)
+{
+	if (x <= 9) {
+		*c = x + '0';
+	} else  if (x >= 10 && x <= 15) {
+		*c = x - 10 + 'a';
+	} else {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static size_t bin2hex(const uint8_t *buf, size_t buflen, char *hex, size_t hexlen)
+{
+	if ((hexlen + 1) < buflen * 2) {
+		return 0;
+	}
+
+	for (size_t i = 0; i < buflen; i++) {
+		if (hex2char(buf[i] >> 4, &hex[2 * i]) < 0) {
+			return 0;
+		}
+		if (hex2char(buf[i] & 0xf, &hex[2 * i + 1]) < 0) {
+			return 0;
+		}
+	}
+
+	hex[2 * buflen] = '\0';
+	return 2 * buflen;
+}
+
+static size_t hex2bin(const char *hex, size_t hexlen, uint8_t *buf, size_t buflen)
+{
+	uint8_t dec;
+
+	if (buflen < hexlen / 2 + hexlen % 2) {
+		return 0;
+	}
+
+	/* if hexlen is uneven, insert leading zero nibble */
+	if (hexlen % 2) {
+		if (char2hex(hex[0], &dec) < 0) {
+			return 0;
+		}
+		buf[0] = dec;
+		hex++;
+		buf++;
+	}
+
+	/* regular hex conversion */
+	for (size_t i = 0; i < hexlen / 2; i++) {
+		if (char2hex(hex[2 * i], &dec) < 0) {
+			return 0;
+		}
+		buf[i] = dec << 4;
+
+		if (char2hex(hex[2 * i + 1], &dec) < 0) {
+			return 0;
+		}
+		buf[i] += dec;
+	}
+
+	return hexlen / 2 + hexlen % 2;
+}
+
+static int mkdirpath(const char *path)
+{
+    int  ret;
+    char str[KV_MAX_KEYPATH_LEN];
+    int need_exit;
+
+    if (path[0] != '/') {
+        return -1;
+    }
+
+    char *ch = strchr(path, '/');
+
+    need_exit = 0;
+    while (ch && !need_exit) {
+        ch = strchr(ch + 1, '/');
+        if (ch == NULL) {
+            if (path[strlen(path) - 1] == '/') {
+                break;
+            }
+            ch = (char *)path + strlen(path);
+            need_exit = 1;
+        }
+        snprintf(str, ch - path + 1, "%s", path);
+        if (access(str, 0) != 0) {
+            ret = mkdir(str, 0644);
+            LOGD(TAG, "mkdir %s ret=%s", str, strerror(ret));
+        }
+    }
+
+    return 0;
+}
+
+static int rmdirpath(const char *path)
+{
+    DIR *          dir;
+    struct dirent *ptr;
+
+    if ((dir = opendir(path)) == NULL) {
+        LOGE(TAG, "%s: opendir error,%s", __FUNCTION__, strerror(errno));
+        return -1;
+    }
+
+    while ((ptr = readdir(dir)) != NULL) {
+        if (strcmp(ptr->d_name, ".") == 0 ||
+            strcmp(ptr->d_name, "..") == 0) ///current dir OR parrent dir
+            continue;
+        else if (ptr->d_type == DT_REG) {
+            
+            char *hexkey = ptr->d_name;
+            char key[KV_MAX_KEY_LEN * 2];
+            size_t ret = hex2bin(hexkey, strlen(hexkey), (uint8_t *)key, sizeof(key));
+            key[ret] = '\0';
+            LOGD(TAG, "%s: remove key %s", __FUNCTION__, key);
+
+            char keypath[KV_MAX_KEYPATH_LEN];
+            snprintf(keypath, sizeof(keypath), "%s/%s", path, hexkey);
+            remove(keypath);
+        } else if (ptr->d_type == DT_DIR) {
+            LOGW(TAG, "%s: dir(%s) in kv path", __FUNCTION__, ptr->d_name);
+        } else {
+            LOGW(TAG, "%s: unvalid file(%s) type(%d) in kv path", __FUNCTION__, ptr->d_name,
+                 ptr->d_type);
+        }
+    }
+    closedir(dir);
+
+    return 0;
+}
+
+int kv_init(kv_t *kv, const char *path)
+{
+    if (kv == NULL) {
+        LOGE(TAG, "%s: param error, path=%p", __FUNCTION__, path);
+        return -1;
+    }
+
+    /* like mkdir -p */
+    mkdirpath(path);
+
+    /* copy path to kv obj */
+    snprintf(kv->path, sizeof(kv->path), "%s", path);
+
+    /* just seem as yoc */
+    kv->handle = 1;
+
+    return 0;
+}
+
+int kv_reset(kv_t *kv)
+{
+    if (kv == NULL) {
+        LOGE(TAG, "%s: param error", __FUNCTION__);
+        return -1;
+    }
+
+    rmdirpath(kv->path);
+
+    return 0;
+}
+
+int kv_set(kv_t *kv, const char *key, void *value, int size)
+{
+    FILE *fd = NULL;
+
+    if (kv == NULL || value == NULL || size == 0) {
+        LOGE(TAG, "%s: param error, kv=%p value=%p size=%d", __FUNCTION__, key, value, size);
+        return -1;
+    }
+
+    char hexkey[KV_MAX_KEY_LEN * 2];
+    bin2hex((uint8_t *)key, strlen(key), hexkey, sizeof(hexkey));
+
+    char keypath[KV_MAX_KEYPATH_LEN];
+    snprintf(keypath, sizeof(keypath), "%s/%s", kv->path, hexkey);
+
+    fd = fopen(keypath, "w");
+    if (fd == NULL) {
+        LOGE(TAG, "open %s file error %s", key, strerror(errno));
+        return -1;
+    }
+
+    size_t wlen = fwrite(value, 1, size, fd);
+    if (wlen != size) {
+        LOGE(TAG, "write size error, write size=%d ret=%d", size, wlen);
+        fclose(fd);
+        return -1;
+    }
+
+    fflush(fd);
+    fsync(fileno(fd));
+
+    fclose(fd);
+    return wlen;
+}
+
+int kv_get(kv_t *kv, const char *key, void *value, int size)
+{
+    FILE *fd = NULL;
+
+    if (kv == NULL || value == NULL || size == 0) {
+        LOGE(TAG, "%s: param error, kv=%p value=%p size=%d", __FUNCTION__, key, value, size);
+        return -1;
+    }
+
+    char hexkey[KV_MAX_KEY_LEN * 2];
+    bin2hex((uint8_t *)key, strlen(key), hexkey, sizeof(hexkey));
+
+    char keypath[KV_MAX_KEYPATH_LEN];
+    snprintf(keypath, sizeof(keypath), "%s/%s", kv->path, hexkey);
+
+    fd = fopen(keypath, "rb");
+    if (fd == NULL) {
+        //LOGE(TAG, "open %s file error %s", key, strerror(errno));
+        return -1;
+    }
+
+    size_t rlen = fread(value, 1, size, fd);
+
+    fclose(fd);
+
+    return rlen;
+}
+
+int kv_rm(kv_t *kv, const char *key)
+{
+    if (kv == NULL || key == NULL) {
+        LOGE(TAG, "%s: param error, kv=%p key=%p", __FUNCTION__, kv, key);
+        return -1;
+    }
+
+    char hexkey[KV_MAX_KEY_LEN * 2];
+    bin2hex((uint8_t *)key, strlen(key), hexkey, sizeof(hexkey));
+
+    char keypath[KV_MAX_KEYPATH_LEN];
+    snprintf(keypath, sizeof(keypath), "%s/%s", kv->path, hexkey);
+
+    /* remove key */
+    int ret = remove(keypath);
+    if (ret < 0) {
+        LOGE(TAG, "%s: remove key error, key=%s ret=%d", __FUNCTION__, keypath, ret);
+    }
+
+    return ret;
+}
+
+void kv_iter(kv_t *kv, void (*func)(char *key, char *val, uint16_t val_size, void *arg), void *arg)
+{
+    DIR *          dir;
+    struct dirent *ptr;
+
+    if (kv == NULL || func == NULL) {
+        LOGE(TAG, "%s: param error", __FUNCTION__);
+        return;
+    }
+
+    if ((dir = opendir(kv->path)) == NULL) {
+        LOGE(TAG, "%s: opendir error,%s", __FUNCTION__, strerror(errno));
+        return;
+    }
+
+    while ((ptr = readdir(dir)) != NULL) {
+        if (strcmp(ptr->d_name, ".") == 0 ||
+            strcmp(ptr->d_name, "..") == 0) ///current dir OR parrent dir
+            continue;
+        else if (ptr->d_type == DT_REG) {
+
+            void *value = malloc(KV_MAX_VALUE_LEN);
+            char *hexkey   = ptr->d_name;
+
+            char key[KV_MAX_KEY_LEN * 2];
+            size_t ret = hex2bin(hexkey, strlen(hexkey), (uint8_t *)key, sizeof(key));
+            key[ret] = '\0';
+
+            //LOGD(TAG, "%s: iter key %s", __FUNCTION__, key);
+
+            int   rlen  = kv_get(kv, key, value, KV_MAX_VALUE_LEN);
+            func(key, value, rlen, arg);
+            free(value);
+
+        } else if (ptr->d_type == DT_DIR) {
+            LOGW(TAG, "%s: dir(%s) in kv path", __FUNCTION__, ptr->d_name);
+        } else {
+            LOGW(TAG, "%s: unvalid file(%s) type(%d) in kv path", __FUNCTION__, ptr->d_name,
+                 ptr->d_type);
+        }
+    }
+    closedir(dir);
+}
+
+/********************************
+ * MUTEX
+ ********************************/
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+typedef struct {
+    pthread_mutex_t lock;
+    int             ready;
+} pthread_mutex_shm_t;
+
+int aos_kv_mutex_new(aos_kv_mutex_t *mutex)
+{
+    /* generate share mm keyid */
+    key_t shmkey = ftok(mutex->key_file, 0);
+    LOGD(TAG, "shmkey=0x%x file=%s", shmkey, mutex->key_file);
+    if (shmkey < 0) {
+        LOGE(TAG, "%s: shmkey error", __FUNCTION__);
+        return -1;
+    }
+
+    /* create or get share mm by keyid */
+    int shmid = shmget(shmkey, sizeof(pthread_mutex_shm_t), 0666 | IPC_CREAT);
+    if (shmid < 0) {
+        LOGE(TAG, "%s: shmget error", __FUNCTION__);
+        return -1;
+    }
+
+    /* get share mm addr by shmid */
+    void *share_addr = shmat(shmid, (void *)0, 0);
+    if (share_addr == NULL) {
+        LOGE(TAG, "%s: shmat error", __FUNCTION__);
+        shmctl(shmid, IPC_RMID, NULL);
+        return -1;
+    }
+
+    /* shm stat check, firt ref, clean buffer */
+    struct shmid_ds shm_stat;
+    if (shmctl(shmid, IPC_STAT, &shm_stat) < 0) {
+        LOGE(TAG, "%s: shmctl get stat error", __FUNCTION__);
+        return -1;
+    }
+
+    if (shm_stat.shm_nattch == 1) {
+        LOGD(TAG, "first process init shm buffer");
+        memset(share_addr, 0, sizeof(pthread_mutex_shm_t));
+    }
+
+    /* create share mutex */
+    pthread_mutex_shm_t *shm_mutex = (pthread_mutex_shm_t *)share_addr;
+
+    if (shm_mutex->ready != 0x05050505) {
+        pthread_mutexattr_t mutex_attr;
+        pthread_mutexattr_init(&mutex_attr);
+        pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED);
+        pthread_mutex_init(&shm_mutex->lock, &mutex_attr);
+        shm_mutex->ready = 0x05050505;
+        LOGD(TAG, "%s: create share mutex shmid=%d", __FUNCTION__, shmid);
+    } else {
+        LOGD(TAG, "%s: get share mutex shmid=%d", __FUNCTION__, shmid);
+    }
+
+    /* save handle */
+    mutex->hdl = shm_mutex;
+
+    return 0;
+}
+
+void aos_kv_mutex_free(aos_kv_mutex_t *mutex)
+{
+    LOGE(TAG, "%s: no implement", __FUNCTION__);
+}
+
+int aos_kv_mutex_lock(aos_kv_mutex_t *mutex, unsigned int timeout)
+{
+    if (mutex->hdl == NULL) {
+        LOGI(TAG, "%s: mutex no hdl", __FUNCTION__);
+        return -1;
+    }
+
+    pthread_mutex_shm_t *shm_mutex = (pthread_mutex_shm_t *)mutex->hdl;
+
+    return pthread_mutex_lock(&shm_mutex->lock);
+}
+
+int aos_kv_mutex_unlock(aos_kv_mutex_t *mutex)
+{
+    if (mutex->hdl == NULL) {
+        LOGI(TAG, "%s: mutex no hdl", __FUNCTION__);
+        return -1;
+    }
+
+    pthread_mutex_shm_t *shm_mutex = (pthread_mutex_shm_t *)mutex->hdl;
+
+    return pthread_mutex_unlock(&shm_mutex->lock);
+}

+ 125 - 0
components/kv/kv_linux.h

@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2019-2020 Alibaba Group Holding Limited
+ */
+
+#ifndef AOS_KVSET_H
+#define AOS_KVSET_H
+
+#include <stdint.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define KV_MAX_KEYPATH_LEN 1024
+#define KV_MAX_KEY_LEN     256
+#define KV_MAX_VALUE_LEN   4096
+
+typedef struct kvset kv_t;
+
+struct kvset {
+    int  handle;
+    char path[KV_MAX_KEYPATH_LEN - KV_MAX_KEY_LEN];
+};
+
+/**
+ * @brief  init the kv fs
+ * @param  [in] kv          : object must be alloced
+ * @param  [in] path        : kv set base path
+ * @return 0/-1
+ */
+int kv_init(kv_t *kv, const char *path);
+
+/**
+ * @brief  reset the kv fs
+ * @param  [in] kv
+ * @return 0/-1
+ */
+int kv_reset(kv_t *kv);
+
+/**
+ * @brief  set key-value pair
+ * @param  [in] kv
+ * @param  [in] key
+ * @param  [in] value
+ * @param  [in] size  : size of the value
+ * @return size on success
+ */
+int kv_set(kv_t *kv, const char *key, void *value, int size);
+
+/**
+ * @brief  get value by the key-string
+ * @param  [in] kv
+ * @param  [in] key
+ * @param  [in] value
+ * @param  [in] size  : size of the value
+ * @return > 0 on success
+ */
+int kv_get(kv_t *kv, const char *key, void *value, int size);
+
+/**
+ * @brief  delete the key from kv fs
+ * @param  [in] kv
+ * @param  [in] key
+ * @return 0 on success
+ */
+int kv_rm(kv_t *kv, const char *key);
+
+/**
+ * @brief  iterate all valid kv pair
+ * @param  [in] kv
+ * @param  [in] func   : callback
+ * @param  [in] arg    : opaque of the fn callback
+ * @return 0 on success
+ */
+void kv_iter(kv_t *kv, void (*func)(char *key, char *val, uint16_t val_size, void *arg), void *arg);
+/********************************
+ * MUTEX
+ ********************************/
+#include <pthread.h>
+typedef struct __aos_kv_mutex_t {
+    void *      hdl;
+    const char *key_file;
+} aos_kv_mutex_t;
+
+/**
+ * Alloc a mutex.
+ *
+ * @param[in]  mutex  pointer of mutex object, mutex object must be alloced,
+ *                    hdl pointer in aos_kv_mutex_t will refer a kernel obj internally.
+ *
+ * @return  0: success.
+ */
+int aos_kv_mutex_new(aos_kv_mutex_t *mutex);
+
+/**
+ * Free a mutex.
+ *
+ * @param[in]  mutex  mutex object, mem refered by hdl pointer in aos_kv_mutex_t will
+ *                    be freed internally.
+ */
+void aos_kv_mutex_free(aos_kv_mutex_t *mutex);
+
+/**
+ * Lock a mutex.
+ *
+ * @param[in]  mutex    mutex object, it contains kernel obj pointer which aos_kv_mutex_new alloced.
+ * @param[in]  timeout  waiting until timeout in milliseconds.
+ *
+ * @return  0: success.
+ */
+int aos_kv_mutex_lock(aos_kv_mutex_t *mutex, unsigned int timeout);
+
+/**
+ * Unlock a mutex.
+ *
+ * @param[in]  mutex  mutex object, it contains kernel obj pointer which oc_mutex_new alloced.
+ *
+ * @return  0: success.
+ */
+int aos_kv_mutex_unlock(aos_kv_mutex_t *mutex);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

Some files were not shown because too many files changed in this diff