Преглед изворни кода

Merge branch 'master' into 'bugfix/btdm_modify_code_format_and_comments'

# Conflicts:
#   examples/bluetooth/gatt_server_service_table/main/gatts_table_creat_demo.c
Zhi Wei Jian пре 8 година
родитељ
комит
833ecfdec9
85 измењених фајлова са 7604 додато и 3495 уклоњено
  1. 7 0
      .gitlab-ci.yml
  2. 1 1
      components/bt/bluedroid/api/esp_bt_main.c
  3. 1 1
      components/bt/bluedroid/hci/hci_hal_h4.c
  4. 1 1
      components/bt/bluedroid/hci/hci_layer.c
  5. 1 1
      components/bt/bt.c
  6. 3 244
      components/bt/include/bt.h
  7. 244 0
      components/bt/include/esp_bt.h
  8. 1 1
      components/console/argtable3/argtable3.c
  9. 7 4
      components/driver/include/driver/ledc.h
  10. 12 8
      components/esp32/Kconfig
  11. 2 2
      components/esp32/include/esp_intr_alloc.h
  12. 52 15
      components/fatfs/src/vfs_fat.c
  13. 12 7
      components/fatfs/test/test_fatfs_common.c
  14. 1 1
      components/fatfs/test/test_fatfs_common.h
  15. 1 1
      components/fatfs/test/test_fatfs_sdmmc.c
  16. 1 1
      components/fatfs/test/test_fatfs_spiflash.c
  17. 274 343
      components/freertos/include/freertos/event_groups.h
  18. 391 485
      components/freertos/include/freertos/queue.h
  19. 143 115
      components/freertos/include/freertos/ringbuf.h
  20. 360 423
      components/freertos/include/freertos/semphr.h
  21. 422 349
      components/freertos/include/freertos/task.h
  22. 33 92
      components/freertos/include/freertos/timers.h
  23. 105 49
      components/freertos/tasks.c
  24. 52 12
      components/freertos/test/test_freertos_task_delete.c
  25. 2 2
      components/heap/heap_trace.c
  26. 187 74
      components/json/include/cJSON.h
  27. 2637 634
      components/json/library/cJSON.c
  28. 1434 386
      components/json/port/cJSON_Utils.c
  29. 59 15
      components/json/port/include/cJSON_Utils.h
  30. 15 1
      components/lwip/api/api_lib.c
  31. 12 0
      components/mbedtls/port/include/mbedtls/esp_debug.h
  32. 2 1
      components/mdns/mdns.c
  33. 24 18
      components/newlib/time.c
  34. 2 2
      components/nvs_flash/src/nvs_item_hash_list.cpp
  35. 5 1
      components/openssl/platform/ssl_pm.c
  36. 12 7
      components/soc/esp32/include/soc/ledc_struct.h
  37. 10 1
      components/soc/esp32/include/soc/rtc_cntl_reg.h
  38. 9 0
      components/soc/esp32/test/test_rtc_clk.c
  39. 31 0
      components/spi_flash/Kconfig
  40. 80 4
      components/spi_flash/flash_ops.c
  41. 19 0
      components/spiffs/Kconfig
  42. 45 2
      components/spiffs/esp_spiffs.c
  43. 1 1
      components/spiffs/include/spiffs_config.h
  44. 42 0
      components/spiffs/test/test_spiffs.c
  45. 4 0
      components/ulp/README.rst
  46. 2 2
      components/ulp/include/esp32/ulp.h
  47. 21 2
      docs/Doxyfile
  48. BIN
      docs/_static/esp32-pico-kit-v4-dimensions-back.jpg
  49. BIN
      docs/_static/esp32-pico-kit-v4-dimensions-side.jpg
  50. BIN
      docs/_static/esp32-pico-kit-v4-functional-block-diagram.png
  51. BIN
      docs/_static/esp32-pico-kit-v4-layout.jpg
  52. 1 1
      docs/api-guides/build-system.rst
  53. 135 78
      docs/api-guides/freertos-smp.rst
  54. 157 22
      docs/api-guides/ulp_instruction_set.rst
  55. 90 3
      docs/api-guides/unit-tests.rst
  56. 5 5
      docs/api-reference/peripherals/i2s.rst
  57. 42 0
      docs/api-reference/system/freertos.rst
  58. 2 2
      docs/api-reference/system/hooks.rst
  59. 6 5
      docs/api-reference/system/index.rst
  60. 126 9
      docs/get-started/get-started-pico-kit.rst
  61. 12 8
      docs/get-started/index.rst
  62. 1 1
      examples/bluetooth/a2dp_sink/main/main.c
  63. 1 1
      examples/bluetooth/ble_adv/main/app_bt.c
  64. 1 1
      examples/bluetooth/ble_eddystone/main/esp_eddystone_demo.c
  65. 1 1
      examples/bluetooth/ble_ibeacon/main/ibeacon_demo.c
  66. 1 1
      examples/bluetooth/ble_spp_client/main/spp_client_demo.c
  67. 1 1
      examples/bluetooth/ble_spp_server/main/ble_spp_server_demo.c
  68. 1 1
      examples/bluetooth/blufi/main/blufi_example_main.c
  69. 1 1
      examples/bluetooth/blufi/main/blufi_security.c
  70. 1 1
      examples/bluetooth/controller_hci_uart/main/controller_hci_uart_demo.c
  71. 1 1
      examples/bluetooth/gatt_client/main/gattc_demo.c
  72. 1 1
      examples/bluetooth/gatt_security_client/main/example_ble_sec_gattc_demo.c
  73. 1 1
      examples/bluetooth/gatt_security_server/main/example_ble_sec_gatts_demo.c
  74. 1 1
      examples/bluetooth/gatt_server/main/gatts_demo.c
  75. 7 8
      examples/bluetooth/gatt_server_service_table/main/gatts_table_creat_demo.c
  76. 1 1
      examples/bluetooth/gattc_multi_connect/main/gattc_multi_connect.c
  77. 2 2
      examples/peripherals/gpio/main/gpio_example_main.c
  78. 5 0
      examples/system/ulp/main/ulp/pulse_cnt.S
  79. 1 1
      examples/system/ulp_adc/main/ulp/adc.S
  80. 7 1
      make/component_wrapper.mk
  81. 40 0
      tools/unit-test-app/components/unity/include/test_utils.h
  82. 81 22
      tools/unit-test-app/components/unity/include/unity_config.h
  83. 31 0
      tools/unit-test-app/components/unity/test_utils.c
  84. 57 4
      tools/unit-test-app/components/unity/unity_platform.c
  85. 1 1
      tools/unit-test-app/main/app_main.c

+ 7 - 0
.gitlab-ci.yml

@@ -751,6 +751,13 @@ UT_003_08:
     - UT_T1_1
     - UT_single_core
 
+UT_003_09:
+  <<: *unit_test_template
+  tags:
+    - ESP32_IDF
+    - UT_T1_1
+    - UT_single_core
+
 UT_004_01:
   <<: *unit_test_template
   tags:

+ 1 - 1
components/bt/bluedroid/api/esp_bt_main.c

@@ -16,7 +16,7 @@
 #include "esp_bt_main.h"
 #include "btc_task.h"
 #include "btc_main.h"
-#include "bt.h"
+#include "esp_bt.h"
 #include "future.h"
 #include "allocator.h"
 

+ 1 - 1
components/bt/bluedroid/hci/hci_hal_h4.c

@@ -25,7 +25,7 @@
 #include "hci_internals.h"
 #include "hci_layer.h"
 #include "thread.h"
-#include "bt.h"
+#include "esp_bt.h"
 
 #define HCI_HAL_SERIAL_BUFFER_SIZE 1026
 #define HCI_BLE_EVENT 0x3e

+ 1 - 1
components/bt/bluedroid/hci/hci_layer.c

@@ -16,7 +16,7 @@
  *
  ******************************************************************************/
 #include <string.h>
-#include "bt.h"
+#include "esp_bt.h"
 #include "bt_defs.h"
 #include "bt_trace.h"
 #include "hcidefs.h"

+ 1 - 1
components/bt/bt.c

@@ -31,7 +31,7 @@
 #include "esp_intr.h"
 #include "esp_attr.h"
 #include "esp_phy_init.h"
-#include "bt.h"
+#include "esp_bt.h"
 #include "esp_err.h"
 #include "esp_log.h"
 #include "esp_pm.h"

+ 3 - 244
components/bt/include/bt.h

@@ -1,244 +1,3 @@
-// Copyright 2015-2016 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 __BT_H__
-#define __BT_H__
-
-#include <stdint.h>
-#include <stdbool.h>
-#include "esp_err.h"
-#include "sdkconfig.h"
-#include "esp_task.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * @brief Controller config options, depend on config mask.
- *        Config mask indicate which functions enabled, this means 
- *        some options or parameters of some functions enabled by config mask.
- */
-typedef struct {
-    uint16_t controller_task_stack_size;    /*!< Bluetooth controller task stack size */
-    uint8_t controller_task_prio;           /*!< Bluetooth controller task priority */
-    uint8_t hci_uart_no;                    /*!< If use UART1/2 as HCI IO interface, indicate UART number */
-    uint32_t hci_uart_baudrate;             /*!< If use UART1/2 as HCI IO interface, indicate UART baudrate */
-} esp_bt_controller_config_t;
-
-#ifdef CONFIG_BT_ENABLED
-
-#ifdef CONFIG_BT_HCI_UART_NO
-#define BT_HCI_UART_NO_DEFAULT CONFIG_BT_HCI_UART_NO
-#else
-#define BT_HCI_UART_NO_DEFAULT 1
-#endif /* BT_HCI_UART_NO_DEFAULT */
-
-#ifdef CONFIG_BT_HCI_UART_BAUDRATE
-#define BT_HCI_UART_BAUDRATE_DEFAULT CONFIG_BT_HCI_UART_BAUDRATE
-#else
-#define BT_HCI_UART_BAUDRATE_DEFAULT 921600
-#endif /* BT_HCI_UART_BAUDRATE_DEFAULT */
-
-#define BT_CONTROLLER_INIT_CONFIG_DEFAULT() {                       \
-    .controller_task_stack_size = ESP_TASK_BT_CONTROLLER_STACK,     \
-    .controller_task_prio = ESP_TASK_BT_CONTROLLER_PRIO,            \
-    .hci_uart_no = BT_HCI_UART_NO_DEFAULT,                          \
-    .hci_uart_baudrate = BT_HCI_UART_BAUDRATE_DEFAULT,              \
-};
-#else
-#define BT_CONTROLLER_INIT_CONFIG_DEFAULT() {0}; _Static_assert(0, "please enable bluetooth in menuconfig to use bt.h");
-#endif
-
-/**
- * @brief Bluetooth mode for controller enable/disable
- */
-typedef enum {
-    ESP_BT_MODE_IDLE       = 0x00,   /*!< Bluetooth is not running */
-    ESP_BT_MODE_BLE        = 0x01,   /*!< Run BLE mode */
-    ESP_BT_MODE_CLASSIC_BT = 0x02,   /*!< Run Classic BT mode */
-    ESP_BT_MODE_BTDM       = 0x03,   /*!< Run dual mode */
-} esp_bt_mode_t;
-
-/**
- * @brief Bluetooth controller enable/disable/initialised/de-initialised status
- */
-typedef enum {
-    ESP_BT_CONTROLLER_STATUS_IDLE = 0,
-    ESP_BT_CONTROLLER_STATUS_INITED,
-    ESP_BT_CONTROLLER_STATUS_ENABLED,
-    ESP_BT_CONTROLLER_STATUS_NUM,
-} esp_bt_controller_status_t;
-
-
-/**
- * @brief BLE tx power type
- *        ESP_BLE_PWR_TYPE_CONN_HDL0-8: for each connection, and only be set after connetion completed.
- *                                      when disconnect, the correspond TX power is not effected.
- *        ESP_BLE_PWR_TYPE_ADV : for advertising/scan response.
- *        ESP_BLE_PWR_TYPE_SCAN : for scan.
- *        ESP_BLE_PWR_TYPE_DEFAULT : if each connection's TX power is not set, it will use this default value.
- *                                   if neither in scan mode nor in adv mode, it will use this default value.
- *        If none of power type is set, system will use ESP_PWR_LVL_P1 as default for ADV/SCAN/CONN0-9.
- */
-typedef enum {
-    ESP_BLE_PWR_TYPE_CONN_HDL0  = 0,            /*!< For connection handle 0 */
-    ESP_BLE_PWR_TYPE_CONN_HDL1  = 1,            /*!< For connection handle 1 */
-    ESP_BLE_PWR_TYPE_CONN_HDL2  = 2,            /*!< For connection handle 2 */
-    ESP_BLE_PWR_TYPE_CONN_HDL3  = 3,            /*!< For connection handle 3 */
-    ESP_BLE_PWR_TYPE_CONN_HDL4  = 4,            /*!< For connection handle 4 */
-    ESP_BLE_PWR_TYPE_CONN_HDL5  = 5,            /*!< For connection handle 5 */
-    ESP_BLE_PWR_TYPE_CONN_HDL6  = 6,            /*!< For connection handle 6 */
-    ESP_BLE_PWR_TYPE_CONN_HDL7  = 7,            /*!< For connection handle 7 */
-    ESP_BLE_PWR_TYPE_CONN_HDL8  = 8,            /*!< For connection handle 8 */
-    ESP_BLE_PWR_TYPE_ADV        = 9,            /*!< For advertising */
-    ESP_BLE_PWR_TYPE_SCAN       = 10,           /*!< For scan */
-    ESP_BLE_PWR_TYPE_DEFAULT    = 11,           /*!< For default, if not set other, it will use default value */
-    ESP_BLE_PWR_TYPE_NUM        = 12,           /*!< TYPE numbers */
-} esp_ble_power_type_t;
-
-/**
- * @brief Bluetooth TX power level(index), it's just a index corresponding to power(dbm).
- */
-typedef enum {
-    ESP_PWR_LVL_N14 = 0,            /*!< Corresponding to -14dbm */
-    ESP_PWR_LVL_N11 = 1,            /*!< Corresponding to -11dbm */
-    ESP_PWR_LVL_N8  = 2,            /*!< Corresponding to  -8dbm */
-    ESP_PWR_LVL_N5  = 3,            /*!< Corresponding to  -5dbm */
-    ESP_PWR_LVL_N2  = 4,            /*!< Corresponding to  -2dbm */
-    ESP_PWR_LVL_P1  = 5,            /*!< Corresponding to   1dbm */
-    ESP_PWR_LVL_P4  = 6,            /*!< Corresponding to   4dbm */
-    ESP_PWR_LVL_P7  = 7,            /*!< Corresponding to   7dbm */
-} esp_power_level_t;
-
-/**
- * @brief  Set BLE TX power
- *         Connection Tx power should only be set after connection created.
- * @param  power_type : The type of which tx power, could set Advertising/Connection/Default and etc
- * @param  power_level: Power level(index) corresponding to absolute value(dbm)
- * @return              ESP_OK - success, other - failed
- */
-esp_err_t esp_ble_tx_power_set(esp_ble_power_type_t power_type, esp_power_level_t power_level);
-
-/**
- * @brief  Get BLE TX power
- *         Connection Tx power should only be get after connection created.
- * @param  power_type : The type of which tx power, could set Advertising/Connection/Default and etc
- * @return             >= 0 - Power level, < 0 - Invalid
- */
-esp_power_level_t esp_ble_tx_power_get(esp_ble_power_type_t power_type);
-
-
-/**
- * @brief  Initialize BT controller to allocate task and other resource.
- * @param  cfg: Initial configuration of BT controller.
- * This function should be called only once, before any other BT functions are called.
- * @return       ESP_OK - success, other - failed
- */
-esp_err_t esp_bt_controller_init(esp_bt_controller_config_t *cfg);
-
-/**
- * @brief  De-initialize BT controller to free resource and delete task.
- *
- * This function should be called only once, after any other BT functions are called.
- * This function is not whole completed, esp_bt_controller_init cannot called after this function.
- * @return  ESP_OK - success, other - failed
- */
-esp_err_t esp_bt_controller_deinit(void);
-
-/**
- * @brief Enable BT controller.
- *               Due to a known issue, you cannot call esp_bt_controller_enable() a second time
- *               to change the controller mode dynamically. To change controller mode, call
- *               esp_bt_controller_disable() and then call esp_bt_controller_enable() with the new mode.
- * @param mode : the mode(BLE/BT/BTDM) to enable.
- * @return       ESP_OK - success, other - failed
- */
-esp_err_t esp_bt_controller_enable(esp_bt_mode_t mode);
-
-/**
- * @brief  Disable BT controller
- * @return       ESP_OK - success, other - failed
- */
-esp_err_t esp_bt_controller_disable(void);
-
-/**
- * @brief  Get BT controller is initialised/de-initialised/enabled/disabled
- * @return status value
- */
-esp_bt_controller_status_t esp_bt_controller_get_status(void);
-
-/** @brief esp_vhci_host_callback
- *  used for vhci call host function to notify what host need to do
- */
-typedef struct esp_vhci_host_callback {
-    void (*notify_host_send_available)(void);               /*!< callback used to notify that the host can send packet to controller */
-    int (*notify_host_recv)(uint8_t *data, uint16_t len);   /*!< callback used to notify that the controller has a packet to send to the host*/
-} esp_vhci_host_callback_t;
-
-/** @brief esp_vhci_host_check_send_available
- *  used for check actively if the host can send packet to controller or not.
- *  @return true for ready to send, false means cannot send packet
- */
-bool esp_vhci_host_check_send_available(void);
-
-/** @brief esp_vhci_host_send_packet
- * host send packet to controller
- * @param data the packet point
- *,@param len the packet length
- */
-void esp_vhci_host_send_packet(uint8_t *data, uint16_t len);
-
-/** @brief esp_vhci_host_register_callback
- * register the vhci referece callback, the call back
- * struct defined by vhci_host_callback structure.
- * @param callback esp_vhci_host_callback type variable
- */
-void esp_vhci_host_register_callback(const esp_vhci_host_callback_t *callback);
-
-/** @brief esp_bt_controller_mem_release
- * release the memory by mode, if never use the bluetooth mode
- * it can release the .bbs, .data and other section to heap.
- * The total size is about 70k bytes.
- *
- * If esp_bt_controller_enable(mode) has already been called, calling
- * esp_bt_controller_mem_release(ESP_BT_MODE_BTDM) will automatically
- * release all memory which is not needed for the currently enabled
- * Bluetooth controller mode.
- *
- * For example, calling esp_bt_controller_enable(ESP_BT_MODE_BLE) then
- * esp_bt_controller_mem_release(ESP_BT_MODE_BTDM) will enable BLE modes
- * and release memory only used by BT Classic. Also, call esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT)
- * is the same.
- *
- * Note that once BT controller memory is released, the process cannot be reversed.
- * If your firmware will later upgrade the Bluetooth controller mode (BLE -> BT Classic or disabled -> enabled)
- * then do not call this function.
- *
- * If user never use bluetooth controller, could call esp_bt_controller_mem_release(ESP_BT_MODE_BTDM)
- * before esp_bt_controller_init or after esp_bt_controller_deinit.
- *
- * For example, user only use bluetooth to config SSID and PASSWORD of WIFI, after config, will never use bluetooth.
- * Then, could call esp_bt_controller_mem_release(ESP_BT_MODE_BTDM) after esp_bt_controller_deinit.
- *
- * @param mode : the mode want to release memory
- * @return ESP_OK - success, other - failed
- */
-esp_err_t esp_bt_controller_mem_release(esp_bt_mode_t mode);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __BT_H__ */
+#pragma once
+#warning "This header is deprecated, please use functions defined in esp_bt.h instead."
+#include "esp_bt.h"

+ 244 - 0
components/bt/include/esp_bt.h

@@ -0,0 +1,244 @@
+// Copyright 2015-2016 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 __ESP_BT_H__
+#define __ESP_BT_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "esp_err.h"
+#include "sdkconfig.h"
+#include "esp_task.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Controller config options, depend on config mask.
+ *        Config mask indicate which functions enabled, this means 
+ *        some options or parameters of some functions enabled by config mask.
+ */
+typedef struct {
+    uint16_t controller_task_stack_size;    /*!< Bluetooth controller task stack size */
+    uint8_t controller_task_prio;           /*!< Bluetooth controller task priority */
+    uint8_t hci_uart_no;                    /*!< If use UART1/2 as HCI IO interface, indicate UART number */
+    uint32_t hci_uart_baudrate;             /*!< If use UART1/2 as HCI IO interface, indicate UART baudrate */
+} esp_bt_controller_config_t;
+
+#ifdef CONFIG_BT_ENABLED
+
+#ifdef CONFIG_BT_HCI_UART_NO
+#define BT_HCI_UART_NO_DEFAULT CONFIG_BT_HCI_UART_NO
+#else
+#define BT_HCI_UART_NO_DEFAULT 1
+#endif /* BT_HCI_UART_NO_DEFAULT */
+
+#ifdef CONFIG_BT_HCI_UART_BAUDRATE
+#define BT_HCI_UART_BAUDRATE_DEFAULT CONFIG_BT_HCI_UART_BAUDRATE
+#else
+#define BT_HCI_UART_BAUDRATE_DEFAULT 921600
+#endif /* BT_HCI_UART_BAUDRATE_DEFAULT */
+
+#define BT_CONTROLLER_INIT_CONFIG_DEFAULT() {                       \
+    .controller_task_stack_size = ESP_TASK_BT_CONTROLLER_STACK,     \
+    .controller_task_prio = ESP_TASK_BT_CONTROLLER_PRIO,            \
+    .hci_uart_no = BT_HCI_UART_NO_DEFAULT,                          \
+    .hci_uart_baudrate = BT_HCI_UART_BAUDRATE_DEFAULT,              \
+};
+#else
+#define BT_CONTROLLER_INIT_CONFIG_DEFAULT() {0}; _Static_assert(0, "please enable bluetooth in menuconfig to use bt.h");
+#endif
+
+/**
+ * @brief Bluetooth mode for controller enable/disable
+ */
+typedef enum {
+    ESP_BT_MODE_IDLE       = 0x00,   /*!< Bluetooth is not running */
+    ESP_BT_MODE_BLE        = 0x01,   /*!< Run BLE mode */
+    ESP_BT_MODE_CLASSIC_BT = 0x02,   /*!< Run Classic BT mode */
+    ESP_BT_MODE_BTDM       = 0x03,   /*!< Run dual mode */
+} esp_bt_mode_t;
+
+/**
+ * @brief Bluetooth controller enable/disable/initialised/de-initialised status
+ */
+typedef enum {
+    ESP_BT_CONTROLLER_STATUS_IDLE = 0,
+    ESP_BT_CONTROLLER_STATUS_INITED,
+    ESP_BT_CONTROLLER_STATUS_ENABLED,
+    ESP_BT_CONTROLLER_STATUS_NUM,
+} esp_bt_controller_status_t;
+
+
+/**
+ * @brief BLE tx power type
+ *        ESP_BLE_PWR_TYPE_CONN_HDL0-8: for each connection, and only be set after connetion completed.
+ *                                      when disconnect, the correspond TX power is not effected.
+ *        ESP_BLE_PWR_TYPE_ADV : for advertising/scan response.
+ *        ESP_BLE_PWR_TYPE_SCAN : for scan.
+ *        ESP_BLE_PWR_TYPE_DEFAULT : if each connection's TX power is not set, it will use this default value.
+ *                                   if neither in scan mode nor in adv mode, it will use this default value.
+ *        If none of power type is set, system will use ESP_PWR_LVL_P1 as default for ADV/SCAN/CONN0-9.
+ */
+typedef enum {
+    ESP_BLE_PWR_TYPE_CONN_HDL0  = 0,            /*!< For connection handle 0 */
+    ESP_BLE_PWR_TYPE_CONN_HDL1  = 1,            /*!< For connection handle 1 */
+    ESP_BLE_PWR_TYPE_CONN_HDL2  = 2,            /*!< For connection handle 2 */
+    ESP_BLE_PWR_TYPE_CONN_HDL3  = 3,            /*!< For connection handle 3 */
+    ESP_BLE_PWR_TYPE_CONN_HDL4  = 4,            /*!< For connection handle 4 */
+    ESP_BLE_PWR_TYPE_CONN_HDL5  = 5,            /*!< For connection handle 5 */
+    ESP_BLE_PWR_TYPE_CONN_HDL6  = 6,            /*!< For connection handle 6 */
+    ESP_BLE_PWR_TYPE_CONN_HDL7  = 7,            /*!< For connection handle 7 */
+    ESP_BLE_PWR_TYPE_CONN_HDL8  = 8,            /*!< For connection handle 8 */
+    ESP_BLE_PWR_TYPE_ADV        = 9,            /*!< For advertising */
+    ESP_BLE_PWR_TYPE_SCAN       = 10,           /*!< For scan */
+    ESP_BLE_PWR_TYPE_DEFAULT    = 11,           /*!< For default, if not set other, it will use default value */
+    ESP_BLE_PWR_TYPE_NUM        = 12,           /*!< TYPE numbers */
+} esp_ble_power_type_t;
+
+/**
+ * @brief Bluetooth TX power level(index), it's just a index corresponding to power(dbm).
+ */
+typedef enum {
+    ESP_PWR_LVL_N14 = 0,            /*!< Corresponding to -14dbm */
+    ESP_PWR_LVL_N11 = 1,            /*!< Corresponding to -11dbm */
+    ESP_PWR_LVL_N8  = 2,            /*!< Corresponding to  -8dbm */
+    ESP_PWR_LVL_N5  = 3,            /*!< Corresponding to  -5dbm */
+    ESP_PWR_LVL_N2  = 4,            /*!< Corresponding to  -2dbm */
+    ESP_PWR_LVL_P1  = 5,            /*!< Corresponding to   1dbm */
+    ESP_PWR_LVL_P4  = 6,            /*!< Corresponding to   4dbm */
+    ESP_PWR_LVL_P7  = 7,            /*!< Corresponding to   7dbm */
+} esp_power_level_t;
+
+/**
+ * @brief  Set BLE TX power
+ *         Connection Tx power should only be set after connection created.
+ * @param  power_type : The type of which tx power, could set Advertising/Connection/Default and etc
+ * @param  power_level: Power level(index) corresponding to absolute value(dbm)
+ * @return              ESP_OK - success, other - failed
+ */
+esp_err_t esp_ble_tx_power_set(esp_ble_power_type_t power_type, esp_power_level_t power_level);
+
+/**
+ * @brief  Get BLE TX power
+ *         Connection Tx power should only be get after connection created.
+ * @param  power_type : The type of which tx power, could set Advertising/Connection/Default and etc
+ * @return             >= 0 - Power level, < 0 - Invalid
+ */
+esp_power_level_t esp_ble_tx_power_get(esp_ble_power_type_t power_type);
+
+
+/**
+ * @brief  Initialize BT controller to allocate task and other resource.
+ * @param  cfg: Initial configuration of BT controller.
+ * This function should be called only once, before any other BT functions are called.
+ * @return       ESP_OK - success, other - failed
+ */
+esp_err_t esp_bt_controller_init(esp_bt_controller_config_t *cfg);
+
+/**
+ * @brief  De-initialize BT controller to free resource and delete task.
+ *
+ * This function should be called only once, after any other BT functions are called.
+ * This function is not whole completed, esp_bt_controller_init cannot called after this function.
+ * @return  ESP_OK - success, other - failed
+ */
+esp_err_t esp_bt_controller_deinit(void);
+
+/**
+ * @brief Enable BT controller.
+ *               Due to a known issue, you cannot call esp_bt_controller_enable() a second time
+ *               to change the controller mode dynamically. To change controller mode, call
+ *               esp_bt_controller_disable() and then call esp_bt_controller_enable() with the new mode.
+ * @param mode : the mode(BLE/BT/BTDM) to enable.
+ * @return       ESP_OK - success, other - failed
+ */
+esp_err_t esp_bt_controller_enable(esp_bt_mode_t mode);
+
+/**
+ * @brief  Disable BT controller
+ * @return       ESP_OK - success, other - failed
+ */
+esp_err_t esp_bt_controller_disable(void);
+
+/**
+ * @brief  Get BT controller is initialised/de-initialised/enabled/disabled
+ * @return status value
+ */
+esp_bt_controller_status_t esp_bt_controller_get_status(void);
+
+/** @brief esp_vhci_host_callback
+ *  used for vhci call host function to notify what host need to do
+ */
+typedef struct esp_vhci_host_callback {
+    void (*notify_host_send_available)(void);               /*!< callback used to notify that the host can send packet to controller */
+    int (*notify_host_recv)(uint8_t *data, uint16_t len);   /*!< callback used to notify that the controller has a packet to send to the host*/
+} esp_vhci_host_callback_t;
+
+/** @brief esp_vhci_host_check_send_available
+ *  used for check actively if the host can send packet to controller or not.
+ *  @return true for ready to send, false means cannot send packet
+ */
+bool esp_vhci_host_check_send_available(void);
+
+/** @brief esp_vhci_host_send_packet
+ * host send packet to controller
+ * @param data the packet point
+ *,@param len the packet length
+ */
+void esp_vhci_host_send_packet(uint8_t *data, uint16_t len);
+
+/** @brief esp_vhci_host_register_callback
+ * register the vhci referece callback, the call back
+ * struct defined by vhci_host_callback structure.
+ * @param callback esp_vhci_host_callback type variable
+ */
+void esp_vhci_host_register_callback(const esp_vhci_host_callback_t *callback);
+
+/** @brief esp_bt_controller_mem_release
+ * release the memory by mode, if never use the bluetooth mode
+ * it can release the .bbs, .data and other section to heap.
+ * The total size is about 70k bytes.
+ *
+ * If esp_bt_controller_enable(mode) has already been called, calling
+ * esp_bt_controller_mem_release(ESP_BT_MODE_BTDM) will automatically
+ * release all memory which is not needed for the currently enabled
+ * Bluetooth controller mode.
+ *
+ * For example, calling esp_bt_controller_enable(ESP_BT_MODE_BLE) then
+ * esp_bt_controller_mem_release(ESP_BT_MODE_BTDM) will enable BLE modes
+ * and release memory only used by BT Classic. Also, call esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT)
+ * is the same.
+ *
+ * Note that once BT controller memory is released, the process cannot be reversed.
+ * If your firmware will later upgrade the Bluetooth controller mode (BLE -> BT Classic or disabled -> enabled)
+ * then do not call this function.
+ *
+ * If user never use bluetooth controller, could call esp_bt_controller_mem_release(ESP_BT_MODE_BTDM)
+ * before esp_bt_controller_init or after esp_bt_controller_deinit.
+ *
+ * For example, user only use bluetooth to config SSID and PASSWORD of WIFI, after config, will never use bluetooth.
+ * Then, could call esp_bt_controller_mem_release(ESP_BT_MODE_BTDM) after esp_bt_controller_deinit.
+ *
+ * @param mode : the mode want to release memory
+ * @return ESP_OK - success, other - failed
+ */
+esp_err_t esp_bt_controller_mem_release(esp_bt_mode_t mode);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ESP_BT_H__ */

+ 1 - 1
components/console/argtable3/argtable3.c

@@ -270,7 +270,7 @@ extern   char *suboptarg;               /* getsubopt(3) external variable */
  */
 
 #ifndef lint
-static const char rcsid[]="$Id: getopt_long.c,v 1.1 2009/10/16 19:50:28 rodney Exp rodney $";
+//static const char rcsid[]="$Id: getopt_long.c,v 1.1 2009/10/16 19:50:28 rodney Exp rodney $";
 #endif /* lint */
 /*-
  * Copyright (c) 2000 The NetBSD Foundation, Inc.

+ 7 - 4
components/driver/include/driver/ledc.h

@@ -99,10 +99,13 @@ typedef struct {
  * @brief Configuration parameters of LEDC Timer timer for ledc_timer_config function
  */
 typedef struct {
-    ledc_mode_t speed_mode;            /*!< LEDC speed speed_mode, high-speed mode or low-speed mode */
-    ledc_timer_bit_t duty_resolution;  /*!< LEDC channel duty resolution */
-    ledc_timer_t  timer_num;           /*!< The timer source of channel (0 - 3) */
-    uint32_t freq_hz;                  /*!< LEDC timer frequency (Hz) */
+    ledc_mode_t speed_mode;                /*!< LEDC speed speed_mode, high-speed mode or low-speed mode */
+    union {
+        ledc_timer_bit_t duty_resolution;  /*!< LEDC channel duty resolution */
+        ledc_timer_bit_t bit_num __attribute__((deprecated)); /*!< Deprecated in ESP-IDF 3.0. This is an alias to 'duty_resolution' for backward compatibility with ESP-IDF 2.1 */
+    };
+    ledc_timer_t  timer_num;               /*!< The timer source of channel (0 - 3) */
+    uint32_t freq_hz;                      /*!< LEDC timer frequency (Hz) */
 } ledc_timer_config_t;
 
 typedef intr_handle_t ledc_isr_handle_t;

+ 12 - 8
components/esp32/Kconfig

@@ -631,6 +631,10 @@ config BROWNOUT_DET_LVL
     default 7 if BROWNOUT_DET_LVL_SEL_7
 
 
+# Note about the use of "FRC1" name: currently FRC1 timer is not used for
+# high resolution timekeeping anymore. Instead the esp_timer API, implemented
+# using FRC2 timer, is used.
+# FRC1 name in the option name is kept for compatibility.
 choice ESP32_TIME_SYSCALL
     prompt "Timers used for gettimeofday function"
     default ESP32_TIME_SYSCALL_USE_RTC_FRC1
@@ -638,12 +642,12 @@ choice ESP32_TIME_SYSCALL
         This setting defines which hardware timers are used to
         implement 'gettimeofday' and 'time' functions in C library.
 
-        - If only FRC1 timer is used, gettimeofday will provide time at
-          microsecond resolution. Time will not be preserved when going
-          into deep sleep mode.
-        - If both FRC1 and RTC timers are used, timekeeping will
+        - If both high-resolution and RTC timers are used, timekeeping will
           continue in deep sleep. Time will be reported at 1 microsecond
-          resolution.
+          resolution. This is the default, and the recommended option.
+        - If only high-resolution timer is used, gettimeofday will
+          provide time at microsecond resolution. 
+          Time will not be preserved when going into deep sleep mode.
         - If only RTC timer is used, timekeeping will continue in
           deep sleep, but time will be measured at 6.(6) microsecond
           resolution. Also the gettimeofday function itself may take
@@ -653,12 +657,12 @@ choice ESP32_TIME_SYSCALL
         - When RTC is used for timekeeping, two RTC_STORE registers are
           used to keep time in deep sleep mode.
 
+config ESP32_TIME_SYSCALL_USE_RTC_FRC1
+    bool "RTC and high-resolution timer"
 config ESP32_TIME_SYSCALL_USE_RTC
     bool "RTC"
-config ESP32_TIME_SYSCALL_USE_RTC_FRC1
-    bool "RTC and FRC1"
 config ESP32_TIME_SYSCALL_USE_FRC1
-    bool "FRC1"
+    bool "High-resolution timer"
 config ESP32_TIME_SYSCALL_USE_NONE
     bool "None"
 endchoice

+ 2 - 2
components/esp32/include/esp_intr_alloc.h

@@ -37,13 +37,13 @@ extern "C" {
  */
 
 //Keep the LEVELx values as they are here; they match up with (1<<level)
-#define ESP_INTR_FLAG_LEVEL1		(1<<1)	///< Accept a Level 1 interrupt vector
+#define ESP_INTR_FLAG_LEVEL1		(1<<1)	///< Accept a Level 1 interrupt vector (lowest priority)
 #define ESP_INTR_FLAG_LEVEL2		(1<<2)	///< Accept a Level 2 interrupt vector
 #define ESP_INTR_FLAG_LEVEL3		(1<<3)	///< Accept a Level 3 interrupt vector
 #define ESP_INTR_FLAG_LEVEL4		(1<<4)	///< Accept a Level 4 interrupt vector
 #define ESP_INTR_FLAG_LEVEL5		(1<<5)	///< Accept a Level 5 interrupt vector
 #define ESP_INTR_FLAG_LEVEL6		(1<<6)	///< Accept a Level 6 interrupt vector
-#define ESP_INTR_FLAG_NMI			(1<<7)	///< Accept a Level 7 interrupt vector
+#define ESP_INTR_FLAG_NMI			(1<<7)	///< Accept a Level 7 interrupt vector (highest priority)
 #define ESP_INTR_FLAG_SHARED		(1<<8)	///< Interrupt can be shared between ISRs
 #define ESP_INTR_FLAG_EDGE			(1<<9)	///< Edge-triggered interrupt
 #define ESP_INTR_FLAG_IRAM			(1<<10)	///< ISR can be called if cache is disabled

+ 52 - 15
components/fatfs/src/vfs_fat.c

@@ -43,6 +43,25 @@ typedef struct {
     struct dirent cur_dirent;
 } vfs_fat_dir_t;
 
+/* Date and time storage formats in FAT */
+typedef union {
+    struct {
+        uint16_t mday : 5;  /* Day of month, 1 - 31 */
+        uint16_t mon : 4;   /* Month, 1 - 12 */
+        uint16_t year : 7;  /* Year, counting from 1980. E.g. 37 for 2017 */
+    };
+    uint16_t as_int;
+} fat_date_t;
+
+typedef union {
+    struct {
+        uint16_t sec : 5;   /* Seconds divided by 2. E.g. 21 for 42 seconds */
+        uint16_t min : 6;   /* Minutes, 0 - 59 */
+        uint16_t hour : 5;  /* Hour, 0 - 23 */
+    };
+    uint16_t as_int;
+} fat_time_t;
+
 static const char* TAG = "vfs_fat";
 
 static ssize_t vfs_fat_write(void* p, int fd, const void * data, size_t size);
@@ -389,11 +408,29 @@ static int vfs_fat_fstat(void* ctx, int fd, struct stat * st)
     FIL* file = &fat_ctx->files[fd];
     st->st_size = f_size(file);
     st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO | S_IFREG;
+    st->st_mtime = 0;
+    st->st_atime = 0;
+    st->st_ctime = 0;
     return 0;
 }
 
+static inline mode_t get_stat_mode(bool is_dir)
+{
+    return S_IRWXU | S_IRWXG | S_IRWXO |
+            ((is_dir) ? S_IFDIR : S_IFREG);
+}
+
 static int vfs_fat_stat(void* ctx, const char * path, struct stat * st)
 {
+    if (strcmp(path, "/") == 0) {
+        /* FatFS f_stat function does not work for the drive root.
+         * Just pretend that this is a directory.
+         */
+        memset(st, 0, sizeof(*st));
+        st->st_mode = get_stat_mode(true);
+        return 0;
+    }
+
     vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
     _lock_acquire(&fat_ctx->lock);
     prepend_drive_to_path(fat_ctx, &path, NULL);
@@ -405,23 +442,23 @@ static int vfs_fat_stat(void* ctx, const char * path, struct stat * st)
         errno = fresult_to_errno(res);
         return -1;
     }
+
+    memset(st, 0, sizeof(*st));
     st->st_size = info.fsize;
-    st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO |
-            ((info.fattrib & AM_DIR) ? S_IFDIR : S_IFREG);
-    struct tm tm;
-    uint16_t fdate = info.fdate;
-    tm.tm_mday = fdate & 0x1f;
-    fdate >>= 5;
-    tm.tm_mon = (fdate & 0xf) - 1;
-    fdate >>=4;
-    tm.tm_year = fdate + 80;
-    uint16_t ftime = info.ftime;
-    tm.tm_sec = (ftime & 0x1f) * 2;
-    ftime >>= 5;
-    tm.tm_min = (ftime & 0x3f);
-    ftime >>= 6;
-    tm.tm_hour = (ftime & 0x1f);
+    st->st_mode = get_stat_mode((info.fattrib & AM_DIR) != 0);
+    fat_date_t fdate = { .as_int = info.fdate };
+    fat_time_t ftime = { .as_int = info.ftime };
+    struct tm tm = {
+        .tm_mday = fdate.mday,
+        .tm_mon = fdate.mon - 1,    /* unlike tm_mday, tm_mon is zero-based */
+        .tm_year = fdate.year + 80,
+        .tm_sec = ftime.sec * 2,
+        .tm_min = ftime.min,
+        .tm_hour = ftime.hour
+    };
     st->st_mtime = mktime(&tm);
+    st->st_atime = 0;
+    st->st_ctime = 0;
     return 0;
 }
 

+ 12 - 7
components/fatfs/test/test_fatfs_common.c

@@ -125,15 +125,15 @@ void test_fatfs_lseek(const char* filename)
     TEST_ASSERT_EQUAL(0, fclose(f));
 }
 
-void test_fatfs_stat(const char* filename)
+void test_fatfs_stat(const char* filename, const char* root_dir)
 {
     struct tm tm;
-    tm.tm_year = 2016 - 1900;
-    tm.tm_mon = 0;
-    tm.tm_mday = 10;
-    tm.tm_hour = 16;
-    tm.tm_min = 30;
-    tm.tm_sec = 0;
+    tm.tm_year = 2017 - 1900;
+    tm.tm_mon = 11;
+    tm.tm_mday = 8;
+    tm.tm_hour = 19;
+    tm.tm_min = 51;
+    tm.tm_sec = 10;
     time_t t = mktime(&tm);
     printf("Setting time: %s", asctime(&tm));
     struct timeval now = { .tv_sec = t };
@@ -151,6 +151,11 @@ void test_fatfs_stat(const char* filename)
 
     TEST_ASSERT(st.st_mode & S_IFREG);
     TEST_ASSERT_FALSE(st.st_mode & S_IFDIR);
+
+    memset(&st, 0, sizeof(st));
+    TEST_ASSERT_EQUAL(0, stat(root_dir, &st));
+    TEST_ASSERT(st.st_mode & S_IFDIR);
+    TEST_ASSERT_FALSE(st.st_mode & S_IFREG);
 }
 
 void test_fatfs_unlink(const char* filename)

+ 1 - 1
components/fatfs/test/test_fatfs_common.h

@@ -43,7 +43,7 @@ void test_fatfs_open_max_files(const char* filename_prefix, size_t files_count);
 
 void test_fatfs_lseek(const char* filename);
 
-void test_fatfs_stat(const char* filename);
+void test_fatfs_stat(const char* filename, const char* root_dir);
 
 void test_fatfs_unlink(const char* filename);
 

+ 1 - 1
components/fatfs/test/test_fatfs_sdmmc.c

@@ -105,7 +105,7 @@ TEST_CASE("(SD) can lseek", "[fatfs][sdcard][ignore]")
 TEST_CASE("(SD) stat returns correct values", "[fatfs][ignore]")
 {
     test_setup();
-    test_fatfs_stat("/sdcard/stat.txt");
+    test_fatfs_stat("/sdcard/stat.txt", "/sdcard");
     test_teardown();
 }
 

+ 1 - 1
components/fatfs/test/test_fatfs_spiflash.c

@@ -100,7 +100,7 @@ TEST_CASE("(WL) can lseek", "[fatfs][wear_levelling]")
 TEST_CASE("(WL) stat returns correct values", "[fatfs][wear_levelling]")
 {
     test_setup();
-    test_fatfs_stat("/spiflash/stat.txt");
+    test_fatfs_stat("/spiflash/stat.txt", "/spiflash");
     test_teardown();
 }
 

+ 274 - 343
components/freertos/include/freertos/event_groups.h

@@ -104,7 +104,6 @@ extern "C" {
  * used to create a synchronisation point between multiple tasks (a
  * 'rendezvous').
  *
- * \defgroup EventGroup
  */
 
 
@@ -116,7 +115,6 @@ extern "C" {
  * xEventGroupCreate() returns an EventGroupHandle_t variable that can then
  * be used as a parameter to other event group functions.
  *
- * \defgroup EventGroupHandle_t EventGroupHandle_t
  * \ingroup EventGroup
  */
 typedef void * EventGroupHandle_t;
@@ -126,17 +124,11 @@ typedef void * EventGroupHandle_t;
  * number of bits it holds is set by configUSE_16_BIT_TICKS (16 bits if set to 1,
  * 32 bits if set to 0. 
  *
- * \defgroup EventBits_t EventBits_t
  * \ingroup EventGroup
  */
 typedef TickType_t EventBits_t;
 
 /**
- * event_groups.h
- *<pre>
- EventGroupHandle_t xEventGroupCreate( void );
- </pre>
- *
  * Create a new event group.
  *
  * Internally, within the FreeRTOS implementation, event groups use a [small]
@@ -162,25 +154,24 @@ typedef TickType_t EventBits_t;
  * event group then NULL is returned.  See http://www.freertos.org/a00111.html
  *
  * Example usage:
-   <pre>
-	// Declare a variable to hold the created event group.
-	EventGroupHandle_t xCreatedEventGroup;
-
-	// Attempt to create the event group.
-	xCreatedEventGroup = xEventGroupCreate();
-
-	// Was the event group created successfully?
-	if( xCreatedEventGroup == NULL )
-	{
-		// The event group was not created because there was insufficient
-		// FreeRTOS heap available.
-	}
-	else
-	{
-		// The event group was created.
-	}
-   </pre>
- * \defgroup xEventGroupCreate xEventGroupCreate
+ * @code{c}
+ * 	// Declare a variable to hold the created event group.
+ * 	EventGroupHandle_t xCreatedEventGroup;
+ *
+ * 	// Attempt to create the event group.
+ * 	xCreatedEventGroup = xEventGroupCreate();
+ *
+ * 	// Was the event group created successfully?
+ * 	if( xCreatedEventGroup == NULL )
+ * 	{
+ * 		// The event group was not created because there was insufficient
+ * 		// FreeRTOS heap available.
+ * 	}
+ * 	else
+ * 	{
+ * 		// The event group was created.
+ * 	}
+ * @endcode
  * \ingroup EventGroup
  */
 #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
@@ -188,11 +179,6 @@ typedef TickType_t EventBits_t;
 #endif
 
 /**
- * event_groups.h
- *<pre>
- EventGroupHandle_t xEventGroupCreateStatic( EventGroupHandle_t * pxEventGroupBuffer );
- </pre>
- *
  * Create a new event group.
  *
  * Internally, within the FreeRTOS implementation, event groups use a [small]
@@ -221,35 +207,26 @@ typedef TickType_t EventBits_t;
  * returned.  If pxEventGroupBuffer was NULL then NULL is returned.
  *
  * Example usage:
-   <pre>
-	// StaticEventGroup_t is a publicly accessible structure that has the same
-	// size and alignment requirements as the real event group structure.  It is
-	// provided as a mechanism for applications to know the size of the event
-	// group (which is dependent on the architecture and configuration file
-	// settings) without breaking the strict data hiding policy by exposing the
-	// real event group internals.  This StaticEventGroup_t variable is passed
-	// into the xSemaphoreCreateEventGroupStatic() function and is used to store
-	// the event group's data structures
-	StaticEventGroup_t xEventGroupBuffer;
-
-	// Create the event group without dynamically allocating any memory.
-	xEventGroup = xEventGroupCreateStatic( &xEventGroupBuffer );
-   </pre>
+ * @code{c}
+ * 	// StaticEventGroup_t is a publicly accessible structure that has the same
+ * 	// size and alignment requirements as the real event group structure.  It is
+ * 	// provided as a mechanism for applications to know the size of the event
+ * 	// group (which is dependent on the architecture and configuration file
+ * 	// settings) without breaking the strict data hiding policy by exposing the
+ * 	// real event group internals.  This StaticEventGroup_t variable is passed
+ * 	// into the xSemaphoreCreateEventGroupStatic() function and is used to store
+ * 	// the event group's data structures
+ * 	StaticEventGroup_t xEventGroupBuffer;
+ *
+ * 	// Create the event group without dynamically allocating any memory.
+ * 	xEventGroup = xEventGroupCreateStatic( &xEventGroupBuffer );
+ * @endcode
  */
 #if( configSUPPORT_STATIC_ALLOCATION == 1 )
 	EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer ) PRIVILEGED_FUNCTION;
 #endif
 
 /**
- * event_groups.h
- *<pre>
-	EventBits_t xEventGroupWaitBits( 	EventGroupHandle_t xEventGroup,
-										const EventBits_t uxBitsToWaitFor,
-										const BaseType_t xClearOnExit,
-										const BaseType_t xWaitForAllBits,
-										const TickType_t xTicksToWait );
- </pre>
- *
  * [Potentially] block to wait for one or more bits to be set within a
  * previously created event group.
  *
@@ -292,54 +269,48 @@ typedef TickType_t EventBits_t;
  * pdTRUE.
  *
  * Example usage:
-   <pre>
-   #define BIT_0	( 1 << 0 )
-   #define BIT_4	( 1 << 4 )
-
-   void aFunction( EventGroupHandle_t xEventGroup )
-   {
-   EventBits_t uxBits;
-   const TickType_t xTicksToWait = 100 / portTICK_PERIOD_MS;
-
-		// Wait a maximum of 100ms for either bit 0 or bit 4 to be set within
-		// the event group.  Clear the bits before exiting.
-		uxBits = xEventGroupWaitBits(
-					xEventGroup,	// The event group being tested.
-					BIT_0 | BIT_4,	// The bits within the event group to wait for.
-					pdTRUE,			// BIT_0 and BIT_4 should be cleared before returning.
-					pdFALSE,		// Don't wait for both bits, either bit will do.
-					xTicksToWait );	// Wait a maximum of 100ms for either bit to be set.
-
-		if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) )
-		{
-			// xEventGroupWaitBits() returned because both bits were set.
-		}
-		else if( ( uxBits & BIT_0 ) != 0 )
-		{
-			// xEventGroupWaitBits() returned because just BIT_0 was set.
-		}
-		else if( ( uxBits & BIT_4 ) != 0 )
-		{
-			// xEventGroupWaitBits() returned because just BIT_4 was set.
-		}
-		else
-		{
-			// xEventGroupWaitBits() returned because xTicksToWait ticks passed
-			// without either BIT_0 or BIT_4 becoming set.
-		}
-   }
-   </pre>
- * \defgroup xEventGroupWaitBits xEventGroupWaitBits
+ * @code{c}
+ *    #define BIT_0	( 1 << 0 )
+ *    #define BIT_4	( 1 << 4 )
+ *
+ *    void aFunction( EventGroupHandle_t xEventGroup )
+ *    {
+ *    EventBits_t uxBits;
+ *    const TickType_t xTicksToWait = 100 / portTICK_PERIOD_MS;
+ *
+ * 		// Wait a maximum of 100ms for either bit 0 or bit 4 to be set within
+ * 		// the event group.  Clear the bits before exiting.
+ * 		uxBits = xEventGroupWaitBits(
+ * 					xEventGroup,	// The event group being tested.
+ * 					BIT_0 | BIT_4,	// The bits within the event group to wait for.
+ * 					pdTRUE,			// BIT_0 and BIT_4 should be cleared before returning.
+ * 					pdFALSE,		// Don't wait for both bits, either bit will do.
+ * 					xTicksToWait );	// Wait a maximum of 100ms for either bit to be set.
+ *
+ * 		if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) )
+ * 		{
+ * 			// xEventGroupWaitBits() returned because both bits were set.
+ * 		}
+ * 		else if( ( uxBits & BIT_0 ) != 0 )
+ * 		{
+ * 			// xEventGroupWaitBits() returned because just BIT_0 was set.
+ * 		}
+ * 		else if( ( uxBits & BIT_4 ) != 0 )
+ * 		{
+ * 			// xEventGroupWaitBits() returned because just BIT_4 was set.
+ * 		}
+ * 		else
+ * 		{
+ * 			// xEventGroupWaitBits() returned because xTicksToWait ticks passed
+ * 			// without either BIT_0 or BIT_4 becoming set.
+ * 		}
+ *    }
+ * @endcode{c}
  * \ingroup EventGroup
  */
 EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION;
 
 /**
- * event_groups.h
- *<pre>
-	EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear );
- </pre>
- *
  * Clear bits within an event group.  This function cannot be called from an
  * interrupt.
  *
@@ -352,51 +323,45 @@ EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits
  * @return The value of the event group before the specified bits were cleared.
  *
  * Example usage:
-   <pre>
-   #define BIT_0	( 1 << 0 )
-   #define BIT_4	( 1 << 4 )
-
-   void aFunction( EventGroupHandle_t xEventGroup )
-   {
-   EventBits_t uxBits;
-
-		// Clear bit 0 and bit 4 in xEventGroup.
-		uxBits = xEventGroupClearBits(
-								xEventGroup,	// The event group being updated.
-								BIT_0 | BIT_4 );// The bits being cleared.
-
-		if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) )
-		{
-			// Both bit 0 and bit 4 were set before xEventGroupClearBits() was
-			// called.  Both will now be clear (not set).
-		}
-		else if( ( uxBits & BIT_0 ) != 0 )
-		{
-			// Bit 0 was set before xEventGroupClearBits() was called.  It will
-			// now be clear.
-		}
-		else if( ( uxBits & BIT_4 ) != 0 )
-		{
-			// Bit 4 was set before xEventGroupClearBits() was called.  It will
-			// now be clear.
-		}
-		else
-		{
-			// Neither bit 0 nor bit 4 were set in the first place.
-		}
-   }
-   </pre>
- * \defgroup xEventGroupClearBits xEventGroupClearBits
+ * @code{c}
+ *    #define BIT_0	( 1 << 0 )
+ *    #define BIT_4	( 1 << 4 )
+ *
+ *    void aFunction( EventGroupHandle_t xEventGroup )
+ *    {
+ *    EventBits_t uxBits;
+ *
+ * 		// Clear bit 0 and bit 4 in xEventGroup.
+ * 		uxBits = xEventGroupClearBits(
+ * 								xEventGroup,	// The event group being updated.
+ * 								BIT_0 | BIT_4 );// The bits being cleared.
+ *
+ * 		if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) )
+ * 		{
+ * 			// Both bit 0 and bit 4 were set before xEventGroupClearBits() was
+ * 			// called.  Both will now be clear (not set).
+ * 		}
+ * 		else if( ( uxBits & BIT_0 ) != 0 )
+ * 		{
+ * 			// Bit 0 was set before xEventGroupClearBits() was called.  It will
+ * 			// now be clear.
+ * 		}
+ * 		else if( ( uxBits & BIT_4 ) != 0 )
+ * 		{
+ * 			// Bit 4 was set before xEventGroupClearBits() was called.  It will
+ * 			// now be clear.
+ * 		}
+ * 		else
+ * 		{
+ * 			// Neither bit 0 nor bit 4 were set in the first place.
+ * 		}
+ *    }
+ * @endcode
  * \ingroup EventGroup
  */
 EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear ) PRIVILEGED_FUNCTION;
 
 /**
- * event_groups.h
- *<pre>
-	BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet );
- </pre>
- *
  * A version of xEventGroupClearBits() that can be called from an interrupt.
  *
  * Setting bits in an event group is not a deterministic operation because there
@@ -420,28 +385,27 @@ EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBit
  * if the timer service queue was full.
  *
  * Example usage:
-   <pre>
-   #define BIT_0	( 1 << 0 )
-   #define BIT_4	( 1 << 4 )
-
-   // An event group which it is assumed has already been created by a call to
-   // xEventGroupCreate().
-   EventGroupHandle_t xEventGroup;
-
-   void anInterruptHandler( void )
-   {
-		// Clear bit 0 and bit 4 in xEventGroup.
-		xResult = xEventGroupClearBitsFromISR(
-							xEventGroup,	 // The event group being updated.
-							BIT_0 | BIT_4 ); // The bits being set.
-
-		if( xResult == pdPASS )
-		{
-			// The message was posted successfully.
-		}
-  }
-   </pre>
- * \defgroup xEventGroupSetBitsFromISR xEventGroupSetBitsFromISR
+ * @code{c}
+ *    #define BIT_0	( 1 << 0 )
+ *    #define BIT_4	( 1 << 4 )
+ *
+ *    // An event group which it is assumed has already been created by a call to
+ *    // xEventGroupCreate().
+ *    EventGroupHandle_t xEventGroup;
+ *
+ *    void anInterruptHandler( void )
+ *    {
+ * 		// Clear bit 0 and bit 4 in xEventGroup.
+ * 		xResult = xEventGroupClearBitsFromISR(
+ * 							xEventGroup,	 // The event group being updated.
+ * 							BIT_0 | BIT_4 ); // The bits being set.
+ *
+ * 		if( xResult == pdPASS )
+ * 		{
+ * 			// The message was posted successfully.
+ * 		}
+ *   }
+ * @endcode
  * \ingroup EventGroup
  */
 #if( configUSE_TRACE_FACILITY == 1 )
@@ -451,11 +415,6 @@ EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBit
 #endif
 
 /**
- * event_groups.h
- *<pre>
-	EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet );
- </pre>
- *
  * Set bits within an event group.
  * This function cannot be called from an interrupt.  xEventGroupSetBitsFromISR()
  * is a version that can be called from an interrupt.
@@ -480,56 +439,50 @@ EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBit
  * event group value before the call to xEventGroupSetBits() returns.
  *
  * Example usage:
-   <pre>
-   #define BIT_0	( 1 << 0 )
-   #define BIT_4	( 1 << 4 )
-
-   void aFunction( EventGroupHandle_t xEventGroup )
-   {
-   EventBits_t uxBits;
-
-		// Set bit 0 and bit 4 in xEventGroup.
-		uxBits = xEventGroupSetBits(
-							xEventGroup,	// The event group being updated.
-							BIT_0 | BIT_4 );// The bits being set.
-
-		if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) )
-		{
-			// Both bit 0 and bit 4 remained set when the function returned.
-		}
-		else if( ( uxBits & BIT_0 ) != 0 )
-		{
-			// Bit 0 remained set when the function returned, but bit 4 was
-			// cleared.  It might be that bit 4 was cleared automatically as a
-			// task that was waiting for bit 4 was removed from the Blocked
-			// state.
-		}
-		else if( ( uxBits & BIT_4 ) != 0 )
-		{
-			// Bit 4 remained set when the function returned, but bit 0 was
-			// cleared.  It might be that bit 0 was cleared automatically as a
-			// task that was waiting for bit 0 was removed from the Blocked
-			// state.
-		}
-		else
-		{
-			// Neither bit 0 nor bit 4 remained set.  It might be that a task
-			// was waiting for both of the bits to be set, and the bits were
-			// cleared as the task left the Blocked state.
-		}
-   }
-   </pre>
- * \defgroup xEventGroupSetBits xEventGroupSetBits
+ * @code{c}
+ *    #define BIT_0	( 1 << 0 )
+ *    #define BIT_4	( 1 << 4 )
+ *
+ *    void aFunction( EventGroupHandle_t xEventGroup )
+ *    {
+ *    EventBits_t uxBits;
+ *
+ * 		// Set bit 0 and bit 4 in xEventGroup.
+ * 		uxBits = xEventGroupSetBits(
+ * 							xEventGroup,	// The event group being updated.
+ * 							BIT_0 | BIT_4 );// The bits being set.
+ *
+ * 		if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) )
+ * 		{
+ * 			// Both bit 0 and bit 4 remained set when the function returned.
+ * 		}
+ * 		else if( ( uxBits & BIT_0 ) != 0 )
+ * 		{
+ * 			// Bit 0 remained set when the function returned, but bit 4 was
+ * 			// cleared.  It might be that bit 4 was cleared automatically as a
+ * 			// task that was waiting for bit 4 was removed from the Blocked
+ * 			// state.
+ * 		}
+ * 		else if( ( uxBits & BIT_4 ) != 0 )
+ * 		{
+ * 			// Bit 4 remained set when the function returned, but bit 0 was
+ * 			// cleared.  It might be that bit 0 was cleared automatically as a
+ * 			// task that was waiting for bit 0 was removed from the Blocked
+ * 			// state.
+ * 		}
+ * 		else
+ * 		{
+ * 			// Neither bit 0 nor bit 4 remained set.  It might be that a task
+ * 			// was waiting for both of the bits to be set, and the bits were
+ * 			// cleared as the task left the Blocked state.
+ * 		}
+ *    }
+ * @endcode{c}
  * \ingroup EventGroup
  */
 EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet ) PRIVILEGED_FUNCTION;
 
 /**
- * event_groups.h
- *<pre>
-	BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, BaseType_t *pxHigherPriorityTaskWoken );
- </pre>
- *
  * A version of xEventGroupSetBits() that can be called from an interrupt.
  *
  * Setting bits in an event group is not a deterministic operation because there
@@ -561,39 +514,38 @@ EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_
  * if the timer service queue was full.
  *
  * Example usage:
-   <pre>
-   #define BIT_0	( 1 << 0 )
-   #define BIT_4	( 1 << 4 )
-
-   // An event group which it is assumed has already been created by a call to
-   // xEventGroupCreate().
-   EventGroupHandle_t xEventGroup;
-
-   void anInterruptHandler( void )
-   {
-   BaseType_t xHigherPriorityTaskWoken, xResult;
-
-		// xHigherPriorityTaskWoken must be initialised to pdFALSE.
-		xHigherPriorityTaskWoken = pdFALSE;
-
-		// Set bit 0 and bit 4 in xEventGroup.
-		xResult = xEventGroupSetBitsFromISR(
-							xEventGroup,	// The event group being updated.
-							BIT_0 | BIT_4   // The bits being set.
-							&xHigherPriorityTaskWoken );
-
-		// Was the message posted successfully?
-		if( xResult == pdPASS )
-		{
-			// If xHigherPriorityTaskWoken is now set to pdTRUE then a context
-			// switch should be requested.  The macro used is port specific and 
-			// will be either portYIELD_FROM_ISR() or portEND_SWITCHING_ISR() - 
-			// refer to the documentation page for the port being used.
-			portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
-		}
-  }
-   </pre>
- * \defgroup xEventGroupSetBitsFromISR xEventGroupSetBitsFromISR
+ * @code{c}
+ *    #define BIT_0	( 1 << 0 )
+ *    #define BIT_4	( 1 << 4 )
+ *
+ *    // An event group which it is assumed has already been created by a call to
+ *    // xEventGroupCreate().
+ *    EventGroupHandle_t xEventGroup;
+ *
+ *    void anInterruptHandler( void )
+ *    {
+ *    BaseType_t xHigherPriorityTaskWoken, xResult;
+ *
+ * 		// xHigherPriorityTaskWoken must be initialised to pdFALSE.
+ * 		xHigherPriorityTaskWoken = pdFALSE;
+ *
+ * 		// Set bit 0 and bit 4 in xEventGroup.
+ * 		xResult = xEventGroupSetBitsFromISR(
+ * 							xEventGroup,	// The event group being updated.
+ * 							BIT_0 | BIT_4   // The bits being set.
+ * 							&xHigherPriorityTaskWoken );
+ *
+ * 		// Was the message posted successfully?
+ * 		if( xResult == pdPASS )
+ * 		{
+ * 			// If xHigherPriorityTaskWoken is now set to pdTRUE then a context
+ * 			// switch should be requested.  The macro used is port specific and
+ * 			// will be either portYIELD_FROM_ISR() or portEND_SWITCHING_ISR() -
+ * 			// refer to the documentation page for the port being used.
+ * 			portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
+ * 		}
+ *   }
+ * @endcode
  * \ingroup EventGroup
  */
 #if( configUSE_TRACE_FACILITY == 1 )
@@ -603,14 +555,6 @@ EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_
 #endif
 
 /**
- * event_groups.h
- *<pre>
-	EventBits_t xEventGroupSync(	EventGroupHandle_t xEventGroup,
-									const EventBits_t uxBitsToSet,
-									const EventBits_t uxBitsToWaitFor,
-									TickType_t xTicksToWait );
- </pre>
- *
  * Atomically set bits within an event group, then wait for a combination of
  * bits to be set within the same event group.  This functionality is typically
  * used to synchronise multiple tasks, where each task has to wait for the other
@@ -648,93 +592,87 @@ EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_
  * automatically cleared.
  *
  * Example usage:
- <pre>
- // Bits used by the three tasks.
- #define TASK_0_BIT		( 1 << 0 )
- #define TASK_1_BIT		( 1 << 1 )
- #define TASK_2_BIT		( 1 << 2 )
-
- #define ALL_SYNC_BITS ( TASK_0_BIT | TASK_1_BIT | TASK_2_BIT )
-
- // Use an event group to synchronise three tasks.  It is assumed this event
- // group has already been created elsewhere.
- EventGroupHandle_t xEventBits;
-
- void vTask0( void *pvParameters )
- {
- EventBits_t uxReturn;
- TickType_t xTicksToWait = 100 / portTICK_PERIOD_MS;
-
-	 for( ;; )
-	 {
-		// Perform task functionality here.
-
-		// Set bit 0 in the event flag to note this task has reached the
-		// sync point.  The other two tasks will set the other two bits defined
-		// by ALL_SYNC_BITS.  All three tasks have reached the synchronisation
-		// point when all the ALL_SYNC_BITS are set.  Wait a maximum of 100ms
-		// for this to happen.
-		uxReturn = xEventGroupSync( xEventBits, TASK_0_BIT, ALL_SYNC_BITS, xTicksToWait );
-
-		if( ( uxReturn & ALL_SYNC_BITS ) == ALL_SYNC_BITS )
-		{
-			// All three tasks reached the synchronisation point before the call
-			// to xEventGroupSync() timed out.
-		}
-	}
- }
-
- void vTask1( void *pvParameters )
- {
-	 for( ;; )
-	 {
-		// Perform task functionality here.
-
-		// Set bit 1 in the event flag to note this task has reached the
-		// synchronisation point.  The other two tasks will set the other two
-		// bits defined by ALL_SYNC_BITS.  All three tasks have reached the
-		// synchronisation point when all the ALL_SYNC_BITS are set.  Wait
-		// indefinitely for this to happen.
-		xEventGroupSync( xEventBits, TASK_1_BIT, ALL_SYNC_BITS, portMAX_DELAY );
-
-		// xEventGroupSync() was called with an indefinite block time, so
-		// this task will only reach here if the syncrhonisation was made by all
-		// three tasks, so there is no need to test the return value.
-	 }
- }
-
- void vTask2( void *pvParameters )
- {
-	 for( ;; )
-	 {
-		// Perform task functionality here.
-
-		// Set bit 2 in the event flag to note this task has reached the
-		// synchronisation point.  The other two tasks will set the other two
-		// bits defined by ALL_SYNC_BITS.  All three tasks have reached the
-		// synchronisation point when all the ALL_SYNC_BITS are set.  Wait
-		// indefinitely for this to happen.
-		xEventGroupSync( xEventBits, TASK_2_BIT, ALL_SYNC_BITS, portMAX_DELAY );
-
-		// xEventGroupSync() was called with an indefinite block time, so
-		// this task will only reach here if the syncrhonisation was made by all
-		// three tasks, so there is no need to test the return value.
-	}
- }
-
- </pre>
- * \defgroup xEventGroupSync xEventGroupSync
+ * @code{c}
+ *  // Bits used by the three tasks.
+ *  #define TASK_0_BIT		( 1 << 0 )
+ *  #define TASK_1_BIT		( 1 << 1 )
+ *  #define TASK_2_BIT		( 1 << 2 )
+ *
+ *  #define ALL_SYNC_BITS ( TASK_0_BIT | TASK_1_BIT | TASK_2_BIT )
+ *
+ *  // Use an event group to synchronise three tasks.  It is assumed this event
+ *  // group has already been created elsewhere.
+ *  EventGroupHandle_t xEventBits;
+ *
+ *  void vTask0( void *pvParameters )
+ *  {
+ *  EventBits_t uxReturn;
+ *  TickType_t xTicksToWait = 100 / portTICK_PERIOD_MS;
+ *
+ * 	 for( ;; )
+ * 	 {
+ * 		// Perform task functionality here.
+ *
+ * 		// Set bit 0 in the event flag to note this task has reached the
+ * 		// sync point.  The other two tasks will set the other two bits defined
+ * 		// by ALL_SYNC_BITS.  All three tasks have reached the synchronisation
+ * 		// point when all the ALL_SYNC_BITS are set.  Wait a maximum of 100ms
+ * 		// for this to happen.
+ * 		uxReturn = xEventGroupSync( xEventBits, TASK_0_BIT, ALL_SYNC_BITS, xTicksToWait );
+ *
+ * 		if( ( uxReturn & ALL_SYNC_BITS ) == ALL_SYNC_BITS )
+ * 		{
+ * 			// All three tasks reached the synchronisation point before the call
+ * 			// to xEventGroupSync() timed out.
+ * 		}
+ * 	}
+ *  }
+ *
+ *  void vTask1( void *pvParameters )
+ *  {
+ * 	 for( ;; )
+ * 	 {
+ * 		// Perform task functionality here.
+ *
+ * 		// Set bit 1 in the event flag to note this task has reached the
+ * 		// synchronisation point.  The other two tasks will set the other two
+ * 		// bits defined by ALL_SYNC_BITS.  All three tasks have reached the
+ * 		// synchronisation point when all the ALL_SYNC_BITS are set.  Wait
+ * 		// indefinitely for this to happen.
+ * 		xEventGroupSync( xEventBits, TASK_1_BIT, ALL_SYNC_BITS, portMAX_DELAY );
+ *
+ * 		// xEventGroupSync() was called with an indefinite block time, so
+ * 		// this task will only reach here if the syncrhonisation was made by all
+ * 		// three tasks, so there is no need to test the return value.
+ * 	 }
+ *  }
+ *
+ *  void vTask2( void *pvParameters )
+ *  {
+ * 	 for( ;; )
+ * 	 {
+ * 		// Perform task functionality here.
+ *
+ * 		// Set bit 2 in the event flag to note this task has reached the
+ * 		// synchronisation point.  The other two tasks will set the other two
+ * 		// bits defined by ALL_SYNC_BITS.  All three tasks have reached the
+ * 		// synchronisation point when all the ALL_SYNC_BITS are set.  Wait
+ * 		// indefinitely for this to happen.
+ * 		xEventGroupSync( xEventBits, TASK_2_BIT, ALL_SYNC_BITS, portMAX_DELAY );
+ *
+ * 		// xEventGroupSync() was called with an indefinite block time, so
+ * 		// this task will only reach here if the syncrhonisation was made by all
+ * 		// three tasks, so there is no need to test the return value.
+ * 	}
+ *  }
+ *
+ * @endcode
  * \ingroup EventGroup
  */
 EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, const EventBits_t uxBitsToWaitFor, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION;
 
 
 /**
- * event_groups.h
- *<pre>
-	EventBits_t xEventGroupGetBits( EventGroupHandle_t xEventGroup );
- </pre>
- *
  * Returns the current value of the bits in an event group.  This function
  * cannot be used from an interrupt.
  *
@@ -742,33 +680,22 @@ EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup, const EventBits_t u
  *
  * @return The event group bits at the time xEventGroupGetBits() was called.
  *
- * \defgroup xEventGroupGetBits xEventGroupGetBits
  * \ingroup EventGroup
  */
 #define xEventGroupGetBits( xEventGroup ) xEventGroupClearBits( xEventGroup, 0 )
 
 /**
- * event_groups.h
- *<pre>
-	EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup );
- </pre>
- *
  * A version of xEventGroupGetBits() that can be called from an ISR.
  *
  * @param xEventGroup The event group being queried.
  *
  * @return The event group bits at the time xEventGroupGetBitsFromISR() was called.
  *
- * \defgroup xEventGroupGetBitsFromISR xEventGroupGetBitsFromISR
  * \ingroup EventGroup
  */
 EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup );
 
 /**
- * event_groups.h
- *<pre>
-	void xEventGroupDelete( EventGroupHandle_t xEventGroup );
- </pre>
  *
  * Delete an event group that was previously created by a call to
  * xEventGroupCreate().  Tasks that are blocked on the event group will be
@@ -778,6 +705,8 @@ EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup );
  */
 void vEventGroupDelete( EventGroupHandle_t xEventGroup );
 
+/** @cond */
+
 /* For internal use only. */
 void vEventGroupSetBitsCallback( void *pvEventGroup, const uint32_t ulBitsToSet );
 void vEventGroupClearBitsCallback( void *pvEventGroup, const uint32_t ulBitsToClear );
@@ -786,6 +715,8 @@ void vEventGroupClearBitsCallback( void *pvEventGroup, const uint32_t ulBitsToCl
 	UBaseType_t uxEventGroupGetNumber( void* xEventGroup );
 #endif
 
+/** @endcond */
+
 #ifdef __cplusplus
 }
 #endif

Разлика између датотеке није приказан због своје велике величине
+ 391 - 485
components/freertos/include/freertos/queue.h


+ 143 - 115
components/freertos/include/freertos/ringbuf.h

@@ -9,50 +9,58 @@
 extern "C" {
 #endif
 
-/*
-Header definitions for a FreeRTOS ringbuffer object
-
-A ringbuffer instantiated by these functions essentially acts like a FreeRTOS queue, with the
-difference that it's strictly FIFO and with the main advantage that you can put in randomly-sized
-items. The capacity, accordingly, isn't measured in the amount of items, but the amount of memory
-that is used for storing the items. Dependent on the size of the items, more or less of them will 
-fit in the ring buffer.
-
-This ringbuffer tries to be efficient with memory: when inserting an item, the item data will
-be copied to the ringbuffer memory. When retrieving an item, however, a reference to ringbuffer
-memory will be returned. The returned memory is guaranteed to be 32-bit aligned and contiguous.
-The application can use this memory, but as long as it does, ringbuffer writes that would write
-to this bit of memory will block.
-
-The requirement for items to be contiguous is slightly problematic when the only way to place
-the next item would involve a wraparound from the end to the beginning of the ringbuffer. This can
-be solved (or not) in a few ways:
-- type = RINGBUF_TYPE_ALLOWSPLIT: The insertion code will split the item in two items; one which fits
-in the space left at the end of the ringbuffer, one that contains the remaining data which is placed
-in the beginning. Two xRingbufferReceive calls will be needed to retrieve the data.
-- type = RINGBUF_TYPE_NOSPLIT: The insertion code will leave the room at the end of the ringbuffer
-unused and instead will put the entire item at the start of the ringbuffer, as soon as there is 
-enough free space.
-- type = RINGBUF_TYPE_BYTEBUF: This is your conventional byte-based ringbuffer. It does have no
-overhead, but it has no item contiguousness either: a read will just give you the entire written
-buffer space, or the space up to the end of the buffer, and writes can be broken up in any way 
-possible. Note that this type cannot do a 2nd read before returning the memory of the 1st.
-
-The maximum size of an item will be affected by this decision. When split items are allowed, it's
-acceptable to push items of (buffer_size)-16 bytes into the buffer. When it's not allowed, the
-maximum size is (buffer_size/2)-8 bytes. The bytebuf can fill the entire buffer with data, it has
-no overhead.
-*/
-
 #include <freertos/queue.h>
 
 //An opaque handle for a ringbuff object.
 typedef void * RingbufHandle_t;
 
-//The various types of buffer
+/**
+ * @brief The various types of buffer
+ *
+ * A ringbuffer instantiated by these functions essentially acts like a
+ * FreeRTOS queue, with the difference that it's strictly FIFO and with
+ * the main advantage that you can put in randomly-sized items. The capacity,
+ * accordingly, isn't measured in the amount of items, but the amount of
+ * memory that is used for storing the items. Dependent on the size of
+ * the items, more or less of them will fit in the ring buffer.
+ *
+ * This ringbuffer tries to be efficient with memory: when inserting an item,
+ * the item data will be copied to the ringbuffer memory. When retrieving
+ * an item, however, a reference to ringbuffer memory will be returned.
+ * The returned memory is guaranteed to be 32-bit aligned and contiguous.
+ * The application can use this memory, but as long as it does, ringbuffer
+ * writes that would write to this bit of memory will block.
+ *
+ * The requirement for items to be contiguous is slightly problematic when
+ * the only way to place the next item would involve a wraparound from the end
+ * to the beginning of the ringbuffer. This can be solved (or not) in a few ways,
+ * see descriptions of possible ringbuf_type_t types below.
+ *
+ * The maximum size of an item will be affected by ringbuffer type.
+ * When split items are allowed, it is acceptable to push items of
+ * (buffer_size)-16 bytes into the buffer.
+ * When it's not allowed, the maximum size is (buffer_size/2)-8 bytes.
+ * The bytebuf can fill the entire buffer with data, it has no overhead.
+ */
 typedef enum {
+	/** The insertion code will leave the room at the end of the ringbuffer
+	 * unused and instead will put the entire item at the start of the ringbuffer,
+	 * as soon as there is enough free space.
+	 */
 	RINGBUF_TYPE_NOSPLIT = 0,
+	/** The insertion code will split the item in two items; one which fits
+	 * in the space left at the end of the ringbuffer, one that contains
+	 * the remaining data which is placed in the beginning.
+	 * Two xRingbufferReceive calls will be needed to retrieve the data.
+	 */
 	RINGBUF_TYPE_ALLOWSPLIT,
+	/** This is your conventional byte-based ringbuffer. It does have no
+	 * overhead, but it has no item contiguousness either: a read will just
+	 * give you the entire written buffer space, or the space up to the end
+	 * of the buffer, and writes can be broken up in any way possible.
+	 * Note that this type cannot do a 2nd read before returning the memory
+	 * of the 1st.
+	 */
 	RINGBUF_TYPE_BYTEBUF
 } ringbuf_type_t;
 
@@ -60,22 +68,19 @@ typedef enum {
 /**
  * @brief  Create a ring buffer
  *
- * @param  buf_length : Length of circular buffer, in bytes. Each entry will take up its own length, plus a header
- *                      that at the moment is equal to sizeof(size_t).
- * @param allow_split_items : pdTRUE if it is acceptable that item data is inserted as two
- *                            items instead of one.
+ * @param  buf_length  Length of circular buffer, in bytes. Each entry will
+ *                     take up its own length, plus a header that at the moment
+ *                     is equal to sizeof(size_t).
+ * @param  type  Type of ring buffer, see ringbuf_type_t.
  *
  * @return A RingbufHandle_t handle to the created ringbuffer, or NULL in case of error.
  */
 RingbufHandle_t xRingbufferCreate(size_t buf_length, ringbuf_type_t type);
 
-
 /**
  * @brief  Delete a ring buffer
  *
- * @param  ringbuf - Ring buffer to delete
- *
- * @return void
+ * @param  ringbuf  Ring buffer to delete
  */
 void vRingbufferDelete(RingbufHandle_t ringbuf);
 
@@ -83,7 +88,7 @@ void vRingbufferDelete(RingbufHandle_t ringbuf);
 /**
  * @brief  Get maximum size of an item that can be placed in the ring buffer
  *
- * @param  ringbuf - Ring buffer to query
+ * @param  ringbuf  Ring buffer to query
  *
  * @return Maximum size, in bytes, of an item that can be placed in a ring buffer.
  */
@@ -93,13 +98,15 @@ size_t xRingbufferGetMaxItemSize(RingbufHandle_t ringbuf);
 /**
  * @brief  Insert an item into the ring buffer
  *
- * @param  ringbuf - Ring buffer to insert the item into
- * @param  data - Pointer to data to insert. NULL is allowed if data_size is 0.
- * @param  data_size - Size of data to insert. A value of 0 is allowed.
- * @param  xTicksToWait - Ticks to wait for room in the ringbuffer.
+ * @param  ringbuf  Ring buffer to insert the item into
+ * @param  data  Pointer to data to insert. NULL is allowed if data_size is 0.
+ * @param  data_size  Size of data to insert. A value of 0 is allowed.
+ * @param  ticks_to_wait  Ticks to wait for room in the ringbuffer.
  *
- * @return pdTRUE if succeeded, pdFALSE on time-out or when the buffer is larger 
- *         than indicated by xRingbufferGetMaxItemSize(ringbuf).
+ * @return
+ *      - pdTRUE if succeeded
+ *      - pdFALSE on time-out or when the buffer is larger than indicated
+ *        by xRingbufferGetMaxItemSize(ringbuf).
  */
 BaseType_t xRingbufferSend(RingbufHandle_t ringbuf, void *data, size_t data_size, TickType_t ticks_to_wait);
 
@@ -107,11 +114,11 @@ BaseType_t xRingbufferSend(RingbufHandle_t ringbuf, void *data, size_t data_size
 /**
  * @brief  Insert an item into the ring buffer from an ISR
  *
- * @param  ringbuf - Ring buffer to insert the item into
- * @param  data - Pointer to data to insert. NULL is allowed if data_size is 0.
- * @param  data_size - Size of data to insert. A value of 0 is allowed.
- * @param  higher_prio_task_awoken - Value pointed to will be set to pdTRUE if the push woke up a higher
- *                                     priority task.
+ * @param  ringbuf  Ring buffer to insert the item into
+ * @param  data  Pointer to data to insert. NULL is allowed if data_size is 0.
+ * @param  data_size  Size of data to insert. A value of 0 is allowed.
+ * @param[out]  higher_prio_task_awoken  Value pointed to will be set to pdTRUE
+ *                                  if the push woke up a higher priority task.
  *
  * @return pdTRUE if succeeded, pdFALSE when the ring buffer does not have space.
  */
@@ -120,14 +127,18 @@ BaseType_t xRingbufferSendFromISR(RingbufHandle_t ringbuf, void *data, size_t da
 /**
  * @brief  Retrieve an item from the ring buffer
  *
- * @note A call to vRingbufferReturnItem() is required after this to free up the data received.
+ * @note A call to vRingbufferReturnItem() is required after this to free up
+ * the data received.
  *
- * @param  ringbuf - Ring buffer to retrieve the item from
- * @param  item_size - Pointer to a variable to which the size of the retrieved item will be written.
- * @param  xTicksToWait - Ticks to wait for items in the ringbuffer.
+ * @param  ringbuf  Ring buffer to retrieve the item from
+ * @param[out] item_size  Pointer to a variable to which the size of the
+ *                        retrieved item will be written.
+ * @param  ticks_to_wait  Ticks to wait for items in the ringbuffer.
  *
- * @return Pointer to the retrieved item on success; *item_size filled with the length of the 
- *         item. NULL on timeout, *item_size is untouched in that case.
+ * @return
+ *      - pointer to the retrieved item on success; *item_size filled with
+ *        the length of the item.
+ *      - NULL on timeout, *item_size is untouched in that case.
  */
 void *xRingbufferReceive(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait);
 
@@ -135,44 +146,58 @@ void *xRingbufferReceive(RingbufHandle_t ringbuf, size_t *item_size, TickType_t
 /**
  * @brief  Retrieve an item from the ring buffer from an ISR
  *
- * @note A call to vRingbufferReturnItemFromISR() is required after this to free up the data received
+ * @note A call to vRingbufferReturnItemFromISR() is required after this to
+ * free up the data received
  *
- * @param  ringbuf - Ring buffer to retrieve the item from
- * @param  item_size - Pointer to a variable to which the size of the retrieved item will be written.
+ * @param  ringbuf  Ring buffer to retrieve the item from
+ * @param[out] item_size  Pointer to a variable to which the size of the
+ *                        retrieved item will be written.
  *
- * @return Pointer to the retrieved item on success; *item_size filled with the length of the 
- *         item. NULL when the ringbuffer is empty, *item_size is untouched in that case.
+ * @return
+ *      - Pointer to the retrieved item on success; *item_size filled with
+ *        the length of the item.
+ *      - NULL when the ringbuffer is empty, *item_size is untouched in that case.
  */
 void *xRingbufferReceiveFromISR(RingbufHandle_t ringbuf, size_t *item_size);
 
 
 /**
- * @brief  Retrieve bytes from a ByteBuf type of ring buffer, specifying the maximum amount of bytes
- * to return
-
- * @note A call to vRingbufferReturnItem() is required after this to free up the data received.
- *
- * @param  ringbuf - Ring buffer to retrieve the item from
- * @param  item_size - Pointer to a variable to which the size of the retrieved item will be written.
- * @param  xTicksToWait - Ticks to wait for items in the ringbuffer.
- *
- * @return Pointer to the retrieved item on success; *item_size filled with the length of the 
- *         item. NULL on timeout, *item_size is untouched in that case.
+ * @brief  Retrieve bytes from a ByteBuf type of ring buffer,
+ * specifying the maximum amount of bytes to return
+ *
+ * @note A call to vRingbufferReturnItem() is required after this to free up
+ * the data received.
+ *
+ * @param  ringbuf  Ring buffer to retrieve the item from
+ * @param[out] item_size  Pointer to a variable to which the size
+ *                        of the retrieved item will be written.
+ * @param  ticks_to_wait  Ticks to wait for items in the ringbuffer.
+ * @param  wanted_size  Maximum number of bytes to return.
+ *
+ * @return
+ *      - Pointer to the retrieved item on success; *item_size filled with
+ *        the length of the item.
+ *      - NULL on timeout, *item_size is untouched in that case.
  */
 void *xRingbufferReceiveUpTo(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait, size_t wanted_size);
 
 
 /**
- * @brief  Retrieve bytes from a ByteBuf type of ring buffer, specifying the maximum amount of bytes
- * to return. Call this from an ISR.
+ * @brief  Retrieve bytes from a ByteBuf type of ring buffer,
+ * specifying the maximum amount of bytes to return. Call this from an ISR.
  *
- * @note A call to vRingbufferReturnItemFromISR() is required after this to free up the data received
+ * @note A call to vRingbufferReturnItemFromISR() is required after this
+ * to free up the data received.
  *
- * @param  ringbuf - Ring buffer to retrieve the item from
- * @param  item_size - Pointer to a variable to which the size of the retrieved item will be written.
+ * @param  ringbuf  Ring buffer to retrieve the item from
+ * @param[out] item_size  Pointer to a variable to which the size of the
+ *                        retrieved item will be written.
+ * @param  wanted_size  Maximum number of bytes to return.
  *
- * @return Pointer to the retrieved item on success; *item_size filled with the length of the 
- *         item. NULL when the ringbuffer is empty, *item_size is untouched in that case.
+ * @return
+ *      - Pointer to the retrieved item on success; *item_size filled with
+ *        the length of the item.
+ *      - NULL when the ringbuffer is empty, *item_size is untouched in that case.
  */
 void *xRingbufferReceiveUpToFromISR(RingbufHandle_t ringbuf, size_t *item_size, size_t wanted_size);
 
@@ -181,10 +206,8 @@ void *xRingbufferReceiveUpToFromISR(RingbufHandle_t ringbuf, size_t *item_size,
 /**
  * @brief  Return a previously-retrieved item to the ringbuffer
  *
- * @param  ringbuf - Ring buffer the item was retrieved from
- * @param  item - Item that was received earlier
- *
- * @return void
+ * @param  ringbuf  Ring buffer the item was retrieved from
+ * @param  item  Item that was received earlier
  */
 void vRingbufferReturnItem(RingbufHandle_t ringbuf, void *item);
 
@@ -193,34 +216,37 @@ void vRingbufferReturnItem(RingbufHandle_t ringbuf, void *item);
 /**
  * @brief  Return a previously-retrieved item to the ringbuffer from an ISR
  *
- * @param  ringbuf - Ring buffer the item was retrieved from
- * @param  item - Item that was received earlier
- * @param  higher_prio_task_awoken - Value pointed to will be set to pdTRUE if the push woke up a higher
- *                                     priority task.
- *
- * @return void
+ * @param  ringbuf  Ring buffer the item was retrieved from
+ * @param  item  Item that was received earlier
+ * @param[out]  higher_prio_task_awoken  Value pointed to will be set to pdTRUE
+ *                                  if the push woke up a higher priority task.
  */
 void vRingbufferReturnItemFromISR(RingbufHandle_t ringbuf, void *item, BaseType_t *higher_prio_task_awoken);
 
 
 /**
- * @brief  Add the ringbuffer to a queue set. This specifically adds the semaphore that indicates
- *         more space has become available in the ringbuffer.
+ * @brief  Add the ringbuffer to a queue set.
  *
- * @param  ringbuf - Ring buffer to add to the queue set
- * @param  xQueueSet - Queue set to add the ringbuffer to
+ * This specifically adds the semaphore that indicates more space
+ * has become available in the ringbuffer.
  *
- * @return pdTRUE on success, pdFALSE otherwise
+ * @param  ringbuf  Ring buffer to add to the queue set
+ * @param  xQueueSet  Queue set to add the ringbuffer to
+ *
+ * @return
+ *      - pdTRUE on success, pdFALSE otherwise
  */
 BaseType_t xRingbufferAddToQueueSetRead(RingbufHandle_t ringbuf, QueueSetHandle_t xQueueSet);
 
 
 /**
- * @brief  Add the ringbuffer to a queue set. This specifically adds the semaphore that indicates
- *         something has been written into the ringbuffer.
+ * @brief  Add the ringbuffer to a queue set.
  *
- * @param  ringbuf - Ring buffer to add to the queue set
- * @param  xQueueSet - Queue set to add the ringbuffer to
+ * This specifically adds the semaphore that indicates something has been
+ * written into the ringbuffer.
+ *
+ * @param  ringbuf  Ring buffer to add to the queue set
+ * @param  xQueueSet  Queue set to add the ringbuffer to
  *
  * @return pdTRUE on success, pdFALSE otherwise
  */
@@ -228,11 +254,13 @@ BaseType_t xRingbufferAddToQueueSetWrite(RingbufHandle_t ringbuf, QueueSetHandle
 
 
 /**
- * @brief  Remove the ringbuffer from a queue set. This specifically removes the semaphore that indicates
- *         more space has become available in the ringbuffer.
+ * @brief  Remove the ringbuffer from a queue set.
+ *
+ * This specifically removes the semaphore that indicates more space
+ * has become available in the ringbuffer.
  *
- * @param  ringbuf - Ring buffer to remove from the queue set
- * @param  xQueueSet - Queue set to remove the ringbuffer from
+ * @param  ringbuf  Ring buffer to remove from the queue set
+ * @param  xQueueSet  Queue set to remove the ringbuffer from
  *
  * @return pdTRUE on success, pdFALSE otherwise
  */
@@ -240,11 +268,13 @@ BaseType_t xRingbufferRemoveFromQueueSetRead(RingbufHandle_t ringbuf, QueueSetHa
 
 
 /**
- * @brief  Remove the ringbuffer from a queue set. This specifically removes the semaphore that indicates
- *         something has been written to the ringbuffer.
+ * @brief  Remove the ringbuffer from a queue set.
  *
- * @param  ringbuf - Ring buffer to remove from the queue set
- * @param  xQueueSet - Queue set to remove the ringbuffer from
+ * This specifically removes the semaphore that indicates something
+ * has been written to the ringbuffer.
+ *
+ * @param  ringbuf  Ring buffer to remove from the queue set
+ * @param  xQueueSet  Queue set to remove the ringbuffer from
  *
  * @return pdTRUE on success, pdFALSE otherwise
  */
@@ -254,9 +284,7 @@ BaseType_t xRingbufferRemoveFromQueueSetWrite(RingbufHandle_t ringbuf, QueueSetH
 /**
  * @brief  Debugging function to print the internal pointers in the ring buffer
  *
- * @param  ringbuf - Ring buffer to show
- *
- * @return void
+ * @param  ringbuf  Ring buffer to show
  */
 void xRingbufferPrintInfo(RingbufHandle_t ringbuf);
 

Разлика између датотеке није приказан због своје велике величине
+ 360 - 423
components/freertos/include/freertos/semphr.h


Разлика између датотеке није приказан због своје велике величине
+ 422 - 349
components/freertos/include/freertos/task.h


+ 33 - 92
components/freertos/include/freertos/timers.h

@@ -117,24 +117,18 @@ or interrupt version of the queue send function should be used. */
  */
 typedef void * TimerHandle_t;
 
-/*
+/**
  * Defines the prototype to which timer callback functions must conform.
  */
 typedef void (*TimerCallbackFunction_t)( TimerHandle_t xTimer );
 
-/*
+/**
  * Defines the prototype to which functions used with the
  * xTimerPendFunctionCallFromISR() function must conform.
  */
 typedef void (*PendedFunction_t)( void *, uint32_t );
 
 /**
- * TimerHandle_t xTimerCreate( 	const char * const pcTimerName,
- * 								TickType_t xTimerPeriodInTicks,
- * 								UBaseType_t uxAutoReload,
- * 								void * pvTimerID,
- * 								TimerCallbackFunction_t pxCallbackFunction );
- *
  * Creates a new software timer instance, and returns a handle by which the
  * created software timer can be referenced.
  *
@@ -184,7 +178,7 @@ typedef void (*PendedFunction_t)( void *, uint32_t );
  * structures, or the timer period was set to 0) then NULL is returned.
  *
  * Example usage:
- * @verbatim
+ * @code{c}
  * #define NUM_TIMERS 5
  *
  * // An array to hold handles to the created timers.
@@ -263,7 +257,7 @@ typedef void (*PendedFunction_t)( void *, uint32_t );
  *     // Should not reach here.
  *     for( ;; );
  * }
- * @endverbatim
+ * @endcode
  */
 #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
 	TimerHandle_t xTimerCreate(	const char * const pcTimerName,
@@ -274,13 +268,6 @@ typedef void (*PendedFunction_t)( void *, uint32_t );
 #endif
 
  /**
-  * TimerHandle_t xTimerCreateStatic(const char * const pcTimerName,
-  * 									TickType_t xTimerPeriodInTicks,
-  * 									UBaseType_t uxAutoReload,
-  * 									void * pvTimerID,
-  * 									TimerCallbackFunction_t pxCallbackFunction,
-  *									StaticTimer_t *pxTimerBuffer );
-  *
   * Creates a new software timer instance, and returns a handle by which the
   * created software timer can be referenced.
   *
@@ -332,7 +319,7 @@ typedef void (*PendedFunction_t)( void *, uint32_t );
   * returned.  If pxTimerBuffer was NULL then NULL is returned.
   *
   * Example usage:
-  * @verbatim
+  * @code{c}
   *
   * // The buffer used to hold the software timer's data structure.
   * static StaticTimer_t xTimerBuffer;
@@ -393,20 +380,18 @@ typedef void (*PendedFunction_t)( void *, uint32_t );
   *     // Should not reach here.
   *     for( ;; );
   * }
-  * @endverbatim
+  * @endcode
   */
  #if( configSUPPORT_STATIC_ALLOCATION == 1 )
- 	TimerHandle_t xTimerCreateStatic(	const char * const pcTimerName,
- 										const TickType_t xTimerPeriodInTicks,
- 										const UBaseType_t uxAutoReload,
- 										void * const pvTimerID,
- 										TimerCallbackFunction_t pxCallbackFunction,
- 										StaticTimer_t *pxTimerBuffer ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
+	TimerHandle_t xTimerCreateStatic(	const char * const pcTimerName,
+										const TickType_t xTimerPeriodInTicks,
+										const UBaseType_t uxAutoReload,
+										void * const pvTimerID,
+										TimerCallbackFunction_t pxCallbackFunction,
+										StaticTimer_t *pxTimerBuffer ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
  #endif /* configSUPPORT_STATIC_ALLOCATION */
 
 /**
- * void *pvTimerGetTimerID( TimerHandle_t xTimer );
- *
  * Returns the ID assigned to the timer.
  *
  * IDs are assigned to timers using the pvTimerID parameter of the call to
@@ -427,8 +412,6 @@ typedef void (*PendedFunction_t)( void *, uint32_t );
 void *pvTimerGetTimerID( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
 
 /**
- * void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID );
- *
  * Sets the ID assigned to the timer.
  *
  * IDs are assigned to timers using the pvTimerID parameter of the call to
@@ -448,12 +431,12 @@ void *pvTimerGetTimerID( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
 void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ) PRIVILEGED_FUNCTION;
 
 /**
- * BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer );
- *
  * Queries a timer to see if it is active or dormant.
  *
  * A timer will be dormant if:
+ *
  *     1) It has been created but not started, or
+ *
  *     2) It is an expired one-shot timer that has not been restarted.
  *
  * Timers are created in the dormant state.  The xTimerStart(), xTimerReset(),
@@ -467,7 +450,7 @@ void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ) PRIVILEGED_FUNCTION
  * pdFALSE will be returned if the timer is active.
  *
  * Example usage:
- * @verbatim
+ * @code{c}
  * // This function assumes xTimer has already been created.
  * void vAFunction( TimerHandle_t xTimer )
  * {
@@ -480,13 +463,11 @@ void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ) PRIVILEGED_FUNCTION
  *         // xTimer is not active, do something else.
  *     }
  * }
- * @endverbatim
+ * @endcode
  */
 BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
 
 /**
- * TaskHandle_t xTimerGetTimerDaemonTaskHandle( void );
- *
  * xTimerGetTimerDaemonTaskHandle() is only available if
  * INCLUDE_xTimerGetTimerDaemonTaskHandle is set to 1 in FreeRTOSConfig.h.
  *
@@ -496,8 +477,6 @@ BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
 TaskHandle_t xTimerGetTimerDaemonTaskHandle( void );
 
 /**
- * TickType_t xTimerGetPeriod( TimerHandle_t xTimer );
- *
  * Returns the period of a timer.
  *
  * @param xTimer The handle of the timer being queried.
@@ -507,8 +486,6 @@ TaskHandle_t xTimerGetTimerDaemonTaskHandle( void );
 TickType_t xTimerGetPeriod( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
 
 /**
- * TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer );
- *
  * Returns the time in ticks at which the timer will expire.  If this is less
  * than the current tick count then the expiry time has overflowed from the
  * current time.
@@ -522,8 +499,6 @@ TickType_t xTimerGetPeriod( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
 TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
 
 /**
- * BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xTicksToWait );
- *
  * Timer functionality is provided by a timer service/daemon task.  Many of the
  * public FreeRTOS timer API functions send commands to the timer service task
  * through a queue called the timer command queue.  The timer command queue is
@@ -574,8 +549,6 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
 #define xTimerStart( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_START, ( xTaskGetTickCount() ), NULL, ( xTicksToWait ) )
 
 /**
- * BaseType_t xTimerStop( TimerHandle_t xTimer, TickType_t xTicksToWait );
- *
  * Timer functionality is provided by a timer service/daemon task.  Many of the
  * public FreeRTOS timer API functions send commands to the timer service task
  * through a queue called the timer command queue.  The timer command queue is
@@ -616,10 +589,6 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
 #define xTimerStop( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_STOP, 0U, NULL, ( xTicksToWait ) )
 
 /**
- * BaseType_t xTimerChangePeriod( 	TimerHandle_t xTimer,
- *										TickType_t xNewPeriod,
- *										TickType_t xTicksToWait );
- *
  * Timer functionality is provided by a timer service/daemon task.  Many of the
  * public FreeRTOS timer API functions send commands to the timer service task
  * through a queue called the timer command queue.  The timer command queue is
@@ -661,7 +630,7 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
  * configTIMER_TASK_PRIORITY configuration constant.
  *
  * Example usage:
- * @verbatim
+ * @code{c}
  * // This function assumes xTimer has already been created.  If the timer
  * // referenced by xTimer is already active when it is called, then the timer
  * // is deleted.  If the timer referenced by xTimer is not active when it is
@@ -691,13 +660,11 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
  *         }
  *     }
  * }
- * @endverbatim
+ * @endcode
  */
  #define xTimerChangePeriod( xTimer, xNewPeriod, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_CHANGE_PERIOD, ( xNewPeriod ), NULL, ( xTicksToWait ) )
 
 /**
- * BaseType_t xTimerDelete( TimerHandle_t xTimer, TickType_t xTicksToWait );
- *
  * Timer functionality is provided by a timer service/daemon task.  Many of the
  * public FreeRTOS timer API functions send commands to the timer service task
  * through a queue called the timer command queue.  The timer command queue is
@@ -734,8 +701,6 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
 #define xTimerDelete( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_DELETE, 0U, NULL, ( xTicksToWait ) )
 
 /**
- * BaseType_t xTimerReset( TimerHandle_t xTimer, TickType_t xTicksToWait );
- *
  * Timer functionality is provided by a timer service/daemon task.  Many of the
  * public FreeRTOS timer API functions send commands to the timer service task
  * through a queue called the timer command queue.  The timer command queue is
@@ -781,7 +746,7 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
  * configuration constant.
  *
  * Example usage:
- * @verbatim
+ * @code{c}
  * // When a key is pressed, an LCD back-light is switched on.  If 5 seconds pass
  * // without a key being pressed, then the LCD back-light is switched off.  In
  * // this case, the timer is a one-shot timer.
@@ -853,14 +818,11 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
  *     // Should not reach here.
  *     for( ;; );
  * }
- * @endverbatim
+ * @endcode
  */
 #define xTimerReset( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_RESET, ( xTaskGetTickCount() ), NULL, ( xTicksToWait ) )
 
 /**
- * BaseType_t xTimerStartFromISR( 	TimerHandle_t xTimer,
- *									BaseType_t *pxHigherPriorityTaskWoken );
- *
  * A version of xTimerStart() that can be called from an interrupt service
  * routine.
  *
@@ -888,7 +850,7 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
  * configuration constant.
  *
  * Example usage:
- * @verbatim
+ * @code{c}
  * // This scenario assumes xBacklightTimer has already been created.  When a
  * // key is pressed, an LCD back-light is switched on.  If 5 seconds pass
  * // without a key being pressed, then the LCD back-light is switched off.  In
@@ -939,14 +901,11 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
  *         // depends on the FreeRTOS port being used).
  *     }
  * }
- * @endverbatim
+ * @endcode
  */
 #define xTimerStartFromISR( xTimer, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_START_FROM_ISR, ( xTaskGetTickCountFromISR() ), ( pxHigherPriorityTaskWoken ), 0U )
 
 /**
- * BaseType_t xTimerStopFromISR( 	TimerHandle_t xTimer,
- *									BaseType_t *pxHigherPriorityTaskWoken );
- *
  * A version of xTimerStop() that can be called from an interrupt service
  * routine.
  *
@@ -972,7 +931,7 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
  * priority is set by the configTIMER_TASK_PRIORITY configuration constant.
  *
  * Example usage:
- * @verbatim
+ * @code{c}
  * // This scenario assumes xTimer has already been created and started.  When
  * // an interrupt occurs, the timer should be simply stopped.
  *
@@ -1002,15 +961,11 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
  *         // depends on the FreeRTOS port being used).
  *     }
  * }
- * @endverbatim
+ * @endcode
  */
 #define xTimerStopFromISR( xTimer, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_STOP_FROM_ISR, 0, ( pxHigherPriorityTaskWoken ), 0U )
 
 /**
- * BaseType_t xTimerChangePeriodFromISR( TimerHandle_t xTimer,
- *										 TickType_t xNewPeriod,
- *										 BaseType_t *pxHigherPriorityTaskWoken );
- *
  * A version of xTimerChangePeriod() that can be called from an interrupt
  * service routine.
  *
@@ -1045,7 +1000,7 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
  * priority is set by the configTIMER_TASK_PRIORITY configuration constant.
  *
  * Example usage:
- * @verbatim
+ * @code{c}
  * // This scenario assumes xTimer has already been created and started.  When
  * // an interrupt occurs, the period of xTimer should be changed to 500ms.
  *
@@ -1075,14 +1030,11 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
  *         // depends on the FreeRTOS port being used).
  *     }
  * }
- * @endverbatim
+ * @endcode
  */
 #define xTimerChangePeriodFromISR( xTimer, xNewPeriod, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_CHANGE_PERIOD_FROM_ISR, ( xNewPeriod ), ( pxHigherPriorityTaskWoken ), 0U )
 
 /**
- * BaseType_t xTimerResetFromISR( 	TimerHandle_t xTimer,
- *									BaseType_t *pxHigherPriorityTaskWoken );
- *
  * A version of xTimerReset() that can be called from an interrupt service
  * routine.
  *
@@ -1110,7 +1062,7 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
  * task priority is set by the configTIMER_TASK_PRIORITY configuration constant.
  *
  * Example usage:
- * @verbatim
+ * @code{c}
  * // This scenario assumes xBacklightTimer has already been created.  When a
  * // key is pressed, an LCD back-light is switched on.  If 5 seconds pass
  * // without a key being pressed, then the LCD back-light is switched off.  In
@@ -1161,18 +1113,12 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
  *         // depends on the FreeRTOS port being used).
  *     }
  * }
- * @endverbatim
+ * @endcode
  */
 #define xTimerResetFromISR( xTimer, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_RESET_FROM_ISR, ( xTaskGetTickCountFromISR() ), ( pxHigherPriorityTaskWoken ), 0U )
 
 
 /**
- * BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend,
- *                                          void *pvParameter1,
- *                                          uint32_t ulParameter2,
- *                                          BaseType_t *pxHigherPriorityTaskWoken );
- *
- *
  * Used from application interrupt service routines to defer the execution of a
  * function to the RTOS daemon task (the timer service task, hence this function
  * is implemented in timers.c and is prefixed with 'Timer').
@@ -1214,7 +1160,7 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
  * timer daemon task, otherwise pdFALSE is returned.
  *
  * Example usage:
- * @verbatim
+ * @code{c}
  *
  *	// The callback function that will execute in the context of the daemon task.
  *  // Note callback functions must all use this same prototype.
@@ -1252,17 +1198,11 @@ TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
  *		portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
  *
  *	}
- * @endverbatim
+ * @endcode
  */
 BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, BaseType_t *pxHigherPriorityTaskWoken );
 
  /**
-  * BaseType_t xTimerPendFunctionCall( PendedFunction_t xFunctionToPend,
-  *                                    void *pvParameter1,
-  *                                    uint32_t ulParameter2,
-  *                                    TickType_t xTicksToWait );
-  *
-  *
   * Used to defer the execution of a function to the RTOS daemon task (the timer
   * service task, hence this function is implemented in timers.c and is prefixed
   * with 'Timer').
@@ -1291,8 +1231,6 @@ BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend, void
 BaseType_t xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, TickType_t xTicksToWait );
 
 /**
- * const char * const pcTimerGetTimerName( TimerHandle_t xTimer );
- *
  * Returns the name that was assigned to a timer when the timer was created.
  *
  * @param xTimer The handle of the timer being queried.
@@ -1301,6 +1239,7 @@ BaseType_t xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, void *pvPar
  */
 const char * pcTimerGetTimerName( TimerHandle_t xTimer ); /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
 
+/** @cond */
 /*
  * Functions beyond this part are not part of the public API and are intended
  * for use by the kernel only.
@@ -1308,6 +1247,8 @@ const char * pcTimerGetTimerName( TimerHandle_t xTimer ); /*lint !e971 Unqualifi
 BaseType_t xTimerCreateTimerTask( void ) PRIVILEGED_FUNCTION;
 BaseType_t xTimerGenericCommand( TimerHandle_t xTimer, const BaseType_t xCommandID, const TickType_t xOptionalValue, BaseType_t * const pxHigherPriorityTaskWoken, const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION;
 
+/** @endcond */
+
 #ifdef __cplusplus
 }
 #endif

+ 105 - 49
components/freertos/tasks.c

@@ -541,6 +541,12 @@ static portTASK_FUNCTION_PROTO( prvIdleTask, pvParameters );
 
 #endif
 
+//Function to call the Thread Local Storage Pointer Deletion Callbacks. Will be
+//called during task deletion before prvDeleteTCB is called.
+#if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) && ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS )
+	static void prvDeleteTLS( TCB_t *pxTCB );
+#endif
+
 /*
  * Used only by the idle task.  This checks to see if anything has been placed
  * in the list of tasks waiting to be deleted.  If so the task is cleaned up
@@ -1201,19 +1207,25 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode
 /*-----------------------------------------------------------*/
 
 #if ( INCLUDE_vTaskDelete == 1 )
+
 	void vTaskDelete( TaskHandle_t xTaskToDelete )
 	{
+	//The following vTaskDelete() is backported from FreeRTOS v9.0.0 and modified for SMP.
+	//v9.0.0 vTaskDelete() will immediately free task memory if the task being deleted is
+	//NOT currently running and not pinned to the other core. Otherwise, freeing of task memory
+	//will still be delegated to the Idle Task.
+
 	TCB_t *pxTCB;
+	int core = xPortGetCoreID();	//Current core
+	UBaseType_t free_now;	//Flag to indicate if task memory can be freed immediately
+
 		taskENTER_CRITICAL(&xTaskQueueMutex);
 		{
 			/* If null is passed in here then it is the calling task that is
 			being deleted. */
 			pxTCB = prvGetTCBFromHandle( xTaskToDelete );
 
-			/* Remove task from the ready list and place in the	termination list.
-			This will stop the task from be scheduled.  The idle task will check
-			the termination list and free up any memory allocated by the
-			scheduler for the TCB and stack. */
+			/* Remove task from the ready list. */
 			if( uxListRemove( &( pxTCB->xGenericListItem ) ) == ( UBaseType_t ) 0 )
 			{
 				taskRESET_READY_PRIORITY( pxTCB->uxPriority );
@@ -1233,29 +1245,67 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode
 				mtCOVERAGE_TEST_MARKER();
 			}
 
-			vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xGenericListItem ) );
+			/* Increment the uxTaskNumber also so kernel aware debuggers can
+			detect that the task lists need re-generating.  This is done before
+			portPRE_TASK_DELETE_HOOK() as in the Windows port that macro will
+			not return. */
+			uxTaskNumber++;
 
-			/* Increment the ucTasksDeleted variable so the idle task knows
-			there is a task that has been deleted and that it should therefore
-			check the xTasksWaitingTermination list. */
-			++uxTasksDeleted;
+			//If task to be deleted is currently running on either core or is pinned to the other core. Let Idle free memory
+			if( pxTCB == pxCurrentTCB[ core ] ||
+				(portNUM_PROCESSORS > 1 && pxTCB == pxCurrentTCB[ !core ]) ||
+				(portNUM_PROCESSORS > 1 && pxTCB->xCoreID == (!core)) )
+			{
+				/* Deleting a currently running task. This cannot complete
+				within the task itself, as a context switch to another task is
+				required. Place the task in the termination list.  The idle task
+				will check the termination list and free up any memory allocated
+				by the scheduler for the TCB and stack of the deleted task. */
+				vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xGenericListItem ) );
+
+				/* Increment the ucTasksDeleted variable so the idle task knows
+				there is a task that has been deleted and that it should therefore
+				check the xTasksWaitingTermination list. */
+				++uxTasksDeleted;
 
-			/* Increment the uxTaskNumberVariable also so kernel aware debuggers
-			can detect that the task lists need re-generating. */
-			uxTaskNumber++;
+				/* The pre-delete hook is primarily for the Windows simulator,
+				in which Windows specific clean up operations are performed,
+				after which it is not possible to yield away from this task -
+				hence xYieldPending is used to latch that a context switch is
+				required. */
+				portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending );
+
+				free_now = pdFALSE;		//Let Idle Task free task memory
+			}
+			else	//Task is not currently running and not pinned to the other core
+			{
+				--uxCurrentNumberOfTasks;
+
+				/* Reset the next expected unblock time in case it referred to
+				the task that has just been deleted. */
+				prvResetNextTaskUnblockTime();
+				free_now = pdTRUE;		//Set flag to free task memory immediately
+			}
 
 			traceTASK_DELETE( pxTCB );
 		}
 		taskEXIT_CRITICAL(&xTaskQueueMutex);
 
+		if(free_now == pdTRUE){		//Free task memory. Outside critical section due to deletion callbacks
+			#if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) && ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS )
+				prvDeleteTLS( pxTCB );	//Run deletion callbacks before deleting TCB
+			#endif
+			prvDeleteTCB( pxTCB );	//Must only be called after del cb
+		}
+
 		/* Force a reschedule if it is the currently running task that has just
 		been deleted. */
 		if( xSchedulerRunning != pdFALSE )
 		{
 			//No mux; no harm done if this misfires. The deleted task won't get scheduled anyway.
-			if( pxTCB == pxCurrentTCB[ xPortGetCoreID() ] )
+			if( pxTCB == pxCurrentTCB[ core ] )	//If task was currently running on this core
 			{
-				configASSERT( uxSchedulerSuspended[ xPortGetCoreID() ] == 0 );
+				configASSERT( uxSchedulerSuspended[ core ] == 0 );
 
 				/* The pre-delete hook is primarily for the Windows simulator,
 				in which Windows specific clean up operations are performed,
@@ -1265,20 +1315,14 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode
 				portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending[xPortGetCoreID()] );
 				portYIELD_WITHIN_API();
 			}
-			else if ( portNUM_PROCESSORS > 1 && pxTCB == pxCurrentTCB[ !xPortGetCoreID() ] )
+			else if ( portNUM_PROCESSORS > 1 && pxTCB == pxCurrentTCB[ !core] )	//If task was currently running on the other core
 			{
 				/* if task is running on the other CPU, force a yield on that CPU to take it off */
-				vPortYieldOtherCore( !xPortGetCoreID() );
+				vPortYieldOtherCore( !core );
 			}
 			else
 			{
-				/* Reset the next expected unblock time in case it referred to
-				the task that has just been deleted. */
-				taskENTER_CRITICAL(&xTaskQueueMutex);
-				{
-					prvResetNextTaskUnblockTime();
-				}
-				taskEXIT_CRITICAL(&xTaskQueueMutex);
+				mtCOVERAGE_TEST_MARKER();
 			}
 		}
 	}
@@ -3583,52 +3627,48 @@ static void prvCheckTasksWaitingTermination( void )
 	#if ( INCLUDE_vTaskDelete == 1 )
 	{
 		BaseType_t xListIsEmpty;
+		int core = xPortGetCoreID();
 
 		/* ucTasksDeleted is used to prevent vTaskSuspendAll() being called
 		too often in the idle task. */
 		while(uxTasksDeleted > ( UBaseType_t ) 0U )
 		{
 			TCB_t *pxTCB = NULL;
+
 			taskENTER_CRITICAL(&xTaskQueueMutex);
 			{
 				xListIsEmpty = listLIST_IS_EMPTY( &xTasksWaitingTermination );
-			}
-
-			if( xListIsEmpty == pdFALSE )
-			{
+				if( xListIsEmpty == pdFALSE )
 				{
-					pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xTasksWaitingTermination ) );
 					/* We only want to kill tasks that ran on this core because e.g. _xt_coproc_release needs to
-					   be called on the core the process is pinned on, if any */
-					if( pxTCB->xCoreID == tskNO_AFFINITY || pxTCB->xCoreID == xPortGetCoreID()) {
-						( void ) uxListRemove( &( pxTCB->xGenericListItem ) );
+					be called on the core the process is pinned on, if any */
+					ListItem_t *target = listGET_HEAD_ENTRY(&xTasksWaitingTermination);
+					for( ; target != listGET_END_MARKER(&xTasksWaitingTermination); target = listGET_NEXT(target) ){
+						int coreid = (( TCB_t * )listGET_LIST_ITEM_OWNER(target))->xCoreID;
+						if(coreid == core || coreid == tskNO_AFFINITY){		//Find first item not pinned to other core
+							pxTCB = ( TCB_t * )listGET_LIST_ITEM_OWNER(target);
+							break;
+						}
+					}
+					if(pxTCB != NULL){
+						( void ) uxListRemove( target );	//Remove list item from list
 						--uxCurrentNumberOfTasks;
 						--uxTasksDeleted;
-					} else {
-						/* Need to wait until the idle task on the other processor kills that task first. */
-						taskEXIT_CRITICAL(&xTaskQueueMutex);
-						break;
 					}
 				}
 			}
-			taskEXIT_CRITICAL(&xTaskQueueMutex);
+			taskEXIT_CRITICAL(&xTaskQueueMutex);	//Need to call deletion callbacks outside critical section
 
-			if (pxTCB != NULL) {
-                #if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) && ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS )
-				int x;
-				for( x = 0; x < ( UBaseType_t ) configNUM_THREAD_LOCAL_STORAGE_POINTERS; x++ )
-				{
-					if (pxTCB->pvThreadLocalStoragePointersDelCallback[ x ] != NULL)
-					{
-						pxTCB->pvThreadLocalStoragePointersDelCallback[ x ](x, pxTCB->pvThreadLocalStoragePointers[ x ]);
-					}
-				}
-                #endif
-	    		prvDeleteTCB( pxTCB );
+			if (pxTCB != NULL) {	//Call deletion callbacks and free TCB memory
+				#if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) && ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS )
+					prvDeleteTLS( pxTCB );
+				#endif
+				prvDeleteTCB( pxTCB );
 			}
 			else
 			{
 				mtCOVERAGE_TEST_MARKER();
+				break;	//No TCB found that could be freed by this core, break out of loop
 			}
 		}
 	}
@@ -3831,7 +3871,6 @@ BaseType_t xTaskGetAffinity( TaskHandle_t xTask )
 
 #if ( INCLUDE_vTaskDelete == 1 )
 
-
 	static void prvDeleteTCB( TCB_t *pxTCB )
 	{
 		/* Free up the memory allocated by the scheduler for the task.  It is up
@@ -3886,6 +3925,23 @@ BaseType_t xTaskGetAffinity( TaskHandle_t xTask )
 #endif /* INCLUDE_vTaskDelete */
 /*-----------------------------------------------------------*/
 
+#if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) && ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS )
+
+	static void prvDeleteTLS( TCB_t *pxTCB )
+	{
+		configASSERT( pxTCB );
+		for( int x = 0; x < ( UBaseType_t ) configNUM_THREAD_LOCAL_STORAGE_POINTERS; x++ )
+		{
+			if (pxTCB->pvThreadLocalStoragePointersDelCallback[ x ] != NULL)	//If del cb is set
+			{
+				pxTCB->pvThreadLocalStoragePointersDelCallback[ x ](x, pxTCB->pvThreadLocalStoragePointers[ x ]);	//Call del cb
+			}
+		}
+	}
+
+#endif /* ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) && ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS ) */
+/*-----------------------------------------------------------*/
+
 static void prvResetNextTaskUnblockTime( void )
 {
 TCB_t *pxTCB;

+ 52 - 12
components/freertos/test/test_freertos_task_delete.c

@@ -1,26 +1,66 @@
+/*
+ * Test backported deletion behavior by creating tasks of various affinities and
+ * check if the task memory is freed immediately under the correct conditions.
+ *
+ * The behavior of vTaskDelete() has been backported form FreeRTOS v9.0.0. This
+ * results in the immediate freeing of task memory and the immediate execution
+ * of deletion callbacks under the following conditions...
+ * - When deleting a task that is not currently running on either core
+ * - When deleting a task that is pinned to the same core (with respect to
+ *   the core that calls vTaskDelete()
+ *
+ * If the two conditions are not met, freeing of task memory and execution of
+ * deletion callbacks will still be carried out by the Idle Task.
+ */
 #include <stdio.h>
 
 #include "freertos/FreeRTOS.h"
 #include "freertos/task.h"
-#include "freertos/semphr.h"
-#include "freertos/queue.h"
-#include "freertos/event_groups.h"
+#include "esp_heap_caps.h"
+
 #include "unity.h"
 
-static void task_delete_self(void *param)
+#define NO_OF_TSKS  3
+#define DELAY_TICKS 2
+#define HEAP_CAPS   (MALLOC_CAP_INTERNAL|MALLOC_CAP_DEFAULT)
+
+
+static void tsk_self_del(void *param)
 {
-    printf("Task %p running on core %d. Deleting shortly...\n", xTaskGetCurrentTaskHandle(), xPortGetCoreID());
-    vTaskDelay(5);
-    vTaskDelete(NULL);
+    vTaskDelete(NULL);  //Deleting self means deleting currently running task
+}
+
+static void tsk_extern_del(void *param)
+{
+    vTaskDelay(portMAX_DELAY);  //Await external deletion
 }
 
 TEST_CASE("FreeRTOS Delete Tasks", "[freertos]")
 {
+/* -------------- Test vTaskDelete() on currently running tasks ----------------*/
     uint32_t before_count = uxTaskGetNumberOfTasks();
-
-    xTaskCreatePinnedToCore(task_delete_self, "tsk_self_a", 4096, NULL, configMAX_PRIORITIES - 1, NULL, 0);
-    xTaskCreatePinnedToCore(task_delete_self, "tsk_self_a", 4096, NULL, configMAX_PRIORITIES - 1, NULL, 0);
-    TEST_ASSERT_EQUAL(before_count + 2, uxTaskGetNumberOfTasks());
-    vTaskDelay(200 / portTICK_PERIOD_MS);
+    uint32_t before_heap = heap_caps_get_free_size(HEAP_CAPS);
+    for(int i = 0; i < portNUM_PROCESSORS; i++){
+        for(int j = 0; j < NO_OF_TSKS; j++){
+            TEST_ASSERT_EQUAL(pdTRUE, xTaskCreatePinnedToCore(tsk_self_del, "tsk_self", 1024, NULL, configMAX_PRIORITIES - 1, NULL, i));
+        }
+    }
+    vTaskDelay(DELAY_TICKS);    //Minimal delay to see if Idle task cleans up all tasks awaiting deletion in a single tick
     TEST_ASSERT_EQUAL(before_count, uxTaskGetNumberOfTasks());
+    TEST_ASSERT_EQUAL(before_heap, heap_caps_get_free_size(HEAP_CAPS));
+
+/* ------------- Test vTaskDelete() on not currently running tasks ------------ */
+    TaskHandle_t handles[NO_OF_TSKS];
+    before_heap = heap_caps_get_free_size(HEAP_CAPS);
+    //Create task pinned to the same core that will not run during task deletion
+    for(int j = 0 ; j < NO_OF_TSKS; j++){
+        TEST_ASSERT_EQUAL(pdTRUE, xTaskCreatePinnedToCore(tsk_extern_del, "tsk_extern", 4096, NULL, configMAX_PRIORITIES - 1, &handles[j], xPortGetCoreID()));
+    }
+    TEST_ASSERT_NOT_EQUAL(before_heap, heap_caps_get_free_size(HEAP_CAPS));    //Check tasks have been created
+    //Delete the tasks, memory should be freed immediately
+    for(int j = 0; j < NO_OF_TSKS; j++){
+        vTaskDelete(handles[j]);
+    }
+    TEST_ASSERT_EQUAL(before_heap, heap_caps_get_free_size(HEAP_CAPS));
+
 }

+ 2 - 2
components/heap/heap_trace.c

@@ -373,11 +373,11 @@ static IRAM_ATTR __attribute__((noinline)) void *trace_realloc(void *p, size_t s
             record_free(p, callers);
         }
         heap_trace_record_t rec = {
-            .address = p,
+            .address = r,
             .ccount = ccount,
             .size = size,
         };
-        memcpy(rec.alloced_by, callers, sizeof(heap_trace_record_t) * STACK_DEPTH);
+        memcpy(rec.alloced_by, callers, sizeof(void *) * STACK_DEPTH);
         record_allocation(&rec);
     }
     return r;

+ 187 - 74
components/json/include/cJSON.h

@@ -1,16 +1,16 @@
 /*
-  Copyright (c) 2009 Dave Gamble
- 
+  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
@@ -28,120 +28,233 @@ extern "C"
 {
 #endif
 
+/* project version */
+#define CJSON_VERSION_MAJOR 1
+#define CJSON_VERSION_MINOR 6
+#define CJSON_VERSION_PATCH 0
+
+#include <stddef.h>
+
 /* cJSON Types: */
-#define cJSON_False 0
-#define cJSON_True 1
-#define cJSON_NULL 2
-#define cJSON_Number 3
-#define cJSON_String 4
-#define cJSON_Array 5
-#define cJSON_Object 6
-	
+#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 {
-	struct cJSON *next,*prev;	/* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
-	struct cJSON *child;		/* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
+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;
 
-	int type;					/* The type of the item, as above. */
+    /* The type of the item, as above. */
+    int type;
 
-	char *valuestring;			/* The item's string, if type==cJSON_String */
-	int valueint;				/* The item's number, if type==cJSON_Number */
-	double valuedouble;			/* The item's number, if type==cJSON_Number */
+    /* 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;
 
-	char *string;				/* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
+    /* 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 {
+typedef struct cJSON_Hooks
+{
       void *(*malloc_fn)(size_t sz);
       void (*free_fn)(void *ptr);
 } cJSON_Hooks;
 
+typedef int cJSON_bool;
+
+#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 2 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
+
+*/
+
+/* 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 __stdcall
+#elif defined(CJSON_EXPORT_SYMBOLS)
+#define CJSON_PUBLIC(type)   __declspec(dllexport) type __stdcall
+#elif defined(CJSON_IMPORT_SYMBOLS)
+#define CJSON_PUBLIC(type)   __declspec(dllimport) type __stdcall
+#endif
+#else /* !WIN32 */
+#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
+
+/* 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 */
-extern void cJSON_InitHooks(cJSON_Hooks* hooks);
+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);
+/* 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);
 
-/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */
-extern cJSON *cJSON_Parse(const char *value);
-/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */
-extern char  *cJSON_Print(cJSON *item);
-/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */
-extern char  *cJSON_PrintUnformatted(cJSON *item);
+/* 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 */
-extern char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt);
+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. */
-extern void   cJSON_Delete(cJSON *c);
+CJSON_PUBLIC(void) cJSON_Delete(cJSON *c);
 
 /* Returns the number of items in an array (or object). */
-extern int	  cJSON_GetArraySize(cJSON *array);
+CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
 /* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
-extern cJSON *cJSON_GetArrayItem(cJSON *array,int item);
+CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
 /* Get item "string" from object. Case insensitive. */
-extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
-
+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. */
-extern const char *cJSON_GetErrorPtr(void);
-	
+CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
+
+/* 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. */
-extern cJSON *cJSON_CreateNull(void);
-extern cJSON *cJSON_CreateTrue(void);
-extern cJSON *cJSON_CreateFalse(void);
-extern cJSON *cJSON_CreateBool(int b);
-extern cJSON *cJSON_CreateNumber(double num);
-extern cJSON *cJSON_CreateDouble(double num,int i_num);
-extern cJSON *cJSON_CreateString(const char *string);
-extern cJSON *cJSON_CreateArray(void);
-extern cJSON *cJSON_CreateObject(void);
+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);
 
 /* These utilities create an Array of count items. */
-extern cJSON *cJSON_CreateIntArray(const int *numbers,int count);
-extern cJSON *cJSON_CreateFloatArray(const float *numbers,int count);
-extern cJSON *cJSON_CreateDoubleArray(const double *numbers,int count);
-extern cJSON *cJSON_CreateStringArray(const char **strings,int count);
+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 **strings, int count);
 
 /* Append item to the specified array/object. */
-extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
-extern void	cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item);
-extern void	cJSON_AddItemToObjectCS(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 */
+CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item);
+CJSON_PUBLIC(void) 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(void) 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. */
-extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
-extern void	cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item);
+CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
+CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
 
 /* Remove/Detatch items from Arrays/Objects. */
-extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which);
-extern void   cJSON_DeleteItemFromArray(cJSON *array,int which);
-extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string);
-extern void   cJSON_DeleteItemFromObject(cJSON *object,const char *string);
-	
+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. */
-extern void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem);	/* Shifts pre-existing items to the right. */
-extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem);
-extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
+CJSON_PUBLIC(void) 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(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
+CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
+CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem);
 
 /* Duplicate a cJSON item */
-extern cJSON *cJSON_Duplicate(cJSON *item,int recurse);
+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);
 
-/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
-extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated);
 
-extern void cJSON_Minify(char *json);
+CJSON_PUBLIC(void) cJSON_Minify(char *json);
 
 /* Macros for creating things quickly. */
-#define cJSON_AddNullToObject(object,name)		cJSON_AddItemToObject(object, name, cJSON_CreateNull())
-#define cJSON_AddTrueToObject(object,name)		cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
-#define cJSON_AddFalseToObject(object,name)		cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
-#define cJSON_AddBoolToObject(object,name,b)	cJSON_AddItemToObject(object, name, cJSON_CreateBool(b))
-#define cJSON_AddNumberToObject(object,name,n)	cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
-#define cJSON_AddStringToObject(object,name,s)	cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
+#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull())
+#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
+#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
+#define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b))
+#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
+#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
+#define cJSON_AddRawToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateRaw(s))
 
 /* When assigning an integer value, it needs to be propagated to valuedouble too. */
-#define cJSON_SetIntValue(object,val)			((object)?(object)->valueint=(object)->valuedouble=(val):(val))
-#define cJSON_SetNumberValue(object,val)		((object)?(object)->valueint=(object)->valuedouble=(val):(val))
+#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))
+
+/* 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);
 
 #ifdef __cplusplus
 }

+ 2637 - 634
components/json/library/cJSON.c

@@ -1,5 +1,5 @@
 /*
-  Copyright (c) 2009 Dave Gamble
+  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
@@ -23,6 +23,20 @@
 /* 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>
@@ -30,722 +44,2711 @@
 #include <float.h>
 #include <limits.h>
 #include <ctype.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"
 
-static const char *ep;
+/* define our own boolean type */
+#define true ((cJSON_bool)1)
+#define false ((cJSON_bool)0)
+
+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);
+}
+
+/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */
+#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 6) || (CJSON_VERSION_PATCH != 0)
+    #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;
+        }
+    }
 
-const char *cJSON_GetErrorPtr(void) {return ep;}
+    return tolower(*string1) - tolower(*string2);
+}
 
-static int cJSON_strcasecmp(const char *s1,const char *s2)
+typedef struct internal_hooks
+{
+    void *(*allocate)(size_t size);
+    void (*deallocate)(void *pointer);
+    void *(*reallocate)(void *pointer, size_t size);
+} internal_hooks;
+
+#if defined(_MSC_VER)
+/* work around MSVC error C2322: '...' address of dillimport '...' is not static */
+static void *internal_malloc(size_t size)
+{
+    return malloc(size);
+}
+static void internal_free(void *pointer)
+{
+    free(pointer);
+}
+static void *internal_realloc(void *pointer, size_t size)
 {
-	if (!s1) return (s1==s2)?0:1;if (!s2) return 1;
-	for(; tolower(*(const unsigned char *)s1) == tolower(*(const unsigned char *)s2); ++s1, ++s2)	if(*s1 == 0)	return 0;
-	return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2);
+    return realloc(pointer, size);
 }
+#else
+#define internal_malloc malloc
+#define internal_free free
+#define internal_realloc realloc
+#endif
 
-static void *(*cJSON_malloc)(size_t sz) = malloc;
-static void (*cJSON_free)(void *ptr) = free;
+static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc };
 
-static char* cJSON_strdup(const char* str)
+static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks)
 {
-      size_t len;
-      char* copy;
+    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);
 
-      len = strlen(str) + 1;
-      if (!(copy = (char*)cJSON_malloc(len))) return 0;
-      memcpy(copy,str,len);
-      return copy;
+    return copy;
 }
 
-void cJSON_InitHooks(cJSON_Hooks* hooks)
+CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks)
 {
-    if (!hooks) { /* Reset hooks */
-        cJSON_malloc = malloc;
-        cJSON_free = free;
+    if (hooks == NULL)
+    {
+        /* Reset hooks */
+        global_hooks.allocate = malloc;
+        global_hooks.deallocate = free;
+        global_hooks.reallocate = realloc;
         return;
     }
 
-	cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc;
-	cJSON_free	 = (hooks->free_fn)?hooks->free_fn:free;
+    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(void)
+static cJSON *cJSON_New_Item(const internal_hooks * const hooks)
 {
-	cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
-	if (node) memset(node,0,sizeof(cJSON));
-	return node;
+    cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON));
+    if (node)
+    {
+        memset(node, '\0', sizeof(cJSON));
+    }
+
+    return node;
 }
 
 /* Delete a cJSON structure. */
-void cJSON_Delete(cJSON *c)
+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)
 {
-	cJSON *next;
-	while (c)
-	{
-		next=c->next;
-		if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child);
-		if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring);
-		if (!(c->type&cJSON_StringIsConst) && c->string) cJSON_free(c->string);
-		cJSON_free(c);
-		c=next;
-	}
+#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 const char *parse_number(cJSON *item,const char *num)
+static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer)
 {
-	double n=0,sign=1,scale=0;int subscale=0,signsubscale=1;
+    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 <= INT_MIN)
+    {
+        item->valueint = INT_MIN;
+    }
+    else
+    {
+        item->valueint = (int)number;
+    }
 
-	if (*num=='-') sign=-1,num++;	/* Has sign? */
-	if (*num=='0') num++;			/* is zero */
-	if (*num>='1' && *num<='9')	do	n=(n*10.0)+(*num++ -'0');	while (*num>='0' && *num<='9');	/* Number? */
-	if (*num=='.' && num[1]>='0' && num[1]<='9') {num++;		do	n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');}	/* Fractional part? */
-	if (*num=='e' || *num=='E')		/* Exponent? */
-	{	num++;if (*num=='+') num++;	else if (*num=='-') signsubscale=-1,num++;		/* With sign? */
-		while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0');	/* Number? */
-	}
+    item->type = cJSON_Number;
 
-	n=sign*n*pow(10.0,(scale+subscale*signsubscale));	/* number = +/- number.fraction * 10^+/- exponent */
-	
-	item->valuedouble=n;
-	item->valueint=(int)n;
-	item->type=cJSON_Number;
-	return num;
+    input_buffer->offset += (size_t)(after_end - number_c_string);
+    return true;
 }
 
-static int pow2gt (int x)	{	--x;	x|=x>>1;	x|=x>>2;	x|=x>>4;	x|=x>>8;	x|=x>>16;	return x+1;	}
+/* 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 <= INT_MIN)
+    {
+        object->valueint = INT_MIN;
+    }
+    else
+    {
+        object->valueint = (int)number;
+    }
 
-typedef struct {char *buffer; int length; int offset; } printbuffer;
+    return object->valuedouble = number;
+}
 
-static char* ensure(printbuffer *p,int needed)
+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)
 {
-	char *newbuffer;int newsize;
-	if (!p || !p->buffer) return 0;
-	needed+=p->offset;
-	if (needed<=p->length) return p->buffer+p->offset;
+    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;
+    }
 
-	newsize=pow2gt(needed);
-	newbuffer=(char*)cJSON_malloc(newsize);
-	if (!newbuffer) {cJSON_free(p->buffer);p->length=0,p->buffer=0;return 0;}
-	if (newbuffer) memcpy(newbuffer,p->buffer,p->length);
-	cJSON_free(p->buffer);
-	p->length=newsize;
-	p->buffer=newbuffer;
-	return newbuffer+p->offset;
+    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;
 }
 
-static int update(printbuffer *p)
+/* calculate the new length of the string in a printbuffer and update the offset */
+static void update_offset(printbuffer * const buffer)
 {
-	char *str;
-	if (!p || !p->buffer) return 0;
-	str=p->buffer+p->offset;
-	return p->offset+strlen(str);
+    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);
 }
 
 /* Render the number nicely from the given item into a string. */
-static char *print_number(cJSON *item,printbuffer *p)
-{
-	char *str=0;
-	double d=item->valuedouble;
-	if (d==0)
-	{
-		if (p)	str=ensure(p,2);
-		else	str=(char*)cJSON_malloc(2);	/* special case for 0. */
-		if (str) strcpy(str,"0");
-	}
-	else if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN)
-	{
-		if (p)	str=ensure(p,21);
-		else	str=(char*)cJSON_malloc(21);	/* 2^64+1 can be represented in 21 chars. */
-		if (str)	sprintf(str,"%d",item->valueint);
-	}
-	else
-	{
-		if (p)	str=ensure(p,64);
-		else	str=(char*)cJSON_malloc(64);	/* This is a nice tradeoff. */
-		if (str)
-		{
-			if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60)sprintf(str,"%.0f",d);
-			else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9)			sprintf(str,"%e",d);
-			else												sprintf(str,"%f",d);
-		}
-	}
-	return str;
-}
-
-static unsigned parse_hex4(const char *str)
-{
-	unsigned h=0;
-	if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
-	h=h<<4;str++;
-	if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
-	h=h<<4;str++;
-	if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
-	h=h<<4;str++;
-	if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
-	return h;
-}
-
-/* Parse the input text into an unescaped cstring, and populate item. */
-static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
-static const char *parse_string(cJSON *item,const char *str)
-{
-	const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2;
-	if (*str!='\"') {ep=str;return 0;}	/* not a string! */
-	
-	while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++;	/* Skip escaped quotes. */
-	
-	out=(char*)cJSON_malloc(len+1);	/* This is how long we need for the string, roughly. */
-	if (!out) return 0;
-	
-	ptr=str+1;ptr2=out;
-	while (*ptr!='\"' && *ptr)
-	{
-		if (*ptr!='\\') *ptr2++=*ptr++;
-		else
-		{
-			ptr++;
-			switch (*ptr)
-			{
-				case 'b': *ptr2++='\b';	break;
-				case 'f': *ptr2++='\f';	break;
-				case 'n': *ptr2++='\n';	break;
-				case 'r': *ptr2++='\r';	break;
-				case 't': *ptr2++='\t';	break;
-				case 'u':	 /* transcode utf16 to utf8. */
-					uc=parse_hex4(ptr+1);ptr+=4;	/* get the unicode char. */
-
-					if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0)	break;	/* check for invalid.	*/
-
-					if (uc>=0xD800 && uc<=0xDBFF)	/* UTF16 surrogate pairs.	*/
-					{
-						if (ptr[1]!='\\' || ptr[2]!='u')	break;	/* missing second-half of surrogate.	*/
-						uc2=parse_hex4(ptr+3);ptr+=6;
-						if (uc2<0xDC00 || uc2>0xDFFF)		break;	/* invalid second-half of surrogate.	*/
-						uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF));
-					}
-
-					len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len;
-					
-					switch (len) {
-						case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
-						case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
-						case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
-						case 1: *--ptr2 =(uc | firstByteMark[len]);
-					}
-					ptr2+=len;
-					break;
-				default:  *ptr2++=*ptr; break;
-			}
-			ptr++;
-		}
-	}
-	*ptr2=0;
-	if (*ptr=='\"') ptr++;
-	item->valuestring=out;
-	item->type=cJSON_String;
-	return ptr;
+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]; /* temporary buffer to print the number into */
+    unsigned char decimal_point = get_decimal_point();
+    double test;
+
+    if (output_buffer == NULL)
+    {
+        return false;
+    }
+
+    /* This checks for NaN and Infinity */
+    if ((d * 0) != 0)
+    {
+        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) || ((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 occured */
+    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);
+    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 char *print_string_ptr(const char *str,printbuffer *p)
-{
-	const char *ptr;char *ptr2,*out;int len=0,flag=0;unsigned char token;
-	
-	for (ptr=str;*ptr;ptr++) flag|=((*ptr>0 && *ptr<32)||(*ptr=='\"')||(*ptr=='\\'))?1:0;
-	if (!flag)
-	{
-		len=ptr-str;
-		if (p) out=ensure(p,len+3);
-		else		out=(char*)cJSON_malloc(len+3);
-		if (!out) return 0;
-		ptr2=out;*ptr2++='\"';
-		strcpy(ptr2,str);
-		ptr2[len]='\"';
-		ptr2[len+1]=0;
-		return out;
-	}
-	
-	if (!str)
-	{
-		if (p)	out=ensure(p,3);
-		else	out=(char*)cJSON_malloc(3);
-		if (!out) return 0;
-		strcpy(out,"\"\"");
-		return out;
-	}
-	ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;}
-	
-	if (p)	out=ensure(p,len+3);
-	else	out=(char*)cJSON_malloc(len+3);
-	if (!out) return 0;
-
-	ptr2=out;ptr=str;
-	*ptr2++='\"';
-	while (*ptr)
-	{
-		if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++;
-		else
-		{
-			*ptr2++='\\';
-			switch (token=*ptr++)
-			{
-				case '\\':	*ptr2++='\\';	break;
-				case '\"':	*ptr2++='\"';	break;
-				case '\b':	*ptr2++='b';	break;
-				case '\f':	*ptr2++='f';	break;
-				case '\n':	*ptr2++='n';	break;
-				case '\r':	*ptr2++='r';	break;
-				case '\t':	*ptr2++='t';	break;
-				default: sprintf(ptr2,"u%04x",token);ptr2+=5;	break;	/* escape and print */
-			}
-		}
-	}
-	*ptr2++='\"';*ptr2++=0;
-	return out;
-}
-/* Invote print_string_ptr (which is useful) on an item. */
-static char *print_string(cJSON *item,printbuffer *p)	{return print_string_ptr(item->valuestring,p);}
+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 const char *parse_value(cJSON *item,const char *value);
-static char *print_value(cJSON *item,int depth,int fmt,printbuffer *p);
-static const char *parse_array(cJSON *item,const char *value);
-static char *print_array(cJSON *item,int depth,int fmt,printbuffer *p);
-static const char *parse_object(cJSON *item,const char *value);
-static char *print_object(cJSON *item,int depth,int fmt,printbuffer *p);
+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 const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;}
+static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer)
+{
+    if ((buffer == NULL) || (buffer->content == NULL))
+    {
+        return NULL;
+    }
 
-/* Parse an object - create a new root, and populate. */
-cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated)
+    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)
 {
-	const char *end=0;
-	cJSON *c=cJSON_New_Item();
-	ep=0;
-	if (!c) return 0;       /* memory fail */
+    if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0))
+    {
+        return NULL;
+    }
 
-	end=parse_value(c,skip(value));
-	if (!end)	{cJSON_Delete(c);return 0;}	/* parse failure. ep is set. */
+    if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0))
+    {
+        buffer->offset += 3;
+    }
 
-	/* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
-	if (require_null_terminated) {end=skip(end);if (*end) {cJSON_Delete(c);ep=end;return 0;}}
-	if (return_parse_end) *return_parse_end=end;
-	return c;
+    return buffer;
 }
-/* Default options for cJSON_Parse */
-cJSON *cJSON_Parse(const char *value) {return cJSON_ParseWithOpts(value,0,0);}
 
-/* Render a cJSON item/entity/structure to text. */
-char *cJSON_Print(cJSON *item)				{return print_value(item,0,1,0);}
-char *cJSON_PrintUnformatted(cJSON *item)	{return print_value(item,0,0,0);}
+/* Parse an object - create a new root, and populate. */
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, 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)
+    {
+        goto fail;
+    }
+
+    buffer.content = (const unsigned char*)value;
+    buffer.length = strlen((const char*)value) + sizeof("");
+    buffer.offset = 0;
+    buffer.hooks = global_hooks;
+
+    item = cJSON_New_Item(&global_hooks);
+    if (item == NULL) /* memory fail */
+    {
+        goto fail;
+    }
 
-char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt)
+    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)
 {
-	printbuffer p;
-	p.buffer=(char*)cJSON_malloc(prebuffer);
-	p.length=prebuffer;
-	p.offset=0;
-	return print_value(item,0,fmt,&p);
-	return p.buffer;
+    return cJSON_ParseWithOpts(value, 0, 0);
 }
 
+#define cjson_min(a, b) ((a < b) ? a : b)
 
-/* Parser core - when encountering text, process appropriately. */
-static const char *parse_value(cJSON *item,const char *value)
+static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks)
 {
-	if (!value)						return 0;	/* Fail on null. */
-	if (!strncmp(value,"null",4))	{ item->type=cJSON_NULL;  return value+4; }
-	if (!strncmp(value,"false",5))	{ item->type=cJSON_False; return value+5; }
-	if (!strncmp(value,"true",4))	{ item->type=cJSON_True; item->valueint=1;	return value+4; }
-	if (*value=='\"')				{ return parse_string(item,value); }
-	if (*value=='-' || (*value>='0' && *value<='9'))	{ return parse_number(item,value); }
-	if (*value=='[')				{ return parse_array(item,value); }
-	if (*value=='{')				{ return parse_object(item,value); }
+    printbuffer buffer[1];
+    unsigned char *printed = NULL;
+
+    memset(buffer, 0, sizeof(buffer));
+
+    /* create buffer */
+    buffer->buffer = (unsigned char*) hooks->allocate(256);
+    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->length);
+        buffer->buffer = NULL;
+        if (printed == NULL) {
+            goto fail;
+        }
+    }
+    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);
+    }
 
-	ep=value;return 0;	/* failure. */
+    return NULL;
 }
 
-/* Render a value to text. */
-static char *print_value(cJSON *item,int depth,int fmt,printbuffer *p)
-{
-	char *out=0;
-	if (!item) return 0;
-	if (p)
-	{
-		switch ((item->type)&255)
-		{
-			case cJSON_NULL:	{out=ensure(p,5);	if (out) strcpy(out,"null");	break;}
-			case cJSON_False:	{out=ensure(p,6);	if (out) strcpy(out,"false");	break;}
-			case cJSON_True:	{out=ensure(p,5);	if (out) strcpy(out,"true");	break;}
-			case cJSON_Number:	out=print_number(item,p);break;
-			case cJSON_String:	out=print_string(item,p);break;
-			case cJSON_Array:	out=print_array(item,depth,fmt,p);break;
-			case cJSON_Object:	out=print_object(item,depth,fmt,p);break;
-		}
-	}
-	else
-	{
-		switch ((item->type)&255)
-		{
-			case cJSON_NULL:	out=cJSON_strdup("null");	break;
-			case cJSON_False:	out=cJSON_strdup("false");break;
-			case cJSON_True:	out=cJSON_strdup("true"); break;
-			case cJSON_Number:	out=print_number(item,0);break;
-			case cJSON_String:	out=print_string(item,0);break;
-			case cJSON_Array:	out=print_array(item,depth,fmt,0);break;
-			case cJSON_Object:	out=print_object(item,depth,fmt,0);break;
-		}
-	}
-	return out;
+/* Render a cJSON item/entity/structure to text. */
+CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item)
+{
+    return (char*)print(item, true, &global_hooks);
 }
 
-/* Build an array from input text. */
-static const char *parse_array(cJSON *item,const char *value)
+CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item)
 {
-	cJSON *child;
-	if (*value!='[')	{ep=value;return 0;}	/* not an array! */
+    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;
+    }
 
-	item->type=cJSON_Array;
-	value=skip(value+1);
-	if (*value==']') return value+1;	/* empty array. */
+    p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer);
+    if (!p.buffer)
+    {
+        return NULL;
+    }
 
-	item->child=child=cJSON_New_Item();
-	if (!item->child) return 0;		 /* memory fail */
-	value=skip(parse_value(child,skip(value)));	/* skip any spacing, get the value. */
-	if (!value) return 0;
+    p.length = (size_t)prebuffer;
+    p.offset = 0;
+    p.noalloc = false;
+    p.format = fmt;
+    p.hooks = global_hooks;
 
-	while (*value==',')
-	{
-		cJSON *new_item;
-		if (!(new_item=cJSON_New_Item())) return 0; 	/* memory fail */
-		child->next=new_item;new_item->prev=child;child=new_item;
-		value=skip(parse_value(child,skip(value+1)));
-		if (!value) return 0;	/* memory fail */
-	}
+    if (!print_value(item, &p))
+    {
+        global_hooks.deallocate(p.buffer);
+        return NULL;
+    }
 
-	if (*value==']') return value+1;	/* end of array */
-	ep=value;return 0;	/* malformed. */
+    return (char*)p.buffer;
 }
 
-/* Render an array to text */
-static char *print_array(cJSON *item,int depth,int fmt,printbuffer *p)
-{
-	char **entries;
-	char *out=0,*ptr,*ret;int len=5;
-	cJSON *child=item->child;
-	int numentries=0,i=0,fail=0;
-	size_t tmplen=0;
-	
-	/* How many entries in the array? */
-	while (child) numentries++,child=child->next;
-	/* Explicitly handle numentries==0 */
-	if (!numentries)
-	{
-		if (p)	out=ensure(p,3);
-		else	out=(char*)cJSON_malloc(3);
-		if (out) strcpy(out,"[]");
-		return out;
-	}
-
-	if (p)
-	{
-		/* Compose the output array. */
-		i=p->offset;
-		ptr=ensure(p,1);if (!ptr) return 0;	*ptr='[';	p->offset++;
-		child=item->child;
-		while (child && !fail)
-		{
-			print_value(child,depth+1,fmt,p);
-			p->offset=update(p);
-			if (child->next) {len=fmt?2:1;ptr=ensure(p,len+1);if (!ptr) return 0;*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;p->offset+=len;}
-			child=child->next;
-		}
-		ptr=ensure(p,2);if (!ptr) return 0;	*ptr++=']';*ptr=0;
-		out=(p->buffer)+i;
-	}
-	else
-	{
-		/* Allocate an array to hold the values for each */
-		entries=(char**)cJSON_malloc(numentries*sizeof(char*));
-		if (!entries) return 0;
-		memset(entries,0,numentries*sizeof(char*));
-		/* Retrieve all the results: */
-		child=item->child;
-		while (child && !fail)
-		{
-			ret=print_value(child,depth+1,fmt,0);
-			entries[i++]=ret;
-			if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1;
-			child=child->next;
-		}
-		
-		/* If we didn't fail, try to malloc the output string */
-		if (!fail)	out=(char*)cJSON_malloc(len);
-		/* If that fails, we fail. */
-		if (!out) fail=1;
-
-		/* Handle failure. */
-		if (fail)
-		{
-			for (i=0;i<numentries;i++) if (entries[i]) cJSON_free(entries[i]);
-			cJSON_free(entries);
-			return 0;
-		}
-		
-		/* Compose the output array. */
-		*out='[';
-		ptr=out+1;*ptr=0;
-		for (i=0;i<numentries;i++)
-		{
-			tmplen=strlen(entries[i]);memcpy(ptr,entries[i],tmplen);ptr+=tmplen;
-			if (i!=numentries-1) {*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;}
-			cJSON_free(entries[i]);
-		}
-		cJSON_free(entries);
-		*ptr++=']';*ptr++=0;
-	}
-	return out;	
+CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const cJSON_bool fmt)
+{
+    printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
+
+    if ((len < 0) || (buf == NULL))
+    {
+        return false;
+    }
+
+    p.buffer = (unsigned char*)buf;
+    p.length = (size_t)len;
+    p.offset = 0;
+    p.noalloc = true;
+    p.format = fmt;
+    p.hooks = global_hooks;
+
+    return print_value(item, &p);
 }
 
-/* Build an object from the text. */
-static const char *parse_object(cJSON *item,const char *value)
-{
-	cJSON *child;
-	if (*value!='{')	{ep=value;return 0;}	/* not an object! */
-	
-	item->type=cJSON_Object;
-	value=skip(value+1);
-	if (*value=='}') return value+1;	/* empty array. */
-	
-	item->child=child=cJSON_New_Item();
-	if (!item->child) return 0;
-	value=skip(parse_string(child,skip(value)));
-	if (!value) return 0;
-	child->string=child->valuestring;child->valuestring=0;
-	if (*value!=':') {ep=value;return 0;}	/* fail! */
-	value=skip(parse_value(child,skip(value+1)));	/* skip any spacing, get the value. */
-	if (!value) return 0;
-	
-	while (*value==',')
-	{
-		cJSON *new_item;
-		if (!(new_item=cJSON_New_Item()))	return 0; /* memory fail */
-		child->next=new_item;new_item->prev=child;child=new_item;
-		value=skip(parse_string(child,skip(value+1)));
-		if (!value) return 0;
-		child->string=child->valuestring;child->valuestring=0;
-		if (*value!=':') {ep=value;return 0;}	/* fail! */
-		value=skip(parse_value(child,skip(value+1)));	/* skip any spacing, get the value. */
-		if (!value) return 0;
-	}
-	
-	if (*value=='}') return value+1;	/* end of array */
-	ep=value;return 0;	/* malformed. */
+/* 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 an object to text. */
-static char *print_object(cJSON *item,int depth,int fmt,printbuffer *p)
-{
-	char **entries=0,**names=0;
-	char *out=0,*ptr,*ret,*str;int len=7,i=0,j;
-	cJSON *child=item->child;
-	int numentries=0,fail=0;
-	size_t tmplen=0;
-	/* Count the number of entries. */
-	while (child) numentries++,child=child->next;
-	/* Explicitly handle empty object case */
-	if (!numentries)
-	{
-		if (p) out=ensure(p,fmt?depth+4:3);
-		else	out=(char*)cJSON_malloc(fmt?depth+4:3);
-		if (!out)	return 0;
-		ptr=out;*ptr++='{';
-		if (fmt) {*ptr++='\n';for (i=0;i<depth-1;i++) *ptr++='\t';}
-		*ptr++='}';*ptr++=0;
-		return out;
-	}
-	if (p)
-	{
-		/* Compose the output: */
-		i=p->offset;
-		len=fmt?2:1;	ptr=ensure(p,len+1);	if (!ptr) return 0;
-		*ptr++='{';	if (fmt) *ptr++='\n';	*ptr=0;	p->offset+=len;
-		child=item->child;depth++;
-		while (child)
-		{
-			if (fmt)
-			{
-				ptr=ensure(p,depth);	if (!ptr) return 0;
-				for (j=0;j<depth;j++) *ptr++='\t';
-				p->offset+=depth;
-			}
-			print_string_ptr(child->string,p);
-			p->offset=update(p);
-			
-			len=fmt?2:1;
-			ptr=ensure(p,len);	if (!ptr) return 0;
-			*ptr++=':';if (fmt) *ptr++='\t';
-			p->offset+=len;
-			
-			print_value(child,depth,fmt,p);
-			p->offset=update(p);
-
-			len=(fmt?1:0)+(child->next?1:0);
-			ptr=ensure(p,len+1); if (!ptr) return 0;
-			if (child->next) *ptr++=',';
-			if (fmt) *ptr++='\n';*ptr=0;
-			p->offset+=len;
-			child=child->next;
-		}
-		ptr=ensure(p,fmt?(depth+1):2);	 if (!ptr) return 0;
-		if (fmt)	for (i=0;i<depth-1;i++) *ptr++='\t';
-		*ptr++='}';*ptr=0;
-		out=(p->buffer)+i;
-	}
-	else
-	{
-		/* Allocate space for the names and the objects */
-		entries=(char**)cJSON_malloc(numentries*sizeof(char*));
-		if (!entries) return 0;
-		names=(char**)cJSON_malloc(numentries*sizeof(char*));
-		if (!names) {cJSON_free(entries);return 0;}
-		memset(entries,0,sizeof(char*)*numentries);
-		memset(names,0,sizeof(char*)*numentries);
-
-		/* Collect all the results into our arrays: */
-		child=item->child;depth++;if (fmt) len+=depth;
-		while (child)
-		{
-			names[i]=str=print_string_ptr(child->string,0);
-			entries[i++]=ret=print_value(child,depth,fmt,0);
-			if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1;
-			child=child->next;
-		}
-		
-		/* Try to allocate the output string */
-		if (!fail)	out=(char*)cJSON_malloc(len);
-		if (!out) fail=1;
-
-		/* Handle failure */
-		if (fail)
-		{
-			for (i=0;i<numentries;i++) {if (names[i]) cJSON_free(names[i]);if (entries[i]) cJSON_free(entries[i]);}
-			cJSON_free(names);cJSON_free(entries);
-			return 0;
-		}
-		
-		/* Compose the output: */
-		*out='{';ptr=out+1;if (fmt)*ptr++='\n';*ptr=0;
-		for (i=0;i<numentries;i++)
-		{
-			if (fmt) for (j=0;j<depth;j++) *ptr++='\t';
-			tmplen=strlen(names[i]);memcpy(ptr,names[i],tmplen);ptr+=tmplen;
-			*ptr++=':';if (fmt) *ptr++='\t';
-			strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
-			if (i!=numentries-1) *ptr++=',';
-			if (fmt) *ptr++='\n';*ptr=0;
-			cJSON_free(names[i]);cJSON_free(entries[i]);
-		}
-		
-		cJSON_free(names);cJSON_free(entries);
-		if (fmt) for (i=0;i<depth-1;i++) *ptr++='\t';
-		*ptr++='}';*ptr++=0;
-	}
-	return out;	
+/* 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)
+            {
+                if (!output_buffer->noalloc)
+                {
+                    output_buffer->hooks.deallocate(output_buffer->buffer);
+                }
+                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;
+    }
 }
 
-/* Get Array size/item / object item. */
-int    cJSON_GetArraySize(cJSON *array)							{cJSON *c=array->child;int i=0;while(c)i++,c=c->next;return i;}
-cJSON *cJSON_GetArrayItem(cJSON *array,int item)				{cJSON *c=array->child;  while (c && item>0) item--,c=c->next; return c;}
-cJSON *cJSON_GetObjectItem(cJSON *object,const char *string)	{cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;}
+/* 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;
 
-/* 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(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;}
+    if (input_buffer->depth >= CJSON_NESTING_LIMIT)
+    {
+        return false; /* to deeply nested */
+    }
+    input_buffer->depth++;
 
-/* Add item to array/object. */
-void   cJSON_AddItemToArray(cJSON *array, cJSON *item)						{cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}}
-void   cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item)	{if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);}
-void   cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item)	{if (!item) return; if (!(item->type&cJSON_StringIsConst) && item->string) cJSON_free(item->string);item->string=(char*)string;item->type|=cJSON_StringIsConst;cJSON_AddItemToArray(object,item);}
-void	cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)						{cJSON_AddItemToArray(array,create_reference(item));}
-void	cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item)	{cJSON_AddItemToObject(object,string,create_reference(item));}
-
-cJSON *cJSON_DetachItemFromArray(cJSON *array,int which)			{cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0;
-	if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;}
-void   cJSON_DeleteItemFromArray(cJSON *array,int which)			{cJSON_Delete(cJSON_DetachItemFromArray(array,which));}
-cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;}
-void   cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));}
+    if (buffer_at_offset(input_buffer)[0] != '[')
+    {
+        /* not an array */
+        goto fail;
+    }
 
-/* Replace array/object items with new ones. */
-void   cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem)		{cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) {cJSON_AddItemToArray(array,newitem);return;}
-	newitem->next=c;newitem->prev=c->prev;c->prev=newitem;if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;}
-void   cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem)		{cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return;
-	newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem;
-	if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);}
-void   cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}}
+    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;
+    }
 
-/* Create basic types: */
-cJSON *cJSON_CreateNull(void)					{cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;}
-cJSON *cJSON_CreateTrue(void)					{cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;}
-cJSON *cJSON_CreateFalse(void)					{cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;}
-cJSON *cJSON_CreateBool(int b)					{cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;}
-cJSON *cJSON_CreateNumber(double num)			{cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;}
-cJSON *cJSON_CreateDouble(double num,int i_num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=i_num;}return item;}
-cJSON *cJSON_CreateString(const char *string)	{cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;}
-cJSON *cJSON_CreateArray(void)					{cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;}
-cJSON *cJSON_CreateObject(void)					{cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;}
+    /* check if we skipped to the end of the buffer */
+    if (cannot_access_at_index(input_buffer, 0))
+    {
+        input_buffer->offset--;
+        goto fail;
+    }
 
-/* Create Arrays: */
-cJSON *cJSON_CreateIntArray(const int *numbers,int count)		{int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
-cJSON *cJSON_CreateFloatArray(const float *numbers,int count)	{int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
-cJSON *cJSON_CreateDoubleArray(const double *numbers,int count)	{int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
-cJSON *cJSON_CreateStringArray(const char **strings,int count)	{int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateString(strings[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
+    /* 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] == ','));
 
-/* Duplication */
-cJSON *cJSON_Duplicate(cJSON *item,int recurse)
-{
-	cJSON *newitem,*cptr,*nptr=0,*newchild;
-	/* Bail on bad ptr */
-	if (!item) return 0;
-	/* Create new item */
-	newitem=cJSON_New_Item();
-	if (!newitem) return 0;
-	/* Copy over all vars */
-	newitem->type=item->type&(~cJSON_IsReference),newitem->valueint=item->valueint,newitem->valuedouble=item->valuedouble;
-	if (item->valuestring)	{newitem->valuestring=cJSON_strdup(item->valuestring);	if (!newitem->valuestring)	{cJSON_Delete(newitem);return 0;}}
-	if (item->string)		{newitem->string=cJSON_strdup(item->string);			if (!newitem->string)		{cJSON_Delete(newitem);return 0;}}
-	/* If non-recursive, then we're done! */
-	if (!recurse) return newitem;
-	/* Walk the ->next chain for the child. */
-	cptr=item->child;
-	while (cptr)
-	{
-		newchild=cJSON_Duplicate(cptr,1);		/* Duplicate (with recurse) each item in the ->next chain */
-		if (!newchild) {cJSON_Delete(newitem);return 0;}
-		if (nptr)	{nptr->next=newchild,newchild->prev=nptr;nptr=newchild;}	/* If newitem->child already set, then crosswire ->prev and ->next and move on */
-		else		{newitem->child=newchild;nptr=newchild;}					/* Set newitem->child and move to it */
-		cptr=cptr->next;
-	}
-	return newitem;
-}
-
-void cJSON_Minify(char *json)
-{
-	char *into=json;
-	while (*json)
-	{
-		if (*json==' ') json++;
-		else if (*json=='\t') json++;	/* Whitespace characters. */
-		else if (*json=='\r') json++;
-		else if (*json=='\n') json++;
-		else if (*json=='/' && json[1]=='/')  while (*json && *json!='\n') json++;	/* double-slash comments, to end of line. */
-		else if (*json=='/' && json[1]=='*') {while (*json && !(*json=='*' && json[1]=='/')) json++;json+=2;}	/* multiline comments. */
-		else if (*json=='\"'){*into++=*json++;while (*json && *json!='\"'){if (*json=='\\') *into++=*json++;*into++=*json++;}*into++=*json++;} /* string literals, which are \" sensitive. */
-		else *into++=*json++;			/* All other characters. */
-	}
-	*into=0;	/* and null-terminate. */
+    if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']')
+    {
+        goto fail; /* expected end of array */
+    }
+
+success:
+    input_buffer->depth--;
+
+    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; /* faile 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--;
+
+    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) + (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) && (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;
+        }
+    }
+
+    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;
+}
+
+/* Add item to array/object. */
+CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item)
+{
+    cJSON *child = NULL;
+
+    if ((item == NULL) || (array == NULL))
+    {
+        return;
+    }
+
+    child = array->child;
+
+    if (child == NULL)
+    {
+        /* list is empty, start new one */
+        array->child = item;
+    }
+    else
+    {
+        /* append to the end */
+        while (child->next)
+        {
+            child = child->next;
+        }
+        suffix_object(child, item);
+    }
+}
+
+CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item)
+{
+    if (item == NULL)
+    {
+        return;
+    }
+
+    /* call cJSON_AddItemToObjectCS for code reuse */
+    cJSON_AddItemToObjectCS(object, (char*)cJSON_strdup((const unsigned char*)string, &global_hooks), item);
+    /* remove cJSON_StringIsConst flag */
+    item->type &= ~cJSON_StringIsConst;
+}
+
+#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
+
+/* Add an item to an object with constant string as key */
+CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item)
+{
+    if ((item == NULL) || (string == NULL))
+    {
+        return;
+    }
+    if (!(item->type & cJSON_StringIsConst) && item->string)
+    {
+        global_hooks.deallocate(item->string);
+    }
+    item->string = (char*)string;
+    item->type |= cJSON_StringIsConst;
+    cJSON_AddItemToArray(object, item);
+}
+#if defined(__clang__) || (defined(__GNUC__)  && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
+    #pragma GCC diagnostic pop
+#endif
+
+CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)
+{
+    if (array == NULL)
+    {
+        return;
+    }
+
+    cJSON_AddItemToArray(array, create_reference(item, &global_hooks));
+}
+
+CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item)
+{
+    if ((object == NULL) || (string == NULL))
+    {
+        return;
+    }
+
+    cJSON_AddItemToObject(object, string, create_reference(item, &global_hooks));
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item)
+{
+    if ((parent == NULL) || (item == NULL))
+    {
+        return NULL;
+    }
+
+    if (item->prev != NULL)
+    {
+        /* 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;
+    }
+    /* 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(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem)
+{
+    cJSON *after_inserted = NULL;
+
+    if (which < 0)
+    {
+        return;
+    }
+
+    after_inserted = get_array_item(array, (size_t)which);
+    if (after_inserted == NULL)
+    {
+        cJSON_AddItemToArray(array, newitem);
+        return;
+    }
+
+    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;
+    }
+}
+
+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 (replacement->prev != NULL)
+    {
+        replacement->prev->next = replacement;
+    }
+    if (parent->child == item)
+    {
+        parent->child = replacement;
+    }
+
+    item->next = NULL;
+    item->prev = NULL;
+    cJSON_Delete(item);
+
+    return true;
+}
+
+CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem)
+{
+    if (which < 0)
+    {
+        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;
+
+    cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement);
+
+    return true;
+}
+
+CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem)
+{
+    replace_item_in_object(object, string, newitem, false);
+}
+
+CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem)
+{
+    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 b)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if(item)
+    {
+        item->type = b ? 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 <= 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_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 **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;
+}
+
+CJSON_PUBLIC(void) cJSON_Minify(char *json)
+{
+    unsigned char *into = (unsigned char*)json;
+
+    if (json == NULL)
+    {
+        return;
+    }
+
+    while (*json)
+    {
+        if (*json == ' ')
+        {
+            json++;
+        }
+        else if (*json == '\t')
+        {
+            /* Whitespace characters. */
+            json++;
+        }
+        else if (*json == '\r')
+        {
+            json++;
+        }
+        else if (*json=='\n')
+        {
+            json++;
+        }
+        else if ((*json == '/') && (json[1] == '/'))
+        {
+            /* double-slash comments, to end of line. */
+            while (*json && (*json != '\n'))
+            {
+                json++;
+            }
+        }
+        else if ((*json == '/') && (json[1] == '*'))
+        {
+            /* multiline comments. */
+            while (*json && !((*json == '*') && (json[1] == '/')))
+            {
+                json++;
+            }
+            json += 2;
+        }
+        else if (*json == '\"')
+        {
+            /* string literals, which are \" sensitive. */
+            *into++ = (unsigned char)*json++;
+            while (*json && (*json != '\"'))
+            {
+                if (*json == '\\')
+                {
+                    *into++ = (unsigned char)*json++;
+                }
+                *into++ = (unsigned char)*json++;
+            }
+            *into++ = (unsigned char)*json++;
+        }
+        else
+        {
+            /* All other characters. */
+            *into++ = (unsigned char)*json++;
+        }
+    }
+
+    /* 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 (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);
 }

+ 1434 - 386
components/json/port/cJSON_Utils.c

@@ -1,395 +1,1443 @@
+/*
+  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>
+
+#if defined(_MSC_VER)
+#pragma warning (pop)
+#endif
+#ifdef __GNUCC__
+#pragma GCC visibility pop
+#endif
+
 #include "cJSON_Utils.h"
 
-static int cJSONUtils_strcasecmp(const char *s1,const char *s2)
-{
-	if (!s1) return (s1==s2)?0:1;if (!s2) return 1;
-	for(; tolower(*(const unsigned char *)s1) == tolower(*(const unsigned char *)s2); ++s1, ++s2)	if(*s1 == 0)	return 0;
-	return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2);
-}
-
-/* JSON Pointer implementation: */
-static int cJSONUtils_Pstrcasecmp(const char *a,const char *e)
-{
-	if (!a || !e) return (a==e)?0:1;
-	for (;*a && *e && *e!='/';a++,e++) {
-		if (*e=='~') {if (!(e[1]=='0' && *a=='~') && !(e[1]=='1' && *a=='/')) return 1;  else e++;}
-		else if (tolower(*(const unsigned char *)a)!=tolower(*(const unsigned char *)e)) return 1;
-	}
-	if ((*e!=0 && *e!='/') != (*a!=0)) return 1;
-	return 0;
-}
-
-static int cJSONUtils_PointerEncodedstrlen(const char *s)	{int l=0;for (;*s;s++,l++) if (*s=='~' || *s=='/') l++;return l;}
-
-static void cJSONUtils_PointerEncodedstrcpy(char *d,const char *s)
-{
-	for (;*s;s++)
-	{
-		if (*s=='/') {*d++='~';*d++='1';}
-		else if (*s=='~') {*d++='~';*d++='0';}
-		else *d++=*s;
-	}
-	*d=0;
-}
-
-char *cJSONUtils_FindPointerFromObjectTo(cJSON *object,cJSON *target)
-{
-	int type=object->type,c=0;cJSON *obj=0;
-
-	if (object==target) return strdup("");
-
-	for (obj=object->child;obj;obj=obj->next,c++)
-	{
-		char *found=cJSONUtils_FindPointerFromObjectTo(obj,target);
-		if (found)
-		{
-			if (type==cJSON_Array)
-			{
-				char *ret=(char*)malloc(strlen(found)+23);
-				sprintf(ret,"/%d%s",c,found);
-				free(found);
-				return ret;
-			}
-			else if (type==cJSON_Object)
-			{
-				char *ret=(char*)malloc(strlen(found)+cJSONUtils_PointerEncodedstrlen(obj->string)+2);
-				*ret='/';cJSONUtils_PointerEncodedstrcpy(ret+1,obj->string);
-				strcat(ret,found);
-				free(found);
-				return ret;
-			}
-			free(found);
-			return 0;
-		}
-	}
-	return 0;
-}
-
-cJSON *cJSONUtils_GetPointer(cJSON *object,const char *pointer)
-{
-	while (*pointer++=='/' && object)
-	{
-		if (object->type==cJSON_Array)
-		{
-			int which=0; while (*pointer>='0' && *pointer<='9') which=(10*which) + *pointer++ - '0';
-			if (*pointer && *pointer!='/') return 0;
-			object=cJSON_GetArrayItem(object,which);
-		}
-		else if (object->type==cJSON_Object)
-		{
-			object=object->child;	while (object && cJSONUtils_Pstrcasecmp(object->string,pointer)) object=object->next;	/* GetObjectItem. */
-			while (*pointer && *pointer!='/') pointer++;
-		}
-		else return 0;
-	}
-	return object;
+/* define our own boolean type */
+#define true ((cJSON_bool)1)
+#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);
+}
+
+/* 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[1] = '1';
+            destination++;
+        }
+        else if (source[0] == '~')
+        {
+            destination[0] = '~';
+            destination[1] = '1';
+            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);
+                    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 cJSONUtils_InplaceDecodePointerString(char *string)
-{
-	char *s2=string;
-	for (;*string;s2++,string++) *s2=(*string!='~')?(*string):((*(++string)=='0')?'~':'/');
-	*s2=0;
-}
-
-static cJSON *cJSONUtils_PatchDetach(cJSON *object,const char *path)
-{
-	char *parentptr=0,*childptr=0;cJSON *parent=0,*ret=0;
-
-	parentptr=strdup(path);	childptr=strrchr(parentptr,'/');	if (childptr) *childptr++=0;
-	parent=cJSONUtils_GetPointer(object,parentptr);
-	cJSONUtils_InplaceDecodePointerString(childptr);
-
-	if (!parent) ret=0;	/* Couldn't find object to remove child from. */
-	else if (parent->type==cJSON_Array)		ret=cJSON_DetachItemFromArray(parent,atoi(childptr));
-	else if (parent->type==cJSON_Object)	ret=cJSON_DetachItemFromObject(parent,childptr);
-	free(parentptr);
-	return ret;
-}
-
-static int cJSONUtils_Compare(cJSON *a,cJSON *b)
-{
-	if (a->type!=b->type)	return -1;	/* mismatched type. */
-	switch (a->type)
-	{
-	case cJSON_Number:	return (a->valueint!=b->valueint || a->valuedouble!=b->valuedouble)?-2:0;	/* numeric mismatch. */
-	case cJSON_String:	return (strcmp(a->valuestring,b->valuestring)!=0)?-3:0;						/* string mismatch. */
-	case cJSON_Array:	for (a=a->child,b=b->child;a && b;a=a->next,b=b->next)	{int err=cJSONUtils_Compare(a,b);if (err) return err;}
-						return (a || b)?-4:0;	/* array size mismatch. */
-	case cJSON_Object:
-						cJSONUtils_SortObject(a);
-						cJSONUtils_SortObject(b);
-						a=a->child,b=b->child;
-						while (a && b)
-						{
-							int err;
-							if (cJSONUtils_strcasecmp(a->string,b->string))	return -6;	/* missing member */
-							err=cJSONUtils_Compare(a,b);if (err) return err;
-							a=a->next,b=b->next;
-						}
-						return (a || b)?-5:0;	/* object length mismatch */
-
-	default:			break;
-	}
-	return 0;
-}
-
-static int cJSONUtils_ApplyPatch(cJSON *object,cJSON *patch)
-{
-	cJSON *op=0,*path=0,*value=0,*parent=0;int opcode=0;char *parentptr=0,*childptr=0;
-
-	op=cJSON_GetObjectItem(patch,"op");
-	path=cJSON_GetObjectItem(patch,"path");
-	if (!op || !path) return 2;	/* malformed patch. */
-
-	if		(!strcmp(op->valuestring,"add"))	opcode=0;
-	else if (!strcmp(op->valuestring,"remove")) opcode=1;
-	else if (!strcmp(op->valuestring,"replace"))opcode=2;
-	else if (!strcmp(op->valuestring,"move"))	opcode=3;
-	else if (!strcmp(op->valuestring,"copy"))	opcode=4;
-	else if (!strcmp(op->valuestring,"test"))	return cJSONUtils_Compare(cJSONUtils_GetPointer(object,path->valuestring),cJSON_GetObjectItem(patch,"value"));
-	else return 3; /* unknown opcode. */
-
-	if (opcode==1 || opcode==2)	/* Remove/Replace */
-	{
-		cJSON_Delete(cJSONUtils_PatchDetach(object,path->valuestring));	/* Get rid of old. */
-		if (opcode==1) return 0;	/* For Remove, this is job done. */
-	}
-
-	if (opcode==3 || opcode==4)	/* Copy/Move uses "from". */
-	{
-		cJSON *from=cJSON_GetObjectItem(patch,"from");	if (!from) return 4; /* missing "from" for copy/move. */
-
-		if (opcode==3) value=cJSONUtils_PatchDetach(object,from->valuestring);
-		if (opcode==4) value=cJSONUtils_GetPointer(object,from->valuestring);
-		if (!value) return 5; /* missing "from" for copy/move. */
-		if (opcode==4) value=cJSON_Duplicate(value,1);
-		if (!value) return 6; /* out of memory for copy/move. */
-	}
-	else	/* Add/Replace uses "value". */
-	{
-		value=cJSON_GetObjectItem(patch,"value");
-		if (!value) return 7; /* missing "value" for add/replace. */
-		value=cJSON_Duplicate(value,1);
-		if (!value) return 8; /* out of memory for add/replace. */
-	}
-		
-	/* Now, just add "value" to "path". */
-
-	parentptr=strdup(path->valuestring);	childptr=strrchr(parentptr,'/');	if (childptr) *childptr++=0;
-	parent=cJSONUtils_GetPointer(object,parentptr);
-	cJSONUtils_InplaceDecodePointerString(childptr);
-
-	/* add, remove, replace, move, copy, test. */
-	if (!parent) {free(parentptr); cJSON_Delete(value); return 9;}	/* Couldn't find object to add to. */
-	else if (parent->type==cJSON_Array)
-	{
-		if (!strcmp(childptr,"-"))	cJSON_AddItemToArray(parent,value);
-		else						cJSON_InsertItemInArray(parent,atoi(childptr),value);
-	}
-	else if (parent->type==cJSON_Object)
-	{
-		cJSON_DeleteItemFromObject(parent,childptr);
-		cJSON_AddItemToObject(parent,childptr,value);
-	}
-	else
-	{
-		cJSON_Delete(value);
-	}
-	free(parentptr);
-	return 0;
-}
-
-
-int cJSONUtils_ApplyPatches(cJSON *object,cJSON *patches)
-{
-	int err;
-	if (patches->type!=cJSON_Array) return 1;	/* malformed patches. */
-	if (patches) patches=patches->child;
-	while (patches)
-	{
-		if ((err=cJSONUtils_ApplyPatch(object,patches))) return err;
-		patches=patches->next;
-	}
-	return 0;
-}
-
-static void cJSONUtils_GeneratePatch(cJSON *patches,const char *op,const char *path,const char *suffix,cJSON *val)
-{
-	cJSON *patch=cJSON_CreateObject();
-	cJSON_AddItemToObject(patch,"op",cJSON_CreateString(op));
-	if (suffix)
-	{
-		char *newpath=(char*)malloc(strlen(path)+cJSONUtils_PointerEncodedstrlen(suffix)+2);
-		cJSONUtils_PointerEncodedstrcpy(newpath+sprintf(newpath,"%s/",path),suffix);
-		cJSON_AddItemToObject(patch,"path",cJSON_CreateString(newpath));
-		free(newpath);
-	}
-	else	cJSON_AddItemToObject(patch,"path",cJSON_CreateString(path));
-	if (val) cJSON_AddItemToObject(patch,"value",cJSON_Duplicate(val,1));
-	cJSON_AddItemToArray(patches,patch);
-}
-
-void cJSONUtils_AddPatchToArray(cJSON *array,const char *op,const char *path,cJSON *val)	{cJSONUtils_GeneratePatch(array,op,path,0,val);}
-
-static void cJSONUtils_CompareToPatch(cJSON *patches,const char *path,cJSON *from,cJSON *to)
-{
-	if (from->type!=to->type)	{cJSONUtils_GeneratePatch(patches,"replace",path,0,to);	return;	}
-	
-	switch (from->type)
-	{
-	case cJSON_Number:	
-		if (from->valueint!=to->valueint || from->valuedouble!=to->valuedouble)
-			cJSONUtils_GeneratePatch(patches,"replace",path,0,to);
-		return;
-						
-	case cJSON_String:	
-		if (strcmp(from->valuestring,to->valuestring)!=0)
-			cJSONUtils_GeneratePatch(patches,"replace",path,0,to);
-		return;
-
-	case cJSON_Array:
-	{
-		int c;char *newpath=(char*)malloc(strlen(path)+23);	/* Allow space for 64bit int. */
-		for (c=0,from=from->child,to=to->child;from && to;from=from->next,to=to->next,c++){
-										sprintf(newpath,"%s/%d",path,c);	cJSONUtils_CompareToPatch(patches,newpath,from,to);
-		}
-		for (;from;from=from->next,c++)	{sprintf(newpath,"%d",c);	cJSONUtils_GeneratePatch(patches,"remove",path,newpath,0);	}
-		for (;to;to=to->next,c++)		cJSONUtils_GeneratePatch(patches,"add",path,"-",to);
-		free(newpath);
-		return;
-	}
-
-	case cJSON_Object:
-	{
-		cJSON *a,*b;
-		cJSONUtils_SortObject(from);
-		cJSONUtils_SortObject(to);
-		
-		a=from->child,b=to->child;
-		while (a || b)
-		{
-			int diff=(!a)?1:(!b)?-1:cJSONUtils_strcasecmp(a->string,b->string);
-			if (!diff)
-			{
-				char *newpath=(char*)malloc(strlen(path)+cJSONUtils_PointerEncodedstrlen(a->string)+2);
-				cJSONUtils_PointerEncodedstrcpy(newpath+sprintf(newpath,"%s/",path),a->string);
-				cJSONUtils_CompareToPatch(patches,newpath,a,b);
-				free(newpath);
-				a=a->next;
-				b=b->next;
-			}
-			else if (diff<0)	{cJSONUtils_GeneratePatch(patches,"remove",path,a->string,0);	a=a->next;}
-			else				{cJSONUtils_GeneratePatch(patches,"add",path,b->string,b);		b=b->next;}
-		}
-		return;
-	}
-
-	default:			break;
-	}
-}
-
-
-cJSON* cJSONUtils_GeneratePatches(cJSON *from,cJSON *to)
-{
-	cJSON *patches=cJSON_CreateArray();	
-	cJSONUtils_CompareToPatch(patches,"",from,to);
-	return patches;
-}
-
-
-static cJSON *cJSONUtils_SortList(cJSON *list)
-{
-	cJSON *first=list,*second=list,*ptr=list;
-
-	if (!list || !list->next) return list;	/* One entry is sorted already. */
-	
-	while (ptr && ptr->next && cJSONUtils_strcasecmp(ptr->string,ptr->next->string)<0) ptr=ptr->next;	/* Test for list sorted. */
-	if (!ptr || !ptr->next) return list;	/* Leave sorted lists unmodified. */
-	ptr=list;
-
-	while (ptr) {second=second->next;ptr=ptr->next;if (ptr) ptr=ptr->next;}	/* Walk two pointers to find the middle. */
-	if (second && second->prev) second->prev->next=0;	/* Split the lists */
-
-	first=cJSONUtils_SortList(first);	/* Recursively sort the sub-lists. */
-	second=cJSONUtils_SortList(second);
-	list=ptr=0;
-
-	while (first && second)	/* Merge the sub-lists */
-	{		
-		if (cJSONUtils_strcasecmp(first->string,second->string)<0)
-		{
-			if (!list) list=ptr=first;
-			else	{ptr->next=first;first->prev=ptr;ptr=first;}
-			first=first->next;
-		}
-		else 
-		{
-			if (!list) list=ptr=second;
-			else	{ptr->next=second;second->prev=ptr;ptr=second;}
-			second=second->next;
-		}
-	}
-	if (first)	{	if (!list) return first;	ptr->next=first;	first->prev=ptr;	}	/* Append any tails. */
-	if (second)	{	if (!list) return second;	ptr->next=second;	second->prev=ptr;	}
-
-	return list;
-}
-
-void cJSONUtils_SortObject(cJSON *object)	{object->child=cJSONUtils_SortList(object->child);}
-
-cJSON* cJSONUtils_MergePatch(cJSON *target, cJSON *patch)
-{
-	if (!patch || patch->type != cJSON_Object) {cJSON_Delete(target);return cJSON_Duplicate(patch,1);}
-	if (!target || target->type != cJSON_Object) {cJSON_Delete(target);target=cJSON_CreateObject();}
-
-	patch=patch->child;
-	while (patch)
-	{
-		if (patch->type == cJSON_NULL) cJSON_DeleteItemFromObject(target,patch->string);
-		else
-		{
-			cJSON *replaceme=cJSON_DetachItemFromObject(target,patch->string);
-			cJSON_AddItemToObject(target,patch->string,cJSONUtils_MergePatch(replaceme,patch));
-		}
-		patch=patch->next;
-	}
-	return target;
-}
-
-cJSON *cJSONUtils_GenerateMergePatch(cJSON *from,cJSON *to)
-{
-	cJSON *patch=0;
-	if (!to) return cJSON_CreateNull();
-	if (to->type!=cJSON_Object || !from || from->type!=cJSON_Object) return cJSON_Duplicate(to,1);
-	cJSONUtils_SortObject(from);
-	cJSONUtils_SortObject(to);
-	from=from->child;to=to->child;
-	patch=cJSON_CreateObject();
-	while (from || to)
-	{
-		int compare=from?(to?strcmp(from->string,to->string):-1):1;
-		if (compare<0)
-		{
-			cJSON_AddItemToObject(patch,from->string,cJSON_CreateNull());
-			from=from->next;
-		}
-		else if (compare>0)
-		{
-			cJSON_AddItemToObject(patch,to->string,cJSON_Duplicate(to,1));
-			to=to->next;
-		}
-		else
-		{
-			if (cJSONUtils_Compare(from,to)) cJSON_AddItemToObject(patch,to->string,cJSONUtils_GenerateMergePatch(from,to));
-			from=from->next;to=to->next;
-		}
-	}
-	if (!patch->child) {cJSON_Delete(patch);return 0;}
-	return patch;
-}
+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;
+    }
+
+    /* 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, false) < 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) || (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;
+    }
+
+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) || (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);
+}

+ 59 - 15
components/json/port/include/cJSON_Utils.h

@@ -1,30 +1,74 @@
+/*
+  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.
+*/
+
 #include "cJSON.h"
 
-/* Implement RFC6901 (https://tools.ietf.org/html/rfc6901) JSON Pointer spec.	*/
-cJSON *cJSONUtils_GetPointer(cJSON *object,const char *pointer);
+/* 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.		*/
-cJSON* cJSONUtils_GeneratePatches(cJSON *from,cJSON *to);
-void cJSONUtils_AddPatchToArray(cJSON *array,const char *op,const char *path,cJSON *val);	/* Utility for generating patch array entries.	*/
-int cJSONUtils_ApplyPatches(cJSON *object,cJSON *patches);	/* Returns 0 for success. */
+/* 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;
+//    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. */
-cJSON* cJSONUtils_MergePatch(cJSON *target, cJSON *patch); /* target will be modified by patch. return value is new ptr for target. */
-cJSON *cJSONUtils_GenerateMergePatch(cJSON *from,cJSON *to); /* generates a patch to move from -> to */
+/* 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);
 
-char *cJSONUtils_FindPointerFromObjectTo(cJSON *object,cJSON *target);	/* Given a root object and a target object, construct a pointer from one to the other.	*/
+/* 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);
 
-void cJSONUtils_SortObject(cJSON *object);	/* Sorts the members of the object into alphabetical order.	*/
+/* 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);

+ 15 - 1
components/lwip/api/api_lib.c

@@ -136,6 +136,15 @@ netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_cal
   return conn;
 }
 
+static inline bool is_created_by_socket(struct netconn *conn)
+{
+#if LWIP_SOCKET
+  if (conn && (conn->socket != -1)) {
+    return true;
+  }
+#endif
+  return false;
+}
 /**
  * Close a netconn 'connection' and free its resources.
  * UDP and RAW connection are completely closed, TCP pcbs might still be in a waitstate
@@ -174,7 +183,12 @@ netconn_delete(struct netconn *conn)
     return err;
   }
 
-#if !ESP_THREAD_SAFE
+#if ESP_THREAD_SAFE
+  if (is_created_by_socket(conn) == false) {
+    LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("netconn_delete - free conn\n"));
+    netconn_free(conn);
+  }
+#else
   LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("netconn_delete - free conn\n"));
   netconn_free(conn);
 #endif

+ 12 - 0
components/mbedtls/port/include/mbedtls/esp_debug.h

@@ -11,6 +11,12 @@
 // 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 _ESP_DEBUG_H_
+#define _ESP_DEBUG_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
 
 #include "sdkconfig.h"
 #ifdef CONFIG_MBEDTLS_DEBUG
@@ -43,3 +49,9 @@ void mbedtls_esp_disable_debug_log(mbedtls_ssl_config *conf);
 
 
 #endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ESP_DEBUG_H__ */

+ 2 - 1
components/mdns/mdns.c

@@ -569,7 +569,8 @@ static const uint8_t * _mdns_read_fqdn(const uint8_t * packet, const uint8_t * s
                     && (strcmp(buf, MDNS_DEFAULT_DOMAIN) != 0)
                     && (strcmp(buf, "ip6") != 0)
                     && (strcmp(buf, "in-addr") != 0)) {
-                snprintf((char*)name, MDNS_NAME_BUF_LEN, "%s.%s", name->host, buf);
+                strlcat(name->host, ".", sizeof(name->host));
+                strlcat(name->host, buf, sizeof(name->host));
             } else if (strcmp(buf, MDNS_SUB_STR) == 0) {
                 name->sub = 1;
             } else {

+ 24 - 18
components/newlib/time.c

@@ -42,7 +42,7 @@
 #endif
 
 #if defined( CONFIG_ESP32_TIME_SYSCALL_USE_FRC1 ) || defined( CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1 )
-#define WITH_FRC1 1
+#define WITH_FRC 1
 #endif
 
 #ifdef WITH_RTC
@@ -72,19 +72,21 @@ static uint64_t get_rtc_time_us()
 // s_boot_time: time from Epoch to the first boot time
 #ifdef WITH_RTC
 // when RTC is used to persist time, two RTC_STORE registers are used to store boot time
-#elif defined(WITH_FRC1)
+#elif defined(WITH_FRC)
 static uint64_t s_boot_time;
-#endif
+#endif // WITH_RTC
 
-#if defined(WITH_RTC) || defined(WITH_FRC1)
+#if defined(WITH_RTC) || defined(WITH_FRC)
 static _lock_t s_boot_time_lock;
 #endif
 
-#ifdef WITH_RTC
+// Offset between FRC timer and the RTC.
+// Initialized after reset or light sleep.
+#if defined(WITH_RTC) && defined(WITH_FRC)
 uint64_t s_microseconds_offset;
 #endif
 
-#if defined(WITH_RTC) || defined(WITH_FRC1)
+#if defined(WITH_RTC) || defined(WITH_FRC)
 static void set_boot_time(uint64_t time_us)
 {
     _lock_acquire(&s_boot_time_lock);
@@ -109,7 +111,7 @@ static uint64_t get_boot_time()
     _lock_release(&s_boot_time_lock);
     return result;
 }
-#endif //defined(WITH_RTC) || defined(WITH_FRC1)
+#endif //defined(WITH_RTC) || defined(WITH_FRC)
 
 
 void esp_clk_slowclk_cal_set(uint32_t new_cal)
@@ -139,10 +141,10 @@ uint32_t esp_clk_slowclk_cal_get()
 
 void esp_set_time_from_rtc()
 {
-#if defined( WITH_FRC1 ) && defined( WITH_RTC )
+#if defined( WITH_FRC ) && defined( WITH_RTC )
     // initialize time from RTC clock
     s_microseconds_offset = get_rtc_time_us() - esp_timer_get_time();
-#endif // WITH_FRC1 && WITH_RTC
+#endif // WITH_FRC && WITH_RTC
 }
 
 uint64_t esp_clk_rtc_time(void)
@@ -166,23 +168,27 @@ clock_t IRAM_ATTR _times_r(struct _reent *r, struct tms *ptms)
     return (clock_t) tv.tv_sec;
 }
 
-#if defined( WITH_FRC1 ) || defined( WITH_RTC )
+#if defined( WITH_FRC ) || defined( WITH_RTC )
 static uint64_t get_time_since_boot()
 {
     uint64_t microseconds = 0;
-#ifdef WITH_FRC1
+#ifdef WITH_FRC
+#ifdef WITH_RTC
     microseconds = s_microseconds_offset + esp_timer_get_time();
+#else
+    microseconds = esp_timer_get_time();
+#endif // WITH_RTC
 #elif defined(WITH_RTC)
     microseconds = get_rtc_time_us();
-#endif
+#endif // WITH_FRC
     return microseconds;
 }
-#endif // defined( WITH_FRC1 ) || defined( WITH_RTC )
+#endif // defined( WITH_FRC ) || defined( WITH_RTC )
 
 int IRAM_ATTR _gettimeofday_r(struct _reent *r, struct timeval *tv, void *tz)
 {
     (void) tz;
-#if defined( WITH_FRC1 ) || defined( WITH_RTC )
+#if defined( WITH_FRC ) || defined( WITH_RTC )
     if (tv) {
         uint64_t microseconds = get_boot_time() + get_time_since_boot();
         tv->tv_sec = microseconds / 1000000;
@@ -192,13 +198,13 @@ int IRAM_ATTR _gettimeofday_r(struct _reent *r, struct timeval *tv, void *tz)
 #else
     __errno_r(r) = ENOSYS;
     return -1;
-#endif // defined( WITH_FRC1 ) || defined( WITH_RTC )
+#endif // defined( WITH_FRC ) || defined( WITH_RTC )
 }
 
 int settimeofday(const struct timeval *tv, const struct timezone *tz)
 {
     (void) tz;
-#if defined( WITH_FRC1 ) || defined( WITH_RTC )
+#if defined( WITH_FRC ) || defined( WITH_RTC )
     if (tv) {
         uint64_t now = ((uint64_t) tv->tv_sec) * 1000000LL + tv->tv_usec;
         uint64_t since_boot = get_time_since_boot();
@@ -233,7 +239,7 @@ unsigned int sleep(unsigned int seconds)
 
 uint32_t system_get_time(void)
 {
-#if defined( WITH_FRC1 ) || defined( WITH_RTC )
+#if defined( WITH_FRC ) || defined( WITH_RTC )
     return get_time_since_boot();
 #else
     return 0;
@@ -244,7 +250,7 @@ uint32_t system_get_current_time(void) __attribute__((alias("system_get_time")))
 
 uint32_t system_relative_time(uint32_t current_time)
 {
-#if defined( WITH_FRC1 ) || defined( WITH_RTC )
+#if defined( WITH_FRC ) || defined( WITH_RTC )
     return get_time_since_boot() - current_time;
 #else
     return 0;

+ 2 - 2
components/nvs_flash/src/nvs_item_hash_list.cpp

@@ -62,7 +62,7 @@ void HashList::insert(const Item& item, size_t index)
 
 void HashList::erase(size_t index)
 {
-    for (auto it = std::begin(mBlockList); it != std::end(mBlockList);) {
+    for (auto it = mBlockList.begin(); it != mBlockList.end();) {
         bool haveEntries = false;
         for (size_t i = 0; i < it->mCount; ++i) {
             if (it->mNodes[i].mIndex == index) {
@@ -88,7 +88,7 @@ void HashList::erase(size_t index)
 size_t HashList::find(size_t start, const Item& item)
 {
     const uint32_t hash_24 = item.calculateCrc32WithoutValue() & 0xffffff;
-    for (auto it = std::begin(mBlockList); it != std::end(mBlockList); ++it) {
+    for (auto it = mBlockList.begin(); it != mBlockList.end(); ++it) {
         for (size_t index = 0; index < it->mCount; ++index) {
             HashListNode& e = it->mNodes[index];
             if (e.mIndex >= start &&

+ 5 - 1
components/openssl/platform/ssl_pm.c

@@ -154,7 +154,11 @@ int ssl_pm_new(SSL *ssl)
     }
 
     if (ssl->ctx->ssl_alpn.alpn_status == ALPN_ENABLE) {
-	 mbedtls_ssl_conf_alpn_protocols( &ssl_pm->conf, ssl->ctx->ssl_alpn.alpn_list );
+#ifdef MBEDTLS_SSL_ALPN
+        mbedtls_ssl_conf_alpn_protocols( &ssl_pm->conf, ssl->ctx->ssl_alpn.alpn_list );
+#else
+        SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "CONFIG_MBEDTLS_SSL_ALPN must be enabled to use ALPN", -1);
+#endif // MBEDTLS_SSL_ALPN
     }
     mbedtls_ssl_conf_rng(&ssl_pm->conf, mbedtls_ctr_drbg_random, &ssl_pm->ctr_drbg);
 

+ 12 - 7
components/soc/esp32/include/soc/ledc_struct.h

@@ -69,13 +69,18 @@ typedef volatile struct {
         struct {
             union {
                 struct {
-                    uint32_t duty_resolution:  5;         /*This register controls resolution of PWN duty by defining the bith width of timer's counter. The max bit width of the counter is 20.*/
-                    uint32_t clock_divider:   18;         /*This register is used to configure the divider of clock at the entry of timer. The least significant eight bits represent the decimal part.*/
-                    uint32_t pause:            1;         /*This bit is used to pause the counter in high speed timer*/
-                    uint32_t rst:              1;         /*This bit is used to reset high speed timer the counter will be 0 after reset.*/
-                    uint32_t tick_sel:         1;         /*This bit is used to choose apb_clk or ref_tick for high speed timer. 1'b1:apb_clk  0:ref_tick*/
-                    uint32_t low_speed_update: 1;         /*This bit is only useful for low speed timer channels, reserved for high speed timers*/
-                    uint32_t reserved26:       5;
+                    uint32_t duty_resolution:   5;     /*This register controls resolution of PWN duty by defining the bit width of timer's counter. The max bit width of the counter is 20.*/
+                    uint32_t clock_divider:    18;     /*This register is used to configure the divider of clock at the entry of timer. The least significant eight bits represent the decimal part.*/
+                    uint32_t pause:             1;     /*This bit is used to pause the counter in high speed timer*/
+                    uint32_t rst:               1;     /*This bit is used to reset high speed timer the counter will be 0 after reset.*/
+                    uint32_t tick_sel:          1;     /*This bit is used to choose apb_clk or ref_tick for high speed timer. 1'b1:apb_clk  0:ref_tick*/
+                    uint32_t low_speed_update:  1;     /*This bit is only useful for low speed timer channels, reserved for high speed timers*/
+                    uint32_t reserved26:        5;
+                };
+                struct {
+                    uint32_t bit_num:           5 __attribute__((deprecated));  /*Deprecated in ESP-IDF 3.0. This is an alias to 'duty_resolution' for backward compatibility with ESP-IDF 2.1.*/
+                    uint32_t div_num:          18 __attribute__((deprecated));  /*Deprecated in ESP-IDF 3.0. This is an alias to 'clock_divider' for backward compatibility with ESP-IDF 2.1.*/
+                    uint32_t place_holder:      9 __attribute__((deprecated));  /*A place holder to accommodate deprecated members*/
                 };
                 uint32_t val;
             } conf;

+ 10 - 1
components/soc/esp32/include/soc/rtc_cntl_reg.h

@@ -1830,7 +1830,16 @@
 #define RTC_CNTL_SCRATCH7_V  0xFFFFFFFF
 #define RTC_CNTL_SCRATCH7_S  0
 
-#define RTC_CNTL_DIAG0_REG          (DR_REG_RTCCNTL_BASE + 0xc0)
+#define RTC_CNTL_LOW_POWER_ST_REG          (DR_REG_RTCCNTL_BASE + 0xc0)
+/* RTC_CNTL_RDY_FOR_WAKEUP : R/0; bitpos:[19]; default: 0 */
+/*description: 1 if RTC controller is ready to execute WAKE instruction, 0 otherwise */
+#define RTC_CNTL_RDY_FOR_WAKEUP  (BIT(19))
+#define RTC_CNTL_RDY_FOR_WAKEUP_M  (BIT(19))
+#define RTC_CNTL_RDY_FOR_WAKEUP_V  0x1
+#define RTC_CNTL_RDY_FOR_WAKEUP_S  19
+
+/* Compatibility definition */
+#define RTC_CNTL_DIAG0_REG RTC_CNTL_LOW_POWER_ST_REG
 /* RTC_CNTL_LOW_POWER_DIAG0 : RO ;bitpos:[31:0] ;default: 0 ; */
 /*description: */
 #define RTC_CNTL_LOW_POWER_DIAG0  0xFFFFFFFF

+ 9 - 0
components/soc/esp32/test/test_rtc_clk.c

@@ -112,6 +112,15 @@ static void test_clock_switching(void (*switch_func)(rtc_cpu_freq_t))
     ref_clock_deinit();
 }
 
+TEST_CASE("Calculate 8M clock frequency", "[rtc_clk]")
+{
+    // calibrate 8M/256 clock against XTAL, get 8M/256 clock period
+    uint32_t rtc_8md256_period = rtc_clk_cal(RTC_CAL_8MD256, 100);
+    uint32_t rtc_fast_freq_hz = 1000000ULL * (1 << RTC_CLK_CAL_FRACT) * 256 / rtc_8md256_period;
+    printf("RTC_FAST_CLK=%d Hz\n", rtc_fast_freq_hz);
+    TEST_ASSERT_INT32_WITHIN(500000, RTC_FAST_CLK_FREQ_APPROX, rtc_fast_freq_hz);
+}
+
 TEST_CASE("Test switching between PLL and XTAL", "[rtc_clk]")
 {
     test_clock_switching(rtc_clk_cpu_freq_set);

+ 31 - 0
components/spi_flash/Kconfig

@@ -1,5 +1,36 @@
 menu "SPI Flash driver"
 
+config SPI_FLASH_VERIFY_WRITE
+    bool "Verify SPI flash writes"
+    default n
+    help
+        If this option is enabled, any time SPI flash is written then the data will be read
+        back and verified. This can catch hardware problems with SPI flash, or flash which
+        was not erased before verification.
+
+config SPI_FLASH_LOG_FAILED_WRITE
+    bool "Log errors if verification fails"
+    depends on SPI_FLASH_VERIFY_WRITE
+    default n
+    help
+        If this option is enabled, if SPI flash write verification fails then a log error line
+        will be written with the address, expected & actual values. This can be useful when
+        debugging hardware SPI flash problems.
+
+config SPI_FLASH_WARN_SETTING_ZERO_TO_ONE
+    bool "Log warning if writing zero bits to ones"
+    depends on SPI_FLASH_VERIFY_WRITE
+    default n
+    help
+        If this option is enabled, any SPI flash write which tries to set zero bits in the flash to
+        ones will log a warning. Such writes will not result in the requested data appearing identically
+        in flash once written, as SPI NOR flash can only set bits to one when an entire sector is erased.
+        After erasing, individual bits can only be written from one to zero.
+
+        Note that some software (such as SPIFFS) which is aware of SPI NOR flash may write one bits as an
+        optimisation, relying on the data in flash becoming a bitwise AND of the new data and any existing data.
+        Such software will log spurious warnings if this option is enabled.
+
 config SPI_FLASH_ENABLE_COUNTERS
     bool "Enable operation counters"
     default 0

+ 80 - 4
components/spi_flash/flash_ops.c

@@ -44,8 +44,9 @@
 #define MAX_WRITE_CHUNK 8192
 #define MAX_READ_CHUNK 16384
 
+static const char *TAG __attribute__((unused)) = "spi_flash";
+
 #if CONFIG_SPI_FLASH_ENABLE_COUNTERS
-static const char *TAG = "spi_flash";
 static spi_flash_counters_t s_flash_stats;
 
 #define COUNTER_START()     uint32_t ts_begin = xthal_get_ccount()
@@ -233,6 +234,81 @@ esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size)
     return spi_flash_translate_rc(rc);
 }
 
+/* Wrapper around esp_rom_spiflash_write() that verifies data as written if CONFIG_SPI_FLASH_VERIFY_WRITE is set.
+
+   If CONFIG_SPI_FLASH_VERIFY_WRITE is not set, this is esp_rom_spiflash_write().
+*/
+static IRAM_ATTR esp_rom_spiflash_result_t spi_flash_write_inner(uint32_t target, const uint32_t *src_addr, int32_t len)
+{
+#ifndef CONFIG_SPI_FLASH_VERIFY_WRITE
+    return esp_rom_spiflash_write(target, src_addr, len);
+#else // CONFIG_SPI_FLASH_VERIFY_WRITE
+    esp_rom_spiflash_result_t res = ESP_ROM_SPIFLASH_RESULT_OK;
+    assert(len % sizeof(uint32_t) == 0);
+
+    uint32_t before_buf[ESP_ROM_SPIFLASH_BUFF_BYTE_READ_NUM / sizeof(uint32_t)];
+    uint32_t after_buf[ESP_ROM_SPIFLASH_BUFF_BYTE_READ_NUM / sizeof(uint32_t)];
+    int32_t remaining = len;
+    for(int i = 0; i < len; i += sizeof(before_buf)) {
+        int i_w = i / sizeof(uint32_t); // index in words (i is an index in bytes)
+
+        int32_t read_len = MIN(sizeof(before_buf), remaining);
+
+        // Read "before" contents from flash
+        res = esp_rom_spiflash_read(target + i, before_buf, read_len);
+        if (res != ESP_ROM_SPIFLASH_RESULT_OK) {
+            break;
+        }
+
+#ifdef CONFIG_SPI_FLASH_WARN_SETTING_ZERO_TO_ONE
+        for (int r = 0; r < read_len; r += sizeof(uint32_t)) {
+            int r_w = r / sizeof(uint32_t); // index in words (r is index in bytes)
+
+            uint32_t write = src_addr[i_w + r_w];
+            uint32_t before = before_buf[r_w];
+            if ((before & write) != write) {
+                spi_flash_guard_end();
+                ESP_LOGW(TAG, "Write at offset 0x%x requests 0x%08x but will write 0x%08x -> 0x%08x",
+                         target + i + r, write, before, before & write);
+                spi_flash_guard_start();
+            }
+        }
+#endif
+
+        res = esp_rom_spiflash_write(target + i, &src_addr[i_w], read_len);
+        if (res != ESP_ROM_SPIFLASH_RESULT_OK) {
+            break;
+        }
+
+        res = esp_rom_spiflash_read(target + i, after_buf, read_len);
+        if (res != ESP_ROM_SPIFLASH_RESULT_OK) {
+            break;
+        }
+
+        for (int r = 0; r < read_len; r += sizeof(uint32_t)) {
+            int r_w = r / sizeof(uint32_t); // index in words (r is index in bytes)
+
+            uint32_t expected = src_addr[i_w + r_w] & before_buf[r_w];
+            uint32_t actual = after_buf[r_w];
+            if (expected != actual) {
+#ifdef CONFIG_SPI_FLASH_LOG_FAILED_WRITE
+                spi_flash_guard_end();
+                ESP_LOGE(TAG, "Bad write at offset 0x%x expected 0x%08x readback 0x%08x", target + i + r, expected, actual);
+                spi_flash_guard_start();
+#endif
+                res = ESP_ROM_SPIFLASH_RESULT_ERR;
+            }
+        }
+        if (res != ESP_ROM_SPIFLASH_RESULT_OK) {
+            break;
+        }
+        remaining -= read_len;
+    }
+    return res;
+#endif // CONFIG_SPI_FLASH_VERIFY_WRITE
+}
+
+
 esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size)
 {
     CHECK_WRITE_ADDRESS(dst, size);
@@ -269,7 +345,7 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size)
         uint32_t t = 0xffffffff;
         memcpy(((uint8_t *) &t) + (dst - left_off), srcc, left_size);
         spi_flash_guard_start();
-        rc = esp_rom_spiflash_write(left_off, &t, 4);
+        rc = spi_flash_write_inner(left_off, &t, 4);
         spi_flash_guard_end();
         if (rc != ESP_ROM_SPIFLASH_RESULT_OK) {
             goto out;
@@ -296,7 +372,7 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size)
                 write_src = (const uint8_t *)write_buf;
             }
             spi_flash_guard_start();
-            rc = esp_rom_spiflash_write(dst + mid_off, (const uint32_t *) write_src, write_size);
+            rc = spi_flash_write_inner(dst + mid_off, (const uint32_t *) write_src, write_size);
             spi_flash_guard_end();
             COUNTER_ADD_BYTES(write, write_size);
             mid_size -= write_size;
@@ -311,7 +387,7 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size)
         uint32_t t = 0xffffffff;
         memcpy(&t, srcc + right_off, right_size);
         spi_flash_guard_start();
-        rc = esp_rom_spiflash_write(dst + right_off, &t, 4);
+        rc = spi_flash_write_inner(dst + right_off, &t, 4);
         spi_flash_guard_end();
         if (rc != ESP_ROM_SPIFLASH_RESULT_OK) {
             goto out;

+ 19 - 0
components/spiffs/Kconfig

@@ -88,6 +88,25 @@ config SPIFFS_USE_MAGIC_LENGTH
         configured and formatted for 4 megabytes will not be accepted 
         for mounting with a configuration defining the filesystem as 2 megabytes.
 
+config SPIFFS_META_LENGTH
+    int "Size of per-file metadata field"
+    default 4
+    help
+        This option sets the number of extra bytes stored in the file header.
+        These bytes can be used in an application-specific manner.
+        Set this to at least 4 bytes to enable support for saving file
+        modification time.
+
+config SPIFFS_USE_MTIME
+    bool "Save file modification time"
+    default "y"
+    depends on SPIFFS_META_LENGTH >= 4
+    help
+        If enabled, then the first 4 bytes of per-file metadata will be used
+        to store file modification time (mtime), accessible through
+        stat/fstat functions.
+        Modification time is updated when the file is opened.
+
 menu "Debug Configuration"
 
 config SPIFFS_DBG

+ 45 - 2
components/spiffs/esp_spiffs.c

@@ -33,6 +33,10 @@
 
 static const char * TAG = "SPIFFS";
 
+#ifdef CONFIG_SPIFFS_USE_MTIME
+_Static_assert(CONFIG_SPIFFS_META_LENGTH >= sizeof(time_t),
+        "SPIFFS_META_LENGTH size should be >= sizeof(time_t)");
+#endif //CONFIG_SPIFFS_USE_MTIME
 /**
  * @brief SPIFFS definition structure
  */
@@ -80,6 +84,8 @@ static long vfs_spiffs_telldir(void* ctx, DIR* pdir);
 static void vfs_spiffs_seekdir(void* ctx, DIR* pdir, long offset);
 static int vfs_spiffs_mkdir(void* ctx, const char* name, mode_t mode);
 static int vfs_spiffs_rmdir(void* ctx, const char* name);
+static void vfs_spiffs_update_mtime(spiffs *fs, spiffs_file f);
+static time_t vfs_spiffs_get_mtime(const spiffs_stat* s);
 
 static esp_spiffs_t * _efs[CONFIG_SPIFFS_MAX_PARTITIONS];
 
@@ -507,12 +513,16 @@ static int vfs_spiffs_open(void* ctx, const char * path, int flags, int mode)
 {
     assert(path);
     esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
-    int fd = SPIFFS_open(efs->fs, path, spiffs_mode_conv(flags), mode);
+    int spiffs_flags = spiffs_mode_conv(flags);
+    int fd = SPIFFS_open(efs->fs, path, spiffs_flags, mode);
     if (fd < 0) {
         errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
         SPIFFS_clearerr(efs->fs);
         return -1;
     }
+    if (!(spiffs_flags & SPIFFS_RDONLY)) {
+        vfs_spiffs_update_mtime(efs->fs, fd);
+    }
     return fd;
 }
 
@@ -562,7 +572,6 @@ static off_t vfs_spiffs_lseek(void* ctx, int fd, off_t offset, int mode)
         return -1;
     }
     return res;
-
 }
 
 static int vfs_spiffs_fstat(void* ctx, int fd, struct stat * st)
@@ -578,6 +587,9 @@ static int vfs_spiffs_fstat(void* ctx, int fd, struct stat * st)
     }
     st->st_size = s.size;
     st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO | S_IFREG;
+    st->st_mtime = vfs_spiffs_get_mtime(&s);
+    st->st_atime = 0;
+    st->st_ctime = 0;
     return res;
 }
 
@@ -597,6 +609,9 @@ static int vfs_spiffs_stat(void* ctx, const char * path, struct stat * st)
     st->st_size = s.size;
     st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO;
     st->st_mode |= (s.type == SPIFFS_TYPE_DIR)?S_IFDIR:S_IFREG;
+    st->st_mtime = vfs_spiffs_get_mtime(&s);
+    st->st_atime = 0;
+    st->st_ctime = 0;
     return res;
 }
 
@@ -764,3 +779,31 @@ static int vfs_spiffs_link(void* ctx, const char* n1, const char* n2)
     errno = ENOTSUP;
     return -1;
 }
+
+static void vfs_spiffs_update_mtime(spiffs *fs, spiffs_file fd)
+{
+#ifdef CONFIG_SPIFFS_USE_MTIME
+    time_t t = time(NULL);
+    spiffs_stat s;
+    int ret = SPIFFS_OK;
+    if (CONFIG_SPIFFS_META_LENGTH > sizeof(t)) {
+        ret = SPIFFS_fstat(fs, fd, &s);
+    }
+    if (ret == SPIFFS_OK) {
+        memcpy(s.meta, &t, sizeof(t));
+        ret = SPIFFS_fupdate_meta(fs, fd, s.meta);
+    }
+    if (ret != SPIFFS_OK) {
+        ESP_LOGW(TAG, "Failed to update mtime (%d)", ret);
+    }
+#endif //CONFIG_SPIFFS_USE_MTIME
+}
+
+static time_t vfs_spiffs_get_mtime(const spiffs_stat* s)
+{
+    time_t t = 0;
+#ifdef CONFIG_SPIFFS_USE_MTIME
+    memcpy(&t, s->meta, sizeof(t));
+#endif
+    return t;
+}

+ 1 - 1
components/spiffs/include/spiffs_config.h

@@ -158,7 +158,7 @@ extern void spiffs_api_unlock(struct spiffs_t *fs);
 // This is derived from following:
 // logical_page_size - (SPIFFS_OBJ_NAME_LEN + sizeof(spiffs_page_header) +
 // spiffs_object_ix_header fields + at least some LUT entries)
-#define SPIFFS_OBJ_META_LEN             (0)
+#define SPIFFS_OBJ_META_LEN             (CONFIG_SPIFFS_META_LENGTH)
 
 // Size of buffer allocated on stack used when copying data.
 // Lower value generates more read/writes. No meaning having it bigger

+ 42 - 0
components/spiffs/test/test_spiffs.c

@@ -505,3 +505,45 @@ TEST_CASE("multiple tasks can use same volume", "[spiffs]")
     test_spiffs_concurrent("/spiffs/f");
     test_teardown();
 }
+
+#ifdef CONFIG_SPIFFS_USE_MTIME
+TEST_CASE("mtime is updated when file is opened", "[spiffs]")
+{
+    /* Open a file, check that mtime is set correctly */
+    const char* filename = "/spiffs/time";
+    test_setup();
+    time_t t_before_create = time(NULL);
+    test_spiffs_create_file_with_text(filename, "\n");
+    time_t t_after_create = time(NULL);
+
+    struct stat st;
+    TEST_ASSERT_EQUAL(0, stat(filename, &st));
+    printf("mtime=%d\n", (int) st.st_mtime);
+    TEST_ASSERT(st.st_mtime >= t_before_create
+             && st.st_mtime <= t_after_create);
+
+    /* Wait a bit, open again, check that mtime is updated */
+    vTaskDelay(2000 / portTICK_PERIOD_MS);
+    time_t t_before_open = time(NULL);
+    FILE *f = fopen(filename, "a");
+    time_t t_after_open = time(NULL);
+    TEST_ASSERT_EQUAL(0, fstat(fileno(f), &st));
+    printf("mtime=%d\n", (int) st.st_mtime);
+    TEST_ASSERT(st.st_mtime >= t_before_open
+             && st.st_mtime <= t_after_open);
+    fclose(f);
+
+    /* Wait a bit, open for reading, check that mtime is not updated */
+    vTaskDelay(2000 / portTICK_PERIOD_MS);
+    time_t t_before_open_ro = time(NULL);
+    f = fopen(filename, "r");
+    TEST_ASSERT_EQUAL(0, fstat(fileno(f), &st));
+    printf("mtime=%d\n", (int) st.st_mtime);
+    TEST_ASSERT(t_before_open_ro > t_after_open
+             && st.st_mtime >= t_before_open
+             && st.st_mtime <= t_after_open);
+    fclose(f);
+
+    test_teardown();
+}
+#endif // CONFIG_SPIFFS_USE_MTIME

+ 4 - 0
components/ulp/README.rst

@@ -18,6 +18,10 @@ In addition to the existing binutils port for the ESP32 ULP coprocessor, it is p
 
 The ``program`` array is an array of ``ulp_insn_t``, i.e. ULP coprocessor instructions. Each ``I_XXX`` preprocessor define translates into a single 32-bit instruction. Arguments of these preprocessor defines can be register numbers (``R0 — R3``) and literal constants. See `ULP coprocessor instruction defines`_ section for descriptions of instructions and arguments they take.
 
+.. note::
+
+    Because some of the instruction macros expand to inline function calls, defining such array in global scope will cause the compiler to produce an "initializer element is not constant" error. To fix this error, move the definition of instructions array into local scope.
+
 Load and store instructions use addresses expressed in 32-bit words. Address 0 corresponds to the first word of ``RTC_SLOW_MEM`` (which is address 0x50000000 as seen by the main CPUs).
 
 To generate branch instructions, special ``M_`` preprocessor defines are used. ``M_LABEL`` define can be used to define a branch target. Label identifier is a 16-bit integer. ``M_Bxxx`` defines can be used to generate branch instructions with target set to a particular label. 

+ 2 - 2
components/ulp/include/esp32/ulp.h

@@ -266,9 +266,9 @@ _Static_assert(sizeof(ulp_insn_t) == 4, "ULP coprocessor instruction size should
  * Delay (nop) for a given number of cycles
  */
 #define I_DELAY(cycles_) { .delay = {\
-    .opcode = OPCODE_DELAY, \
+    .cycles = cycles_, \
     .unused = 0, \
-    .cycles = cycles_ } }
+    .opcode = OPCODE_DELAY } }
 
 /**
  * Halt the coprocessor.

+ 21 - 2
docs/Doxyfile

@@ -153,7 +153,17 @@ INPUT = \
     ../components/esp32/include/esp_pm.h \
     ../components/esp32/include/esp32/pm.h \
     ### esp_timer, High Resolution Timer
-    ../components/esp32/include/esp_timer.h
+    ../components/esp32/include/esp_timer.h \
+    ###
+    ### FreeRTOS
+    ###
+    ../components/freertos/include/freertos/task.h \
+    ../components/freertos/include/freertos/queue.h \
+    ../components/freertos/include/freertos/semphr.h \
+    ../components/freertos/include/freertos/timers.h \
+    ../components/freertos/include/freertos/event_groups.h \
+    ../components/freertos/include/freertos/ringbuf.h
+
 
 
 ## Get warnings for functions that have no documentation for their parameters or return value
@@ -165,7 +175,16 @@ WARN_NO_PARAMDOC = YES
 ENABLE_PREPROCESSING   = YES
 MACRO_EXPANSION        = YES
 EXPAND_ONLY_PREDEF     = YES
-PREDEFINED             = __attribute__(x)=
+PREDEFINED             = \
+    __attribute__(x)= \
+    IRAM_ATTR= \
+    configSUPPORT_DYNAMIC_ALLOCATION=1 \
+    configSUPPORT_STATIC_ALLOCATION=1 \
+    configQUEUE_REGISTRY_SIZE=1 \
+    configUSE_RECURSIVE_MUTEXES=1 \
+    configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS=1 \
+    configNUM_THREAD_LOCAL_STORAGE_POINTERS=1 \
+    configUSE_APPLICATION_TASK_TAG=1
 
 ## Do not complain about not having dot
 ##

BIN
docs/_static/esp32-pico-kit-v4-dimensions-back.jpg


BIN
docs/_static/esp32-pico-kit-v4-dimensions-side.jpg


BIN
docs/_static/esp32-pico-kit-v4-functional-block-diagram.png


BIN
docs/_static/esp32-pico-kit-v4-layout.jpg


+ 1 - 1
docs/api-guides/build-system.rst

@@ -156,7 +156,7 @@ Minimal Component Makefile
 
 The minimal ``component.mk`` file is an empty file(!). If the file is empty, the default component behaviour is set:
 
-- All source files in the same directory as the makefile (``*.c``, ``*.cpp``, ``*.S``) will be compiled into the component library
+- All source files in the same directory as the makefile (``*.c``, ``*.cpp``, ``*.cc``, ``*.S``) will be compiled into the component library
 - A sub-directory "include" will be added to the global include search path for all other components.
 - The component library will be linked into the project app.
 

+ 135 - 78
docs/api-guides/freertos-smp.rst

@@ -20,8 +20,8 @@ found via http://www.freertos.org/a00106.html
 port of FreeRTOS v8.2.0, a number of FreeRTOS v9.0.0 features have been backported
 to ESP-IDF.
 
-:ref:`tasks-and-task-creation`: Use ``xTaskCreatePinnedToCore()`` or 
-``xTaskCreateStaticPinnedToCore()`` to create tasks in ESP-IDF FreeRTOS. The 
+:ref:`tasks-and-task-creation`: Use :cpp:func:`xTaskCreatePinnedToCore` or 
+:cpp:func:`xTaskCreateStaticPinnedToCore` to create tasks in ESP-IDF FreeRTOS. The 
 last parameter of the two functions is ``xCoreID``. This parameter specifies 
 which core the task is pinned to. Acceptable values are ``0`` for **PRO_CPU**, 
 ``1`` for **APP_CPU**, or ``tskNO_AFFINITY`` which allows the task to run on
@@ -34,13 +34,13 @@ enter a blocked state, or are distributed across a wider range of priorities.
 
 :ref:`scheduler-suspension`: Suspending the scheduler in ESP-IDF FreeRTOS will only 
 affect the scheduler on the the calling core. In other words, calling 
-``vTaskSuspendAll()`` on **PRO_CPU** will not prevent **APP_CPU** from scheduling, and
+:cpp:func:`vTaskSuspendAll` on **PRO_CPU** will not prevent **APP_CPU** from scheduling, and
 vice versa. Use critical sections or semaphores instead for simultaneous
 access protection.
 
 :ref:`tick-interrupt-synchronicity`: Tick interrupts of **PRO_CPU** and **APP_CPU** 
-are not synchronized. Do not expect to use ``vTaskDelay`` or 
-``vTaskDelayUntil`` as an accurate method of synchronizing task execution 
+are not synchronized. Do not expect to use :cpp:func:`vTaskDelay` or 
+:cpp:func:`vTaskDelayUntil` as an accurate method of synchronizing task execution 
 between the two cores. Use a counting semaphore instead as their context 
 switches are not tied to tick interrupts due to preemption.
 
@@ -50,12 +50,25 @@ scheduler and interrupts of the calling core. However the other core is left
 unaffected. If the other core attemps to take same mutex, it will spin until
 the calling core has released the mutex by exiting the critical section.
 
-:ref:`deletion-callbacks`: ESP-IDF FreeRTOS has 
-backported the Thread Local Storage Pointers feature. However they have the 
-extra feature of deletion callbacks. Deletion callbacks are used to
-automatically free memory used by Thread Local Storage Pointers during the task
-deletion. Call ``vTaskSetThreadLocalStoragePointerAndDelCallback()``
-to set Thread Local Storage Pointers and deletion callbacks.
+:ref:`floating-points`: The ESP32 supports hardware acceleration of single
+precision floating point arithmetic (``float``). However the use of hardware
+acceleration leads to some behavioral restrictions in ESP-IDF FreeRTOS.
+Therefore, tasks that utilize ``float`` will automatically be pinned to a core if 
+not done so already. Furthermore, ``float`` cannot be used in interrupt service 
+routines.
+
+:ref:`task-deletion`: Task deletion behavior has been backported from FreeRTOS 
+v9.0.0 and modified to be SMP compatible. Task memory will be freed immediately 
+when :cpp:func:`vTaskDelete` is called to delete a task that is not currently running 
+and not pinned to the other core. Otherwise, freeing of task memory will still 
+be delegated to the Idle Task.
+
+:ref:`deletion-callbacks`: ESP-IDF FreeRTOS has backported the Thread Local 
+Storage Pointers (TLSP) feature. However the extra feature of Deletion Callbacks has been
+added. Deletion callbacks are called automatically during task deletion and are
+used to free memory pointed to by TLSP. Call 
+:cpp:func:`vTaskSetThreadLocalStoragePointerAndDelCallback()` to set TLSP and Deletion
+Callbacks.
 
 :ref:`FreeRTOS Hooks<hooks_api_reference>`: Vanilla FreeRTOS Hooks were not designed for SMP.
 ESP-IDF provides its own Idle and Tick Hooks in addition to the Vanilla FreeRTOS
@@ -81,34 +94,34 @@ This feature has been backported from FreeRTOS v9.0.0 to ESP-IDF. The
 in order for static allocation functions to be available. Once enabled, the 
 following functions can be called...
 
- - ``xTaskCreateStatic()`` See :ref:`backporting-notes` below
- - ``xQueueCreateStatic()``
- - ``xSemaphoreCreateBinaryStatic()``
- - ``xSemaphoreCreateCountingStatic()``
- - ``xSemaphoreCreateMutexStatic()``
- - ``xSemaphoreCreateRecursiveMutexStatic()``
- - ``xTimerCreateStatic()``  See :ref:`backporting-notes` below
- - ``xEventGroupCreateStatic()``
+ - :cpp:func:`xTaskCreateStatic` (see :ref:`backporting-notes` below)
+ - :c:macro:`xQueueCreateStatic`
+ - :c:macro:`xSemaphoreCreateBinaryStatic`
+ - :c:macro:`xSemaphoreCreateCountingStatic`
+ - :c:macro:`xSemaphoreCreateMutexStatic`
+ - :c:macro:`xSemaphoreCreateRecursiveMutexStatic`
+ - :cpp:func:`xTimerCreateStatic`  (see :ref:`backporting-notes` below)
+ - :cpp:func:`xEventGroupCreateStatic`
 
 Other Features
 ^^^^^^^^^^^^^^
 
- - ``vTaskSetThreadLocalStoragePointer()`` See :ref:`backporting-notes` below
- - ``pvTaskGetThreadLocalStoragePointer()`` See :ref:`backporting-notes` below
- - ``vTimerSetTimerID()``
- - ``xTimerGetPeriod()``
- - ``xTimerGetExpiryTime()``
- - ``pcQueueGetName()``
- - ``uxSemaphoreGetCount()``
+ - :cpp:func:`vTaskSetThreadLocalStoragePointer` (see :ref:`backporting-notes` below)
+ - :cpp:func:`pvTaskGetThreadLocalStoragePointer` (see :ref:`backporting-notes` below)
+ - :cpp:func:`vTimerSetTimerID`
+ - :cpp:func:`xTimerGetPeriod`
+ - :cpp:func:`xTimerGetExpiryTime`
+ - :cpp:func:`pcQueueGetName`
+ - :c:macro:`uxSemaphoreGetCount`
 
 .. _backporting-notes:
 
 Backporting Notes
 ^^^^^^^^^^^^^^^^^
 
-**1)** ``xTaskCreateStatic`` has been made SMP compatible in a similar 
-fashion to ``xTaskCreate`` (see :ref:`tasks-and-task-creation`). Therefore 
-``xTaskCreateStaticPinnedToCore()`` can also be called.
+**1)** :cpp:func:`xTaskCreateStatic` has been made SMP compatible in a similar 
+fashion to :cpp:func:`xTaskCreate` (see :ref:`tasks-and-task-creation`). Therefore 
+:cpp:func:`xTaskCreateStaticPinnedToCore` can also be called.
 
 **2)** Although vanilla FreeRTOS allows the Timer feature's daemon task to 
 be statically allocated, the daemon task is always dynamically allocated in 
@@ -117,7 +130,7 @@ defined when using statically allocated timers in ESP-IDF FreeRTOS.
 
 **3)** The Thread Local Storage Pointer feature has been modified in ESP-IDF
 FreeRTOS to include Deletion Callbacks (see :ref:`deletion-callbacks`). Therefore
-the function ``vTaskSetThreadLocalStoragePointerAndDelCallback()`` can also be 
+the function :cpp:func:`vTaskSetThreadLocalStoragePointerAndDelCallback` can also be 
 called.
 
 
@@ -129,9 +142,9 @@ Tasks and Task Creation
 Tasks in ESP-IDF FreeRTOS are designed to run on a particular core, therefore 
 two new task creation functions have been added to ESP-IDF FreeRTOS by 
 appending ``PinnedToCore`` to the names of the task creation functions in 
-vanilla FreeRTOS. The vanilla FreeRTOS functions of ``xTaskCreate()``
-and ``xTaskCreateStatic()`` have led to the addition of 
-``xTaskCreatePinnedToCore()`` and ``xTaskCreateStaticPinnedToCore()`` in 
+vanilla FreeRTOS. The vanilla FreeRTOS functions of :cpp:func:`xTaskCreate`
+and :cpp:func:`xTaskCreateStatic` have led to the addition of 
+:cpp:func:`xTaskCreatePinnedToCore` and :cpp:func:`xTaskCreateStaticPinnedToCore` in 
 ESP-IDF FreeRTOS (see :ref:`backported-features`).
 
 For more details see :component_file:`freertos/task.c`
@@ -151,9 +164,9 @@ of 1000 bytes. It should be noted that the ``uxStackDepth`` parameter in
 vanilla FreeRTOS specifies a task’s stack depth in terms of the number of 
 words, whereas ESP-IDF FreeRTOS specifies the stack depth in terms of bytes.
 
-Note that the vanilla FreeRTOS functions ``xTaskCreate`` and 
-``xTaskCreateStatic`` have been macro defined in ESP-IDF FreeRTOS to call 
-``xTaskCreatePinnedToCore()`` and ``xTaskCreateStaticPinnedToCore()``
+Note that the vanilla FreeRTOS functions :cpp:func:`xTaskCreate` and 
+:cpp:func:`xTaskCreateStatic` have been defined in ESP-IDF FreeRTOS as inline functions which call 
+:cpp:func:`xTaskCreatePinnedToCore` and :cpp:func:`xTaskCreateStaticPinnedToCore`
 respectively with ``tskNO_AFFINITY`` as the ``xCoreID`` value. 
 
 Each Task Control Block (TCB) in ESP-IDF stores the ``xCoreID`` as a member. 
@@ -270,18 +283,18 @@ different cores.
 Scheduler Suspension
 ^^^^^^^^^^^^^^^^^^^^
 
-In vanilla FreeRTOS, suspending the scheduler via ``vTaskSuspendAll()`` will 
-prevent calls of ``vTaskSwitchContext()`` from context switching until the 
-scheduler has been resumed with ``vTaskResumeAll()``. However servicing ISRs 
+In vanilla FreeRTOS, suspending the scheduler via :cpp:func:`vTaskSuspendAll` will 
+prevent calls of ``vTaskSwitchContext`` from context switching until the 
+scheduler has been resumed with :cpp:func:`xTaskResumeAll`. However servicing ISRs 
 are still permitted. Therefore any changes in task states as a result from the
 current running task or ISRSs will not be executed until the scheduler is 
 resumed. Scheduler suspension in vanilla FreeRTOS is a common protection method 
 against simultaneous access of data shared between tasks, whilst still allowing 
 ISRs to be serviced.
 
-In ESP-IDF FreeRTOS, ``vTaskSuspendAll()`` will only prevent calls of 
+In ESP-IDF FreeRTOS, :cpp:func:`xTaskResumeAll` will only prevent calls of 
 ``vTaskSwitchContext()`` from switching contexts on the core that called for the
-suspension. Hence if **PRO_CPU** calls ``vTaskSuspendAll()``, **APP_CPU** will 
+suspension. Hence if **PRO_CPU** calls :cpp:func:`vTaskSuspendAll`, **APP_CPU** will 
 still be able to switch contexts. If data is shared between tasks that are 
 pinned to different cores, scheduler suspension is **NOT** a valid method of 
 protection against simultaneous access. Consider using critical sections 
@@ -289,7 +302,7 @@ protection against simultaneous access. Consider using critical sections
 protecting shared resources in ESP-IDF FreeRTOS.
 
 In general, it's better to use other RTOS primitives like mutex semaphores to protect
-against data shared between tasks, rather than ``vTaskSuspendAll()``.
+against data shared between tasks, rather than :cpp:func:`vTaskSuspendAll`.
 
 
 .. _tick-interrupt-synchronicity:
@@ -303,8 +316,8 @@ each core being independent, and the tick interrupts to each core being
 unsynchronized.
 
 In vanilla FreeRTOS the tick interrupt triggers a call to 
-``xTaskIncrementTick()`` which is responsible for incrementing the tick 
-counter, checking if tasks which have called ``vTaskDelay()`` have fulfilled 
+:cpp:func:`xTaskIncrementTick` which is responsible for incrementing the tick 
+counter, checking if tasks which have called :cpp:func:`vTaskDelay` have fulfilled 
 their delay period, and moving those tasks from the Delayed Task List to the 
 Ready Task List. The tick interrupt will then call the scheduler if a context 
 switch is necessary.
@@ -359,11 +372,11 @@ The ESP-IDF FreeRTOS critical section functions have been modified as follows…
 
  - ``taskENTER_CRITICAL(mux)``, ``taskENTER_CRITICAL_ISR(mux)``, 
    ``portENTER_CRITICAL(mux)``, ``portENTER_CRITICAL_ISR(mux)`` are all macro 
-   defined to call ``vTaskEnterCritical()`` 
+   defined to call :cpp:func:`vTaskEnterCritical` 
 
  - ``taskEXIT_CRITICAL(mux)``, ``taskEXIT_CRITICAL_ISR(mux)``, 
    ``portEXIT_CRITICAL(mux)``, ``portEXIT_CRITICAL_ISR(mux)`` are all macro 
-   defined to call ``vTaskExitCritical()``
+   defined to call :cpp:func:`vTaskExitCritical`
 
 For more details see :component_file:`freertos/include/freertos/portmacro.h` 
 and :component_file:`freertos/task.c`
@@ -375,41 +388,85 @@ mutex is provided upon entering and exiting, the type of call should not
 matter.
 
 
+.. _floating-points:
+
+Floating Point Aritmetic
+------------------------
+
+The ESP32 supports hardware acceleration of single precision floating point
+arithmetic (``float``) via Floating Point Units (FPU, also known as coprocessors) 
+attached to each core. The use of the FPUs imposes some behavioral restrictions 
+on ESP-IDF FreeRTOS.
+
+ESP-IDF FreeRTOS implements Lazy Context Switching for FPUs. In other words,
+the state of a core's FPU registers are not immediately saved when a context 
+switch occurs. Therefore, tasks that utilize ``float`` must be pinned to a
+particular core upon creation. If not, ESP-IDF FreeRTOS will automatically pin
+the task in question to whichever core the task was running on upon the task's 
+first use of ``float``. Likewise due to Lazy Context Switching, interrupt service 
+routines must also not use ``float``.
+
+ESP32 does not support hardware acceleration for double precision floating point
+arithmetic (``double``). Instead ``double`` is implemented via software hence the 
+behavioral restrictions with regards to ``float`` do not apply to ``double``. Note
+that due to the lack of hardware acceleration, ``double`` operations may consume
+significantly larger amount of CPU time in comparison to ``float``.
+
+
+.. _task-deletion:
+
+Task Deletion
+-------------
+
+FreeRTOS task deletion prior to v9.0.0 delegated the freeing of task memory 
+entirely to the Idle Task. Currently, the freeing of task memory will occur
+immediately (within :cpp:func:`vTaskDelete`) if the task being deleted is not currently 
+running or is not pinned to the other core (with respect to the core 
+:cpp:func:`vTaskDelete` is called on). TLSP deletion callbacks will also run immediately
+if the same conditions are met.
+
+However, calling :cpp:func:`vTaskDelete` to delete a task that is either currently 
+running or pinned to the other core will still result in the freeing of memory 
+being delegated to the Idle Task.
+
+
 .. _deletion-callbacks:
 
 Thread Local Storage Pointers & Deletion Callbacks
 --------------------------------------------------
 
-Thread Local Storage Pointers are pointers stored directly in the TCB which 
-allows each task to have a pointer to a data structure containing that is 
-specific to that task. However vanilla FreeRTOS provides no functionality to 
-free the memory pointed to by the Thread Local Storage Pointers. Therefore if 
-the memory pointed to by the Thread Local Storage Pointers is not explicitly 
-freed by the user before a task is deleted, memory leak will occur.
-
-ESP-IDF FreeRTOS provides the added feature of deletion callbacks. These 
-deletion callbacks are used to automatically free the memory pointed to by the 
-Thread Local Storage Pointers when a task is deleted. Each Thread Local Storage 
-Pointer can have its own call back, and these call backs are called when the 
-Idle tasks cleans up a deleted tasks.
-
-Vanilla FreeRTOS sets a Thread Local Storage Pointers using 
-``vTaskSetThreadLocalStoragePointer()`` whereas ESP-IDF FreeRTOS sets a Thread 
-Local Storage Pointers and Deletion Callbacks using 
-``vTaskSetThreadLocalStoragePointerAndDelCallback()`` which accepts a pointer 
-to the deletion call back as an extra parameter of type 
-```TlsDeleteCallbackFunction_t``. Calling the vanilla FreeRTOS API 
-``vTaskSetThreadLocalStoragePointer()`` is still valid however it is internally
-defined to call ``vTaskSetThreadLocalStoragePointerAndDelCallback()`` with a
-``NULL`` pointer as the deletion call back. This results in the selected Thread 
-Local Storage Pointer to have no deletion call back.
-
-In IDF the FreeRTOS thread local storage at index 0 is reserved and is used to implement
-the pthreads API thread local storage (pthread_getspecific() & pthread_setspecific()).
-Other indexes can be used for any purpose, provided
-:ref:`CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS` is set to a high enough value.
-
-For more details see :component_file:`freertos/include/freertos/task.h`
+Thread Local Storage Pointers (TLSP) are pointers stored directly in the TCB. 
+TLSP allow each task to have its own unique set of pointers to data structures. 
+However task deletion behavior in vanilla FreeRTOS does not automatically 
+free the memory pointed to by TLSP. Therefore if the memory pointed to by
+TLSP is not explicitly freed by the user before task deletion, memory leak will 
+occur.
+
+ESP-IDF FreeRTOS provides the added feature of Deletion Callbacks. Deletion 
+Callbacks are called automatically during task deletion to free memory pointed
+to by TLSP. Each TLSP can have its own Deletion Callback. Note that due to the
+to :ref:`task-deletion` behavior, there can be instances where Deletion 
+Callbacks are called in the context of the Idle Tasks. Therefore Deletion
+Callbacks **should never attempt to block** and critical sections should be kept
+as short as possible to minimize priority inversion.
+
+Deletion callbacks are of type
+``void (*TlsDeleteCallbackFunction_t)( int, void * )`` where the first parameter
+is the index number of the associated TLSP, and the second parameter is the 
+TLSP itself.
+
+Deletion callbacks are set alongside TLSP by calling 
+:cpp:func:`vTaskSetThreadLocalStoragePointerAndDelCallback`. Calling the vanilla 
+FreeRTOS function :cpp:func:`vTaskSetThreadLocalStoragePointer` will simply set the
+TLSP's associated Deletion Callback to `NULL` meaning that no callback will be
+called for that TLSP during task deletion. If a deletion callback is `NULL`,
+users should manually free the memory pointed to by the associated TLSP before 
+task deletion in order to avoid memory leak.
+
+:ref:`CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS` in menuconfig can be used
+to configure the number TLSP and Deletion Callbacks a TCB will have.
+
+For more details see :doc:`FreeRTOS API reference<../api-reference/system/freertos>`.
 
 
 .. _esp-idf-freertos-configuration:
@@ -434,9 +491,9 @@ number of Thread Local Storage Pointers each task will have in ESP-IDF
 FreeRTOS.
 
 :ref:`CONFIG_SUPPORT_STATIC_ALLOCATION` will enable the backported
-functionality of ``xTaskCreateStaticPinnedToCore()`` in ESP-IDF FreeRTOS
+functionality of :cpp:func:`xTaskCreateStaticPinnedToCore` in ESP-IDF FreeRTOS
     
 :ref:`CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION` will trigger a halt in
 particular functions in ESP-IDF FreeRTOS which have not been fully tested
 in an SMP context.
-    
+

+ 157 - 22
docs/api-guides/ulp_instruction_set.rst

@@ -62,14 +62,32 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
           ST R2, R1, 0      // write value of R2 into the third array element,
                             // i.e. array[2]
 
+Note about instruction execution time
+-------------------------------------
+
+ULP coprocessor is clocked from RTC_FAST_CLK, which is normally derived from the internal 8MHz oscillator. Applications which need to know exact ULP clock frequency can calibrate it against the main XTAL clock::
+
+    #include "soc/rtc.h"
+
+    // calibrate 8M/256 clock against XTAL, get 8M/256 clock period
+    uint32_t rtc_8md256_period = rtc_clk_cal(RTC_CAL_8MD256, 100);
+    uint32_t rtc_fast_freq_hz = 1000000ULL * (1 << RTC_CLK_CAL_FRACT) * 256 / rtc_8md256_period;
+
+ULP coprocessor needs 2 clock cycle to fetch each instuction (fetching is not pipelined), plus certain number of cycles to execute, depending on the instruction. See description of each instruction for details on the execution time.
+
+Note that when accessing RTC memories and RTC registers, ULP coprocessor has lower priority than the main CPUs. This means that ULP coprocessor execution may be suspended while the main CPUs access same memory region as the ULP.
+
+
 **NOP** - no operation
 ----------------------
 
-**Syntax:**
+**Syntax**
   **NOP**
-**Operands:**
+**Operands**
   None
-**Description:**
+**Cycles**
+  2 (fetch) + 1 (execute)
+**Description**
   No operation is performed. Only the PC is incremented.
 
 **Example**::
@@ -80,20 +98,22 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
 **ADD** - Add to register
 -------------------------
 
-**Syntax:**
+**Syntax**
     **ADD**      *Rdst, Rsrc1, Rsrc2*
 
     **ADD**      *Rdst, Rsrc1, imm*
 
 
-**Operands:**
+**Operands**
   - *Rdst* - Register R[0..3]
   - *Rsrc1* - Register R[0..3]
   - *Rsrc2* - Register R[0..3]
   - *Imm* - 16-bit signed value
 
+**Cycles**
+  2 (fetch) + 2 (execute)
 
-**Description:**
+**Description**
   The instruction adds source register to another source register or to a 16-bit signed value and stores result to the destination register.
 
 **Examples**::
@@ -115,21 +135,24 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
 **SUB** - Subtract from register
 --------------------------------
 
-**Syntax:**
+**Syntax**
   **SUB** *Rdst, Rsrc1, Rsrc2*
 
   **SUB** *Rdst, Rsrc1, imm*
 
-**Operands:**
+**Operands**
   - *Rdst*  - Register R[0..3]
   - *Rsrc1* - Register R[0..3]
   - *Rsrc2* - Register R[0..3]
   - *Imm*   - 16-bit signed value
 
-**Description:**
+**Cycles**
+  2 (fetch) + 2 (execute)
+
+**Description**
   The instruction subtracts the source register from another source register or subtracts 16-bit signed value from a source register, and stores result to the destination register.
 
-**Examples:**::
+**Examples**::
 
   1:         SUB R1, R2, R3             //R1 = R2 - R3
   
@@ -146,21 +169,24 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
 **AND** - Logical AND of two operands
 -------------------------------------
 
-**Syntax:**
+**Syntax**
     **AND** *Rdst, Rsrc1, Rsrc2*
 
     **AND** *Rdst, Rsrc1, imm*
 
-**Operands:**
+**Operands**
   - *Rdst* - Register R[0..3]
   - *Rsrc1* - Register R[0..3]
   - *Rsrc2* - Register R[0..3]
   - *Imm* - 16-bit signed value
 
-**Description:**
+**Cycles**
+  2 (fetch) + 2 (execute)
+
+**Description**
   The instruction does logical AND of a source register and another source register or 16-bit signed value and stores result to the destination register.
 
-**Example**::
+**Examples**::
 
   1:        AND R1, R2, R3          //R1 = R2 & R3
 
@@ -183,12 +209,14 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
  
   **OR** *Rdst, Rsrc1, imm*
 
-
 **Operands**
   - *Rdst* - Register R[0..3]
   - *Rsrc1* - Register R[0..3]
   - *Rsrc2* - Register R[0..3]
   - *Imm* - 16-bit signed value
+
+**Cycles**
+  2 (fetch) + 2 (execute)
  
 **Description**
   The instruction does logical OR of a source register and another source register or 16-bit signed value and stores result to the destination register.
@@ -223,6 +251,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
   - *Rsrc2* - Register R[0..3]
   - *Imm* - 16-bit signed value
  
+**Cycles**
+  2 (fetch) + 2 (execute)
+
 **Description**
    The instruction does logical shift to left of source register to number of bits from another source register or 16-bit signed value and store result to the destination register.
 
@@ -255,6 +286,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
   *Rsrc2* - Register R[0..3]
   *Imm* - 16-bit signed value
 
+**Cycles**
+  2 (fetch) + 2 (execute)
+
 **Description**
   The instruction does logical shift to right of source register to number of bits from another source register or 16-bit signed value and store result to the destination register.
 
@@ -286,6 +320,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
   - *Rsrc* – Register R[0..3]
   - *Imm*  – 16-bit signed value
 
+**Cycles**
+  2 (fetch) + 2 (execute)
+
 **Description**
    The instruction move to destination register value from source register or 16-bit signed value.
 
@@ -318,6 +355,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
   - *Rdst* – Register R[0..3], address of the destination, in 32-bit words
   - *Offset* – 10-bit signed value, offset in bytes
 
+**Cycles**
+  2 (fetch) + 4 (execute)
+
 **Description**
   The instruction stores the 16-bit value of Rsrc to the lower half-word of memory with address Rdst+offset. The upper half-word is written with the current program counter (PC), expressed in words, shifted left by 5 bits::
 
@@ -352,6 +392,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
    
    *Offset* – 10-bit signed value, offset in bytes
 
+**Cycles**
+  2 (fetch) + 4 (execute)
+
 **Description**
    The instruction loads lower 16-bit half-word from memory with address Rsrc+offset into the destination register Rdst::
 
@@ -395,6 +438,8 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
      - EQ – jump if last ALU operation result was zero
      - OV – jump if last ALU has set overflow flag
 
+**Cycles**
+  2 (fetch) + 2 (execute)
 
 **Description**
   The instruction makes jump to the specified address. Jump can be either unconditional or based on an ALU flag.
@@ -432,6 +477,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
 
       - *LT* (less than) – jump if value in R0 < threshold
 
+**Cycles**
+  2 (fetch) + 2 (execute)
+
 **Description**
    The instruction makes a jump to a relative address if condition is true. Condition is the result of comparison of R0 register value and the threshold value.
 
@@ -461,6 +509,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
        - *LT* (less than) –  jump if value in stage_cnt < threshold
        - *GT* (greater than) –  jump if value in stage_cnt > threshold
 
+**Cycles**
+  2 (fetch) + 2 (execute)
+
 **Description**
     The instruction makes a jump to a relative address if condition is true. Condition is the result of comparison of count register value and threshold value.
 
@@ -487,6 +538,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
 **Description**
    The instruction sets the stage count register to 0
 
+**Cycles**
+  2 (fetch) + 2 (execute)
+
 **Examples**::
 
    1:       STAGE_RST      // Reset stage count register
@@ -502,6 +556,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
 **Operands**
    - *Value* – 8 bits value
 
+**Cycles**
+  2 (fetch) + 2 (execute)
+
 **Description**
    The instruction increments stage count register by given value.
 
@@ -525,6 +582,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
 **Operands**
    - *Value* – 8 bits value
 
+**Cycles**
+  2 (fetch) + 2 (execute)
+
 **Description**
    The instruction decrements stage count register by given value.
 
@@ -548,17 +608,21 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
 
 **Operands**
   No operands
+
+**Cycles**
+  2 (fetch) + 2 (execute)
+
 **Description**
-   The instruction halt the processor to the power down mode
+   The instruction halts the ULP coprocessor and restarts ULP wakeup timer, if it is enabled.
 
 **Examples**::
 
-  1:       HALT      // Move chip to powerdown
+  1:       HALT      // Halt the coprocessor
 
 
 
-**WAKE** – wakeup the chip
---------------------------
+**WAKE** – Wake up the chip
+---------------------------
 
 **Syntax**
    **WAKE**
@@ -566,6 +630,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
 **Operands**
   No operands
 
+**Cycles**
+  2 (fetch) + 2 (execute)
+
 **Description**
   The instruction sends an interrupt from ULP to RTC controller.
 
@@ -573,11 +640,17 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
 
   - If the SoC is not in deep sleep mode, and ULP interrupt bit (RTC_CNTL_ULP_CP_INT_ENA) is set in RTC_CNTL_INT_ENA_REG register, RTC interrupt will be triggered.
 
+  Note that before using WAKE instruction, ULP program may needs to wait until RTC controller is ready to wake up the main CPU. This is indicated using RTC_CNTL_RDY_FOR_WAKEUP bit of RTC_CNTL_LOW_POWER_ST_REG register. If WAKE instruction is executed while RTC_CNTL_RDY_FOR_WAKEUP is zero, it has no effect (wake up does not occur).
+
 **Examples**::
 
-  1:        WAKE                      // Trigger wake up
-            REG_WR 0x006, 24, 24, 0   // Stop ULP timer (clear RTC_CNTL_ULP_CP_SLP_TIMER_EN)
-            HALT                      // Stop the ULP program
+  1: is_rdy_for_wakeup:                   // Read RTC_CNTL_RDY_FOR_WAKEUP bit
+            READ_RTC_FIELD(RTC_CNTL_LOW_POWER_ST_REG, RTC_CNTL_RDY_FOR_WAKEUP)
+            AND r0, r0, 1
+            JUMP is_rdy_for_wakeup, eq    // Retry until the bit is set
+            WAKE                          // Trigger wake up
+            REG_WR 0x006, 24, 24, 0       // Stop ULP timer (clear RTC_CNTL_ULP_CP_SLP_TIMER_EN)
+            HALT                          // Stop the ULP program
             // After these instructions, SoC will wake up,
             // and ULP will not run again until started by the main program.
 
@@ -592,6 +665,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
 **Operands**
    - *sleep_reg* – 0..4, selects one of ``SENS_ULP_CP_SLEEP_CYCx_REG`` registers.
 
+**Cycles**
+  2 (fetch) + 2 (execute)
+
 **Description**
    The instruction selects which of the ``SENS_ULP_CP_SLEEP_CYCx_REG`` (x = 0..4) register values is to be used by the ULP wakeup timer as wakeup period. By default, the value from ``SENS_ULP_CP_SLEEP_CYC0_REG`` is used.
 
@@ -612,6 +688,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
 **Operands**
    - *Cycles* – number of cycles for wait
 
+**Cycles**
+  2 (fetch) + *Cycles* (execute)
+
 **Description**
    The instruction delays for given number of cycles.
 
@@ -635,6 +714,8 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
   - *Rdst* – Destination Register R[0..3], result will be stored to this register
   - *Wait_Delay* – number of cycles used to perform the measurement
 
+**Cycles**
+  2 (fetch) + *Wait_Delay* + 3 * TSENS_CLK
 
 **Description**
    The instruction performs measurement using TSENS and stores the result into a general purpose register.
@@ -660,6 +741,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
   - *Sar_sel* – Select ADC: 0 = SARADC1, 1 = SARADC2
   - *Mux*  -  selected PAD, SARADC Pad[Mux+1] is enabled
 
+**Cycles**
+  2 (fetch) + 21 + max(1, SAR_AMP_WAIT1) + max(1, SAR_AMP_WAIT2) + max(1, SAR_AMP_WAIT3) + SARx_SAMPLE_CYCLE + SARx_SAMPLE_BIT
+
 **Description**
   The instruction makes measurements from ADC.
 
@@ -667,6 +751,51 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
 
    1:        ADC      R1, 0, 1      // Measure value using ADC1 pad 2 and store result into R1
 
+**I2C_RD** - read single byte from I2C slave
+----------------------------------------------
+
+**Syntax**
+  - **I2C_RD**   *Sub_addr, High, Low, Slave_sel*
+
+**Operands**
+  - *Sub_addr* – Address within the I2C slave to read.
+  - *High*, *Low* — Define range of bits to read. Bits outside of [High, Low] range are masked.
+  - *Slave_sel*  -  Index of I2C slave address to use.
+
+**Cycles**
+  2 (fetch) + I2C communication time
+
+**Description**
+  ``I2C_RD`` instruction reads one byte from I2C slave with index ``Slave_sel``. Slave address (in 7-bit format) has to be set in advance into `SENS_I2C_SLAVE_ADDRx` register field, where ``x == Slave_sel``.
+  8 bits of read result is stored into `R0` register.
+
+**Examples**::
+
+   1:        I2C_RD      0x10, 7, 0, 0      // Read byte from sub-address 0x10 of slave with address set in SENS_I2C_SLAVE_ADDR0
+
+
+**I2C_WR** - write single byte to I2C slave
+----------------------------------------------
+
+**Syntax**
+  - **I2C_WR**   *Sub_addr, Value, High, Low, Slave_sel*
+
+**Operands**
+  - *Sub_addr* – Address within the I2C slave to write.
+  - *Value* – 8-bit value to be written.
+  - *High*, *Low* — Define range of bits to write. Bits outside of [High, Low] range are masked.
+  - *Slave_sel*  -  Index of I2C slave address to use.
+
+**Cycles**
+  2 (fetch) + I2C communication time
+
+**Description**
+  ``I2C_WR`` instruction writes one byte to I2C slave with index ``Slave_sel``. Slave address (in 7-bit format) has to be set in advance into `SENS_I2C_SLAVE_ADDRx` register field, where ``x == Slave_sel``.
+
+**Examples**::
+
+   1:        I2C_WR      0x20, 0x33, 7, 0, 1      // Write byte 0x33 to sub-address 0x20 of slave with address set in SENS_I2C_SLAVE_ADDR1.
+
 
 **REG_RD** – read from peripheral register
 ------------------------------------------
@@ -679,6 +808,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
   - *High* – High part of R0
   - *Low* – Low part of R0
 
+**Cycles**
+  2 (fetch) + 6 (execute)
+
 **Description**
    The instruction reads up to 16 bits from a peripheral register into a general purpose register: ``R0 = REG[Addr][High:Low]``.
 
@@ -704,6 +836,9 @@ Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the fol
   - *Low* – Low part of R0
   - *Data* – value to write, 8 bits
 
+**Cycles**
+  2 (fetch) + 10 (execute)
+
 **Description**
    The instruction writes up to 8 bits from a general purpose register into a peripheral register. ``REG[Addr][High:Low] = data``
 

+ 90 - 3
docs/api-guides/unit-tests.rst

@@ -3,8 +3,8 @@ Unit Testing in ESP32
 
 ESP-IDF comes with a unit test app based on Unity - unit test framework. Unit tests are integrated in the ESP-IDF repository and are placed in ``test`` subdirectory of each component respectively.
 
-Adding unit tests
------------------
+Add normal test cases
+---------------------
 
 Unit tests are added in the ``test`` subdirectory of the respective component.
 Tests are added in C files, a single C file can include multiple test cases.
@@ -31,6 +31,59 @@ Each `test` subdirectory needs to include component.mk file with at least the fo
 
 See http://www.throwtheswitch.org/unity for more information about writing tests in Unity.
 
+
+Add multiple devices test cases
+-------------------------------
+
+The normal test cases will be executed on one DUT (Device Under Test). Components need to communicate with each other (like GPIO, SPI ...) can't be tested with normal test cases.
+Multiple devices test cases support writing and running test with multiple DUTs.
+
+Here's an example of multiple devices test case::
+
+    void gpio_master_test()
+    {
+        gpio_config_t slave_config = {
+                .pin_bit_mask = 1 << MASTER_GPIO_PIN,
+                .mode = GPIO_MODE_INPUT,
+        };
+        gpio_config(&slave_config);
+        unity_wait_for_signal("output high level");
+        TEST_ASSERT(gpio_get_level(MASTER_GPIO_PIN) == 1);
+    }
+
+    void gpio_slave_test()
+    {
+        gpio_config_t master_config = {
+                .pin_bit_mask = 1 << SLAVE_GPIO_PIN,
+                .mode = GPIO_MODE_OUTPUT,
+        };
+        gpio_config(&master_config);
+        gpio_set_level(SLAVE_GPIO_PIN, 1);
+        unity_send_signal("output high level");
+    }
+
+    TEST_CASE_MULTIPLE_DEVICES("gpio multiple devices test example", "[driver]", gpio_master_test, gpio_slave_test);
+
+
+The macro ``TEST_CASE_MULTIPLE_DEVICES`` is used to declare multiple devices test cases.
+First argument is test case name, second argument is test case description.
+From the third argument, upto 5 test functions can be defined, each function will be the entry point of tests running on each DUT.
+
+Running test cases from different DUTs could require synchronizing between DUTs. We provide ``unity_wait_for_signal`` and ``unity_send_signal`` to support synchronizing with UART.
+As the secnario in the above example, slave should get GPIO level after master set level. DUT UART console will prompt and requires user interaction:
+
+DUT1 (master) console::
+
+    Waiting for signal: [output high level]!
+    Please press "Enter" key to once any board send this signal.
+
+DUT2 (slave) console::
+
+    Send signal: [output high level]!
+
+Once the signal is set from DUT2, you need to press "Enter" on DUT1, then DUT1 unblocks from ``unity_wait_for_signal`` and starts to change GPIO level.
+
+
 Building unit test app
 ----------------------
 
@@ -55,7 +108,32 @@ Running unit tests
 
 After flashing reset the ESP32 and it will boot the unit test app.
 
-Unit test app prints a test menu with all available tests.
+When unit test app is idle, press "Enter" will make it print test menu with all available tests::
+
+    Here's the test menu, pick your combo:
+    (1)     "esp_ota_begin() verifies arguments" [ota]
+    (2)     "esp_ota_get_next_update_partition logic" [ota]
+    (3)     "Verify bootloader image in flash" [bootloader_support]
+    (4)     "Verify unit test app image" [bootloader_support]
+    (5)     "can use new and delete" [cxx]
+    (6)     "can call virtual functions" [cxx]
+    (7)     "can use static initializers for non-POD types" [cxx]
+    (8)     "can use std::vector" [cxx]
+    (9)     "static initialization guards work as expected" [cxx]
+    (10)    "global initializers run in the correct order" [cxx]
+    (11)    "before scheduler has started, static initializers work correctly" [cxx]
+    (12)    "adc2 work with wifi" [adc]
+    (13)    "gpio master/slave test example" [ignore][misc][test_env=UT_T2_1]
+            (1)     "gpio_master_test"
+            (2)     "gpio_slave_test"
+    (14)    "SPI Master clockdiv calculation routines" [spi]
+    (15)    "SPI Master test" [spi][ignore]
+    (16)    "SPI Master test, interaction of multiple devs" [spi][ignore]
+    (17)    "SPI Master no response when switch from host1 (HSPI) to host2 (VSPI)" [spi]
+    (18)    "SPI Master DMA test, TX and RX in different regions" [spi]
+    (19)    "SPI Master DMA test: length, start, not aligned" [spi]
+
+Normal case will print the case name and description. Master slave cases will also print the sub-menu (the registered test function names).
 
 Test cases can be run by inputting one of the following:
 
@@ -66,3 +144,12 @@ Test cases can be run by inputting one of the following:
 - Module name in square brackets to run all test cases for a specific module
 
 - An asterisk to run all test cases
+
+After you select multiple devices test case, it will print sub menu::
+
+    Running gpio master/slave test example...
+    gpio master/slave test example
+            (1)     "gpio_master_test"
+            (2)     "gpio_slave_test"
+
+You need to input number to select the test running on the DUT.

+ 5 - 5
docs/api-reference/peripherals/i2s.rst

@@ -34,11 +34,10 @@ Short example of I2S configuration:
          .bits_per_sample = 16,
          .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
          .communication_format = I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB,
-         .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, // high interrupt priority
+         .intr_alloc_flags = 0, // default interrupt priority
          .dma_buf_count = 8,
          .dma_buf_len = 64,
-         .use_apll = 0,
-         .apll_param = I2S_APLL_NONE
+         .use_apll = 0
     };
 
     static const i2s_pin_config_t pin_config = {
@@ -71,9 +70,10 @@ Short example configuring I2S to use internal DAC for analog output::
          .bits_per_sample = 16, /* the DAC module will only take the 8bits from MSB */
          .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
          .communication_format = I2S_COMM_FORMAT_I2S_MSB,
-         .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, // high interrupt priority
+         .intr_alloc_flags = 0, // default interrupt priority
          .dma_buf_count = 8,
-         .dma_buf_len = 64
+         .dma_buf_len = 64,
+         .use_apll = 0
     };
 
     ...

+ 42 - 0
docs/api-reference/system/freertos.rst

@@ -0,0 +1,42 @@
+FreeRTOS
+========
+
+Overview
+--------
+
+This section contains documentation of FreeRTOS types, functions, and macros. It is automatically generated from FreeRTOS header files.
+
+For more information about FreeRTOS features specific to ESP-IDF, see :doc:`ESP-IDF FreeRTOS SMP Changes<../../api-guides/freertos-smp>`.
+
+
+Task API
+--------
+
+.. include:: /_build/inc/task.inc
+
+Queue API
+---------
+
+.. include:: /_build/inc/queue.inc
+
+Semaphore API
+-------------
+
+.. include:: /_build/inc/semphr.inc
+
+Timer API
+---------
+
+.. include:: /_build/inc/timers.inc
+
+
+Event Group API
+---------------
+
+.. include:: /_build/inc/event_groups.inc
+
+Ringbuffer API
+--------------
+
+.. include:: /_build/inc/ringbuf.inc
+

+ 2 - 2
docs/api-reference/system/hooks.rst

@@ -1,7 +1,7 @@
 .. _hooks_api_reference:
 
-ESP-IDF FreeRTOS Hooks
-======================
+FreeRTOS Hooks
+==============
 
 Overview
 --------

+ 6 - 5
docs/api-reference/system/index.rst

@@ -4,19 +4,20 @@ System API
 .. toctree::
    :maxdepth: 1
 
+   FreeRTOS <freertos>
+   FreeRTOS Hooks <hooks>
    Heap Memory Allocation <mem_alloc>
    Heap Memory Debugging <heap_debug>
    Interrupt Allocation <intr_alloc>
    Watchdogs <wdts>
-   Hooks <hooks>
    Inter-Processor Call <ipc>
    High Resolution Timer <esp_timer>
-   Over The Air Updates (OTA) <ota>
-   Sleep Modes <sleep_modes>
-   Power Management <power_management>
    Logging <log>
-   Base MAC address <base_mac_address>
    Application Level Tracing <app_trace>
+   Power Management <power_management>
+   Sleep Modes <sleep_modes>
+   Base MAC address <base_mac_address>
+   Over The Air Updates (OTA) <ota>
 
 
 Example code for this API section is provided in :example:`system` directory of ESP-IDF examples.

+ 126 - 9
docs/get-started/get-started-pico-kit.rst

@@ -11,15 +11,30 @@ What You Need
 * 1 × USB A / Micro USB B cable
 * 1 × PC loaded with Windows, Linux or Mac OS
 
+If you like to start using this board right now, go directly to section `Start Application Development`_.
+
 
 Overview
 --------
 
-ESP32-PICO-KIT V4 is a mini development board based on the ESP32-PICO-D4 SIP module produced by `Espressif <https://espressif.com>`_. For easy interfacing, all the IO signals and system power on ESP32-PICO-D4 are led out through two 20 x 0.1" pitch header pads on both sides of the development board. To make the ESP32-PICO-KIT V4 fit into mini breadboards, the header pads are populated with two 17 pin headers. Remaining 2 x 3 pads grouped on one side of the board besides the antenna are not populated. If required, the additional 2 x 3 pin headers may be soldered later by the user. The development board integrates a USB-UART Bridge circuit, allowing the developers to connect the board to a PC's USB port for downloads and debugging.
+ESP32-PICO-KIT V4 is a mini development board produced by `Espressif <https://espressif.com>`_. At the core of this board is the ESP32-PICO-D4, a System-in-Package (SIP) module with complete Wi-Fi and Bluetooth functionalities. Comparing to other ESP32 chips, the ESP32-PICO-D4 integrates several peripheral components in one single package, that otherwise would need to be installed separately. This includes a 40 MHz crystal oscillator, 4 MB flash, filter capacitors and RF matching links in. This greatly reduces quantity and costs of additional components, subsequent assembly and testing cost, as well as overall product complexity.
+
+The development board integrates a USB-UART Bridge circuit, allowing the developers to connect the board to a PC's USB port for downloads and debugging. 
+
+For easy interfacing, all the IO signals and system power on ESP32-PICO-D4 are led out through two rows of 20 x 0.1" pitch header pads on both sides of the development board. To make the ESP32-PICO-KIT V4 fit into mini breadboards, the header pads are populated with two rows of 17 pin headers. Remaining 2 x 3 pads grouped on each side of the board besides the antenna are not populated. The remaining 2 x 3 pin headers may be soldered later by the user. 
 
 .. note::
 
-    The 2 x 3 pads not populated with pin headers are internally connected to the flash memory embedded in the ESP32-PICO-D4 SIP module. For more details see module's datasheet in :ref:`get-started-pico-kit-v4-related-documents`.
+    The 2 x 3 pads not populated with pin headers are internally connected to the flash memory embedded in the ESP32-PICO-D4 SIP module. For more details see module's datasheet in `Related Documents`_.
+
+The board dimensions are 52 x 20.3 x 10 mm (2.1" x 0.8" x 0.4"), see section `Board Dimensions`_. An overview functional block diagram is shown below.
+
+.. figure:: ../_static/esp32-pico-kit-v4-functional-block-diagram.png
+    :align: center
+    :alt: ESP32-PICO-KIT V4 functional block diagram
+    :figclass: align-center
+
+    ESP32-PICO-KIT V4 functional block diagram
 
 
 Functional Description
@@ -29,16 +44,20 @@ The following list and figure below describe key components, interfaces and cont
 
 ESP32-PICO-D4
     Standard ESP32-PICO-D4 module soldered to the ESP32-PICO-KIT V4 board. The complete system of the ESP32 chip has been integrated into the SIP module, requiring only external antenna with LC matching network, decoupling capacitors and pull-up resistors for EN signals to function properly.
+LDO
+    5V-to-3.3V Low dropout voltage regulator (LDO).
 USB-UART Bridge
     A single chip USB-UART bridge provides up to 1 Mbps transfers rates.
-I/O
-    All the pins on ESP32-PICO-D4 are broken out to the pin headers on the board. Users can program ESP32 to enable multiple functions such as PWM, ADC, DAC, I2C, I2S, SPI, etc.
 Micro USB Port
     USB interface. It functions as the power supply for the board and the communication interface between PC and ESP32-PICO-KIT V4.
-EN Button
-    Reset button; pressing this button resets the system.
+5V Power On LED
+    This light emitting diode lits when the USB or an external 5V power supply is applied to the board. For details see schematic in `Related Documents`_.
+I/O
+    All the pins on ESP32-PICO-D4 are broken out to the pin headers on the board. Users can program ESP32 to enable multiple functions such as PWM, ADC, DAC, I2C, I2S, SPI, etc. For details please see section `Pin Descriptions`_.
 BOOT Button
     Holding down the Boot button and pressing the EN button initiates the firmware download mode. Then user can download firmware through the serial port.
+EN Button
+    Reset button; pressing this button resets the system.
 
 .. _get-started-pico-kit-v4-board-front:
 
@@ -65,7 +84,7 @@ There following options are available to provide power supply to the ESP32-PICO-
 
 
 Start Application Development
-------------------------------
+-----------------------------
 
 Before powering up the ESP32-PICO-KIT V4, please make sure that the board has been received in good condition with no obvious signs of damage.
 
@@ -77,7 +96,106 @@ To start development of applications, proceed to section :doc:`index`, that will
 * :ref:`get-started-build-monitor` instantly what the application is doing
 
 
-.. _get-started-pico-kit-v4-related-documents:
+Pin Descriptions
+----------------
+
+The two tables below provide the **Name** and **Function** of I/O headers on both sides of the board, see :ref:`get-started-pico-kit-v4-board-front`. The pin numbering and header names are the same as on a schematic in `Related Documents`_.
+
+
+Header J2
+"""""""""
+
+======  =================  ======  ======================================================
+No.     Name               Type    Function
+======  =================  ======  ======================================================
+1       FLASH_SD1 (FSD1)   I/O     | GPIO8, SD_DATA1, SPID, HS1_DATA1 :ref:`(1) <get-started-pico-kit-v4-pin-notes>` , U2CTS
+2       FLASH_SD3 (FSD3)   I/O     | GPIO7, SD_DATA0, SPIQ, HS1_DATA0 :ref:`(1) <get-started-pico-kit-v4-pin-notes>` , U2RTS
+3       FLASH_CLK (FCLK)   I/O     | GPIO11, SD_CMD, SPICS0, HS1_CMD :ref:`(1) <get-started-pico-kit-v4-pin-notes>` , U1RTS
+4       IO21               I/O     | GPIO21, VSPIHD, EMAC_TX_EN
+5       IO22               I/O     | GPIO22, VSPIWP, U0RTS, EMAC_TXD1
+6       IO19               I/O     | GPIO19, VSPIQ, U0CTS, EMAC_TXD0
+7       IO23               I/O     | GPIO23, VSPID, HS1_STROBE
+8       IO18               I/O     | GPIO18, VSPICLK, HS1_DATA7
+9       IO5                I/O     | GPIO5, VSPICS0, HS1_DATA6, EMAC_RX_CLK
+10      IO10               I/O     | GPIO10, SD_DATA3, SPIWP, HS1_DATA3, U1TXD
+11      IO9                I/O     | GPIO9, SD_DATA2, SPIHD, HS1_DATA2, U1RXD
+12      RXD0               I/O     | GPIO3, U0RXD :ref:`(4) <get-started-pico-kit-v4-pin-notes>` , CLK_OUT2
+13      TXD0               I/O     | GPIO1, U0TXD :ref:`(4) <get-started-pico-kit-v4-pin-notes>` , CLK_OUT3, EMAC_RXD2
+14      IO35               I       | ADC1_CH7, RTC_GPIO5
+15      IO34               I       | ADC1_CH6, RTC_GPIO4
+16      IO38               I       | GPIO38, ADC1_CH2, ADC_PRE_AMP :ref:`(2b) <get-started-pico-kit-v4-pin-notes>` , RTC_GPIO2
+17      IO37               I       | GPIO37, ADC_PRE_AMP :ref:`(2a) <get-started-pico-kit-v4-pin-notes>` , ADC1_CH1, RTC_GPIO1
+18      EN                 I       | CHIP_PU
+19      GND                P       | Ground
+20      VDD33 (3V3)        P       | 3.3V power supply
+======  =================  ======  ======================================================
+
+
+Header J3
+"""""""""
+
+======  =================  ======  ======================================================
+No.     Name               Type    Function
+======  =================  ======  ======================================================
+1       FLASH_CS (FCS)     I/O     | GPIO16, HS1_DATA4 :ref:`(1) <get-started-pico-kit-v4-pin-notes>` , U2RXD, EMAC_CLK_OUT
+2       FLASH_SD0 (FSD0)   I/O     | GPIO17, HS1_DATA5 :ref:`(1) <get-started-pico-kit-v4-pin-notes>` , U2TXD, EMAC_CLK_OUT_180
+3       FLASH_SD2 (FSD2)   I/O     | GPIO6, SD_CLK, SPICLK, HS1_CLK :ref:`(1) <get-started-pico-kit-v4-pin-notes>` , U1CTS 
+4       SENSOR_VP (FSVP)   I       | GPIO36, ADC1_CH0, ADC_PRE_AMP :ref:`(2a) <get-started-pico-kit-v4-pin-notes>` , RTC_GPIO0
+5       SENSOR_VN (FSVN)   I       | GPIO39, ADC1_CH3, ADC_PRE_AMP :ref:`(2b) <get-started-pico-kit-v4-pin-notes>` , RTC_GPIO3
+6       IO25               I/O     | GPIO25, DAC_1, ADC2_CH8, RTC_GPIO6, EMAC_RXD0
+7       IO26               I/O     | GPIO26, DAC_2, ADC2_CH9, RTC_GPIO7, EMAC_RXD1
+8       IO32               I/O     | 32K_XP :ref:`(3a) <get-started-pico-kit-v4-pin-notes>` , ADC1_CH4, TOUCH9, RTC_GPIO9
+9       IO33               I/O     | 32K_XN :ref:`(3b) <get-started-pico-kit-v4-pin-notes>` , ADC1_CH5, TOUCH8, RTC_GPIO8
+10      IO27               I/O     | GPIO27, ADC2_CH7, TOUCH7, RTC_GPIO17
+                                   | EMAC_RX_DV
+11      IO14               I/O     | ADC2_CH6, TOUCH6, RTC_GPIO16, MTMS, HSPICLK,
+                                   | HS2_CLK, SD_CLK, EMAC_TXD2
+12      IO12               I/O     | ADC2_CH5, TOUCH5, RTC_GPIO15, MTDI :ref:`(5) <get-started-pico-kit-v4-pin-notes>` , HSPIQ,
+                                   | HS2_DATA2, SD_DATA2, EMAC_TXD3
+13      IO13               I/O     | ADC2_CH4, TOUCH4, RTC_GPIO14, MTCK, HSPID,
+                                   | HS2_DATA3, SD_DATA3, EMAC_RX_ER
+14      IO15               I/O     | ADC2_CH3, TOUCH3, RTC_GPIO13, MTDO, HSPICS0
+                                   | HS2_CMD, SD_CMD, EMAC_RXD3
+15      IO2                I/O     | ADC2_CH2, TOUCH2, RTC_GPIO12, HSPIWP,
+                                   | HS2_DATA0, SD_DATA0
+16      IO4                I/O     | ADC2_CH0, TOUCH0, RTC_GPIO10, HSPIHD,
+                                   | HS2_DATA1, SD_DATA1, EMAC_TX_ER
+17      IO0                I/O     | ADC2_CH1, TOUCH1, RTC_GPIO11, CLK_OUT1
+                                   | EMAC_TX_CLK
+18      VDD33 (3V3)        P       | 3.3V power supply
+19      GND                P       | Ground
+20      EXT_5V (5V)        P       | 5V power supply
+======  =================  ======  ======================================================
+
+
+.. _get-started-pico-kit-v4-pin-notes:
+
+**Notes to** `Pin Descriptions`_ 
+
+    1. This pin is connected to the flash pin of ESP32-PICO-D4.
+    2. When used as ADC_PRE_AMP, connect 270 pF capacitors between: (a) SENSOR_VP and IO37, (b) SENSOR_VN and IO38.
+    3. 32.768 kHz crystal oscillator: (a) input, (b) output.
+    4. This pin is connected to the pin of the USB bridge chip on the board.
+    5. The operating voltage of ESP32-PICO-KIT’s embedded SPI flash is 3.3V. Therefore, the strapping pin MTDI should hold bit ”0” during the module power-on reset.
+
+
+Board Dimensions
+----------------
+
+.. figure:: ../_static/esp32-pico-kit-v4-dimensions-back.jpg
+    :align: center
+    :alt: ESP32-PICO-KIT V4 dimensions - back
+    :figclass: align-center
+
+    ESP32-PICO-KIT V4 dimensions - back
+
+.. figure:: ../_static/esp32-pico-kit-v4-dimensions-side.jpg
+    :align: center
+    :alt: ESP32-PICO-KIT V4 dimensions - side
+    :figclass: align-center
+
+    ESP32-PICO-KIT V4 dimensions - side
+
 
 Related Documents
 -----------------
@@ -86,7 +204,6 @@ Related Documents
 * `ESP32-PICO-D4 Datasheet <http://espressif.com/sites/default/files/documentation/esp32-pico-d4_datasheet_en.pdf>`_ (PDF)
 * :doc:`../hw-reference/index`
 
-.. * `ESP32-PICO-KIT Datasheet <http://espressif.com/sites/default/files/documentation/esp32-pico-kit_datasheet_en.pdf>`_ (PDF)
 
 .. toctree::
     :hidden:

+ 12 - 8
docs/get-started/index.rst

@@ -127,10 +127,6 @@ ESP-IDF will be downloaded into ``~/esp/esp-idf``.
         cd ~/esp/esp-idf
         git submodule update --init
 
-.. note::
-
-    While cloning submodules on **Windows** platform, the ``git clone`` command may print some output starting ``': not a valid identifier...``. This is a `known issue <https://github.com/espressif/esp-idf/issues/11>`_ but the git clone still succeeds without any problems.
-
 
 .. _get-started-setup-path:
 
@@ -206,9 +202,6 @@ Here are couple of tips on navigation and use of ``menuconfig``:
 
     If you are **Arch Linux** user, navigate to ``SDK tool configuration`` and change the name of ``Python 2 interpreter`` from ``python`` to ``python2``.
 
-.. note::
-
-    Most ESP32 development boards have a 40MHz crystal installed. However, some boards use a 26MHz crystal. If your board uses a 26MHz crystal, or you get garbage output from serial port after code upload, adjust the :ref:`CONFIG_ESP32_XTAL_FREQ_SEL` option in menuconfig.
 
 .. _get-started-build-flash:
 
@@ -283,7 +276,18 @@ Several lines below, after start up and diagnostic log, you should see "Hello wo
     Restarting in 8 seconds...
     Restarting in 7 seconds...
 
-To exit monitor use shortcut ``Ctrl+]``. To execute ``make flash`` and ``make monitor`` in one shoot type ``make flash monitor``. Check section :doc:`IDF Monitor <idf-monitor>` for handy shortcuts and more details on using this application.
+To exit the monitor use shortcut ``Ctrl+]``. 
+
+.. note::
+
+    If instead of the messages above, you see a random garbage similar to::
+
+        e���)(Xn@�y.!��(�PW+)��Hn9a؅/9�!�t5��P�~�k��e�ea�5�jA
+        ~zY��Y(1�,1�� e���)(Xn@�y.!Dr�zY(�jpi�|�+z5Ymvp
+
+    or monitor fails shortly after upload, your board is likely using 26MHz crystal, while the ESP-IDF assumes default of 40MHz. Exit the monitor, go back to the :ref:`menuconfig <get-started-configure>`, change :ref:`CONFIG_ESP32_XTAL_FREQ_SEL` to 26MHz, then :ref:`build and flash <get-started-build-flash>` the application again.
+
+To execute ``make flash`` and ``make monitor`` in one go, type ``make flash monitor``. Check section :doc:`IDF Monitor <idf-monitor>` for handy shortcuts and more details on using this application.
 
 That's all what you need to get started with ESP32! 
 

+ 1 - 1
examples/bluetooth/a2dp_sink/main/main.c

@@ -23,7 +23,7 @@
 #include "esp_system.h"
 #include "esp_log.h"
 
-#include "bt.h"
+#include "esp_bt.h"
 #include "bt_app_core.h"
 #include "bt_app_av.h"
 #include "esp_bt_main.h"

+ 1 - 1
examples/bluetooth/ble_adv/main/app_bt.c

@@ -16,7 +16,7 @@
 #include <string.h>
 #include "freertos/FreeRTOS.h"
 #include "freertos/task.h"
-#include "bt.h"
+#include "esp_bt.h"
 #include "esp_log.h"
 #include "nvs_flash.h"
 

+ 1 - 1
examples/bluetooth/ble_eddystone/main/esp_eddystone_demo.c

@@ -23,7 +23,7 @@
 #include <stdint.h>
 #include <string.h>
 
-#include "bt.h"
+#include "esp_bt.h"
 #include "nvs_flash.h"
 #include "esp_log.h"
 #include "esp_bt_defs.h"

+ 1 - 1
examples/bluetooth/ble_ibeacon/main/ibeacon_demo.c

@@ -23,7 +23,7 @@
 #include "controller.h"
 #include "nvs_flash.h"
 
-#include "bt.h"
+#include "esp_bt.h"
 #include "esp_gap_ble_api.h"
 #include "esp_gattc_api.h"
 #include "esp_gatt_defs.h"

+ 1 - 1
examples/bluetooth/ble_spp_client/main/spp_client_demo.c

@@ -27,7 +27,7 @@
 #include "controller.h"
 #include "driver/uart.h"
 
-#include "bt.h"
+#include "esp_bt.h"
 #include "nvs_flash.h"
 #include "esp_bt_device.h"
 #include "esp_gap_ble_api.h"

+ 1 - 1
examples/bluetooth/ble_spp_server/main/ble_spp_server_demo.c

@@ -19,7 +19,7 @@
 #include "esp_system.h"
 #include "esp_log.h"
 #include "nvs_flash.h"
-#include "bt.h"
+#include "esp_bt.h"
 #include "driver/uart.h"
 #include "string.h"
 

+ 1 - 1
examples/bluetooth/blufi/main/blufi_example_main.c

@@ -23,7 +23,7 @@
 #include "esp_event_loop.h"
 #include "esp_log.h"
 #include "nvs_flash.h"
-#include "bt.h"
+#include "esp_bt.h"
 
 #include "esp_blufi_api.h"
 #include "esp_bt_defs.h"

+ 1 - 1
examples/bluetooth/blufi/main/blufi_security.c

@@ -23,7 +23,7 @@
 #include "esp_event_loop.h"
 #include "esp_log.h"
 #include "nvs_flash.h"
-#include "bt.h"
+#include "esp_bt.h"
 
 #include "esp_blufi_api.h"
 #include "esp_bt_defs.h"

+ 1 - 1
examples/bluetooth/controller_hci_uart/main/controller_hci_uart_demo.c

@@ -15,7 +15,7 @@
 #include <stdio.h>
 #include <string.h>
 #include "nvs_flash.h"
-#include "bt.h"
+#include "esp_bt.h"
 #include "driver/uart.h"
 #include "esp_log.h"
 

+ 1 - 1
examples/bluetooth/gatt_client/main/gattc_demo.c

@@ -31,7 +31,7 @@
 #include "nvs_flash.h"
 #include "controller.h"
 
-#include "bt.h"
+#include "esp_bt.h"
 #include "esp_gap_ble_api.h"
 #include "esp_gattc_api.h"
 #include "esp_gatt_defs.h"

+ 1 - 1
examples/bluetooth/gatt_security_client/main/example_ble_sec_gattc_demo.c

@@ -30,7 +30,7 @@
 #include "nvs_flash.h"
 #include "controller.h"
 
-#include "bt.h"
+#include "esp_bt.h"
 #include "esp_gap_ble_api.h"
 #include "esp_gattc_api.h"
 #include "esp_gatt_defs.h"

+ 1 - 1
examples/bluetooth/gatt_security_server/main/example_ble_sec_gatts_demo.c

@@ -18,7 +18,7 @@
 #include "esp_system.h"
 #include "esp_log.h"
 #include "nvs_flash.h"
-#include "bt.h"
+#include "esp_bt.h"
 
 #include "esp_gap_ble_api.h"
 #include "esp_gatts_api.h"

+ 1 - 1
examples/bluetooth/gatt_server/main/gatts_demo.c

@@ -31,7 +31,7 @@
 #include "esp_system.h"
 #include "esp_log.h"
 #include "nvs_flash.h"
-#include "bt.h"
+#include "esp_bt.h"
 
 #include "esp_gap_ble_api.h"
 #include "esp_gatts_api.h"

+ 7 - 8
examples/bluetooth/gatt_server_service_table/main/gatts_table_creat_demo.c

@@ -22,13 +22,13 @@
 ****************************************************************************/
 
 
-#include "freertos/FreeRTOS.h"
-#include "freertos/task.h"
-#include "freertos/event_groups.h"
-#include "esp_system.h"
-#include "esp_log.h"
-#include "nvs_flash.h"
-#include "bt.h"
+ #include "freertos/FreeRTOS.h"
+ #include "freertos/task.h"
+ #include "freertos/event_groups.h"
+ #include "esp_system.h"
+ #include "esp_log.h"
+ #include "nvs_flash.h"
+ #include "esp_bt.h"
 
 #include "esp_gap_ble_api.h"
 #include "esp_gatts_api.h"
@@ -572,4 +572,3 @@ void app_main()
         ESP_LOGE(GATTS_TABLE_TAG, "set local  MTU failed, error code = %x", local_mtu_ret);
     }
 }
-

+ 1 - 1
examples/bluetooth/gattc_multi_connect/main/gattc_multi_connect.c

@@ -34,7 +34,7 @@
 #include "nvs_flash.h"
 #include "controller.h"
 
-#include "bt.h"
+#include "esp_bt.h"
 #include "esp_gap_ble_api.h"
 #include "esp_gattc_api.h"
 #include "esp_gatt_defs.h"

+ 2 - 2
examples/peripherals/gpio/main/gpio_example_main.c

@@ -33,10 +33,10 @@
 
 #define GPIO_OUTPUT_IO_0    18
 #define GPIO_OUTPUT_IO_1    19
-#define GPIO_OUTPUT_PIN_SEL  ((1<<GPIO_OUTPUT_IO_0) | (1<<GPIO_OUTPUT_IO_1))
+#define GPIO_OUTPUT_PIN_SEL  ((1ULL<<GPIO_OUTPUT_IO_0) | (1ULL<<GPIO_OUTPUT_IO_1))
 #define GPIO_INPUT_IO_0     4
 #define GPIO_INPUT_IO_1     5
-#define GPIO_INPUT_PIN_SEL  ((1<<GPIO_INPUT_IO_0) | (1<<GPIO_INPUT_IO_1))
+#define GPIO_INPUT_PIN_SEL  ((1ULL<<GPIO_INPUT_IO_0) | (1ULL<<GPIO_INPUT_IO_1))
 #define ESP_INTR_FLAG_DEFAULT 0
 
 static xQueueHandle gpio_evt_queue = NULL;

+ 5 - 0
examples/system/ulp/main/ulp/pulse_cnt.S

@@ -130,6 +130,11 @@ edge_detected:
 
 	.global wake_up
 wake_up:
+	/* Check if the system can be woken up */
+	READ_RTC_FIELD(RTC_CNTL_LOW_POWER_ST_REG, RTC_CNTL_RDY_FOR_WAKEUP)
+	and r0, r0, 1
+	jump wake_up, eq
+
 	/* Wake up the SoC, end program */
 	wake
 	halt

+ 1 - 1
examples/system/ulp_adc/main/ulp/adc.S

@@ -105,7 +105,7 @@ exit:
 	.global wake_up
 wake_up:
 	/* Check if the system can be woken up */
-	READ_RTC_REG(RTC_CNTL_DIAG0_REG, 19, 1)
+	READ_RTC_FIELD(RTC_CNTL_LOW_POWER_ST_REG, RTC_CNTL_RDY_FOR_WAKEUP)
 	and r0, r0, 1
 	jump exit, eq
 

+ 7 - 1
make/component_wrapper.mk

@@ -85,11 +85,12 @@ include $(COMPONENT_MAKEFILE)
 ifndef COMPONENT_CONFIG_ONLY  # Skip steps 3-5 if COMPONENT_CONFIG_ONLY is set
 
 # Object files which need to be linked into the library
-# By default we take all .c, .cpp & .S files in COMPONENT_SRCDIRS.
+# By default we take all .c, .cpp, .cc & .S files in COMPONENT_SRCDIRS.
 ifndef COMPONENT_OBJS
 # Find all source files in all COMPONENT_SRCDIRS
 COMPONENT_OBJS := $(foreach compsrcdir,$(COMPONENT_SRCDIRS),$(patsubst %.c,%.o,$(wildcard $(COMPONENT_PATH)/$(compsrcdir)/*.c)))
 COMPONENT_OBJS += $(foreach compsrcdir,$(COMPONENT_SRCDIRS),$(patsubst %.cpp,%.o,$(wildcard $(COMPONENT_PATH)/$(compsrcdir)/*.cpp)))
+COMPONENT_OBJS += $(foreach compsrcdir,$(COMPONENT_SRCDIRS),$(patsubst %.cc,%.o,$(wildcard $(COMPONENT_PATH)/$(compsrcdir)/*.cc)))
 COMPONENT_OBJS += $(foreach compsrcdir,$(COMPONENT_SRCDIRS),$(patsubst %.S,%.o,$(wildcard $(COMPONENT_PATH)/$(compsrcdir)/*.S)))
 # Make relative by removing COMPONENT_PATH from all found object paths
 COMPONENT_OBJS := $(patsubst $(COMPONENT_PATH)/%,%,$(COMPONENT_OBJS))
@@ -221,6 +222,11 @@ $(1)/%.o: $$(COMPONENT_PATH)/$(1)/%.cpp $(COMMON_MAKEFILES) $(COMPONENT_MAKEFILE
 	$$(CXX) $$(CXXFLAGS) $$(CPPFLAGS) $$(addprefix -I,$$(COMPONENT_INCLUDES)) $$(addprefix -I,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@
 	$(call AppendSourceToDependencies,$$<,$$@)
 
+$(1)/%.o: $$(COMPONENT_PATH)/$(1)/%.cc $(COMMON_MAKEFILES) $(COMPONENT_MAKEFILE) | $(COMPONENT_SRCDIRS)
+	$$(summary) CXX $$(patsubst $$(PWD)/%,%,$$(CURDIR))/$$@
+	$$(CXX) $$(CXXFLAGS) $$(CPPFLAGS) $$(addprefix -I,$$(COMPONENT_INCLUDES)) $$(addprefix -I,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@
+	$(call AppendSourceToDependencies,$$<,$$@)
+
 $(1)/%.o: $$(COMPONENT_PATH)/$(1)/%.S $(COMMON_MAKEFILES) $(COMPONENT_MAKEFILE) | $(COMPONENT_SRCDIRS)
 	$$(summary) AS $$(patsubst $$(PWD)/%,%,$$(CURDIR))/$$@
 	$$(CC) $$(CPPFLAGS) $$(DEBUG_FLAGS) $$(addprefix -I ,$$(COMPONENT_INCLUDES)) $$(addprefix -I ,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@

+ 40 - 0
tools/unit-test-app/components/unity/include/test_utils.h

@@ -36,8 +36,48 @@ void ref_clock_init();
  */
 void ref_clock_deinit();
 
+
 /**
  * @brief Get reference clock timestamp
  * @return number of microseconds since the reference clock was initialized
  */
 uint64_t ref_clock_get();
+
+/**
+ * @brief wait for signals.
+ *
+ * for multiple devices test cases, DUT might need to wait for other DUTs before continue testing.
+ * As all DUTs are independent, need user (or test script) interaction to make test synchronized.
+ *
+ * Here we provide signal functions for this.
+ * For example, we're testing GPIO, DUT1 has one pin connect to with DUT2.
+ * DUT2 will output high level and then DUT1 will read input.
+ * DUT1 should call `unity_wait_for_signal("output high level");` before it reads input.
+ * DUT2 should call `unity_send_signal("output high level");` after it finished setting output high level.
+ * According to the console logs:
+ *
+ * DUT1 console:
+ *
+ * ```
+ *     Waiting for signal: [output high level]!
+ *     Please press "Enter" key to once any board send this signal.
+ * ```
+ *
+ * DUT2 console:
+ *
+ * ```
+ *     Send signal: [output high level]!
+ * ```
+ *
+ * Then we press Enter key on DUT1's console, DUT1 starts to read input and then test success.
+ *
+ * @param signal_name signal name which DUT expected to wait before proceed testing
+ */
+void unity_wait_for_signal(const char* signal_name);
+
+/**
+ * @brief DUT send signal.
+ *
+ * @param signal_name signal name which DUT send once it finished preparing.
+ */
+void unity_send_signal(const char* signal_name);

+ 81 - 22
tools/unit-test-app/components/unity/include/unity_config.h

@@ -20,21 +20,50 @@
 #define UNITY_OUTPUT_FLUSH unity_flush
 
 // Define helpers to register test cases from multiple files
-
 #define UNITY_EXPAND2(a, b) a ## b
 #define UNITY_EXPAND(a, b) UNITY_EXPAND2(a, b)
 #define UNITY_TEST_UID(what) UNITY_EXPAND(what, __LINE__)
 
 #define UNITY_TEST_REG_HELPER reg_helper ## UNITY_TEST_UID
 #define UNITY_TEST_DESC_UID desc ## UNITY_TEST_UID
+
+
+// get count of __VA_ARGS__
+#define PP_NARG(...) \
+         PP_NARG_(__VA_ARGS__,PP_RSEQ_N())
+#define PP_NARG_(...) \
+         PP_ARG_N(__VA_ARGS__)
+#define PP_ARG_N( \
+          _1, _2, _3, _4, _5, _6, _7, _8, _9, N, ...) N
+#define PP_RSEQ_N() 9,8,7,6,5,4,3,2,1,0
+
+// support max 5 test func now
+#define FN_NAME_SET_1(a)                {#a}
+#define FN_NAME_SET_2(a, b)             {#a, #b}
+#define FN_NAME_SET_3(a, b, c)          {#a, #b, #c}
+#define FN_NAME_SET_4(a, b, c, d)       {#a, #b, #c, #d}
+#define FN_NAME_SET_5(a, b, c, d, e)    {#a, #b, #c, #d, #e}
+
+#define FN_NAME_SET2(n) FN_NAME_SET_##n
+#define FN_NAME_SET(n, ...) FN_NAME_SET2(n)(__VA_ARGS__)
+
+#define UNITY_TEST_FN_SET(...)  \
+    static test_func UNITY_TEST_UID(test_functions)[] = {__VA_ARGS__}; \
+    static char* UNITY_TEST_UID(test_fn_name)[] = FN_NAME_SET(PP_NARG(__VA_ARGS__), __VA_ARGS__)
+
+
+typedef void (* test_func)(void);
+
 struct test_desc_t
 {
-	const char* name;
-	const char* desc;
-	void (*fn)(void);
-	const char* file;
-	int line;
-	struct test_desc_t* next;
+    const char* name;
+    const char* desc;
+    test_func* fn;
+    const char* file;
+    int line;
+    uint8_t test_fn_count;
+    char ** test_fn_name;
+    struct test_desc_t* next;
 };
 
 void unity_testcase_register(struct test_desc_t* desc);
@@ -46,7 +75,7 @@ void unity_run_tests_with_filter(const char* filter);
 void unity_run_all_tests();
 
 /*  Test case macro, a-la CATCH framework.
-	First argument is a free-form description, 
+	First argument is a free-form description,
 	second argument is (by convention) a list of identifiers, each one in square brackets.
 	Identifiers are used to group related tests, or tests with specific properties.
     Use like:
@@ -56,21 +85,51 @@ void unity_run_all_tests();
 		// test goes here
 	}
 */
+
 #define TEST_CASE(name_, desc_) \
-	static void UNITY_TEST_UID(test_func_) (void); \
-	static void __attribute__((constructor)) UNITY_TEST_UID(test_reg_helper_) () \
-	{ \
-		static struct test_desc_t UNITY_TEST_UID(test_desc_) = { \
-			.name = name_, \
-			.desc = desc_, \
-			.fn = &UNITY_TEST_UID(test_func_), \
-			.file = __FILE__, \
-			.line = __LINE__, \
-			.next = NULL \
-		}; \
-		unity_testcase_register( & UNITY_TEST_UID(test_desc_) ); \
-	}\
-	static void UNITY_TEST_UID(test_func_) (void)
+    static void UNITY_TEST_UID(test_func_) (void); \
+    static void __attribute__((constructor)) UNITY_TEST_UID(test_reg_helper_) () \
+    { \
+        static test_func test_fn_[] = {&UNITY_TEST_UID(test_func_)}; \
+        static struct test_desc_t UNITY_TEST_UID(test_desc_) = { \
+            .name = name_, \
+            .desc = desc_, \
+            .fn = test_fn_, \
+            .file = __FILE__, \
+            .line = __LINE__, \
+            .test_fn_count = 1, \
+            .test_fn_name = NULL, \
+            .next = NULL \
+        }; \
+        unity_testcase_register( & UNITY_TEST_UID(test_desc_) ); \
+    }\
+    static void UNITY_TEST_UID(test_func_) (void)
+
+
+/*
+ * First argument is a free-form description,
+ * second argument is (by convention) a list of identifiers, each one in square brackets.
+ * subsequent arguments are names of test functions for different DUTs
+ * e.g:
+ * TEST_CASE_MULTIPLE_DEVICES("master and slave spi","[spi][test_env=UT_T2_1]", master_test, slave_test);
+ * */
+
+#define TEST_CASE_MULTIPLE_DEVICES(name_, desc_, ...) \
+    UNITY_TEST_FN_SET(__VA_ARGS__); \
+    static void __attribute__((constructor)) UNITY_TEST_UID(test_reg_helper_) () \
+    { \
+        static struct test_desc_t UNITY_TEST_UID(test_desc_) = { \
+            .name = name_, \
+            .desc = desc_, \
+            .fn = UNITY_TEST_UID(test_functions), \
+            .file = __FILE__, \
+            .line = __LINE__, \
+            .test_fn_count = PP_NARG(__VA_ARGS__), \
+            .test_fn_name = UNITY_TEST_UID(test_fn_name), \
+            .next = NULL \
+        }; \
+        unity_testcase_register( & UNITY_TEST_UID(test_desc_) ); \
+    }
 /**
  * Note: initialization of test_desc_t fields above has to be done exactly
  * in the same order as the fields are declared in the structure.

+ 31 - 0
tools/unit-test-app/components/unity/test_utils.c

@@ -12,8 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <string.h>
 #include "unity.h"
 #include "test_utils.h"
+#include "rom/ets_sys.h"
+#include "rom/uart.h"
 
 const esp_partition_t *get_test_data_partition()
 {
@@ -23,3 +26,31 @@ const esp_partition_t *get_test_data_partition()
     TEST_ASSERT_NOT_NULL(result); /* means partition table set wrong */
     return result;
 }
+
+// wait user to send "Enter" key
+static void wait_user_control()
+{
+    char sign[5] = {0};
+    while(strlen(sign) == 0)
+    {
+        /* Flush anything already in the RX buffer */
+        while(uart_rx_one_char((uint8_t *) sign) == OK) {
+        }
+        /* Read line */
+        UartRxString((uint8_t*) sign, sizeof(sign) - 1);
+    }
+}
+
+// signal functions, used for sync between unity DUTs for multiple devices cases
+void unity_wait_for_signal(const char* signal_name)
+{
+    printf("Waiting for signal: [%s]!\n"
+            "Please press \"Enter\" key to once any board send this signal.\n", signal_name);
+    wait_user_control();
+}
+
+void unity_send_signal(const char* signal_name)
+{
+    printf("Send signal: [%s]!\n", signal_name);
+}
+

+ 57 - 4
tools/unit-test-app/components/unity/unity_platform.c

@@ -145,12 +145,57 @@ void unity_testcase_register(struct test_desc_t* desc)
     }
 }
 
+/* print the multiple devices case name and its sub-menu
+ * e.g:
+ * (1) spi master/slave case
+ *       (1)master case
+ *       (2)slave case
+ * */
+static void print_multiple_devices_test_menu(const struct test_desc_t* test_ms)
+ {
+    unity_printf("%s\n", test_ms->name);
+    for (int i = 0; i < test_ms->test_fn_count; i++)
+    {
+        unity_printf("\t(%d)\t\"%s\"\n", i+1, test_ms->test_fn_name[i]);
+    }
+ }
+
+void multiple_devices_option(const struct test_desc_t* test_ms)
+{
+    int selection;
+    char cmdline[256] = {0};
+
+    print_multiple_devices_test_menu(test_ms);
+    while(strlen(cmdline) == 0)
+    {
+        /* Flush anything already in the RX buffer */
+        while(uart_rx_one_char((uint8_t *) cmdline) == OK) {
+
+        }
+        UartRxString((uint8_t*) cmdline, sizeof(cmdline) - 1);
+        if(strlen(cmdline) == 0) {
+            /* if input was newline, print a new menu */
+            print_multiple_devices_test_menu(test_ms);
+        }
+    }
+    selection = atoi((const char *) cmdline) - 1;
+    if(selection >= 0 && selection < test_ms->test_fn_count) {
+        UnityDefaultTestRun(test_ms->fn[selection], test_ms->name, test_ms->line);
+    } else {
+        printf("Invalid selection, your should input number 1-%d!", test_ms->test_fn_count);
+    }
+}
+
 static void unity_run_single_test(const struct test_desc_t* test)
 {
     printf("Running %s...\n", test->name);
     Unity.TestFile = test->file;
     Unity.CurrentDetail1 = test->desc;
-    UnityDefaultTestRun(test->fn, test->name, test->line);
+    if(test->test_fn_count == 1) {
+        UnityDefaultTestRun(test->fn[0], test->name, test->line);
+    } else {
+        multiple_devices_option(test);
+    }
 }
 
 static void unity_run_single_test_by_index(int index)
@@ -158,6 +203,7 @@ static void unity_run_single_test_by_index(int index)
     const struct test_desc_t* test;
     for (test = s_unity_tests_first; test != NULL && index != 0; test = test->next, --index)
     {
+
     }
     if (test != NULL)
     {
@@ -201,7 +247,7 @@ static void unity_run_single_test_by_name(const char* filter)
         {
             unity_run_single_test(test);
         }
-    }    
+    }
 }
 
 void unity_run_all_tests()
@@ -253,8 +299,15 @@ static int print_test_menu(void)
          test = test->next, ++test_counter)
     {
         unity_printf("(%d)\t\"%s\" %s\n", test_counter + 1, test->name, test->desc);
-    }
-    return test_counter;
+        if(test->test_fn_count > 1)
+        {
+            for (int i = 0; i < test->test_fn_count; i++)
+            {
+                unity_printf("\t(%d)\t\"%s\"\n", i+1, test->test_fn_name[i]);
+            }
+         }
+     }
+     return test_counter;
 }
 
 static int get_test_count(void)

+ 1 - 1
tools/unit-test-app/main/app_main.c

@@ -6,7 +6,7 @@
 
 void unityTask(void *pvParameters)
 {
-    vTaskDelay(30); /* Delay a bit to let the main task be deleted */
+    vTaskDelay(2); /* Delay a bit to let the main task be deleted */
     unity_run_menu(); /* Doesn't return */
 }
 

Неке датотеке нису приказане због велике количине промена