]> granicus.if.org Git - esp-idf/commitdiff
Merge branch 'bugfix/spi_gpio0' into 'master'
authorJeroen Domburg <jeroen@espressif.com>
Wed, 7 Feb 2018 10:50:13 +0000 (18:50 +0800)
committerJeroen Domburg <jeroen@espressif.com>
Wed, 7 Feb 2018 10:50:13 +0000 (18:50 +0800)
fix(spi): several fixes about maros (flags) and GPIO0.

See merge request idf/esp-idf!1666

115 files changed:
.gitlab-ci.yml
.gitmodules
components/app_trace/Kconfig
components/app_trace/sys_view/Sample/Config/SEGGER_SYSVIEW_Config_FreeRTOS.c
components/aws_iot/Kconfig
components/aws_iot/aws-iot-device-sdk-embedded-C
components/aws_iot/include/aws_iot_config.h
components/bootloader_support/src/flash_partitions.c
components/bt/Kconfig
components/bt/bluedroid/api/esp_gatt_common_api.c
components/bt/bluedroid/api/include/esp_a2dp_api.h
components/bt/bluedroid/bta/av/bta_av_act.c
components/bt/bluedroid/bta/dm/bta_dm_act.c
components/bt/bluedroid/bta/gatt/bta_gattc_act.c
components/bt/bluedroid/bta/gatt/bta_gattc_utils.c
components/bt/bluedroid/bta/include/bta_gattc_int.h
components/bt/bluedroid/bta/include/bta_sys.h
components/bt/bluedroid/bta/sys/bta_sys_main.c
components/bt/bluedroid/btc/core/btc_main.c
components/bt/bluedroid/btc/core/btc_task.c
components/bt/bluedroid/btc/include/btc_main.h
components/bt/bluedroid/btc/include/btc_task.h
components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink.c
components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_source.c
components/bt/bluedroid/btc/profile/std/gatt/btc_gatt_common.c [new file with mode: 0644]
components/bt/bluedroid/btc/profile/std/include/btc_gatt_common.h [new file with mode: 0644]
components/bt/bluedroid/osi/include/thread.h
components/bt/bluedroid/stack/avdt/avdt_ccb.c
components/bt/bluedroid/stack/avdt/avdt_scb.c
components/bt/bluedroid/stack/btm/btm_ble_gap.c
components/bt/bluedroid/stack/btm/btm_devctl.c
components/bt/bluedroid/stack/btm/btm_inq.c
components/bt/bluedroid/stack/btu/btu_task.c
components/bt/bluedroid/stack/gatt/gatt_main.c
components/bt/bluedroid/stack/gatt/gatt_utils.c
components/bt/bluedroid/stack/include/btu.h
components/bt/bluedroid/stack/l2cap/include/l2c_int.h
components/bt/bluedroid/stack/l2cap/l2c_fcr.c
components/bt/bluedroid/stack/l2cap/l2c_utils.c
components/bt/bluedroid/stack/rfcomm/include/rfc_int.h
components/bt/bluedroid/stack/rfcomm/port_utils.c
components/bt/bluedroid/stack/rfcomm/rfc_utils.c
components/bt/bluedroid/stack/sdp/sdp_utils.c
components/bt/lib
components/cxx/test/test_cxx.cpp
components/driver/gpio.c
components/driver/include/driver/gpio.h
components/driver/include/driver/sdmmc_host.h
components/driver/include/driver/sdmmc_types.h
components/driver/sdmmc_host.c
components/driver/test/test_adc2.c
components/esp32/include/esp_flash_data_types.h
components/esp32/ld/esp32.common.ld
components/esp32/lib
components/idf_test/include/idf_performance.h
components/json/LICENSE [deleted file]
components/json/cJSON [new submodule]
components/json/component.mk
components/json/include/cJSON.h [deleted file]
components/json/library/cJSON.c [deleted file]
components/json/port/cJSON_Utils.c [deleted file]
components/json/port/include/cJSON_Utils.h [deleted file]
components/log/include/esp_log.h
components/log/log.c
components/lwip/Kconfig
components/lwip/api/api_lib.c
components/lwip/api/api_msg.c
components/lwip/api/netbuf.c
components/lwip/api/sockets.c
components/lwip/api/tcpip.c
components/lwip/apps/ping/esp_ping.h
components/lwip/core/inet_chksum.c
components/lwip/core/ipv4/ip4.c
components/lwip/core/ipv4/ip4_addr.c
components/lwip/core/pbuf.c
components/lwip/core/timers.c
components/lwip/core/udp.c
components/lwip/include/lwip/port/lwipopts.h
components/lwip/netif/etharp.c
components/lwip/netif/ethernet.c
components/lwip/port/freertos/sys_arch.c
components/lwip/port/netif/wlanif.c
components/partition_table/gen_esp32part.py
components/partition_table/test_gen_esp32part_host/gen_esp32part_tests.py
components/sdmmc/sdmmc_cmd.c
components/sdmmc/test/test_sd.c
components/soc/esp32/include/soc/io_mux_reg.h
components/spiffs/Kconfig
components/spiffs/esp_spiffs.c
components/spiffs/include/spiffs_config.h
components/spiffs/test/test_spiffs.c
docs/_static/esp32-devkitc-dimensions-back.jpg [new file with mode: 0644]
docs/_static/esp32-devkitc-functional-overview.jpg [new file with mode: 0644]
docs/_static/esp32-devkitc-v2-functional-overview.png [moved from docs/_static/esp32-devkitc-functional-overview.png with 100% similarity]
docs/api-guides/partition-tables.rst
docs/api-guides/ulp_instruction_set.rst
docs/get-started/get-started-devkitc-v2.rst [new file with mode: 0644]
docs/get-started/get-started-devkitc.rst
docs/hw-reference/modules-and-boards-previous.rst
docs/hw-reference/modules-and-boards.rst
examples/wifi/iperf/components/iperf.c
examples/wifi/iperf/components/iperf.h
examples/wifi/iperf/sdkconfig.defaults
tools/ci/mirror-list.txt
tools/idf_monitor.py
tools/tiny-test-fw/CIAssignExampleTest.py
tools/tiny-test-fw/CIAssignUnitTest.py [new file with mode: 0644]
tools/tiny-test-fw/DUT.py
tools/tiny-test-fw/IDF/IDFApp.py
tools/tiny-test-fw/Utility/CIAssignTest.py [new file with mode: 0644]
tools/tiny-test-fw/Utility/CaseConfig.py
tools/tiny-test-fw/Utility/GitlabCIJob.py
tools/unit-test-app/components/unity/unity_platform.c
tools/unit-test-app/tools/UnitTestParser.py
tools/unit-test-app/unit_test.py [new file with mode: 0644]

index d813ef9812ab21ecd7df46bbac5ea65fe41c6858..2153a3d4ed1582ec497e64d295ada0c941a59c54 100644 (file)
@@ -446,22 +446,21 @@ assign_test:
       - components/idf_test/*/CIConfigs
       - components/idf_test/*/TC.sqlite
       - $EXAMPLE_CONFIG_OUTPUT_PATH
+      - tools/unit-test-app/output
     expire_in: 1 mos
   before_script: *add_gitlab_key_before
   script:
     # first move test bins together: test_bins/CHIP_SDK/TestApp/bin_files
     - mkdir -p $OUTPUT_BIN_PATH
-    # copy and rename folder name to "UT_config"
-    - for CONFIG in $(ls $UT_BIN_PATH); do cp -r "$UT_BIN_PATH/$CONFIG" "$OUTPUT_BIN_PATH/UT_$CONFIG"; done
     - cp -r SSC/ssc_bin/* $OUTPUT_BIN_PATH
     # assign example tests
     - python $TEST_FW_PATH/CIAssignExampleTest.py $IDF_PATH/examples $IDF_PATH/.gitlab-ci.yml $EXAMPLE_CONFIG_OUTPUT_PATH
+    # assign unit test cases
+    - python $TEST_FW_PATH/CIAssignUnitTest.py $IDF_PATH/components/idf_test/unit_test/TestCaseAll.yml $IDF_PATH/.gitlab-ci.yml $IDF_PATH/components/idf_test/unit_test/CIConfigs
     # clone test script to assign tests
     - git clone $TEST_SCRIPT_REPOSITORY
     - cd auto_test_script
     - python $CHECKOUT_REF_SCRIPT auto_test_script
-    # assign unit test cases
-    - python CIAssignTestCases.py -t $IDF_PATH/components/idf_test/unit_test -c $IDF_PATH/.gitlab-ci.yml -b $IDF_PATH/test_bins
     # assgin integration test cases
     - python CIAssignTestCases.py -t $IDF_PATH/components/idf_test/integration_test -c $IDF_PATH/.gitlab-ci.yml -b $IDF_PATH/test_bins
 
@@ -493,6 +492,17 @@ assign_test:
     # run test
     - python Runner.py $TEST_CASE_PATH -c $CONFIG_FILE
 
+.unit_test_template: &unit_test_template
+  <<: *example_test_template
+  stage: unit_test
+  dependencies:
+    - assign_test
+  variables:
+    TEST_FW_PATH: "$CI_PROJECT_DIR/tools/tiny-test-fw"
+    TEST_CASE_PATH: "$CI_PROJECT_DIR/tools/unit-test-app"
+    CONFIG_FILE: "$CI_PROJECT_DIR/components/idf_test/unit_test/CIConfigs/$CI_JOB_NAME.yml"
+    LOG_PATH: "$CI_PROJECT_DIR/TEST_LOGS"
+
 .test_template: &test_template
   stage: test
   when: on_success
@@ -530,18 +540,6 @@ assign_test:
     # run test
     - python CIRunner.py -l "$LOG_PATH/$CI_JOB_NAME" -c $CONFIG_FILE -e $LOCAL_ENV_CONFIG_PATH -t $TEST_CASE_FILE_PATH -m $MODULE_UPDATE_FILE
 
-# template for unit test jobs
-.unit_test_template: &unit_test_template
-  <<: *test_template
-  allow_failure: false
-  stage: unit_test
-  variables:
-    LOCAL_ENV_CONFIG_PATH: "$CI_PROJECT_DIR/ci-test-runner-configs/$CI_RUNNER_DESCRIPTION/ESP32_IDF"
-    LOG_PATH: "$CI_PROJECT_DIR/$CI_COMMIT_SHA"
-    TEST_CASE_FILE_PATH: "$CI_PROJECT_DIR/components/idf_test/unit_test"
-    MODULE_UPDATE_FILE: "$CI_PROJECT_DIR/components/idf_test/ModuleDefinition.yml"
-    CONFIG_FILE: "$CI_PROJECT_DIR/components/idf_test/unit_test/CIConfigs/$CI_JOB_NAME.yml"
-
 nvs_compatible_test:
   <<: *test_template
   artifacts:
index e118821b73c51d2c51d68a9abd720c2ccea37389..84fa4ece2d14916baedced4618a390a1f455cc68 100644 (file)
@@ -33,3 +33,7 @@
 [submodule "components/spiffs/spiffs"]
        path = components/spiffs/spiffs
        url = https://github.com/pellepl/spiffs.git
+
+[submodule "components/json/cJSON"]
+       path = components/json/cJSON
+       url = https://github.com/DaveGamble/cJSON.git
index 6883db57efda003585891fa6f2ded364e8fdb8e3..9426c7539aac2de14ca7cfc0c04ed28e6d138d06 100644 (file)
@@ -64,33 +64,37 @@ config SYSVIEW_ENABLE
         Enables supporrt for SEGGER SystemView tracing functionality.
 
 choice SYSVIEW_TS_SOURCE
-    prompt "ESP32 timer to use as SystemView timestamp source"
+    prompt "Timer to use as timestamp source"
     depends on SYSVIEW_ENABLE
-    default SYSVIEW_TS_SOURCE_TIMER_00
+    default SYSVIEW_TS_SOURCE_CCOUNT if FREERTOS_UNICORE && !PM_ENABLE
+    default SYSVIEW_TS_SOURCE_TIMER_00 if !FREERTOS_UNICORE && !PM_ENABLE
+    default SYSVIEW_TS_SOURCE_ESP_TIMER if PM_ENABLE
     help
         SystemView needs to use a hardware timer as the source of timestamps
-        when tracing 
-        This option selects HW timer for it.
+        when tracing. This option selects the timer for it.
+
+config SYSVIEW_TS_SOURCE_CCOUNT
+       bool "CPU cycle counter (CCOUNT)"
+       depends on FREERTOS_UNICORE && !PM_ENABLE
 
 config SYSVIEW_TS_SOURCE_TIMER_00
     bool "Timer 0, Group 0"
-    help
-        Select this to use timer 0 of group 0
+    depends on !PM_ENABLE
 
 config SYSVIEW_TS_SOURCE_TIMER_01
     bool "Timer 1, Group 0"
-    help
-        Select this to use timer 1 of group 0
+    depends on !PM_ENABLE
 
 config SYSVIEW_TS_SOURCE_TIMER_10
     bool "Timer 0, Group 1"
-    help
-        Select this to use timer 0 of group 1
+    depends on !PM_ENABLE
 
 config SYSVIEW_TS_SOURCE_TIMER_11
     bool "Timer 1, Group 1"
-    help
-        Select this to use timer 1 of group 1
+    depends on !PM_ENABLE
+
+config SYSVIEW_TS_SOURCE_ESP_TIMER
+       bool "esp_timer high resolution timer"
 
 endchoice
 
index 728879b6a27a329830f0b0b0090525f0d13a5102..08e85678efd7b6aeff32e871002359554ff1931c 100644 (file)
@@ -64,9 +64,6 @@ Revision: $Rev: 3734 $
 #include "freertos/FreeRTOS.h"
 #include "SEGGER_SYSVIEW.h"
 #include "rom/ets_sys.h"
-#if CONFIG_FREERTOS_UNICORE == 0
-#include "driver/timer.h"
-#endif
 #include "esp_app_trace.h"
 #include "esp_app_trace_util.h"
 #include "esp_intr_alloc.h"
@@ -86,10 +83,49 @@ extern const SEGGER_SYSVIEW_OS_API SYSVIEW_X_OS_TraceAPI;
 // The target device name
 #define SYSVIEW_DEVICE_NAME     "ESP32"
 
+// Determine which timer to use as timestamp source
+#if CONFIG_SYSVIEW_TS_SOURCE_CCOUNT
+#define TS_USE_CCOUNT 1
+#elif CONFIG_SYSVIEW_TS_SOURCE_ESP_TIMER
+#define TS_USE_ESP_TIMER 1
+#else
+#define TS_USE_TIMERGROUP 1
+#endif
+
+#if TS_USE_TIMERGROUP
+#include "driver/timer.h"
+
 // Timer group timer divisor
 #define SYSVIEW_TIMER_DIV       2
+
 // Frequency of the timestamp.
 #define SYSVIEW_TIMESTAMP_FREQ  (esp_clk_apb_freq() / SYSVIEW_TIMER_DIV)
+
+// Timer ID and group ID
+#if defined(CONFIG_SYSVIEW_TS_SOURCE_TIMER_00) || defined(CONFIG_SYSVIEW_TS_SOURCE_TIMER_01)
+#define TS_TIMER_ID 0
+#else
+#define TS_TIMER_ID 1
+#endif // TIMER_00 || TIMER_01
+
+#if defined(CONFIG_SYSVIEW_TS_SOURCE_TIMER_00) || defined(CONFIG_SYSVIEW_TS_SOURCE_TIMER_10)
+#define TS_TIMER_GROUP 0
+#else
+#define TS_TIMER_GROUP 1
+#endif // TIMER_00 || TIMER_10
+
+#endif // TS_USE_TIMERGROUP
+
+#if TS_USE_ESP_TIMER
+// esp_timer provides 1us resolution
+#define SYSVIEW_TIMESTAMP_FREQ  (1000000)
+#endif // TS_USE_ESP_TIMER
+
+#if TS_USE_CCOUNT
+// CCOUNT is incremented at CPU frequency
+#define SYSVIEW_TIMESTAMP_FREQ  (CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ * 1000000)
+#endif // TS_USE_CCOUNT
+
 // System Frequency.
 #define SYSVIEW_CPU_FREQ        (esp_clk_cpu_freq())
 
@@ -103,11 +139,8 @@ extern const SEGGER_SYSVIEW_OS_API SYSVIEW_X_OS_TraceAPI;
     #define SYSTICK_INTR_ID (ETS_INTERNAL_TIMER1_INTR_SOURCE+ETS_INTERNAL_INTR_SOURCE_OFF)
 #endif
 
-static timer_idx_t s_ts_timer_idx;
-static timer_group_t s_ts_timer_group;
-
 // SystemView is single core specific: it implies that SEGGER_SYSVIEW_LOCK()
-// disables IRQs (disables rescheduling globaly). So we can not use finite timeouts for locks and return error
+// disables IRQs (disables rescheduling globally). So we can not use finite timeouts for locks and return error
 // in case of expiration, because error will not be handled and SEGGER's code will go further implying that
 // everything is fine, so for multi-core env we have to wait on underlying lock forever
 #define SEGGER_LOCK_WAIT_TMO  ESP_APPTRACE_TMO_INFINITE
@@ -213,35 +246,24 @@ static void _cbSendSystemDesc(void) {
 */
 static void SEGGER_SYSVIEW_TS_Init()
 {
-    timer_config_t config;
-
-#if CONFIG_SYSVIEW_TS_SOURCE_TIMER_00
-    s_ts_timer_group = TIMER_GROUP_0;
-    s_ts_timer_idx = TIMER_0;
-#endif
-#if CONFIG_SYSVIEW_TS_SOURCE_TIMER_01
-    s_ts_timer_group = TIMER_GROUP_0;
-    s_ts_timer_idx = TIMER_1;
-#endif
-#if CONFIG_SYSVIEW_TS_SOURCE_TIMER_10
-    s_ts_timer_group = TIMER_GROUP_1;
-    s_ts_timer_idx = TIMER_0;
-#endif
-#if CONFIG_SYSVIEW_TS_SOURCE_TIMER_11
-    s_ts_timer_group = TIMER_GROUP_1;
-    s_ts_timer_idx = TIMER_1;
-#endif
-    config.alarm_en = 0;
-    config.auto_reload = 0;
-    config.counter_dir = TIMER_COUNT_UP;
-    config.divider = SYSVIEW_TIMER_DIV;
-    config.counter_en = 0;
-    /*Configure timer*/
-    timer_init(s_ts_timer_group, s_ts_timer_idx, &config);
-    /*Load counter value */
-    timer_set_counter_value(s_ts_timer_group, s_ts_timer_idx, 0x00000000ULL);
-    /*Enable timer interrupt*/
-    timer_start(s_ts_timer_group, s_ts_timer_idx);
+    /* We only need to initialize something if we use Timer Group.
+     * esp_timer and ccount can be used as is.
+     */
+#if TS_USE_TIMERGROUP
+    timer_config_t config = {
+        .alarm_en = 0,
+        .auto_reload = 0,
+        .counter_dir = TIMER_COUNT_UP,
+        .divider = SYSVIEW_TIMER_DIV,
+        .counter_en = 0
+    };
+    /* Configure timer */
+    timer_init(TS_TIMER_GROUP, TS_TIMER_ID, &config);
+    /* Load counter value */
+    timer_set_counter_value(TS_TIMER_GROUP, TS_TIMER_ID, 0x00000000ULL);
+    /* Start counting */
+    timer_start(TS_TIMER_GROUP, TS_TIMER_ID);
+#endif // TS_USE_TIMERGROUP
 }
 
 void SEGGER_SYSVIEW_Conf(void) {
@@ -296,12 +318,14 @@ void SEGGER_SYSVIEW_Conf(void) {
 
 U32 SEGGER_SYSVIEW_X_GetTimestamp()
 {
-#if CONFIG_FREERTOS_UNICORE == 0
+#if TS_USE_TIMERGROUP
     uint64_t ts = 0;
-    timer_get_counter_value(s_ts_timer_group, s_ts_timer_idx, &ts);
-    return (U32)ts; // return lower part of counter value
-#else
+    timer_get_counter_value(TS_TIMER_GROUP, TS_TIMER_ID, &ts);
+    return (U32) ts; // return lower part of counter value
+#elif TS_USE_CCOUNT
     return portGET_RUN_TIME_COUNTER_VALUE();
+#elif TS_USE_ESP_TIMER
+    return (U32) esp_timer_get_time(); // return lower part of counter value
 #endif
 }
 
index 76b587e9c03642b9d9535048aeeb3c60c5e726d1..9144f166088a450716a99d78228cdf26baf3b3ff 100644 (file)
@@ -83,3 +83,77 @@ config AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL
         Maximum delay between reconnection attempts. If the exponentially increased delay
         interval reaches this value, the client will stop automatically attempting to reconnect.
 
+menu "Thing Shadow"
+    depends on AWS_IOT_SDK
+
+config AWS_IOT_OVERRIDE_THING_SHADOW_RX_BUFFER
+    bool "Override Shadow RX buffer size"
+    depends on AWS_IOT_SDK
+    default n
+    help
+        Allows setting a different Thing Shadow RX buffer
+        size. This is the maximum size of a Thing Shadow
+        message in bytes, plus one.
+
+        If not overridden, the default value is the MQTT RX Buffer length plus one. If overriden, do not set higher than the default value.
+
+config AWS_IOT_SHADOW_MAX_SIZE_OF_RX_BUFFER
+    int "Maximum RX Buffer (bytes)"
+    depends on AWS_IOT_OVERRIDE_THING_SHADOW_RX_BUFFER
+    default 513
+    range 32 65536
+    help
+        Allows setting a different Thing Shadow RX buffer size.
+        This is the maximum size of a Thing Shadow message in bytes,
+        plus one.
+
+
+config AWS_IOT_SHADOW_MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES
+    int "Maximum unique client ID size (bytes)"
+    depends on AWS_IOT_SDK
+    default 80
+    range 4 1000
+    help
+        Maximum size of the Unique Client Id.
+
+config AWS_IOT_SHADOW_MAX_SIMULTANEOUS_ACKS
+    int "Maximum simultaneous responses"
+    depends on AWS_IOT_SDK
+    default 10
+    range 1 100
+    help
+        At any given time we will wait for this many responses. This will correlate to the rate at which the shadow actions are requested
+
+config AWS_IOT_SHADOW_MAX_SIMULTANEOUS_THINGNAMES
+    int "Maximum simultaneous Thing Name operations"
+    depends on AWS_IOT_SDK
+    default 10
+    range 1 100
+    help
+       We could perform shadow action on any thing Name and this is maximum Thing Names we can act on at any given time
+
+config AWS_IOT_SHADOW_MAX_JSON_TOKEN_EXPECTED
+    int "Maximum expected JSON tokens"
+    depends on AWS_IOT_SDK
+    default 120
+    help
+        These are the max tokens that is expected to be in the Shadow JSON document. Includes the metadata which is published
+
+config AWS_IOT_SHADOW_MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME
+    int "Maximum topic length (not including Thing Name)"
+    depends on AWS_IOT_SDK
+    default 60
+    range 10 1000
+    help
+       All shadow actions have to be published or subscribed to a topic which is of the format $aws/things/{thingName}/shadow/update/accepted. This refers to the size of the topic without the Thing Name
+
+config AWS_IOT_SHADOW_MAX_SIZE_OF_THING_NAME
+    int "Maximum Thing Name length"
+    depends on AWS_IOT_SDK
+    default 20
+    range 4 1000
+    help
+        Maximum length of a Thing Name.
+
+endmenu  # Thing Shadow
+
index b9f19d57a0d94e98469cde470003ec45d5819ba4..8bf852db77c360eebfa4b800754fdb90e29ea43e 160000 (submodule)
@@ -1 +1 @@
-Subproject commit b9f19d57a0d94e98469cde470003ec45d5819ba4
+Subproject commit 8bf852db77c360eebfa4b800754fdb90e29ea43e
index 2d10702af88cfff33bbc151f8a5ce61d09f0b331..14a1ced27e40f99dc8671d5a952d8c97d6b82092 100644 (file)
 #define AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS CONFIG_AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS ///< Maximum number of topic filters the MQTT client can handle at any given time. This should be increased appropriately when using Thing Shadow
 
 // Thing Shadow specific configs
-#define SHADOW_MAX_SIZE_OF_RX_BUFFER (AWS_IOT_MQTT_RX_BUF_LEN + 1) ///< Maximum size of the SHADOW buffer to store the received Shadow message
+#ifdef CONFIG_AWS_IOT_OVERRIDE_THING_SHADOW_RX_BUFFER
+#define SHADOW_MAX_SIZE_OF_RX_BUFFER CONFIG AWS_IOT_SHADOW_MAX_SIZE_OF_RX_BUFFER ///< Maximum size of the SHADOW buffer to store the received Shadow message, including NULL termianting byte
+#else
+#define SHADOW_MAX_SIZE_OF_RX_BUFFER (AWS_IOT_MQTT_RX_BUF_LEN + 1)
+#endif
+
 #define MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES 80  ///< Maximum size of the Unique Client Id. For More info on the Client Id refer \ref response "Acknowledgments"
 #define MAX_SIZE_CLIENT_ID_WITH_SEQUENCE (MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES + 10) ///< This is size of the extra sequence number that will be appended to the Unique client Id
 #define MAX_SIZE_CLIENT_TOKEN_CLIENT_SEQUENCE (MAX_SIZE_CLIENT_ID_WITH_SEQUENCE + 20) ///< This is size of the the total clientToken key and value pair in the JSON
-#define MAX_ACKS_TO_COMEIN_AT_ANY_GIVEN_TIME 10 ///< At Any given time we will wait for this many responses. This will correlate to the rate at which the shadow actions are requested
-#define MAX_THINGNAME_HANDLED_AT_ANY_GIVEN_TIME 10 ///< We could perform shadow action on any thing Name and this is maximum Thing Names we can act on at any given time
-#define MAX_JSON_TOKEN_EXPECTED 120 ///< These are the max tokens that is expected to be in the Shadow JSON document. Include the metadata that gets published
-#define MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME 60 ///< All shadow actions have to be published or subscribed to a topic which is of the formablogt $aws/things/{thingName}/shadow/update/accepted. This refers to the size of the topic without the Thing Name
-#define MAX_SIZE_OF_THING_NAME 20 ///< The Thing Name should not be bigger than this value. Modify this if the Thing Name needs to be bigger
+#define MAX_ACKS_TO_COMEIN_AT_ANY_GIVEN_TIME CONFIG_AWS_IOT_SHADOW_MAX_SIMULTANEOUS_ACKS ///< At Any given time we will wait for this many responses. This will correlate to the rate at which the shadow actions are requested
+#define MAX_THINGNAME_HANDLED_AT_ANY_GIVEN_TIME CONFIG_AWS_IOT_SHADOW_MAX_SIMULTANEOUS_THINGNAMES ///< We could perform shadow action on any thing Name and this is maximum Thing Names we can act on at any given time
+#define MAX_JSON_TOKEN_EXPECTED CONFIG_AWS_IOT_SHADOW_MAX_JSON_TOKEN_EXPECTED ///< These are the max tokens that is expected to be in the Shadow JSON document. Include the metadata that gets published
+#define MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME CONFIG_AWS_IOT_SHADOW_MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME ///< All shadow actions have to be published or subscribed to a topic which is of the formablogt $aws/things/{thingName}/shadow/update/accepted. This refers to the size of the topic without the Thing Name
+#define MAX_SIZE_OF_THING_NAME CONFIG_AWS_IOT_SHADOW_MAX_SIZE_OF_THING_NAME ///< The Thing Name should not be bigger than this value. Modify this if the Thing Name needs to be bigger
 #define MAX_SHADOW_TOPIC_LENGTH_BYTES (MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME + MAX_SIZE_OF_THING_NAME) ///< This size includes the length of topic with Thing Name
 
 // Auto Reconnect specific config
index b60968f969609b05db9b66aef0fedbd6cd7b1672..f8a24f26c2bdb49c2a70a697a2833eff2263ccdd 100644 (file)
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
+#include <string.h>
 #include "esp_flash_partitions.h"
 #include "esp_log.h"
 #include "rom/spi_flash.h"
+#include "rom/md5_hash.h"
+#include "esp_flash_data_types.h"
 
 static const char *TAG = "flash_parts";
 
 esp_err_t esp_partition_table_basic_verify(const esp_partition_info_t *partition_table, bool log_errors, int *num_partitions)
 {
-  int num_parts;
-  uint32_t chip_size = g_rom_flashchip.chip_size;
-  *num_partitions = 0;
-
-  for(num_parts = 0; num_parts < ESP_PARTITION_TABLE_MAX_ENTRIES; num_parts++) {
-    const esp_partition_info_t *part = &partition_table[num_parts];
-
-    if (part->magic == 0xFFFF
-        && part->type == PART_TYPE_END
-        && part->subtype == PART_SUBTYPE_END) {
-      /* TODO: check md5 */
-      ESP_LOGD(TAG, "partition table verified, %d entries", num_parts);
-      *num_partitions = num_parts;
-      return ESP_OK;
-    }
+    int md5_found = 0;
+    int num_parts;
+    uint32_t chip_size = g_rom_flashchip.chip_size;
+    *num_partitions = 0;
 
-    if (part->magic != ESP_PARTITION_MAGIC) {
-        if (log_errors) {
-            ESP_LOGE(TAG, "partition %d invalid magic number 0x%x", num_parts, part->magic);
-        }
-        return ESP_ERR_INVALID_STATE;
-    }
+    for (num_parts = 0; num_parts < ESP_PARTITION_TABLE_MAX_ENTRIES; num_parts++) {
+        const esp_partition_info_t *part = &partition_table[num_parts];
+
+        if (part->magic == ESP_PARTITION_MAGIC) {
+            const esp_partition_pos_t *pos = &part->pos;
+            if (pos->offset > chip_size || pos->offset + pos->size > chip_size) {
+                if (log_errors) {
+                    ESP_LOGE(TAG, "partition %d invalid - offset 0x%x size 0x%x exceeds flash chip size 0x%x",
+                             num_parts, pos->offset, pos->size, chip_size);
+                }
+                return ESP_ERR_INVALID_SIZE;
+            }
+        } else if (part->magic == ESP_PARTITION_MAGIC_MD5) {
+            if (md5_found) {
+                if (log_errors) {
+                    ESP_LOGE(TAG, "Only one MD5 checksum is allowed");
+                }
+                return ESP_ERR_INVALID_STATE;
+            }
 
-    const esp_partition_pos_t *pos = &part->pos;
-    if (pos->offset > chip_size || pos->offset + pos->size > chip_size) {
-        if (log_errors) {
-            ESP_LOGE(TAG, "partition %d invalid - offset 0x%x size 0x%x exceeds flash chip size 0x%x",
-                     num_parts, pos->offset, pos->size, chip_size);
+            struct MD5Context context;
+            unsigned char digest[16];
+            MD5Init(&context);
+            MD5Update(&context, (unsigned char *) partition_table, num_parts * sizeof(esp_partition_info_t));
+            MD5Final(digest, &context);
+
+            unsigned char *md5sum = ((unsigned char *) part) + 16; // skip the 2B magic number and the 14B fillup bytes
+
+            if (memcmp(md5sum, digest, sizeof(digest)) != 0) {
+                if (log_errors) {
+                    ESP_LOGE(TAG, "Incorrect MD5 checksum");
+                }
+                return ESP_ERR_INVALID_STATE;
+            }
+            //MD5 checksum matches and we continue with the next interation in
+            //order to detect the end of the partition table
+            md5_found = 1;
+        } else if (part->magic == 0xFFFF
+                   && part->type == PART_TYPE_END
+                   && part->subtype == PART_SUBTYPE_END) {
+            ESP_LOGD(TAG, "partition table verified, %d entries", num_parts);
+            *num_partitions = num_parts - md5_found; //do not count the partition where the MD5 checksum is held
+            return ESP_OK;
+        } else {
+            if (log_errors) {
+                ESP_LOGE(TAG, "partition %d invalid magic number 0x%x", num_parts, part->magic);
+            }
+            return ESP_ERR_INVALID_STATE;
         }
-        return ESP_ERR_INVALID_SIZE;
     }
-  }
 
-  if (log_errors) {
-      ESP_LOGE(TAG, "partition table has no terminating entry, not valid");
-  }
-  return ESP_ERR_INVALID_STATE;
+    if (log_errors) {
+        ESP_LOGE(TAG, "partition table has no terminating entry, not valid");
+    }
+    return ESP_ERR_INVALID_STATE;
 }
 
index ca408d4990aebff6bb741eb7cbf39c5b6bb1fc15..371bab2086cabe8731f1e534969e1b918adc8847 100644 (file)
@@ -130,6 +130,16 @@ config A2DP_SRC_ENABLE
     bool "SOURCE"
 endchoice
 
+config A2DP_SINK_TASK_STACK_SIZE
+    int "A2DP sink (audio stream decoding) task stack size"
+    depends on A2DP_ENABLE && A2DP_SINK_ENABLE
+    default 2048
+
+config A2DP_SOURCE_TASK_STACK_SIZE
+    int "A2DP source (audio stream encoding) task stack size"
+    depends on A2DP_ENABLE && A2DP_SRC_ENABLE
+    default 2048
+
 config BT_SPP_ENABLED
     bool "SPP"
     depends on CLASSIC_BT_ENABLED
index 6d795c1c08e716cd7bd6912f0d2311ed77e6aa85..1146750c7c8e608edc873339366cda4211b2a778 100644 (file)
@@ -16,7 +16,7 @@
 #include "esp_gatt_common_api.h"
 #include "esp_bt_main.h"
 #include "esp_gatt_defs.h"
-#include "btc_main.h"
+#include "btc_gatt_common.h"
 
 /**
  * @brief           This function is called to set local MTU,
@@ -32,7 +32,7 @@
 esp_err_t esp_ble_gatt_set_local_mtu (uint16_t mtu)
 {
     btc_msg_t msg;
-    btc_ble_main_args_t arg;
+    btc_ble_gatt_com_args_t arg;
 
     ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
 
@@ -41,9 +41,9 @@ esp_err_t esp_ble_gatt_set_local_mtu (uint16_t mtu)
     }
 
     msg.sig = BTC_SIG_API_CALL;
-    msg.pid = BTC_PID_MAIN_INIT;
+    msg.pid = BTC_PID_GATT_COMMON;
     msg.act = BTC_GATT_ACT_SET_LOCAL_MTU;
     arg.set_mtu.mtu = mtu;
 
-    return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_main_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
+    return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatt_com_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
 }
\ No newline at end of file
index ab11871467150c2a878811fa8a1b1580664aae33..8117d4c54c8a6a505278b0cce18f4fde8edf673d 100644 (file)
@@ -176,7 +176,8 @@ esp_err_t esp_a2d_register_callback(esp_a2d_cb_t callback);
 /**
  * @brief           Register A2DP sink data output function; For now the output is PCM data stream decoded
  *                  from SBC format. This function should be called only after esp_bluedroid_enable()
- *                  completes successfully, used only by A2DP sink.
+ *                  completes successfully, used only by A2DP sink. The callback is invoked in the context
+ *                  of A2DP sink task whose stack size is configurable through menuconfig
  *
  * @param[in]       callback: A2DP sink data callback function
  *
@@ -291,7 +292,8 @@ esp_err_t esp_a2d_source_deinit(void);
 /**
  * @brief           Register A2DP source data input function; For now the input is PCM data stream.
  *                  This function should be called only after esp_bluedroid_enable() completes
- *                  successfully
+ *                  successfully. The callback is invoked in the context of A2DP source task whose
+ *                  stack size is configurable through menuconfig
  *
  * @param[in]       callback: A2DP source data callback function
  *
index 02d420c31d400f2e0740d62e6fad55fbcb365131..9b00d62cea4e6afed884b4218002b4b761f1f3cf 100644 (file)
@@ -1268,6 +1268,11 @@ void bta_av_disable(tBTA_AV_CB *p_cb, tBTA_AV_DATA *p_data)
         hdr.layer_specific = xx + 1;
         bta_av_api_deregister((tBTA_AV_DATA *)&hdr);
     }
+
+    bta_sys_free_timer(&p_cb->sig_tmr);
+    memset(&p_cb->sig_tmr, 0, sizeof(TIMER_LIST_ENT));
+    bta_sys_free_timer(&p_cb->acp_sig_tmr);
+    memset(&p_cb->acp_sig_tmr, 0, sizeof(TIMER_LIST_ENT));
 }
 
 /*******************************************************************************
index e0b1595a9e06e0497bb598dfb9b6bb85ca7cd69d..f9a6b9685a551613ccc47cac8cf531451b82a3aa 100644 (file)
@@ -282,6 +282,47 @@ void bta_dm_enable(tBTA_DM_MSG *p_data)
     }
 }
 
+/*******************************************************************************
+ *
+ * Function         bta_dm_init_cb
+ *
+ * Description      Initializes the bta_dm_cb control block
+ *
+ *
+ * Returns          void
+ *
+ ******************************************************************************/
+void bta_dm_init_cb(void)
+{
+    memset(&bta_dm_cb, 0, sizeof(bta_dm_cb));
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_dm_deinit_cb
+ *
+ * Description      De-initializes the bta_dm_cb control block
+ *
+ *
+ * Returns          void
+ *
+ ******************************************************************************/
+void bta_dm_deinit_cb(void)
+{
+    bta_sys_free_timer(&bta_dm_cb.disable_timer);
+#if ( BTA_EIR_CANNED_UUID_LIST != TRUE )
+    bta_sys_free_timer(&bta_dm_cb.app_ready_timer);
+#endif
+#if BTM_SSR_INCLUDED == TRUE
+    for (size_t i = 0; i < BTA_DM_NUM_PM_TIMER; i++) {
+        for (size_t j = 0; j < BTA_DM_PM_MODE_TIMER_MAX; j++) {
+            bta_sys_free_timer(&bta_dm_cb.pm_timer[i].timer[j]);
+        }
+    }
+#endif
+    memset(&bta_dm_cb, 0, sizeof(bta_dm_cb));
+}
+
 /*******************************************************************************
 **
 ** Function         bta_dm_sys_hw_cback
@@ -318,7 +359,15 @@ static void bta_dm_sys_hw_cback( tBTA_SYS_HW_EVT status )
         }
 
         /* reinitialize the control block */
-        memset(&bta_dm_cb, 0, sizeof(bta_dm_cb));
+        bta_dm_deinit_cb();
+
+        bta_sys_free_timer(&bta_dm_search_cb.search_timer);
+#if ((defined BLE_INCLUDED) && (BLE_INCLUDED == TRUE))
+#if ((defined BTA_GATT_INCLUDED) && (BTA_GATT_INCLUDED == TRUE) && SDP_INCLUDED == TRUE)
+        bta_sys_free_timer(&bta_dm_search_cb.gatt_close_timer);
+#endif
+#endif
+        memset(&bta_dm_search_cb, 0x00, sizeof(bta_dm_search_cb));
 
         /* unregister from SYS */
         bta_sys_hw_unregister( BTA_SYS_HW_BLUETOOTH );
@@ -332,11 +381,18 @@ static void bta_dm_sys_hw_cback( tBTA_SYS_HW_EVT status )
         /* save security callback */
         temp_cback = bta_dm_cb.p_sec_cback;
         /* make sure the control block is properly initialized */
-        memset(&bta_dm_cb, 0, sizeof(bta_dm_cb));
+        bta_dm_init_cb();
+
         /* and retrieve the callback */
         bta_dm_cb.p_sec_cback = temp_cback;
         bta_dm_cb.is_bta_dm_active = TRUE;
 
+        bta_sys_free_timer(&bta_dm_search_cb.search_timer);
+#if ((defined BLE_INCLUDED) && (BLE_INCLUDED == TRUE))
+#if ((defined BTA_GATT_INCLUDED) && (BTA_GATT_INCLUDED == TRUE) && SDP_INCLUDED == TRUE)
+        bta_sys_free_timer(&bta_dm_search_cb.gatt_close_timer);
+#endif
+#endif
         /* hw is ready, go on with BTA DM initialization */
         memset(&bta_dm_search_cb, 0x00, sizeof(bta_dm_search_cb));
 #if (BTM_SSR_INCLUDED == TRUE)
@@ -2589,7 +2645,7 @@ static UINT8 bta_dm_authorize_cback (BD_ADDR bd_addr, DEV_CLASS dev_class, BD_NA
         return BTM_NOT_AUTHORIZED;
     }
 }
+
 
 
 
@@ -4126,6 +4182,8 @@ static void bta_dm_observe_results_cb (tBTM_INQ_RESULTS *p_inq, UINT8 *p_eir)
     result.inq_res.flag             = p_inq->flag;
     result.inq_res.adv_data_len     = p_inq->adv_data_len;
     result.inq_res.scan_rsp_len     = p_inq->scan_rsp_len;
+    memcpy(result.inq_res.dev_class, p_inq->dev_class, sizeof(DEV_CLASS));
+    result.inq_res.ble_evt_type     = p_inq->ble_evt_type;
 
     /* application will parse EIR to find out remote device name */
     result.inq_res.p_eir = p_eir;
@@ -4282,7 +4340,7 @@ static UINT8 bta_dm_ble_smp_cback (tBTM_LE_EVT event, BD_ADDR bda, tBTM_LE_EVT_D
         } else {
             sec_event.auth_cmpl.success = TRUE;
             if (!p_data->complt.smp_over_br) {
-                
+
             }
         }
 
@@ -5338,7 +5396,7 @@ static void bta_ble_energy_info_cmpl(tBTM_BLE_TX_TIME_MS tx_time,
     if (BTA_SUCCESS == st) {
         ctrl_state = bta_dm_pm_obtain_controller_state();
     }
-#endif  
+#endif
     if (bta_dm_cb.p_energy_info_cback) {
         bta_dm_cb.p_energy_info_cback(tx_time, rx_time, idle_time, energy_used, ctrl_state, st);
     }
index 8c75f131ae3c2cbca2fa65ef857b3c5d7c317f30..37c349daddaaf3b72b37a78cb36b09f5eb06bcfc 100644 (file)
@@ -48,8 +48,6 @@
 
 #if GATTC_INCLUDED == TRUE && BLE_INCLUDED == TRUE
 
-static osi_mutex_t write_ccc_mutex;
-
 /*****************************************************************************
 **  Constants
 *****************************************************************************/
@@ -129,9 +127,9 @@ static void bta_gattc_enable(tBTA_GATTC_CB *p_cb)
         memset(&bta_gattc_cb, 0, sizeof(tBTA_GATTC_CB));
         p_cb->state = BTA_GATTC_STATE_ENABLED;
         // Create a write ccc mutex when the gatt client enable
-        osi_mutex_new(&write_ccc_mutex);
+        osi_mutex_new(&bta_gattc_cb.write_ccc_mutex);
     } else {
-        APPL_TRACE_DEBUG("GATTC is arelady enabled");
+        APPL_TRACE_DEBUG("GATTC is already enabled");
     }
 }
 
@@ -157,7 +155,7 @@ void bta_gattc_disable(tBTA_GATTC_CB *p_cb)
         return;
     }
     // Free the write ccc mutex when the gatt client disable
-    osi_mutex_free(&write_ccc_mutex);
+    osi_mutex_free(&bta_gattc_cb.write_ccc_mutex);
 
     for (i = 0; i < BTA_GATTC_CL_MAX; i ++) {
         if (p_cb->cl_rcb[i].in_use) {
@@ -1644,8 +1642,8 @@ static void bta_gattc_conn_cback(tGATT_IF gattc_if, BD_ADDR bda, UINT16 conn_id,
     else if ((transport == BT_TRANSPORT_LE) && (connected == FALSE) && (p_conn != NULL)){
             p_conn->service_change_ccc_written = FALSE;
             if (p_conn->ccc_timer_used == TRUE){
-                assert(write_ccc_mutex != NULL);
-                osi_mutex_lock(&write_ccc_mutex, OSI_MUTEX_MAX_TIMEOUT);
+                assert(bta_gattc_cb.write_ccc_mutex != NULL);
+                osi_mutex_lock(&bta_gattc_cb.write_ccc_mutex, OSI_MUTEX_MAX_TIMEOUT);
 
                 if (p_conn->service_change_ccc_timer.param != 0) {
                     osi_free((void *)p_conn->service_change_ccc_timer.param);
@@ -1653,7 +1651,7 @@ static void bta_gattc_conn_cback(tGATT_IF gattc_if, BD_ADDR bda, UINT16 conn_id,
                 }
                 bta_sys_stop_timer(&(p_conn->service_change_ccc_timer));
                 p_conn->ccc_timer_used = FALSE;
-                osi_mutex_unlock(&write_ccc_mutex);
+                osi_mutex_unlock(&bta_gattc_cb.write_ccc_mutex);
             }
     }
 
@@ -2354,14 +2352,14 @@ static void bta_gattc_wait4_service_change_ccc_cback (TIMER_LIST_ENT *p_tle)
     BOOLEAN start_ccc_timer = FALSE;
     UINT32 new_timeout;
 
-    assert(write_ccc_mutex != NULL);
-    osi_mutex_lock(&write_ccc_mutex, OSI_MUTEX_MAX_TIMEOUT);
+    assert(bta_gattc_cb.write_ccc_mutex != NULL);
+    osi_mutex_lock(&bta_gattc_cb.write_ccc_mutex, OSI_MUTEX_MAX_TIMEOUT);
 
     tBTA_GATTC_WAIT_CCC_TIMER *p_timer_param = (tBTA_GATTC_WAIT_CCC_TIMER*) p_tle->param;
     p_tle->param = (TIMER_PARAM_TYPE)0;
     if (p_timer_param == NULL){
         APPL_TRACE_ERROR("p_timer_param is NULL in %s\n", __func__);
-        osi_mutex_unlock(&write_ccc_mutex);
+        osi_mutex_unlock(&bta_gattc_cb.write_ccc_mutex);
         return;
     }
 
@@ -2369,7 +2367,7 @@ static void bta_gattc_wait4_service_change_ccc_cback (TIMER_LIST_ENT *p_tle)
     if (p_conn == NULL){
         APPL_TRACE_ERROR("p_conn is NULL in %s\n", __func__);
         osi_free(p_timer_param);
-        osi_mutex_unlock(&write_ccc_mutex);
+        osi_mutex_unlock(&bta_gattc_cb.write_ccc_mutex);
         return;
     }
 
@@ -2401,7 +2399,7 @@ static void bta_gattc_wait4_service_change_ccc_cback (TIMER_LIST_ENT *p_tle)
     }
 
     osi_free(p_timer_param);
-    osi_mutex_unlock(&write_ccc_mutex);
+    osi_mutex_unlock(&bta_gattc_cb.write_ccc_mutex);
 }
 
 #endif
index 70655fd94417648ed71578c3e468ffdeafa4a34d..4e3cf9737d5431b97f21b988a535a8c8959fdf6c 100644 (file)
@@ -862,6 +862,10 @@ BOOLEAN bta_gattc_conn_dealloc(BD_ADDR remote_bda)
     if (p_conn != NULL) {
         p_conn->in_use = FALSE;
         memset(p_conn->remote_bda, 0, BD_ADDR_LEN);
+        osi_mutex_lock(&bta_gattc_cb.write_ccc_mutex, OSI_MUTEX_MAX_TIMEOUT);
+        bta_sys_free_timer(&p_conn->service_change_ccc_timer);
+        p_conn->ccc_timer_used = FALSE;
+        osi_mutex_unlock(&bta_gattc_cb.write_ccc_mutex);
         return TRUE;
     }
     return FALSE;
index c47668a659ee2e5dc7c6fa64aae350e3bc0f6c0b..78d8e969eaade9d2eff2007b125aa918441294a8 100644 (file)
@@ -30,6 +30,7 @@
 #include "bta_gattc_ci.h"
 #include "bta_gattc_co.h"
 #include "fixed_queue.h"
+#include "mutex.h"
 
 /*****************************************************************************
 **  Constants and data types
@@ -357,8 +358,8 @@ enum {
 };
 
 typedef struct {
-    UINT8             state;
-
+    UINT8               state;
+    osi_mutex_t         write_ccc_mutex;
     tBTA_GATTC_CONN     conn_track[BTA_GATTC_CONN_MAX];
     tBTA_GATTC_BG_TCK   bg_track[BTA_GATTC_KNOWN_SR_MAX];
     tBTA_GATTC_RCB      cl_rcb[BTA_GATTC_CL_MAX];
index 94ebd805086e30adf16323a51ec579e39d45b44a..0119008693950c79193f06c5b0c0d2e60a35a811 100644 (file)
@@ -224,6 +224,7 @@ extern UINT16 bta_sys_get_sys_features(void);
 extern void bta_sys_sendmsg(void *p_msg);
 extern void bta_sys_start_timer(TIMER_LIST_ENT *p_tle, UINT16 type, INT32 timeout_ms);
 extern void bta_sys_stop_timer(TIMER_LIST_ENT *p_tle);
+extern void bta_sys_free_timer(TIMER_LIST_ENT *p_tle);
 extern void bta_sys_disable(tBTA_SYS_HW_MODULE module);
 extern UINT32 bta_sys_get_remaining_ticks(TIMER_LIST_ENT *p_target_tle);
 
index e435cea9027c04ce6b8c9095677390a94cf3d101..590c907efcc26fb8ef61d8deefdf4e43425493b6 100644 (file)
@@ -657,6 +657,28 @@ void bta_sys_stop_timer(TIMER_LIST_ENT *p_tle)
     osi_alarm_cancel(alarm);
 }
 
+/*******************************************************************************
+**
+** Function         bta_sys_free_timer
+**
+** Description      Stop and free a BTA timer.
+**
+** Returns          void
+**
+*******************************************************************************/
+void bta_sys_free_timer(TIMER_LIST_ENT *p_tle)
+{
+    assert(p_tle != NULL);
+
+    osi_alarm_t *alarm = hash_map_get(bta_alarm_hash_map, p_tle);
+    if (alarm == NULL) {
+        LOG_DEBUG("%s expected alarm was not in bta alarm hash map.", __func__);
+        return;
+    }
+    osi_alarm_cancel(alarm);
+    hash_map_erase(bta_alarm_hash_map, p_tle);
+}
+
 /*******************************************************************************
 **
 ** Function         bta_sys_disable
index f37801e699edfc0db6024c5d6972f1f66781a2bc..cb5975be1f8178bc056fa8f9b35719b845b4e593 100644 (file)
@@ -20,7 +20,6 @@
 #include "btc_config.h"
 #include "alarm.h"
 #include "btc_ble_storage.h"
-#include "bta_gatt_common.h"
 #include "btc_gap_ble.h"
 #include "bta_gattc_int.h"
 #include "bta_gatts_int.h"
@@ -88,11 +87,6 @@ static void btc_deinit_bluetooth(void)
     future_ready(*btc_main_get_future_p(BTC_MAIN_DEINIT_FUTURE), FUTURE_SUCCESS);
 }
 
-static void btc_set_local_mtu(uint16_t mtu)
-{
-    BTA_GATT_SetLocalMTU(mtu);
-}
-
 void btc_main_call_handler(btc_msg_t *msg)
 {
     LOG_DEBUG("%s act %d\n", __func__, msg->act);
@@ -110,12 +104,6 @@ void btc_main_call_handler(btc_msg_t *msg)
     case BTC_MAIN_ACT_DISABLE:
         btc_disable_bluetooth();
         break;
-    case BTC_GATT_ACT_SET_LOCAL_MTU:
-    {
-        btc_ble_main_args_t *arg = (btc_ble_main_args_t *)(msg->arg);
-        btc_set_local_mtu(arg->set_mtu.mtu);
-        break;
-    }
     default:
         LOG_ERROR("%s UNKNOWN ACT %d\n", __func__, msg->act);
         break;
index 2a1147296435cdb6b99186adef0f4f6c38630d7c..004b67f16b51d3bd780bbce49f5a121e14a72f23 100644 (file)
@@ -24,6 +24,7 @@
 #include "btc_dev.h"
 #include "btc_gatts.h"
 #include "btc_gattc.h"
+#include "btc_gatt_common.h"
 #include "btc_gap_ble.h"
 #include "btc_blufi_prf.h"
 #include "btc_dm.h"
@@ -48,33 +49,36 @@ static xTaskHandle  xBtcTaskHandle = NULL;
 static xQueueHandle xBtcQueue = 0;
 
 static btc_func_t profile_tab[BTC_PID_NUM] = {
-    [BTC_PID_MAIN_INIT] = {btc_main_call_handler,       NULL                    },
-    [BTC_PID_DEV]       = {btc_dev_call_handler,        NULL                    },
+    [BTC_PID_MAIN_INIT]   = {btc_main_call_handler,       NULL                    },
+    [BTC_PID_DEV]         = {btc_dev_call_handler,        NULL                    },
 #if (GATTS_INCLUDED == TRUE)
-    [BTC_PID_GATTS]     = {btc_gatts_call_handler,      btc_gatts_cb_handler    },
+    [BTC_PID_GATTS]       = {btc_gatts_call_handler,      btc_gatts_cb_handler    },
 #endif  ///GATTS_INCLUDED == TRUE
 #if (GATTC_INCLUDED == TRUE)
-    [BTC_PID_GATTC]     = {btc_gattc_call_handler,      btc_gattc_cb_handler    },
+    [BTC_PID_GATTC]       = {btc_gattc_call_handler,      btc_gattc_cb_handler    },
 #endif  ///GATTC_INCLUDED == TRUE
-    [BTC_PID_GAP_BLE]   = {btc_gap_ble_call_handler,    btc_gap_ble_cb_handler  },
-    [BTC_PID_BLE_HID]   = {NULL, NULL},
-    [BTC_PID_SPPLIKE]   = {NULL, NULL},
+#if (GATTS_INCLUDED == TRUE || GATTC_INCLUDED == TRUE)
+    [BTC_PID_GATT_COMMON] = {btc_gatt_com_call_handler,   NULL                    },
+#endif //GATTC_INCLUDED == TRUE || GATTS_INCLUDED == TRUE
+    [BTC_PID_GAP_BLE]     = {btc_gap_ble_call_handler,    btc_gap_ble_cb_handler  },
+    [BTC_PID_BLE_HID]     = {NULL, NULL},
+    [BTC_PID_SPPLIKE]     = {NULL, NULL},
 #if (GATTS_INCLUDED == TRUE)
-    [BTC_PID_BLUFI]     = {btc_blufi_call_handler,      btc_blufi_cb_handler    },
+    [BTC_PID_BLUFI]       = {btc_blufi_call_handler,      btc_blufi_cb_handler    },
 #endif  ///GATTS_INCLUDED == TRUE
-    [BTC_PID_DM_SEC]    = {NULL,                        btc_dm_sec_cb_handler   },
-    [BTC_PID_ALARM]     = {btc_alarm_handler,           NULL                    },
+    [BTC_PID_DM_SEC]      = {NULL,                        btc_dm_sec_cb_handler   },
+    [BTC_PID_ALARM]       = {btc_alarm_handler,           NULL                    },
 #if CONFIG_CLASSIC_BT_ENABLED
 #if (BTC_GAP_BT_INCLUDED == TRUE)
-    [BTC_PID_GAP_BT]    = {btc_gap_bt_call_handler,     NULL                    },
+    [BTC_PID_GAP_BT]      = {btc_gap_bt_call_handler,     NULL                    },
 #endif /* (BTC_GAP_BT_INCLUDED == TRUE) */
-    [BTC_PID_PRF_QUE]   = {btc_profile_queue_handler,   NULL                    },
+    [BTC_PID_PRF_QUE]     = {btc_profile_queue_handler,   NULL                    },
 #if BTC_AV_INCLUDED
-    [BTC_PID_A2DP]      = {btc_a2dp_call_handler,       btc_a2dp_cb_handler     },
-    [BTC_PID_AVRC]      = {btc_avrc_call_handler,       NULL                    },
+    [BTC_PID_A2DP]        = {btc_a2dp_call_handler,       btc_a2dp_cb_handler     },
+    [BTC_PID_AVRC]        = {btc_avrc_call_handler,       NULL                    },
 #endif /* #if BTC_AV_INCLUDED */
 #if CONFIG_BT_SPP_ENABLED
-    [BTC_PID_SPP]       = {btc_spp_call_handler,        btc_spp_cb_handler      },
+    [BTC_PID_SPP]         = {btc_spp_call_handler,        btc_spp_cb_handler      },
 #endif /* #if CONFIG_BT_SPP_ENABLED */
 #endif /* #if CONFIG_CLASSIC_BT_ENABLED */
 };
index 523e1bb982993fb8264f1898be82ea90636b221b..b95ae0bbec205f63215e7d318d5c1346a3aa146f 100644 (file)
@@ -26,7 +26,6 @@ typedef enum {
     BTC_MAIN_ACT_DEINIT,
     BTC_MAIN_ACT_ENABLE,
     BTC_MAIN_ACT_DISABLE,
-    BTC_GATT_ACT_SET_LOCAL_MTU,
 } btc_main_act_t;
 
 typedef enum {
@@ -61,13 +60,5 @@ bt_status_t btc_init_bluetooth(future_t *future);
 void btc_deinit_bluetooth(future_t *future);
 #endif
 
-/* btc_ble_gattc_args_t */
-typedef union {
-    //BTC_GATT_ACT_SET_LOCAL_MTU,
-    struct set_mtu_arg {
-        uint16_t mtu;
-    } set_mtu;
-} btc_ble_main_args_t;
-
 void btc_main_call_handler(btc_msg_t *msg);
 #endif /* __BTC_BT_MAIN_H__ */
index f19c266991a7932a5dc59dc8c1b13f51a6b442c0..16388c685bbd4579f2ed925c6fb4b1c51c964d33 100644 (file)
@@ -41,6 +41,7 @@ typedef enum {
 #if (GATTC_INCLUDED == TRUE)
     BTC_PID_GATTC,
 #endif  ///GATTC_INCLUDED == TRUE
+    BTC_PID_GATT_COMMON,
     BTC_PID_GAP_BLE,
     BTC_PID_BLE_HID,
     BTC_PID_SPPLIKE,
index a246f2c0e673cefa52fe5e3d76f7241535436a1e..671584448459e5497edd790fa4f061b362a15cdb 100644 (file)
@@ -232,13 +232,13 @@ bool btc_a2dp_sink_startup(void)
 
     APPL_TRACE_EVENT("## A2DP SINK START MEDIA THREAD ##");
 
-    btc_aa_snk_queue_set = xQueueCreateSet(BTC_MEDIA_TASK_QUEUE_SET_LEN);
+    btc_aa_snk_queue_set = xQueueCreateSet(BTC_A2DP_SINK_TASK_QUEUE_SET_LEN);
     configASSERT(btc_aa_snk_queue_set);
-    btc_aa_snk_data_queue = xQueueCreate(BTC_MEDIA_DATA_QUEUE_LEN, sizeof(int32_t));
+    btc_aa_snk_data_queue = xQueueCreate(BTC_A2DP_SINK_DATA_QUEUE_LEN, sizeof(int32_t));
     configASSERT(btc_aa_snk_data_queue);
     xQueueAddToSet(btc_aa_snk_data_queue, btc_aa_snk_queue_set);
 
-    btc_aa_snk_ctrl_queue = xQueueCreate(BTC_MEDIA_CTRL_QUEUE_LEN, sizeof(void *));
+    btc_aa_snk_ctrl_queue = xQueueCreate(BTC_A2DP_SINK_CTRL_QUEUE_LEN, sizeof(void *));
     configASSERT(btc_aa_snk_ctrl_queue);
     xQueueAddToSet(btc_aa_snk_ctrl_queue, btc_aa_snk_queue_set);
 
@@ -246,7 +246,7 @@ bool btc_a2dp_sink_startup(void)
         goto error_exit;
     }
 
-    xTaskCreatePinnedToCore(btc_a2dp_sink_task_handler, BTC_MEDIA_TASK_NAME, BTC_MEDIA_TASK_STACK_SIZE, NULL, BTC_MEDIA_TASK_PRIO, &btc_aa_snk_task_hdl, BTC_MEDIA_TASK_PINNED_TO_CORE);
+    xTaskCreatePinnedToCore(btc_a2dp_sink_task_handler, BTC_A2DP_SINK_TASK_NAME, BTC_A2DP_SINK_TASK_STACK_SIZE, NULL, BTC_A2DP_SINK_TASK_PRIO, &btc_aa_snk_task_hdl, BTC_A2DP_SINK_TASK_PINNED_TO_CORE);
     if (btc_aa_snk_task_hdl == NULL) {
         goto error_exit;
     }
index 1b2a01f7ae7437133174a138a5ee653854cada5b..d405917832cd0e378e6d8468862ac0df5c8e77ea 100644 (file)
@@ -307,13 +307,13 @@ bool btc_a2dp_source_startup(void)
 
     APPL_TRACE_EVENT("## A2DP SOURCE START MEDIA THREAD ##");
 
-    btc_aa_src_queue_set = xQueueCreateSet(BTC_MEDIA_TASK_QUEUE_SET_LEN);
+    btc_aa_src_queue_set = xQueueCreateSet(BTC_A2DP_SOURCE_TASK_QUEUE_SET_LEN);
     configASSERT(btc_aa_src_queue_set);
-    btc_aa_src_data_queue = xQueueCreate(BTC_MEDIA_DATA_QUEUE_LEN, sizeof(void *));
+    btc_aa_src_data_queue = xQueueCreate(BTC_A2DP_SOURCE_DATA_QUEUE_LEN, sizeof(void *));
     configASSERT(btc_aa_src_data_queue);
     xQueueAddToSet(btc_aa_src_data_queue, btc_aa_src_queue_set);
 
-    btc_aa_src_ctrl_queue = xQueueCreate(BTC_MEDIA_CTRL_QUEUE_LEN, sizeof(void *));
+    btc_aa_src_ctrl_queue = xQueueCreate(BTC_A2DP_SOURCE_CTRL_QUEUE_LEN, sizeof(void *));
     configASSERT(btc_aa_src_ctrl_queue);
     xQueueAddToSet(btc_aa_src_ctrl_queue, btc_aa_src_queue_set);
 
@@ -321,7 +321,7 @@ bool btc_a2dp_source_startup(void)
         goto error_exit;
     }
 
-    xTaskCreatePinnedToCore(btc_a2dp_source_task_handler, BTC_MEDIA_TASK_NAME, BTC_MEDIA_TASK_STACK_SIZE, NULL, BTC_MEDIA_TASK_PRIO, &btc_aa_src_task_hdl, BTC_MEDIA_TASK_PINNED_TO_CORE);
+    xTaskCreatePinnedToCore(btc_a2dp_source_task_handler, BTC_A2DP_SOURCE_TASK_NAME, BTC_A2DP_SOURCE_TASK_STACK_SIZE, NULL, BTC_A2DP_SOURCE_TASK_PRIO, &btc_aa_src_task_hdl, BTC_A2DP_SOURCE_TASK_PINNED_TO_CORE);
     if (btc_aa_src_task_hdl == NULL) {
         goto error_exit;
     }
diff --git a/components/bt/bluedroid/btc/profile/std/gatt/btc_gatt_common.c b/components/bt/bluedroid/btc/profile/std/gatt/btc_gatt_common.c
new file mode 100644 (file)
index 0000000..af34c71
--- /dev/null
@@ -0,0 +1,47 @@
+// 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.
+
+#include "btc_task.h"
+#include "btc_main.h"
+#include "btc_dm.h"
+#include "future.h"
+#include "esp_err.h"
+#include "btc_config.h"
+#include "alarm.h"
+#include "btc_ble_storage.h"
+#include "btc_gatt_common.h"
+#include "bta_gatt_common.h"
+
+
+static void btc_set_local_mtu(uint16_t mtu)
+{
+    BTA_GATT_SetLocalMTU(mtu);
+}
+
+void btc_gatt_com_call_handler(btc_msg_t *msg)
+{
+    LOG_DEBUG("%s act %d\n", __func__, msg->act);
+    switch (msg->act) {
+    case BTC_GATT_ACT_SET_LOCAL_MTU:
+    {
+        btc_ble_gatt_com_args_t *arg = (btc_ble_gatt_com_args_t *)(msg->arg);
+        btc_set_local_mtu(arg->set_mtu.mtu);
+        break;
+    }
+    default:
+        LOG_ERROR("%s UNKNOWN ACT %d\n", __func__, msg->act);
+        break;
+    }
+}
+
diff --git a/components/bt/bluedroid/btc/profile/std/include/btc_gatt_common.h b/components/bt/bluedroid/btc/profile/std/include/btc_gatt_common.h
new file mode 100644 (file)
index 0000000..41b0fee
--- /dev/null
@@ -0,0 +1,37 @@
+// 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 __BTC_GATT_COMMON_H__
+#define __BTC_GATT_COMMON_H__
+
+#include "future.h"
+#include "bt_types.h"
+#include "bta_api.h"
+#include "btc_main.h"
+#include "btc_task.h"
+
+typedef enum {
+    BTC_GATT_ACT_SET_LOCAL_MTU = 0,
+} btc_gatt_com_act_t;
+
+/* btc_ble_gattc_args_t */
+typedef union {
+    //BTC_GATT_ACT_SET_LOCAL_MTU,
+    struct set_mtu_arg {
+        uint16_t mtu;
+    } set_mtu;
+} btc_ble_gatt_com_args_t;
+
+void btc_gatt_com_call_handler(btc_msg_t *msg);
+#endif /* __BTC_GATT_COMMON_H__ */
index afd3f08d03a4503c9fcd90f338f1a0076d379624..6a92388d37382f34d6576eafa245707bd2e26e08 100644 (file)
@@ -83,13 +83,21 @@ typedef enum {
 #define BTC_TASK_PRIO                   (configMAX_PRIORITIES - 6)
 #define BTC_TASK_QUEUE_LEN              60
 
-#define BTC_MEDIA_TASK_PINNED_TO_CORE   (TASK_PINNED_TO_CORE)
-#define BTC_MEDIA_TASK_STACK_SIZE       (2048 + BT_TASK_EXTRA_STACK_SIZE)
-#define BTC_MEDIA_TASK_NAME             "BtcMediaT"
-#define BTC_MEDIA_TASK_PRIO             (configMAX_PRIORITIES - 3)
-#define BTC_MEDIA_DATA_QUEUE_LEN        (3)
-#define BTC_MEDIA_CTRL_QUEUE_LEN        (5)
-#define BTC_MEDIA_TASK_QUEUE_SET_LEN    (BTC_MEDIA_DATA_QUEUE_LEN + BTC_MEDIA_CTRL_QUEUE_LEN)
+#define BTC_A2DP_SINK_TASK_PINNED_TO_CORE     (TASK_PINNED_TO_CORE)
+#define BTC_A2DP_SINK_TASK_STACK_SIZE         (CONFIG_A2DP_SINK_TASK_STACK_SIZE + BT_TASK_EXTRA_STACK_SIZE) // by menuconfig
+#define BTC_A2DP_SINK_TASK_NAME               "BtA2dSinkT"
+#define BTC_A2DP_SINK_TASK_PRIO               (configMAX_PRIORITIES - 3)
+#define BTC_A2DP_SINK_DATA_QUEUE_LEN          (3)
+#define BTC_A2DP_SINK_CTRL_QUEUE_LEN          (5)
+#define BTC_A2DP_SINK_TASK_QUEUE_SET_LEN      (BTC_A2DP_SINK_DATA_QUEUE_LEN + BTC_A2DP_SINK_CTRL_QUEUE_LEN)
+
+#define BTC_A2DP_SOURCE_TASK_PINNED_TO_CORE   (TASK_PINNED_TO_CORE)
+#define BTC_A2DP_SOURCE_TASK_STACK_SIZE       (CONFIG_A2DP_SOURCE_TASK_STACK_SIZE + BT_TASK_EXTRA_STACK_SIZE) // by menuconfig
+#define BTC_A2DP_SOURCE_TASK_NAME             "BtA2dSourceT"
+#define BTC_A2DP_SOURCE_TASK_PRIO             (configMAX_PRIORITIES - 3)
+#define BTC_A2DP_SOURCE_DATA_QUEUE_LEN        (3)
+#define BTC_A2DP_SOURCE_CTRL_QUEUE_LEN        (5)
+#define BTC_A2DP_SOURCE_TASK_QUEUE_SET_LEN    (BTC_A2DP_SOURCE_DATA_QUEUE_LEN + BTC_A2DP_SOURCE_CTRL_QUEUE_LEN)
 
 #define TASK_POST_NON_BLOCKING          (0)
 #define TASK_POST_BLOCKING              (portMAX_DELAY)
index f047db6bd298398d6bf895abd56e71c792f77bc0..3910f76e4f334b5d2b88a070b26ad80fe1666474 100644 (file)
@@ -407,7 +407,7 @@ void avdt_ccb_dealloc(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data)
     UNUSED(p_data);
 
     AVDT_TRACE_DEBUG("avdt_ccb_dealloc %d\n", avdt_ccb_to_idx(p_ccb));
-    btu_stop_timer(&p_ccb->timer_entry);
+    btu_free_timer(&p_ccb->timer_entry);
     fixed_queue_free(p_ccb->cmd_q, NULL);
     fixed_queue_free(p_ccb->rsp_q, NULL);
     memset(p_ccb, 0, sizeof(tAVDT_CCB));
index 060884e1d80c7a236fe248eed64145579fe15c38..484270d034611471f737ad94eb4540ae87e452ea 100644 (file)
@@ -644,7 +644,7 @@ void avdt_scb_dealloc(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
     UNUSED(p_data);
 
     AVDT_TRACE_DEBUG("avdt_scb_dealloc hdl=%d\n", avdt_scb_to_hdl(p_scb));
-    btu_stop_timer(&p_scb->timer_entry);
+    btu_free_timer(&p_scb->timer_entry);
 
 #if AVDT_MULTIPLEXING == TRUE
     /* free fragments we're holding, if any; it shouldn't happen */
index 2319f95e7fd0cc156192e0a24ead5be87b375651..833a4d77a2824f52a20d441ab2336bcbb64d1b2b 100644 (file)
@@ -3598,6 +3598,14 @@ void btm_ble_write_adv_enable_complete(UINT8 *p)
     } else if (p_cb->p_stop_adv_cb && p_cb->adv_mode == BTM_BLE_ADV_DISABLE) {
         p_cb->state = BTM_BLE_STOP_ADV;
         (*p_cb->p_stop_adv_cb)(status);
+    }else {
+        // p_cb->p_adv_cb is NULL or p_cb->p_stop_adv_cb is NULL
+        if (p_cb->adv_mode == BTM_BLE_ADV_ENABLE) {
+            p_cb->state = BTM_BLE_ADVERTISING;
+        }else {
+            p_cb->state = BTM_BLE_STOP_ADV;
+        }
+        p_cb->adv_callback_twice = FALSE;
     }
     /* if write adv enable/disbale not succeed */
     if (*p != HCI_SUCCESS) {
@@ -3740,6 +3748,9 @@ void btm_ble_init (void)
 
     BTM_TRACE_DEBUG("%s", __func__);
 
+    btu_free_timer(&p_cb->obs_timer_ent);
+    btu_free_timer(&p_cb->scan_timer_ent);
+    btu_free_timer(&p_cb->inq_var.fast_adv_timer);
     memset(p_cb, 0, sizeof(tBTM_BLE_CB));
     memset(&(btm_cb.cmn_ble_vsc_cb), 0 , sizeof(tBTM_BLE_VSC_CB));
     btm_cb.cmn_ble_vsc_cb.values_read = FALSE;
index 00d580b3e80f0091b838b6c538627f2a72a3396c..94e143bfec26581c3ad8777112513f78b66443f8 100644 (file)
@@ -533,7 +533,7 @@ void btm_read_local_name_complete (UINT8 *p, UINT16 evt_len)
     UINT8           status;
     UNUSED(evt_len);
 
-    btu_stop_timer (&btm_cb.devcb.rln_timer);
+    btu_free_timer (&btm_cb.devcb.rln_timer);
 
     /* If there was a callback address for read local name, call it */
     btm_cb.devcb.p_rln_cmpl_cb = NULL;
index d9baa4b81c5490a2827cf0afd38d4f066c2e6198..010d1d93c507037104920e7dfa85da56c35eb7e3 100644 (file)
@@ -1359,6 +1359,12 @@ void btm_inq_db_init (void)
 #if 0  /* cleared in btm_init; put back in if called from anywhere else! */
     memset (&btm_cb.btm_inq_vars, 0, sizeof (tBTM_INQUIRY_VAR_ST));
 #endif
+
+    btu_free_timer(&btm_cb.btm_inq_vars.rmt_name_timer_ent);
+    memset(&btm_cb.btm_inq_vars.rmt_name_timer_ent, 0, sizeof(TIMER_LIST_ENT));
+    btu_free_timer(&btm_cb.btm_inq_vars.inq_timer_ent);
+    memset(&btm_cb.btm_inq_vars.inq_timer_ent, 0, sizeof(TIMER_LIST_ENT));
+
     btm_cb.btm_inq_vars.no_inc_ssp = BTM_NO_SSP_ON_INQUIRY;
 }
 
index f3a738359ce6a676f76ae1852ff0f71ed55c8a6b..ee6bb6d86566e36b550d93be83526e7afd53922d 100644 (file)
@@ -513,15 +513,12 @@ void btu_free_timer(TIMER_LIST_ENT *p_tle)
 {
     assert(p_tle != NULL);
 
-    if (p_tle->in_use == FALSE) {
-        return;
-    }
     p_tle->in_use = FALSE;
 
     // Get the alarm for the timer list entry.
     osi_alarm_t *alarm = hash_map_get(btu_general_alarm_hash_map, p_tle);
     if (alarm == NULL) {
-        LOG_WARN("%s Unable to find expected alarm in hashmap", __func__);
+        LOG_DEBUG("%s Unable to find expected alarm in hashmap", __func__);
         return;
     }
     osi_alarm_cancel(alarm);
@@ -615,6 +612,23 @@ void btu_stop_quick_timer(TIMER_LIST_ENT *p_tle)
     }
     osi_alarm_cancel(alarm);
 }
+
+void btu_free_quick_timer(TIMER_LIST_ENT *p_tle)
+{
+    assert(p_tle != NULL);
+
+    p_tle->in_use = FALSE;
+
+    // Get the alarm for the timer list entry.
+    osi_alarm_t *alarm = hash_map_get(btu_l2cap_alarm_hash_map, p_tle);
+    if (alarm == NULL) {
+        LOG_DEBUG("%s Unable to find expected alarm in hashmap", __func__);
+        return;
+    }
+    osi_alarm_cancel(alarm);
+    hash_map_erase(btu_l2cap_alarm_hash_map, p_tle);
+}
+
 #endif /* defined(QUICK_TIMER_TICKS_PER_SEC) && (QUICK_TIMER_TICKS_PER_SEC > 0) */
 
 void btu_oneshot_alarm_cb(void *data)
index cf5c0e3ad274b91602e32720210f359998809f32..7ceb67ddc025aac86b37350e831810ab9eabb0fb 100644 (file)
@@ -173,6 +173,13 @@ void gatt_free(void)
 
         fixed_queue_free(gatt_cb.tcb[i].pending_ind_q, NULL);
         gatt_cb.tcb[i].pending_ind_q = NULL;
+
+        btu_free_timer(&gatt_cb.tcb[i].conf_timer_ent);
+        memset(&gatt_cb.tcb[i].conf_timer_ent, 0, sizeof(TIMER_LIST_ENT));
+
+        btu_free_timer(&gatt_cb.tcb[i].ind_ack_timer_ent);
+        memset(&gatt_cb.tcb[i].ind_ack_timer_ent, 0, sizeof(TIMER_LIST_ENT));
+    
 #if (GATTS_INCLUDED == TRUE)
         fixed_queue_free(gatt_cb.tcb[i].sr_cmd.multi_rsp_q, NULL);
         gatt_cb.tcb[i].sr_cmd.multi_rsp_q = NULL;
index 5b0db44e9e765df02d9ff13d345fc875638ebedc..98339c04ed967879f0eb4cd6e4085a78022cdd7e 100644 (file)
@@ -1683,6 +1683,7 @@ tGATT_CLCB *gatt_clcb_alloc (UINT16 conn_id)
 void gatt_clcb_dealloc (tGATT_CLCB *p_clcb)
 {
     if (p_clcb && p_clcb->in_use) {
+        btu_free_timer(&p_clcb->rsp_timer_ent);
         memset(p_clcb, 0, sizeof(tGATT_CLCB));
     }
 }
@@ -2211,8 +2212,8 @@ void gatt_cleanup_upon_disc(BD_ADDR bda, UINT16 reason, tBT_TRANSPORT transport)
             }
         }
 
-        btu_stop_timer (&p_tcb->ind_ack_timer_ent);
-        btu_stop_timer (&p_tcb->conf_timer_ent);
+        btu_free_timer (&p_tcb->ind_ack_timer_ent);
+        btu_free_timer (&p_tcb->conf_timer_ent);
         gatt_free_pending_ind(p_tcb);
         gatt_free_pending_enc_queue(p_tcb);
         gatt_free_pending_prepare_write_queue(p_tcb);
index 0401e471c681d2373126931b1cb16ee9e2c96cb9..c59fb7f56d5672e0ddfa588299b2cfbf226ca5a4 100644 (file)
@@ -246,6 +246,7 @@ void btu_uipc_rx_cback(BT_HDR *p_msg);
 #if defined(QUICK_TIMER_TICKS_PER_SEC) && (QUICK_TIMER_TICKS_PER_SEC > 0)
 void btu_start_quick_timer (TIMER_LIST_ENT *p_tle, UINT16 type, UINT32 timeout);
 void btu_stop_quick_timer (TIMER_LIST_ENT *p_tle);
+void btu_free_quick_timer (TIMER_LIST_ENT *p_tle);
 void btu_process_quick_timer_evt (void);
 #endif
 
index 44ef74b28486867d98c61adf2821ab29931598fc..e65b328cf084f4bfdd9732f79fc0e9bc1c3de84c 100644 (file)
@@ -767,7 +767,7 @@ extern BOOLEAN  l2c_fcr_renegotiate_chan(tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg
 extern UINT8    l2c_fcr_process_peer_cfg_req(tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg);
 extern void     l2c_fcr_adj_monitor_retran_timeout (tL2C_CCB *p_ccb);
 extern void     l2c_fcr_stop_timer (tL2C_CCB *p_ccb);
-
+extern void     l2c_fcr_free_timer (tL2C_CCB *p_ccb);
 /* Functions provided by l2c_ble.c
 ************************************
 */
index 640e4908ccb8232328fd0173d578b7e8b73e4a70..10f0c595427bdfc7a5cf6d99a27ec7e6b5233bfe 100644 (file)
@@ -201,6 +201,21 @@ void l2c_fcr_stop_timer (tL2C_CCB *p_ccb)
     }
 }
 
+/*******************************************************************************
+**
+** Function         l2c_fcr_free_timer
+**
+** Description      This function releases the (monitor or transmission) timer.
+**
+** Returns          -
+**
+*******************************************************************************/
+void l2c_fcr_free_timer (tL2C_CCB *p_ccb)
+{
+    assert(p_ccb != NULL);
+    btu_free_quick_timer (&p_ccb->fcrb.mon_retrans_timer);
+}
+
 /*******************************************************************************
 **
 ** Function         l2c_fcr_cleanup
@@ -232,9 +247,12 @@ void l2c_fcr_cleanup (tL2C_CCB *p_ccb)
     fixed_queue_free(p_fcrb->retrans_q, osi_free_func);
     p_fcrb->retrans_q = NULL;
        
-    btu_stop_quick_timer (&p_fcrb->ack_timer);
-    btu_stop_quick_timer (&p_ccb->fcrb.mon_retrans_timer);
-
+    btu_free_quick_timer (&p_fcrb->ack_timer);
+    memset(&p_fcrb->ack_timer, 0, sizeof(TIMER_LIST_ENT));
+    
+    btu_free_quick_timer (&p_ccb->fcrb.mon_retrans_timer);
+    memset(&p_fcrb->mon_retrans_timer, 0, sizeof(TIMER_LIST_ENT));
+    
 #if (L2CAP_ERTM_STATS == TRUE)
     if ( (p_ccb->local_cid >= L2CAP_BASE_APPL_CID) && (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) ) {
         UINT32  dur = osi_time_get_os_boottime_ms() - p_ccb->fcrb.connect_tick_count;
index b40146183f176be618eda6d6dca135e2c50eb27e..119b1f8e60bf8ec9a065191c8f5a970ae5cbcfba 100644 (file)
@@ -54,8 +54,11 @@ tL2C_LCB *l2cu_allocate_lcb (BD_ADDR p_bd_addr, BOOLEAN is_bonding, tBT_TRANSPOR
 
     for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p_lcb++) {
         if (!p_lcb->in_use) {
+            btu_free_timer(&p_lcb->timer_entry);
+            btu_free_timer(&p_lcb->info_timer_entry);
+            btu_free_timer(&p_lcb->upda_con_timer);
+            
             memset (p_lcb, 0, sizeof (tL2C_LCB));
-
             memcpy (p_lcb->remote_bd_addr, p_bd_addr, BD_ADDR_LEN);
 
             p_lcb->in_use          = TRUE;
@@ -127,10 +130,14 @@ void l2cu_release_lcb (tL2C_LCB *p_lcb)
     p_lcb->in_use     = FALSE;
     p_lcb->is_bonding = FALSE;
 
-    /* Stop timers */
-    btu_stop_timer (&p_lcb->timer_entry);
-    btu_stop_timer (&p_lcb->info_timer_entry);
-
+    /* Stop and release timers */
+    btu_free_timer (&p_lcb->timer_entry);
+    memset(&p_lcb->timer_entry, 0, sizeof(TIMER_LIST_ENT));
+    btu_free_timer (&p_lcb->info_timer_entry);
+    memset(&p_lcb->info_timer_entry, 0, sizeof(TIMER_LIST_ENT));
+    btu_free_timer(&p_lcb->upda_con_timer);
+    memset(&p_lcb->upda_con_timer, 0, sizeof(TIMER_LIST_ENT));
+        
     /* Release any unfinished L2CAP packet on this link */
     if (p_lcb->p_hcit_rcv_acl) {
         osi_free(p_lcb->p_hcit_rcv_acl);
@@ -1476,25 +1483,24 @@ tL2C_CCB *l2cu_allocate_ccb (tL2C_LCB *p_lcb, UINT16 cid)
     memset (&p_ccb->ertm_info, 0, sizeof(tL2CAP_ERTM_INFO));
     p_ccb->peer_cfg_already_rejected = FALSE;
     p_ccb->fcr_cfg_tries         = L2CAP_MAX_FCR_CFG_TRIES;
-    p_ccb->fcrb.ack_timer.param  = (TIMER_PARAM_TYPE)p_ccb;
-
-    /* if timer is running, remove it from timer list */
-    if (p_ccb->fcrb.ack_timer.in_use) {
-        btu_stop_quick_timer (&p_ccb->fcrb.ack_timer);
-    }
 
+    /* stop and release timers */
+    btu_free_quick_timer(&p_ccb->fcrb.ack_timer);
+    memset(&p_ccb->fcrb.ack_timer, 0, sizeof(TIMER_LIST_ENT));
+    p_ccb->fcrb.ack_timer.param  = (TIMER_PARAM_TYPE)p_ccb;
+    
+    btu_free_quick_timer(&p_ccb->fcrb.mon_retrans_timer);
+    memset(&p_ccb->fcrb.mon_retrans_timer, 0, sizeof(TIMER_LIST_ENT));
     p_ccb->fcrb.mon_retrans_timer.param  = (TIMER_PARAM_TYPE)p_ccb;
 
 // btla-specific ++
     /*  CSP408639 Fix: When L2CAP send amp move channel request or receive
       * L2CEVT_AMP_MOVE_REQ do following sequence. Send channel move
       * request -> Stop retrans/monitor timer -> Change channel state to CST_AMP_MOVING. */
-    if (p_ccb->fcrb.mon_retrans_timer.in_use) {
-        btu_stop_quick_timer (&p_ccb->fcrb.mon_retrans_timer);
-    }
 // btla-specific --
+
 #if (CLASSIC_BT_INCLUDED == TRUE)
-    l2c_fcr_stop_timer (p_ccb);
+    l2c_fcr_free_timer (p_ccb);
 #endif  ///CLASSIC_BT_INCLUDED == TRUE
     p_ccb->ertm_info.preferred_mode  = L2CAP_FCR_BASIC_MODE;        /* Default mode for channel is basic mode */
     p_ccb->ertm_info.allowed_modes   = L2CAP_FCR_CHAN_OPT_BASIC;    /* Default mode for channel is basic mode */
@@ -1531,6 +1537,8 @@ tL2C_CCB *l2cu_allocate_ccb (tL2C_LCB *p_lcb, UINT16 cid)
     p_ccb->is_flushable = FALSE;
 #endif
 
+    btu_free_timer(&p_ccb->timer_entry);
+    memset(&p_ccb->timer_entry, 0, sizeof(TIMER_LIST_ENT));
     p_ccb->timer_entry.param = (TIMER_PARAM_TYPE)p_ccb;
     p_ccb->timer_entry.in_use = 0;
 
@@ -1628,9 +1636,8 @@ void l2cu_release_ccb (tL2C_CCB *p_ccb)
         btm_sec_clr_temp_auth_service (p_lcb->remote_bd_addr);
     }
 
-    /* Stop the timer */
-    btu_stop_timer (&p_ccb->timer_entry);
-
+    /* Stop and free the timer */
+    btu_free_timer (&p_ccb->timer_entry);
 
     fixed_queue_free(p_ccb->xmit_hold_q, osi_free_func);
     p_ccb->xmit_hold_q = NULL;
index 9194583e5d78e654f064ba2b34d7b3718136957f..35a706ce21d087621bbd8204100ccfdbe87a39db 100644 (file)
@@ -296,8 +296,10 @@ tRFC_MCB  *rfc_alloc_multiplexer_channel (BD_ADDR bd_addr, BOOLEAN is_initiator)
 extern void      rfc_release_multiplexer_channel (tRFC_MCB *p_rfc_mcb);
 extern void      rfc_timer_start (tRFC_MCB *p_rfc_mcb, UINT16 timeout);
 extern void      rfc_timer_stop (tRFC_MCB *p_rfc_mcb);
+extern void      rfc_timer_free (tRFC_MCB *p_rfc_mcb);
 extern void      rfc_port_timer_start (tPORT *p_port, UINT16 tout);
 extern void      rfc_port_timer_stop (tPORT *p_port);
+extern void      rfc_port_timer_free (tPORT *p_port);
 
 BOOLEAN   rfc_check_uih_fcs (UINT8 dlci, UINT8 received_fcs);
 BOOLEAN   rfc_check_fcs (UINT16 len, UINT8 *p, UINT8 received_fcs);
index 19e208839b0de8f98a5e0516eb0658f311b813a8..a4f6c05ff3f5eaa825c30404a0d21c211f109ced 100644 (file)
@@ -265,6 +265,7 @@ void port_release_port (tPORT *p_port)
             memcpy (p_port->bd_addr, BT_BD_ANY, BD_ADDR_LEN);
         } else {
             RFCOMM_TRACE_DEBUG ("port_release_port:Clean-up handle:%d", p_port->inx);
+            rfc_port_timer_free (p_port);
             memset (p_port, 0, sizeof (tPORT));
         }
     }
@@ -568,4 +569,4 @@ void port_flow_control_peer(tPORT *p_port, BOOLEAN enable, UINT16 count)
 }
 
 
-#endif ///(defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE)
\ No newline at end of file
+#endif ///(defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE)
index 6521fb4847de2786490f1af0b9776c4c837c4d7e..7214ffdd856f5cd6fb9c815d09cd655d9565c4bf 100644 (file)
@@ -169,6 +169,7 @@ tRFC_MCB *rfc_alloc_multiplexer_channel (BD_ADDR bd_addr, BOOLEAN is_initiator)
         if (rfc_cb.port.rfc_mcb[j].state == RFC_MX_STATE_IDLE) {
             /* New multiplexer control block */
             fixed_queue_free(p_mcb->cmd_q, NULL);
+            rfc_timer_free(p_mcb);
             memset (p_mcb, 0, sizeof (tRFC_MCB));
             memcpy (p_mcb->bd_addr, bd_addr, BD_ADDR_LEN);
             RFCOMM_TRACE_DEBUG("rfc_alloc_multiplexer_channel:is_initiator:%d, create new p_mcb:%p, index:%d",
@@ -201,7 +202,7 @@ void osi_free_fun(void *p){
 void rfc_release_multiplexer_channel (tRFC_MCB *p_mcb)
 {
 
-    rfc_timer_stop (p_mcb);
+    rfc_timer_free (p_mcb);
 
     fixed_queue_free(p_mcb->cmd_q, osi_free_fun);
 
@@ -228,7 +229,6 @@ void rfc_timer_start (tRFC_MCB *p_mcb, UINT16 timeout)
     btu_start_timer (p_tle, BTU_TTYPE_RFCOMM_MFC, timeout);
 }
 
-
 /*******************************************************************************
 **
 ** Function         rfc_timer_stop
@@ -243,6 +243,20 @@ void rfc_timer_stop (tRFC_MCB *p_mcb)
     btu_stop_timer (&p_mcb->tle);
 }
 
+/*******************************************************************************
+**
+** Function         rfc_timer_free
+**
+** Description      Stop and free RFC Timer
+**
+*******************************************************************************/
+void rfc_timer_free (tRFC_MCB *p_mcb)
+{
+    RFCOMM_TRACE_EVENT ("rfc_timer_free");
+
+    btu_free_timer (&p_mcb->tle);
+    memset(&p_mcb->tle, 0, sizeof(TIMER_LIST_ENT));
+}
 
 /*******************************************************************************
 **
@@ -262,7 +276,6 @@ void rfc_port_timer_start (tPORT *p_port, UINT16 timeout)
     btu_start_timer (p_tle, BTU_TTYPE_RFCOMM_PORT, timeout);
 }
 
-
 /*******************************************************************************
 **
 ** Function         rfc_port_timer_stop
@@ -274,9 +287,23 @@ void rfc_port_timer_stop (tPORT *p_port)
 {
     RFCOMM_TRACE_EVENT ("rfc_port_timer_stop");
 
-    btu_free_timer (&p_port->rfc.tle);
+    btu_stop_timer (&p_port->rfc.tle);
 }
 
+/*******************************************************************************
+**
+** Function         rfc_port_timer_free
+**
+** Description      Stop and free RFC Timer
+**
+*******************************************************************************/
+void rfc_port_timer_free (tPORT *p_port)
+{
+    RFCOMM_TRACE_EVENT ("rfc_port_timer_stop");
+
+    btu_free_timer (&p_port->rfc.tle);
+    memset(&p_port->rfc.tle, 0, sizeof(TIMER_LIST_ENT));
+}
 
 /*******************************************************************************
 **
@@ -480,4 +507,4 @@ void rfc_check_send_cmd(tRFC_MCB *p_mcb, BT_HDR *p_buf)
 }
 
 
-#endif ///(defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE)
\ No newline at end of file
+#endif ///(defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE)
index 223c27400336a64f559ffa81f1c06dd0f29527bb..271013fca7e6d54291ce4bedeb6fb6209942f7f1 100644 (file)
@@ -119,6 +119,7 @@ tCONN_CB *sdpu_allocate_ccb (void)
     /* Look through each connection control block for a free one */
     for (xx = 0, p_ccb = sdp_cb.ccb; xx < SDP_MAX_CONNECTIONS; xx++, p_ccb++) {
         if (p_ccb->con_state == SDP_STATE_IDLE) {
+            btu_free_timer(&p_ccb->timer_entry);
             memset (p_ccb, 0, sizeof (tCONN_CB));
 
             p_ccb->timer_entry.param = (UINT32) p_ccb;
@@ -143,8 +144,8 @@ tCONN_CB *sdpu_allocate_ccb (void)
 *******************************************************************************/
 void sdpu_release_ccb (tCONN_CB *p_ccb)
 {
-    /* Ensure timer is stopped */
-    btu_stop_timer (&p_ccb->timer_entry);
+    /* Ensure timer is stopped and released */
+    btu_free_timer(&p_ccb->timer_entry);
 
     /* Drop any response pointer we may be holding */
     p_ccb->con_state = SDP_STATE_IDLE;
index 429496d568d4dd052d9659545dd3f0936d5529ca..0575731e349ca54b46cfc084a752790c1ddcdbba 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 429496d568d4dd052d9659545dd3f0936d5529ca
+Subproject commit 0575731e349ca54b46cfc084a752790c1ddcdbba
index 64bf1f98600f4da87756b0e9c2c1f926eabf7b05..91bac6313ea1f68512ff7331b5def3fa2052c1b7 100644 (file)
@@ -105,30 +105,35 @@ template<> int SlowInit<2>::mInitBy = -1;
 template<> int SlowInit<2>::mInitCount = 0;
 
 template<int obj>
-static void start_slow_init_task(int id, int affinity)
+static int start_slow_init_task(int id, int affinity)
 {
-    xTaskCreatePinnedToCore(&SlowInit<obj>::task, "slow_init", 2048,
-            reinterpret_cast<void*>(id), 3, NULL, affinity);
+    return xTaskCreatePinnedToCore(&SlowInit<obj>::task, "slow_init", 2048,
+            reinterpret_cast<void*>(id), 3, NULL, affinity) ? 1 : 0;
 }
 
 TEST_CASE("static initialization guards work as expected", "[cxx]")
 {
     s_slow_init_sem = xSemaphoreCreateCounting(10, 0);
     TEST_ASSERT_NOT_NULL(s_slow_init_sem);
+    int task_count = 0;
     // four tasks competing for static initialization of one object
-    start_slow_init_task<1>(0, PRO_CPU_NUM);
-    start_slow_init_task<1>(1, APP_CPU_NUM);
-    start_slow_init_task<1>(2, PRO_CPU_NUM);
-    start_slow_init_task<1>(3, tskNO_AFFINITY);
+    task_count += start_slow_init_task<1>(0, PRO_CPU_NUM);
+#if portNUM_PROCESSORS == 2
+    task_count += start_slow_init_task<1>(1, APP_CPU_NUM);
+#endif
+    task_count += start_slow_init_task<1>(2, PRO_CPU_NUM);
+    task_count += start_slow_init_task<1>(3, tskNO_AFFINITY);
 
     // four tasks competing for static initialization of another object
-    start_slow_init_task<2>(0, PRO_CPU_NUM);
-    start_slow_init_task<2>(1, APP_CPU_NUM);
-    start_slow_init_task<2>(2, PRO_CPU_NUM);
-    start_slow_init_task<2>(3, tskNO_AFFINITY);
+    task_count += start_slow_init_task<2>(0, PRO_CPU_NUM);
+#if portNUM_PROCESSORS == 2
+    task_count += start_slow_init_task<2>(1, APP_CPU_NUM);
+#endif
+    task_count += start_slow_init_task<2>(2, PRO_CPU_NUM);
+    task_count += start_slow_init_task<2>(3, tskNO_AFFINITY);
 
     // All tasks should
-    for (int i = 0; i < 8; ++i) {
+    for (int i = 0; i < task_count; ++i) {
         TEST_ASSERT_TRUE(xSemaphoreTake(s_slow_init_sem, 500/portTICK_PERIOD_MS));
     }
     vSemaphoreDelete(s_slow_init_sem);
index 31f177a771eb5c44db8d7c1c65f1226db061c546..02c9e64f46388c317d4b0515aeafafe92dadc6ae 100644 (file)
@@ -30,46 +30,46 @@ static const char* GPIO_TAG = "gpio";
     }
 
 const uint32_t GPIO_PIN_MUX_REG[GPIO_PIN_COUNT] = {
-    GPIO_PIN_REG_0,
-    GPIO_PIN_REG_1,
-    GPIO_PIN_REG_2,
-    GPIO_PIN_REG_3,
-    GPIO_PIN_REG_4,
-    GPIO_PIN_REG_5,
-    GPIO_PIN_REG_6,
-    GPIO_PIN_REG_7,
-    GPIO_PIN_REG_8,
-    GPIO_PIN_REG_9,
-    GPIO_PIN_REG_10,
-    GPIO_PIN_REG_11,
-    GPIO_PIN_REG_12,
-    GPIO_PIN_REG_13,
-    GPIO_PIN_REG_14,
-    GPIO_PIN_REG_15,
-    GPIO_PIN_REG_16,
-    GPIO_PIN_REG_17,
-    GPIO_PIN_REG_18,
-    GPIO_PIN_REG_19,
+    IO_MUX_GPIO0_REG,
+    IO_MUX_GPIO1_REG,
+    IO_MUX_GPIO2_REG,
+    IO_MUX_GPIO3_REG,
+    IO_MUX_GPIO4_REG,
+    IO_MUX_GPIO5_REG,
+    IO_MUX_GPIO6_REG,
+    IO_MUX_GPIO7_REG,
+    IO_MUX_GPIO8_REG,
+    IO_MUX_GPIO9_REG,
+    IO_MUX_GPIO10_REG,
+    IO_MUX_GPIO11_REG,
+    IO_MUX_GPIO12_REG,
+    IO_MUX_GPIO13_REG,
+    IO_MUX_GPIO14_REG,
+    IO_MUX_GPIO15_REG,
+    IO_MUX_GPIO16_REG,
+    IO_MUX_GPIO17_REG,
+    IO_MUX_GPIO18_REG,
+    IO_MUX_GPIO19_REG,
     0,
-    GPIO_PIN_REG_21,
-    GPIO_PIN_REG_22,
-    GPIO_PIN_REG_23,
+    IO_MUX_GPIO21_REG,
+    IO_MUX_GPIO22_REG,
+    IO_MUX_GPIO23_REG,
     0,
-    GPIO_PIN_REG_25,
-    GPIO_PIN_REG_26,
-    GPIO_PIN_REG_27,
+    IO_MUX_GPIO25_REG,
+    IO_MUX_GPIO26_REG,
+    IO_MUX_GPIO27_REG,
     0,
     0,
     0,
     0,
-    GPIO_PIN_REG_32,
-    GPIO_PIN_REG_33,
-    GPIO_PIN_REG_34,
-    GPIO_PIN_REG_35,
-    GPIO_PIN_REG_36,
-    GPIO_PIN_REG_37,
-    GPIO_PIN_REG_38,
-    GPIO_PIN_REG_39
+    IO_MUX_GPIO32_REG,
+    IO_MUX_GPIO33_REG,
+    IO_MUX_GPIO34_REG,
+    IO_MUX_GPIO35_REG,
+    IO_MUX_GPIO36_REG,
+    IO_MUX_GPIO37_REG,
+    IO_MUX_GPIO38_REG,
+    IO_MUX_GPIO39_REG,
 };
 
 typedef struct {
index 8cc65054b9f9befb4d2acc60d5f1de88f3cd3faf..7cace8836a19cb403bd6c78dee0a2acdd77224d9 100644 (file)
@@ -73,41 +73,41 @@ extern "C" {
 #define GPIO_SEL_38             ((uint64_t)(((uint64_t)1)<<38))  /*!< Pin 38 selected */
 #define GPIO_SEL_39             ((uint64_t)(((uint64_t)1)<<39))  /*!< Pin 39 selected */
 
-#define GPIO_PIN_REG_0          PERIPHS_IO_MUX_GPIO0_U
-#define GPIO_PIN_REG_1          PERIPHS_IO_MUX_U0TXD_U
-#define GPIO_PIN_REG_2          PERIPHS_IO_MUX_GPIO2_U
-#define GPIO_PIN_REG_3          PERIPHS_IO_MUX_U0RXD_U
-#define GPIO_PIN_REG_4          PERIPHS_IO_MUX_GPIO4_U
-#define GPIO_PIN_REG_5          PERIPHS_IO_MUX_GPIO5_U
-#define GPIO_PIN_REG_6          PERIPHS_IO_MUX_SD_CLK_U
-#define GPIO_PIN_REG_7          PERIPHS_IO_MUX_SD_DATA0_U
-#define GPIO_PIN_REG_8          PERIPHS_IO_MUX_SD_DATA1_U
-#define GPIO_PIN_REG_9          PERIPHS_IO_MUX_SD_DATA2_U
-#define GPIO_PIN_REG_10         PERIPHS_IO_MUX_SD_DATA3_U
-#define GPIO_PIN_REG_11         PERIPHS_IO_MUX_SD_CMD_U
-#define GPIO_PIN_REG_12         PERIPHS_IO_MUX_MTDI_U
-#define GPIO_PIN_REG_13         PERIPHS_IO_MUX_MTCK_U
-#define GPIO_PIN_REG_14         PERIPHS_IO_MUX_MTMS_U
-#define GPIO_PIN_REG_15         PERIPHS_IO_MUX_MTDO_U
-#define GPIO_PIN_REG_16         PERIPHS_IO_MUX_GPIO16_U
-#define GPIO_PIN_REG_17         PERIPHS_IO_MUX_GPIO17_U
-#define GPIO_PIN_REG_18         PERIPHS_IO_MUX_GPIO18_U
-#define GPIO_PIN_REG_19         PERIPHS_IO_MUX_GPIO19_U
-#define GPIO_PIN_REG_20         PERIPHS_IO_MUX_GPIO20_U
-#define GPIO_PIN_REG_21         PERIPHS_IO_MUX_GPIO21_U
-#define GPIO_PIN_REG_22         PERIPHS_IO_MUX_GPIO22_U
-#define GPIO_PIN_REG_23         PERIPHS_IO_MUX_GPIO23_U
-#define GPIO_PIN_REG_25         PERIPHS_IO_MUX_GPIO25_U
-#define GPIO_PIN_REG_26         PERIPHS_IO_MUX_GPIO26_U
-#define GPIO_PIN_REG_27         PERIPHS_IO_MUX_GPIO27_U
-#define GPIO_PIN_REG_32         PERIPHS_IO_MUX_GPIO32_U
-#define GPIO_PIN_REG_33         PERIPHS_IO_MUX_GPIO33_U
-#define GPIO_PIN_REG_34         PERIPHS_IO_MUX_GPIO34_U
-#define GPIO_PIN_REG_35         PERIPHS_IO_MUX_GPIO35_U
-#define GPIO_PIN_REG_36         PERIPHS_IO_MUX_GPIO36_U
-#define GPIO_PIN_REG_37         PERIPHS_IO_MUX_GPIO37_U
-#define GPIO_PIN_REG_38         PERIPHS_IO_MUX_GPIO38_U
-#define GPIO_PIN_REG_39         PERIPHS_IO_MUX_GPIO39_U
+#define GPIO_PIN_REG_0          IO_MUX_GPIO0_REG
+#define GPIO_PIN_REG_1          IO_MUX_GPIO1_REG
+#define GPIO_PIN_REG_2          IO_MUX_GPIO2_REG
+#define GPIO_PIN_REG_3          IO_MUX_GPIO3_REG
+#define GPIO_PIN_REG_4          IO_MUX_GPIO4_REG
+#define GPIO_PIN_REG_5          IO_MUX_GPIO5_REG
+#define GPIO_PIN_REG_6          IO_MUX_GPIO6_REG
+#define GPIO_PIN_REG_7          IO_MUX_GPIO7_REG
+#define GPIO_PIN_REG_8          IO_MUX_GPIO8_REG
+#define GPIO_PIN_REG_9          IO_MUX_GPIO9_REG
+#define GPIO_PIN_REG_10         IO_MUX_GPIO10_REG
+#define GPIO_PIN_REG_11         IO_MUX_GPIO11_REG
+#define GPIO_PIN_REG_12         IO_MUX_GPIO12_REG
+#define GPIO_PIN_REG_13         IO_MUX_GPIO13_REG
+#define GPIO_PIN_REG_14         IO_MUX_GPIO14_REG
+#define GPIO_PIN_REG_15         IO_MUX_GPIO15_REG
+#define GPIO_PIN_REG_16         IO_MUX_GPIO16_REG
+#define GPIO_PIN_REG_17         IO_MUX_GPIO17_REG
+#define GPIO_PIN_REG_18         IO_MUX_GPIO18_REG
+#define GPIO_PIN_REG_19         IO_MUX_GPIO19_REG
+#define GPIO_PIN_REG_20         IO_MUX_GPIO20_REG
+#define GPIO_PIN_REG_21         IO_MUX_GPIO21_REG
+#define GPIO_PIN_REG_22         IO_MUX_GPIO22_REG
+#define GPIO_PIN_REG_23         IO_MUX_GPIO23_REG
+#define GPIO_PIN_REG_25         IO_MUX_GPIO25_REG
+#define GPIO_PIN_REG_26         IO_MUX_GPIO26_REG
+#define GPIO_PIN_REG_27         IO_MUX_GPIO27_REG
+#define GPIO_PIN_REG_32         IO_MUX_GPIO32_REG
+#define GPIO_PIN_REG_33         IO_MUX_GPIO33_REG
+#define GPIO_PIN_REG_34         IO_MUX_GPIO34_REG
+#define GPIO_PIN_REG_35         IO_MUX_GPIO35_REG
+#define GPIO_PIN_REG_36         IO_MUX_GPIO36_REG
+#define GPIO_PIN_REG_37         IO_MUX_GPIO37_REG
+#define GPIO_PIN_REG_38         IO_MUX_GPIO38_REG
+#define GPIO_PIN_REG_39         IO_MUX_GPIO39_REG
 
 #define GPIO_APP_CPU_INTR_ENA      (BIT(0))
 #define GPIO_APP_CPU_NMI_INTR_ENA  (BIT(1))
index 63023e90302a7f8bc4fff7b1c461a11962afa85b..c298889ed3c6275beaf8cfed070b8f2801fdd00f 100644 (file)
@@ -39,6 +39,7 @@ extern "C" {
     .io_voltage = 3.3f, \
     .init = &sdmmc_host_init, \
     .set_bus_width = &sdmmc_host_set_bus_width, \
+    .get_bus_width = &sdmmc_host_get_slot_width, \
     .set_card_clk = &sdmmc_host_set_card_clk, \
     .do_transaction = &sdmmc_host_do_transaction, \
     .deinit = &sdmmc_host_deinit, \
@@ -115,6 +116,14 @@ esp_err_t sdmmc_host_init_slot(int slot, const sdmmc_slot_config_t* slot_config)
  */
 esp_err_t sdmmc_host_set_bus_width(int slot, size_t width);
 
+/**
+ * @brief Get bus width configured in ``sdmmc_host_init_slot`` to be used for data transfer
+ *
+ * @param slot  slot number (SDMMC_HOST_SLOT_0 or SDMMC_HOST_SLOT_1)
+ * @return configured bus width of the specified slot.
+ */
+size_t sdmmc_host_get_slot_width(int slot);
+
 /**
  * @brief Set card clock frequency
  *
index 835eaa3fb71b51bd9d327c82c61bc61a0cd45948..cece4174ef7f67e8aa4c4140507c09e6654df6c1 100644 (file)
@@ -125,6 +125,7 @@ typedef struct {
     float io_voltage;           /*!< I/O voltage used by the controller (voltage switching is not supported) */
     esp_err_t (*init)(void);    /*!< Host function to initialize the driver */
     esp_err_t (*set_bus_width)(int slot, size_t width);    /*!< host function to set bus width */
+    size_t (*get_bus_width)(int slot); /*!< host function to get bus width */
     esp_err_t (*set_card_clk)(int slot, uint32_t freq_khz); /*!< host function to set card clock frequency */
     esp_err_t (*do_transaction)(int slot, sdmmc_command_t* cmdinfo);    /*!< host function to do a transaction */
     esp_err_t (*deinit)(void);  /*!< host function to deinitialize the driver */
index e233df0a5ddf0e762e10cdc2cbb630e31ba346b0..4c297a58f75bb0ab2703db57f5ecef9d11b8942b 100644 (file)
@@ -40,6 +40,7 @@ typedef struct {
     uint32_t d5;
     uint32_t d6;
     uint32_t d7;
+    uint8_t d3_gpio;
     uint8_t card_detect;
     uint8_t write_protect;
     uint8_t width;
@@ -57,6 +58,7 @@ static const sdmmc_slot_info_t s_slot_info[2]  = {
         .d1 = PERIPHS_IO_MUX_SD_DATA1_U,
         .d2 = PERIPHS_IO_MUX_SD_DATA2_U,
         .d3 = PERIPHS_IO_MUX_SD_DATA3_U,
+        .d3_gpio = 10,
         .d4 = PERIPHS_IO_MUX_GPIO16_U,
         .d5 = PERIPHS_IO_MUX_GPIO17_U,
         .d6 = PERIPHS_IO_MUX_GPIO5_U,
@@ -72,6 +74,7 @@ static const sdmmc_slot_info_t s_slot_info[2]  = {
         .d1 = PERIPHS_IO_MUX_GPIO4_U,
         .d2 = PERIPHS_IO_MUX_MTDI_U,
         .d3 = PERIPHS_IO_MUX_MTCK_U,
+        .d3_gpio = 13,
         .card_detect = HOST_CARD_DETECT_N_2_IDX,
         .write_protect = HOST_CARD_WRITE_PRT_2_IDX,
         .width = 4
@@ -82,6 +85,7 @@ static const char* TAG = "sdmmc_periph";
 static intr_handle_t s_intr_handle;
 static QueueHandle_t s_event_queue;
 
+size_t s_slot_width[2] = {1,1};
 
 void sdmmc_host_reset()
 {
@@ -324,14 +328,25 @@ esp_err_t sdmmc_host_init_slot(int slot, const sdmmc_slot_config_t* slot_config)
     else if (slot_width > pslot->width) {
         return ESP_ERR_INVALID_ARG;
     }
+    s_slot_width[slot] = slot_width;
 
     configure_pin(pslot->clk);
     configure_pin(pslot->cmd);
     configure_pin(pslot->d0);
+
     if (slot_width >= 4) {
         configure_pin(pslot->d1);
         configure_pin(pslot->d2);
-        configure_pin(pslot->d3);
+        //force pull-up D3 to make slave detect SD mode. connect to peripheral after width configuration.
+        gpio_config_t gpio_conf = {
+            .pin_bit_mask = BIT(pslot->d3_gpio),
+            .mode = GPIO_MODE_OUTPUT ,
+            .pull_up_en = 0,
+            .pull_down_en = 0,
+            .intr_type = GPIO_INTR_DISABLE,
+        };
+        gpio_config( &gpio_conf );
+        gpio_set_level( pslot->d3_gpio, 1 );
         if (slot_width == 8) {
             configure_pin(pslot->d4);
             configure_pin(pslot->d5);
@@ -404,8 +419,10 @@ esp_err_t sdmmc_host_set_bus_width(int slot, size_t width)
     } else if (width == 4) {
         SDMMC.ctype.card_width_8 &= ~mask;
         SDMMC.ctype.card_width |= mask;
+        configure_pin(s_slot_info[slot].d3);   // D3 was set to GPIO high to force slave into SD 1-bit mode, until 4-bit mode is set
     } else if (width == 8){
         SDMMC.ctype.card_width_8 |= mask;
+        configure_pin(s_slot_info[slot].d3);   // D3 was set to GPIO high to force slave into SD 1-bit mode, until 4-bit mode is set
     } else {
         return ESP_ERR_INVALID_ARG;
     }
@@ -413,6 +430,12 @@ esp_err_t sdmmc_host_set_bus_width(int slot, size_t width)
     return ESP_OK;
 }
 
+size_t sdmmc_host_get_slot_width(int slot)
+{
+    assert( slot == 0 || slot == 1 );
+    return s_slot_width[slot];
+}
+
 static void sdmmc_host_dma_init()
 {
     SDMMC.ctrl.dma_enable = 1;
index 815f5c79a9d75d40774fb83b81f1eed83b631889..78ca44bac25d5dd90346db15097ffc2b713b0eed 100644 (file)
@@ -40,7 +40,7 @@ static esp_err_t event_handler(void *ctx, system_event_t *event)
     return ESP_OK;
 }
 
-TEST_CASE("adc2 work with wifi","[adc]")
+TEST_CASE("adc2 work with wifi","[adc][ignore]")
 {
     int     read_raw;
     int     target_value;
index cb6dfc3a1f0189ffd5c60b1a2b26e24f1dd4503b..3e44b2639dc2ebe69a72ade1a26e4a4eec2841a5 100644 (file)
@@ -23,6 +23,7 @@ extern "C"
 
 #define ESP_PARTITION_TABLE_ADDR 0x8000
 #define ESP_PARTITION_MAGIC 0x50AA
+#define ESP_PARTITION_MAGIC_MD5 0xEBEB
 
 /* OTA selection structure (two copies in the OTA data partition.)
    Size of 32 bytes is friendly to flash encryption */
index 32fc089d31dbced647d07224e707563dcbde1eec..79262bb9acb77e615657e94763a6bcaea63ddb4e 100644 (file)
@@ -89,7 +89,6 @@ SECTIONS
     *libesp32.a:core_dump.o(.literal .text .literal.* .text.*)
     *libapp_trace.a:(.literal .text .literal.* .text.*)
     *libxtensa-debug-module.a:eri.o(.literal .text .literal.* .text.*)
-    *libphy.a:(.literal .text .literal.* .text.*)
     *librtc.a:(.literal .text .literal.* .text.*)
     *libsoc.a:(.literal .text .literal.* .text.*)
     *libhal.a:(.literal .text .literal.* .text.*)
index b35fc86bbc5f96e8d52cc32527dbfbabaed0d6fc..32402930b3b2d28bf894cba918d08446b04a38e6 160000 (submodule)
@@ -1 +1 @@
-Subproject commit b35fc86bbc5f96e8d52cc32527dbfbabaed0d6fc
+Subproject commit 32402930b3b2d28bf894cba918d08446b04a38e6
index e4339a12981cf3091a523efc1bd2c34bb2403267..6bd52aed98c6516d342178d67d553c238da2162b 100644 (file)
@@ -9,7 +9,7 @@
 
 
 /* declare the performance here */
-#define IDF_PERFORMANCE_MAX_HTTPS_REQUEST_BIN_SIZE                              610
+#define IDF_PERFORMANCE_MAX_HTTPS_REQUEST_BIN_SIZE                              800
 #define IDF_PERFORMANCE_MAX_FREERTOS_SPINLOCK_CYCLES_PER_OP                     200
 #define IDF_PERFORMANCE_MAX_FREERTOS_SPINLOCK_CYCLES_PER_OP_UNICORE             130
 #define IDF_PERFORMANCE_MAX_ESP_TIMER_GET_TIME_PER_CALL                         1000
diff --git a/components/json/LICENSE b/components/json/LICENSE
deleted file mode 100644 (file)
index fa0a438..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-  Copyright (c) 2009 Dave Gamble
-  Permission is hereby granted, free of charge, to any person obtaining a copy
-  of this software and associated documentation files (the "Software"), to deal
-  in the Software without restriction, including without limitation the rights
-  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-  copies of the Software, and to permit persons to whom the Software is
-  furnished to do so, subject to the following conditions:
-  The above copyright notice and this permission notice shall be included in
-  all copies or substantial portions of the Software.
-  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-  THE SOFTWARE.
-
diff --git a/components/json/cJSON b/components/json/cJSON
new file mode 160000 (submodule)
index 0000000..7cc52f6
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit 7cc52f60356909b3dd260304c7c50c0693699353
index 2dd6ea8b87dc89ff53146b34bd2ea6d21723dd32..a6a424e6db975ad1c5efd1967a2b82e80cd6ca48 100644 (file)
@@ -1,7 +1,6 @@
 #
 # Component Makefile
 #
-COMPONENT_ADD_INCLUDEDIRS := include port/include
-
-COMPONENT_SRCDIRS := library port
-
+COMPONENT_ADD_INCLUDEDIRS := cJSON
+COMPONENT_SRCDIRS := cJSON
+COMPONENT_SUBMODULES := cJSON
diff --git a/components/json/include/cJSON.h b/components/json/include/cJSON.h
deleted file mode 100644 (file)
index 7c4f8e7..0000000
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
-  Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
-
-  Permission is hereby granted, free of charge, to any person obtaining a copy
-  of this software and associated documentation files (the "Software"), to deal
-  in the Software without restriction, including without limitation the rights
-  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-  copies of the Software, and to permit persons to whom the Software is
-  furnished to do so, subject to the following conditions:
-
-  The above copyright notice and this permission notice shall be included in
-  all copies or substantial portions of the Software.
-
-  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-  THE SOFTWARE.
-*/
-
-#ifndef cJSON__h
-#define cJSON__h
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
-/* project version */
-#define CJSON_VERSION_MAJOR 1
-#define CJSON_VERSION_MINOR 6
-#define CJSON_VERSION_PATCH 0
-
-#include <stddef.h>
-
-/* cJSON Types: */
-#define cJSON_Invalid (0)
-#define cJSON_False  (1 << 0)
-#define cJSON_True   (1 << 1)
-#define cJSON_NULL   (1 << 2)
-#define cJSON_Number (1 << 3)
-#define cJSON_String (1 << 4)
-#define cJSON_Array  (1 << 5)
-#define cJSON_Object (1 << 6)
-#define cJSON_Raw    (1 << 7) /* raw json */
-
-#define cJSON_IsReference 256
-#define cJSON_StringIsConst 512
-
-/* The cJSON structure: */
-typedef struct cJSON
-{
-    /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
-    struct cJSON *next;
-    struct cJSON *prev;
-    /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
-    struct cJSON *child;
-
-    /* The type of the item, as above. */
-    int type;
-
-    /* The item's string, if type==cJSON_String  and type == cJSON_Raw */
-    char *valuestring;
-    /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
-    int valueint;
-    /* The item's number, if type==cJSON_Number */
-    double valuedouble;
-
-    /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
-    char *string;
-} cJSON;
-
-typedef struct cJSON_Hooks
-{
-      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 */
-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);
-
-/* Render a cJSON entity to text for transfer/storage. */
-CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
-/* Render a cJSON entity to text for transfer/storage without any formatting. */
-CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
-/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
-CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
-/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
-/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
-CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
-/* Delete a cJSON entity and all subentities. */
-CJSON_PUBLIC(void) cJSON_Delete(cJSON *c);
-
-/* Returns the number of items in an array (or object). */
-CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
-/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
-CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
-/* Get item "string" from object. Case insensitive. */
-CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
-CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
-CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
-/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
-CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
-
-/* These functions check the type of an item */
-CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
-CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
-CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
-CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
-CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
-CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
-CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
-CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
-CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
-CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
-
-/* These calls create a cJSON item of the appropriate type. */
-CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
-CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
-CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
-CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
-CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
-CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
-/* raw json */
-CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
-CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
-CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
-
-/* These utilities create an Array of count items. */
-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. */
-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. */
-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. */
-CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
-CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
-CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
-CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
-CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
-CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
-CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
-
-/* Update array items. */
-CJSON_PUBLIC(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 */
-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);
-
-
-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_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, 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
-}
-#endif
-
-#endif
diff --git a/components/json/library/cJSON.c b/components/json/library/cJSON.c
deleted file mode 100644 (file)
index cef719f..0000000
+++ /dev/null
@@ -1,2754 +0,0 @@
-/*
-  Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
-
-  Permission is hereby granted, free of charge, to any person obtaining a copy
-  of this software and associated documentation files (the "Software"), to deal
-  in the Software without restriction, including without limitation the rights
-  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-  copies of the Software, and to permit persons to whom the Software is
-  furnished to do so, subject to the following conditions:
-
-  The above copyright notice and this permission notice shall be included in
-  all copies or substantial portions of the Software.
-
-  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-  THE SOFTWARE.
-*/
-
-/* cJSON */
-/* JSON parser in C. */
-
-/* disable warnings about old C89 functions in MSVC */
-#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER)
-#define _CRT_SECURE_NO_DEPRECATE
-#endif
-
-#ifdef __GNUC__
-#pragma GCC visibility push(default)
-#endif
-#if defined(_MSC_VER)
-#pragma warning (push)
-/* disable warning about single line comments in system headers */
-#pragma warning (disable : 4001)
-#endif
-
-#include <string.h>
-#include <stdio.h>
-#include <math.h>
-#include <stdlib.h>
-#include <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"
-
-/* 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;
-        }
-    }
-
-    return tolower(*string1) - tolower(*string2);
-}
-
-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)
-{
-    return realloc(pointer, size);
-}
-#else
-#define internal_malloc malloc
-#define internal_free free
-#define internal_realloc realloc
-#endif
-
-static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc };
-
-static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks)
-{
-    size_t length = 0;
-    unsigned char *copy = NULL;
-
-    if (string == NULL)
-    {
-        return NULL;
-    }
-
-    length = strlen((const char*)string) + sizeof("");
-    copy = (unsigned char*)hooks->allocate(length);
-    if (copy == NULL)
-    {
-        return NULL;
-    }
-    memcpy(copy, string, length);
-
-    return copy;
-}
-
-CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks)
-{
-    if (hooks == NULL)
-    {
-        /* Reset hooks */
-        global_hooks.allocate = malloc;
-        global_hooks.deallocate = free;
-        global_hooks.reallocate = realloc;
-        return;
-    }
-
-    global_hooks.allocate = malloc;
-    if (hooks->malloc_fn != NULL)
-    {
-        global_hooks.allocate = hooks->malloc_fn;
-    }
-
-    global_hooks.deallocate = free;
-    if (hooks->free_fn != NULL)
-    {
-        global_hooks.deallocate = hooks->free_fn;
-    }
-
-    /* use realloc only if both free and malloc are used */
-    global_hooks.reallocate = NULL;
-    if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free))
-    {
-        global_hooks.reallocate = realloc;
-    }
-}
-
-/* Internal constructor. */
-static cJSON *cJSON_New_Item(const internal_hooks * const hooks)
-{
-    cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON));
-    if (node)
-    {
-        memset(node, '\0', sizeof(cJSON));
-    }
-
-    return node;
-}
-
-/* Delete a cJSON structure. */
-CJSON_PUBLIC(void) cJSON_Delete(cJSON *item)
-{
-    cJSON *next = NULL;
-    while (item != NULL)
-    {
-        next = item->next;
-        if (!(item->type & cJSON_IsReference) && (item->child != NULL))
-        {
-            cJSON_Delete(item->child);
-        }
-        if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL))
-        {
-            global_hooks.deallocate(item->valuestring);
-        }
-        if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
-        {
-            global_hooks.deallocate(item->string);
-        }
-        global_hooks.deallocate(item);
-        item = next;
-    }
-}
-
-/* get the decimal point character of the current locale */
-static unsigned char get_decimal_point(void)
-{
-#ifdef ENABLE_LOCALES
-    struct lconv *lconv = localeconv();
-    return (unsigned char) lconv->decimal_point[0];
-#else
-    return '.';
-#endif
-}
-
-typedef struct
-{
-    const unsigned char *content;
-    size_t length;
-    size_t offset;
-    size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */
-    internal_hooks hooks;
-} parse_buffer;
-
-/* check if the given size is left to read in a given parse buffer (starting with 1) */
-#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length))
-/* check if the buffer can be accessed at the given index (starting with 0) */
-#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length))
-#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index))
-/* get a pointer to the buffer at the position */
-#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset)
-
-/* Parse the input text to generate a number, and populate the result into item. */
-static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer)
-{
-    double number = 0;
-    unsigned char *after_end = NULL;
-    unsigned char number_c_string[64];
-    unsigned char decimal_point = get_decimal_point();
-    size_t i = 0;
-
-    if ((input_buffer == NULL) || (input_buffer->content == NULL))
-    {
-        return false;
-    }
-
-    /* copy the number into a temporary buffer and replace '.' with the decimal point
-     * of the current locale (for strtod)
-     * This also takes care of '\0' not necessarily being available for marking the end of the input */
-    for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++)
-    {
-        switch (buffer_at_offset(input_buffer)[i])
-        {
-            case '0':
-            case '1':
-            case '2':
-            case '3':
-            case '4':
-            case '5':
-            case '6':
-            case '7':
-            case '8':
-            case '9':
-            case '+':
-            case '-':
-            case 'e':
-            case 'E':
-                number_c_string[i] = buffer_at_offset(input_buffer)[i];
-                break;
-
-            case '.':
-                number_c_string[i] = decimal_point;
-                break;
-
-            default:
-                goto loop_end;
-        }
-    }
-loop_end:
-    number_c_string[i] = '\0';
-
-    number = strtod((const char*)number_c_string, (char**)&after_end);
-    if (number_c_string == after_end)
-    {
-        return false; /* parse_error */
-    }
-
-    item->valuedouble = number;
-
-    /* use saturation in case of overflow */
-    if (number >= INT_MAX)
-    {
-        item->valueint = INT_MAX;
-    }
-    else if (number <= INT_MIN)
-    {
-        item->valueint = INT_MIN;
-    }
-    else
-    {
-        item->valueint = (int)number;
-    }
-
-    item->type = cJSON_Number;
-
-    input_buffer->offset += (size_t)(after_end - number_c_string);
-    return true;
-}
-
-/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */
-CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number)
-{
-    if (number >= INT_MAX)
-    {
-        object->valueint = INT_MAX;
-    }
-    else if (number <= INT_MIN)
-    {
-        object->valueint = INT_MIN;
-    }
-    else
-    {
-        object->valueint = (int)number;
-    }
-
-    return object->valuedouble = number;
-}
-
-typedef struct
-{
-    unsigned char *buffer;
-    size_t length;
-    size_t offset;
-    size_t depth; /* current nesting depth (for formatted printing) */
-    cJSON_bool noalloc;
-    cJSON_bool format; /* is this print a formatted print */
-    internal_hooks hooks;
-} printbuffer;
-
-/* realloc printbuffer if necessary to have at least "needed" bytes more */
-static unsigned char* ensure(printbuffer * const p, size_t needed)
-{
-    unsigned char *newbuffer = NULL;
-    size_t newsize = 0;
-
-    if ((p == NULL) || (p->buffer == NULL))
-    {
-        return NULL;
-    }
-
-    if ((p->length > 0) && (p->offset >= p->length))
-    {
-        /* make sure that offset is valid */
-        return NULL;
-    }
-
-    if (needed > INT_MAX)
-    {
-        /* sizes bigger than INT_MAX are currently not supported */
-        return NULL;
-    }
-
-    needed += p->offset + 1;
-    if (needed <= p->length)
-    {
-        return p->buffer + p->offset;
-    }
-
-    if (p->noalloc) {
-        return NULL;
-    }
-
-    /* calculate new buffer size */
-    if (needed > (INT_MAX / 2))
-    {
-        /* overflow of int, use INT_MAX if possible */
-        if (needed <= INT_MAX)
-        {
-            newsize = INT_MAX;
-        }
-        else
-        {
-            return NULL;
-        }
-    }
-    else
-    {
-        newsize = needed * 2;
-    }
-
-    if (p->hooks.reallocate != NULL)
-    {
-        /* reallocate with realloc if available */
-        newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize);
-        if (newbuffer == NULL)
-        {
-            p->hooks.deallocate(p->buffer);
-            p->length = 0;
-            p->buffer = NULL;
-
-            return NULL;
-        }
-    }
-    else
-    {
-        /* otherwise reallocate manually */
-        newbuffer = (unsigned char*)p->hooks.allocate(newsize);
-        if (!newbuffer)
-        {
-            p->hooks.deallocate(p->buffer);
-            p->length = 0;
-            p->buffer = NULL;
-
-            return NULL;
-        }
-        if (newbuffer)
-        {
-            memcpy(newbuffer, p->buffer, p->offset + 1);
-        }
-        p->hooks.deallocate(p->buffer);
-    }
-    p->length = newsize;
-    p->buffer = newbuffer;
-
-    return newbuffer + p->offset;
-}
-
-/* calculate the new length of the string in a printbuffer and update the offset */
-static void update_offset(printbuffer * const buffer)
-{
-    const unsigned char *buffer_pointer = NULL;
-    if ((buffer == NULL) || (buffer->buffer == NULL))
-    {
-        return;
-    }
-    buffer_pointer = buffer->buffer + buffer->offset;
-
-    buffer->offset += strlen((const char*)buffer_pointer);
-}
-
-/* Render the number nicely from the given item into a string. */
-static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer)
-{
-    unsigned char *output_pointer = NULL;
-    double d = item->valuedouble;
-    int length = 0;
-    size_t i = 0;
-    unsigned char number_buffer[26]; /* 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 cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer)
-{
-    const unsigned char *input_pointer = NULL;
-    unsigned char *output = NULL;
-    unsigned char *output_pointer = NULL;
-    size_t output_length = 0;
-    /* numbers of additional characters needed for escaping */
-    size_t escape_characters = 0;
-
-    if (output_buffer == NULL)
-    {
-        return false;
-    }
-
-    /* empty string */
-    if (input == NULL)
-    {
-        output = ensure(output_buffer, sizeof("\"\""));
-        if (output == NULL)
-        {
-            return false;
-        }
-        strcpy((char*)output, "\"\"");
-
-        return true;
-    }
-
-    /* set "flag" to 1 if something needs to be escaped */
-    for (input_pointer = input; *input_pointer; input_pointer++)
-    {
-        switch (*input_pointer)
-        {
-            case '\"':
-            case '\\':
-            case '\b':
-            case '\f':
-            case '\n':
-            case '\r':
-            case '\t':
-                /* one character escape sequence */
-                escape_characters++;
-                break;
-            default:
-                if (*input_pointer < 32)
-                {
-                    /* UTF-16 escape sequence uXXXX */
-                    escape_characters += 5;
-                }
-                break;
-        }
-    }
-    output_length = (size_t)(input_pointer - input) + escape_characters;
-
-    output = ensure(output_buffer, output_length + sizeof("\"\""));
-    if (output == NULL)
-    {
-        return false;
-    }
-
-    /* no characters have to be escaped */
-    if (escape_characters == 0)
-    {
-        output[0] = '\"';
-        memcpy(output + 1, input, output_length);
-        output[output_length + 1] = '\"';
-        output[output_length + 2] = '\0';
-
-        return true;
-    }
-
-    output[0] = '\"';
-    output_pointer = output + 1;
-    /* copy the string */
-    for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++)
-    {
-        if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\'))
-        {
-            /* normal character, copy */
-            *output_pointer = *input_pointer;
-        }
-        else
-        {
-            /* character needs to be escaped */
-            *output_pointer++ = '\\';
-            switch (*input_pointer)
-            {
-                case '\\':
-                    *output_pointer = '\\';
-                    break;
-                case '\"':
-                    *output_pointer = '\"';
-                    break;
-                case '\b':
-                    *output_pointer = 'b';
-                    break;
-                case '\f':
-                    *output_pointer = 'f';
-                    break;
-                case '\n':
-                    *output_pointer = 'n';
-                    break;
-                case '\r':
-                    *output_pointer = 'r';
-                    break;
-                case '\t':
-                    *output_pointer = 't';
-                    break;
-                default:
-                    /* escape and print as unicode codepoint */
-                    sprintf((char*)output_pointer, "u%04x", *input_pointer);
-                    output_pointer += 4;
-                    break;
-            }
-        }
-    }
-    output[output_length + 1] = '\"';
-    output[output_length + 2] = '\0';
-
-    return true;
-}
-
-/* Invoke print_string_ptr (which is useful) on an item. */
-static cJSON_bool print_string(const cJSON * const item, printbuffer * const p)
-{
-    return print_string_ptr((unsigned char*)item->valuestring, p);
-}
-
-/* Predeclare these prototypes. */
-static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer);
-static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer);
-static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer);
-static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer);
-static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer);
-static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer);
-
-/* Utility to jump whitespace and cr/lf */
-static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer)
-{
-    if ((buffer == NULL) || (buffer->content == NULL))
-    {
-        return NULL;
-    }
-
-    while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32))
-    {
-       buffer->offset++;
-    }
-
-    if (buffer->offset == buffer->length)
-    {
-        buffer->offset--;
-    }
-
-    return buffer;
-}
-
-/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */
-static parse_buffer *skip_utf8_bom(parse_buffer * const buffer)
-{
-    if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0))
-    {
-        return NULL;
-    }
-
-    if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0))
-    {
-        buffer->offset += 3;
-    }
-
-    return buffer;
-}
-
-/* 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;
-    }
-
-    if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer))))
-    {
-        /* parse failure. ep is set. */
-        goto fail;
-    }
-
-    /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
-    if (require_null_terminated)
-    {
-        buffer_skip_whitespace(&buffer);
-        if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0')
-        {
-            goto fail;
-        }
-    }
-    if (return_parse_end)
-    {
-        *return_parse_end = (const char*)buffer_at_offset(&buffer);
-    }
-
-    return item;
-
-fail:
-    if (item != NULL)
-    {
-        cJSON_Delete(item);
-    }
-
-    if (value != NULL)
-    {
-        error local_error;
-        local_error.json = (const unsigned char*)value;
-        local_error.position = 0;
-
-        if (buffer.offset < buffer.length)
-        {
-            local_error.position = buffer.offset;
-        }
-        else if (buffer.length > 0)
-        {
-            local_error.position = buffer.length - 1;
-        }
-
-        if (return_parse_end != NULL)
-        {
-            *return_parse_end = (const char*)local_error.json + local_error.position;
-        }
-
-        global_error = local_error;
-    }
-
-    return NULL;
-}
-
-/* Default options for cJSON_Parse */
-CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value)
-{
-    return cJSON_ParseWithOpts(value, 0, 0);
-}
-
-#define cjson_min(a, b) ((a < b) ? a : b)
-
-static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks)
-{
-    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);
-    }
-
-    return NULL;
-}
-
-/* Render a cJSON item/entity/structure to text. */
-CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item)
-{
-    return (char*)print(item, true, &global_hooks);
-}
-
-CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item)
-{
-    return (char*)print(item, false, &global_hooks);
-}
-
-CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt)
-{
-    printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
-
-    if (prebuffer < 0)
-    {
-        return NULL;
-    }
-
-    p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer);
-    if (!p.buffer)
-    {
-        return NULL;
-    }
-
-    p.length = (size_t)prebuffer;
-    p.offset = 0;
-    p.noalloc = false;
-    p.format = fmt;
-    p.hooks = global_hooks;
-
-    if (!print_value(item, &p))
-    {
-        global_hooks.deallocate(p.buffer);
-        return NULL;
-    }
-
-    return (char*)p.buffer;
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *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);
-}
-
-/* Parser core - when encountering text, process appropriately. */
-static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer)
-{
-    if ((input_buffer == NULL) || (input_buffer->content == NULL))
-    {
-        return false; /* no input */
-    }
-
-    /* parse the different types of values */
-    /* null */
-    if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0))
-    {
-        item->type = cJSON_NULL;
-        input_buffer->offset += 4;
-        return true;
-    }
-    /* false */
-    if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0))
-    {
-        item->type = cJSON_False;
-        input_buffer->offset += 5;
-        return true;
-    }
-    /* true */
-    if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0))
-    {
-        item->type = cJSON_True;
-        item->valueint = 1;
-        input_buffer->offset += 4;
-        return true;
-    }
-    /* string */
-    if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"'))
-    {
-        return parse_string(item, input_buffer);
-    }
-    /* number */
-    if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9'))))
-    {
-        return parse_number(item, input_buffer);
-    }
-    /* array */
-    if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '['))
-    {
-        return parse_array(item, input_buffer);
-    }
-    /* object */
-    if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{'))
-    {
-        return parse_object(item, input_buffer);
-    }
-
-    return false;
-}
-
-/* Render a value to text. */
-static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer)
-{
-    unsigned char *output = NULL;
-
-    if ((item == NULL) || (output_buffer == NULL))
-    {
-        return false;
-    }
-
-    switch ((item->type) & 0xFF)
-    {
-        case cJSON_NULL:
-            output = ensure(output_buffer, 5);
-            if (output == NULL)
-            {
-                return false;
-            }
-            strcpy((char*)output, "null");
-            return true;
-
-        case cJSON_False:
-            output = ensure(output_buffer, 6);
-            if (output == NULL)
-            {
-                return false;
-            }
-            strcpy((char*)output, "false");
-            return true;
-
-        case cJSON_True:
-            output = ensure(output_buffer, 5);
-            if (output == NULL)
-            {
-                return false;
-            }
-            strcpy((char*)output, "true");
-            return true;
-
-        case cJSON_Number:
-            return print_number(item, output_buffer);
-
-        case cJSON_Raw:
-        {
-            size_t raw_length = 0;
-            if (item->valuestring == NULL)
-            {
-                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;
-    }
-}
-
-/* Build an array from input text. */
-static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer)
-{
-    cJSON *head = NULL; /* head of the linked list */
-    cJSON *current_item = NULL;
-
-    if (input_buffer->depth >= CJSON_NESTING_LIMIT)
-    {
-        return false; /* to deeply nested */
-    }
-    input_buffer->depth++;
-
-    if (buffer_at_offset(input_buffer)[0] != '[')
-    {
-        /* not an array */
-        goto fail;
-    }
-
-    input_buffer->offset++;
-    buffer_skip_whitespace(input_buffer);
-    if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']'))
-    {
-        /* empty array */
-        goto success;
-    }
-
-    /* check if we skipped to the end of the buffer */
-    if (cannot_access_at_index(input_buffer, 0))
-    {
-        input_buffer->offset--;
-        goto fail;
-    }
-
-    /* step back to character in front of the first element */
-    input_buffer->offset--;
-    /* loop through the comma separated array elements */
-    do
-    {
-        /* allocate next item */
-        cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
-        if (new_item == NULL)
-        {
-            goto fail; /* allocation failure */
-        }
-
-        /* attach next item to list */
-        if (head == NULL)
-        {
-            /* start the linked list */
-            current_item = head = new_item;
-        }
-        else
-        {
-            /* add to the end and advance */
-            current_item->next = new_item;
-            new_item->prev = current_item;
-            current_item = new_item;
-        }
-
-        /* parse next value */
-        input_buffer->offset++;
-        buffer_skip_whitespace(input_buffer);
-        if (!parse_value(current_item, input_buffer))
-        {
-            goto fail; /* failed to parse value */
-        }
-        buffer_skip_whitespace(input_buffer);
-    }
-    while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
-
-    if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']')
-    {
-        goto fail; /* expected end of array */
-    }
-
-success:
-    input_buffer->depth--;
-
-    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);
-}
diff --git a/components/json/port/cJSON_Utils.c b/components/json/port/cJSON_Utils.c
deleted file mode 100644 (file)
index b83cfcd..0000000
+++ /dev/null
@@ -1,1443 +0,0 @@
-/*
-  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"
-
-/* 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 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);
-}
diff --git a/components/json/port/include/cJSON_Utils.h b/components/json/port/include/cJSON_Utils.h
deleted file mode 100644 (file)
index 03ec10c..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
-  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_PUBLIC(cJSON *) cJSONUtils_GetPointer(cJSON * const object, const char *pointer);
-CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointerCaseSensitive(cJSON * const object, const char *pointer);
-
-/* Implement RFC6902 (https://tools.ietf.org/html/rfc6902) JSON Patch spec. */
-/* NOTE: This modifies objects in 'from' and 'to' by sorting the elements by their key */
-CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON * const from, cJSON * const to);
-CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatchesCaseSensitive(cJSON * const from, cJSON * const to);
-/* Utility for generating patch array entries. */
-CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON * const array, const char * const operation, const char * const path, const cJSON * const value);
-/* Returns 0 for success. */
-CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON * const object, const cJSON * const patches);
-CJSON_PUBLIC(int) cJSONUtils_ApplyPatchesCaseSensitive(cJSON * const object, const cJSON * const patches);
-
-/*
-// Note that ApplyPatches is NOT atomic on failure. To implement an atomic ApplyPatches, use:
-//int cJSONUtils_AtomicApplyPatches(cJSON **object, cJSON *patches)
-//{
-//    cJSON *modme = cJSON_Duplicate(*object, 1);
-//    int error = cJSONUtils_ApplyPatches(modme, patches);
-//    if (!error)
-//    {
-//        cJSON_Delete(*object);
-//        *object = modme;
-//    }
-//    else
-//    {
-//        cJSON_Delete(modme);
-//    }
-//
-//    return error;
-//}
-// Code not added to library since this strategy is a LOT slower.
-*/
-
-/* Implement RFC7386 (https://tools.ietf.org/html/rfc7396) JSON Merge Patch spec. */
-/* target will be modified by patch. return value is new ptr for target. */
-CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, const cJSON * const patch);
-CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatchCaseSensitive(cJSON *target, const cJSON * const patch);
-/* generates a patch to move from -> to */
-/* NOTE: This modifies objects in 'from' and 'to' by sorting the elements by their key */
-CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON * const from, cJSON * const to);
-CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatchCaseSensitive(cJSON * const from, cJSON * const to);
-
-/* Given a root object and a target object, construct a pointer from one to the other. */
-CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(const cJSON * const object, const cJSON * const target);
-
-/* Sorts the members of the object into alphabetical order. */
-CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON * const object);
-CJSON_PUBLIC(void) cJSONUtils_SortObjectCaseSensitive(cJSON * const object);
index 2f251c6b86922c8d17c4c34ec05137f79f641c42..9e41d816a0d1853a86f2ddda3b81d4deccd630e1 100644 (file)
@@ -120,8 +120,12 @@ void esp_log_write(esp_log_level_t level, const char* tag, const char* format, .
  * @param level     level of the log
  *
  */
-#define ESP_LOG_BUFFER_HEX_LEVEL( tag, buffer, buff_len, level ) do {\
-        if ( LOG_LOCAL_LEVEL >= level ) esp_log_buffer_hex_internal( tag, buffer, buff_len, level ); } while(0)
+#define ESP_LOG_BUFFER_HEX_LEVEL( tag, buffer, buff_len, level ) \
+    do {\
+        if ( LOG_LOCAL_LEVEL >= (level) ) { \
+            esp_log_buffer_hex_internal( tag, buffer, buff_len, level ); \
+        } \
+    } while(0)
 
 /**
  * @brief Log a buffer of characters at specified level, separated into 16 bytes each line. Buffer should contain only printable characters.
@@ -135,8 +139,12 @@ void esp_log_write(esp_log_level_t level, const char* tag, const char* format, .
  * @param level     level of the log
  *
  */
-#define ESP_LOG_BUFFER_CHAR_LEVEL( tag, buffer, buff_len, level ) do {\
-    if ( LOG_LOCAL_LEVEL >= level ) esp_log_buffer_char_internal( tag, buffer, buff_len, level ); } while(0)
+#define ESP_LOG_BUFFER_CHAR_LEVEL( tag, buffer, buff_len, level ) \
+    do {\
+        if ( LOG_LOCAL_LEVEL >= (level) ) { \
+            esp_log_buffer_char_internal( tag, buffer, buff_len, level ); \
+        } \
+    } while(0)
 
 /**
  * @brief Dump a buffer to the log at specified level.
@@ -157,11 +165,13 @@ void esp_log_write(esp_log_level_t level, const char* tag, const char* format, .
  * 
  * @param level level of the log
  */
-#define ESP_LOG_BUFFER_HEXDUMP( tag, buffer, buff_len, level ) do {\
-    if ( LOG_LOCAL_LEVEL >= level ) esp_log_buffer_hexdump_internal( tag, buffer, buff_len, level); } while(0)
+#define ESP_LOG_BUFFER_HEXDUMP( tag, buffer, buff_len, level ) \
+    do { \
+        if ( LOG_LOCAL_LEVEL >= (level) ) { \
+            esp_log_buffer_hexdump_internal( tag, buffer, buff_len, level); \
+        } \
+    } while(0)
 
-
-#if (LOG_LOCAL_LEVEL >= ESP_LOG_INFO)
 /**
  * @brief Log a buffer of hex bytes at Info level
  *
@@ -174,7 +184,12 @@ void esp_log_write(esp_log_level_t level, const char* tag, const char* format, .
  * @see ``esp_log_buffer_hex_level``
  *
  */
-#define ESP_LOG_BUFFER_HEX(tag, buffer, buff_len)   ESP_LOG_BUFFER_HEX_LEVEL( tag, buffer, buff_len, ESP_LOG_INFO )
+#define ESP_LOG_BUFFER_HEX(tag, buffer, buff_len) \
+    do { \
+        if (LOG_LOCAL_LEVEL > ESP_LOG_INFO) { \
+            ESP_LOG_BUFFER_HEX_LEVEL( tag, buffer, buff_len, ESP_LOG_INFO ); \
+        }\
+    } while(0)
 
 /**
  * @brief Log a buffer of characters at Info level. Buffer should contain only printable characters.
@@ -188,12 +203,13 @@ void esp_log_write(esp_log_level_t level, const char* tag, const char* format, .
  * @see ``esp_log_buffer_char_level``
  *
  */
-#define ESP_LOG_BUFFER_CHAR(tag, buffer, buff_len)   ESP_LOG_BUFFER_CHAR_LEVEL( tag, buffer, buff_len, ESP_LOG_INFO )
+#define ESP_LOG_BUFFER_CHAR(tag, buffer, buff_len) \
+    do { \
+        if (LOG_LOCAL_LEVEL > ESP_LOG_INFO) { \
+            ESP_LOG_BUFFER_CHAR_LEVEL( tag, buffer, buff_len, ESP_LOG_INFO ); \
+        }\
+    } while(0)
 
-#else
-#define ESP_LOG_BUFFER_HEX(tag, buffer, buff_len)   {}
-#define ESP_LOG_BUFFER_CHAR(tag, buffer, buff_len)  {}
-#endif
 
 //to be back compatible
 #define esp_log_buffer_hex      ESP_LOG_BUFFER_HEX
index 589f1983a1c7ff586262e1c28490bd3e72e61eb2..28bd89d80ccfe09fde5b4b2dca21429d0d37882f 100644 (file)
@@ -164,7 +164,7 @@ void esp_log_level_set(const char* tag, esp_log_level_t level)
 #ifdef LOG_BUILTIN_CHECKS
         assert(i == 0 || s_log_cache[(i - 1) / 2].generation < s_log_cache[i].generation);
 #endif
-        if (s_log_cache[i].tag == tag) {
+        if (strcmp(s_log_cache[i].tag,tag) == 0) {
             s_log_cache[i].level = level;
             break;
         }
index a0b947863eb906c398c9a468ac97dc9e55d4d342..b595544ded7ab6c571e544714e8a045abaf98b6a 100644 (file)
@@ -16,6 +16,17 @@ config L2_TO_L3_COPY
         Please make sure you fully understand the impact of this feature before 
         enabling it.
 
+config LWIP_IRAM_OPTIMIZATION
+    bool "Enable LWIP IRAM optimization"
+    default n
+    help
+        If this feature is enabled, some functions relating to RX/TX in LWIP will be
+        put into IRAM, it can improve UDP/TCP throughput by >10% for single core mode,
+        it doesn't help too much for dual core mode. On the other hand, it needs about
+        10KB IRAM for these optimizations.
+
+        If this feature is disabled, all lwip functions will be put into FLASH.
+
 config LWIP_MAX_SOCKETS
     int "Max number of open sockets"
     range 1 32
index fd2ca16772ce9995599b001e0058cb2248d0ceda..42d80a1ee310ab8728ff8ba93ed85abd99754d53 100755 (executable)
@@ -72,7 +72,7 @@ static err_t netconn_close_shutdown(struct netconn *conn, u8_t how);
  * @param apimsg a struct containing the function to call and its parameters
  * @return ERR_OK if the function was called, another err_t if not
  */
-static err_t
+static err_t ESP_IRAM_ATTR
 tcpip_apimsg(struct api_msg *apimsg)
 {
 #if LWIP_DEBUG
@@ -432,7 +432,7 @@ netconn_accept(struct netconn *conn, struct netconn **new_conn)
  * @return ERR_OK if data has been received, an error code otherwise (timeout,
  *                memory error or another error)
  */
-static err_t
+static err_t ESP_IRAM_ATTR
 netconn_recv_data(struct netconn *conn, void **new_buf)
 {
   void *buf = NULL;
@@ -566,7 +566,7 @@ netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf)
  * @return ERR_OK if data has been received, an error code otherwise (timeout,
  *                memory error or another error)
  */
-err_t
+err_t ESP_IRAM_ATTR
 netconn_recv(struct netconn *conn, struct netbuf **new_buf)
 {
 #if LWIP_TCP
@@ -678,7 +678,7 @@ netconn_sendto(struct netconn *conn, struct netbuf *buf, const ip_addr_t *addr,
  * @param buf a netbuf containing the data to send
  * @return ERR_OK if data was sent, any other err_t on error
  */
-err_t
+err_t ESP_IRAM_ATTR
 netconn_send(struct netconn *conn, struct netbuf *buf)
 {
   API_MSG_VAR_DECLARE(msg);
index a3f21fb53c13452e3e072d0c375b2065c919ebec..a684a33bb5cbb7084a406b387913f477dcbe8f31 100755 (executable)
@@ -1440,7 +1440,7 @@ lwip_netconn_do_listen(void *m)
  *
  * @param msg the api_msg_msg pointing to the connection
  */
-void
+void ESP_IRAM_ATTR
 lwip_netconn_do_send(void *m)
 {
   struct api_msg_msg *msg = (struct api_msg_msg*)m;
index 9ab76a46385ad2e60f8429522fa6fe8971988315..2c33549ef2c17c470b88f4af4ca058211c60f697 100755 (executable)
@@ -103,7 +103,7 @@ netbuf_delete(struct netbuf *buf)
  * @return pointer to the allocated memory
  *         NULL if no memory could be allocated
  */
-void *
+void * ESP_IRAM_ATTR
 netbuf_alloc(struct netbuf *buf, u16_t size)
 {
   LWIP_ERROR("netbuf_alloc: invalid buf", (buf != NULL), return NULL;);
@@ -127,7 +127,7 @@ netbuf_alloc(struct netbuf *buf, u16_t size)
  *
  * @param buf pointer to the netbuf which contains the packet buffer to free
  */
-void
+void ESP_IRAM_ATTR
 netbuf_free(struct netbuf *buf)
 {
   LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;);
index b09cff19fc607a7602514ddf9b7dfc3c77d908d9..badde6613a920370b41d97fc045d46733c3010ac 100755 (executable)
@@ -491,7 +491,7 @@ lwip_socket_thread_cleanup(void)
  * @param s externally used socket index
  * @return struct lwip_sock for the socket or NULL if not found
  */
-static struct lwip_sock *
+static struct lwip_sock * ESP_IRAM_ATTR
 get_socket(int s)
 {
   struct lwip_sock *sock;
@@ -962,7 +962,7 @@ lwip_listen(int s, int backlog)
   return 0;
 }
 
-int
+int ESP_IRAM_ATTR
 lwip_recvfrom(int s, void *mem, size_t len, int flags,
               struct sockaddr *from, socklen_t *fromlen)
 {
@@ -1340,7 +1340,7 @@ lwip_sendmsg(int s, const struct msghdr *msg, int flags)
 #endif /* LWIP_UDP || LWIP_RAW */
 }
 
-int
+int ESP_IRAM_ATTR
 lwip_sendto(int s, const void *data, size_t size, int flags,
        const struct sockaddr *to, socklen_t tolen)
 {
@@ -1817,7 +1817,7 @@ return_copy_fdsets:
  * Callback registered in the netconn layer for each socket-netconn.
  * Processes recvevent (data available) and wakes up tasks waiting for select.
  */
-static void
+static void ESP_IRAM_ATTR
 event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len)
 {
   int s;
@@ -3159,7 +3159,7 @@ static void lwip_socket_drop_registered_memberships(int s)
 
 #if ESP_THREAD_SAFE
 
-int
+int ESP_IRAM_ATTR
 lwip_sendto_r(int s, const void *data, size_t size, int flags,
        const struct sockaddr *to, socklen_t tolen)
 {
@@ -3176,7 +3176,7 @@ lwip_send_r(int s, const void *data, size_t size, int flags)
   LWIP_API_UNLOCK();
 }
 
-int
+int ESP_IRAM_ATTR
 lwip_recvfrom_r(int s, void *mem, size_t len, int flags,
               struct sockaddr *from, socklen_t *fromlen)
 {
index 143cf917cdd6410069fa661cd5d298cbd910c27b..653f6c23949bc92e4db10a2074c0028dab62c5d7 100755 (executable)
@@ -77,7 +77,7 @@ sys_mutex_t lock_tcpip_core;
  *
  * @param arg unused argument
  */
-static void
+static void ESP_IRAM_ATTR
 tcpip_thread(void *arg)
 {
 
@@ -195,7 +195,7 @@ tcpip_thread(void *arg)
  * @param inp the network interface on which the packet was received
  * @param input_fn input function to call
  */
-err_t
+err_t ESP_IRAM_ATTR
 tcpip_inpkt(struct pbuf *p, struct netif *inp, netif_input_fn input_fn)
 {
 #if LWIP_TCPIP_CORE_LOCKING_INPUT
@@ -244,7 +244,7 @@ tcpip_inpkt(struct pbuf *p, struct netif *inp, netif_input_fn input_fn)
  *          NETIF_FLAG_ETHERNET flags)
  * @param inp the network interface on which the packet was received
  */
-err_t
+err_t ESP_IRAM_ATTR
 tcpip_input(struct pbuf *p, struct netif *inp)
 {
 #if LWIP_ETHERNET
@@ -363,7 +363,7 @@ tcpip_untimeout(sys_timeout_handler h, void *arg)
  * @param sem semaphore to wait on
  * @return ERR_OK if the function was called, another err_t if not
  */
-err_t
+err_t ESP_IRAM_ATTR
 tcpip_send_api_msg(tcpip_callback_fn fn, void *apimsg, sys_sem_t* sem)
 {
   LWIP_ASSERT("semaphore not initialized", sys_sem_valid(sem));
index 9605c72afddb18392c251218ffa3e007c9e61780..d3c6488281261478c367690ae2272de3d1c92b76 100644 (file)
@@ -21,7 +21,7 @@
 extern "C" {
 #endif
 
-#define ESP_ERR_PING_BASE      0x5000
+#define ESP_ERR_PING_BASE               0x6000
 
 #define ESP_ERR_PING_INVALID_PARAMS     ESP_ERR_PING_BASE + 0x00
 #define ESP_ERR_PING_NO_MEM             ESP_ERR_PING_BASE + 0x01
index bcaa3dc8275981f8a9e5cbe5e5067f7e51a01260..f104f6b75128f51d90879069931a7dafc1866d1a 100755 (executable)
@@ -259,7 +259,7 @@ lwip_standard_chksum(const void *dataptr, int len)
 #endif
 
 /** Parts of the pseudo checksum which are common to IPv4 and IPv6 */
-static u16_t
+static u16_t ESP_IRAM_ATTR
 inet_cksum_pseudo_base(struct pbuf *p, u8_t proto, u16_t proto_len, u32_t acc)
 {
   struct pbuf *q;
@@ -309,7 +309,7 @@ inet_cksum_pseudo_base(struct pbuf *p, u8_t proto, u16_t proto_len, u32_t acc)
  * @param proto_len length of the ip data part (used for checksum of pseudo header)
  * @return checksum (as u16_t) to be saved directly in the protocol header
  */
-u16_t
+u16_t ESP_IRAM_ATTR
 inet_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
        const ip4_addr_t *src, const ip4_addr_t *dest)
 {
@@ -378,7 +378,7 @@ ip6_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
  * @param proto_len length of the ip data part (used for checksum of pseudo header)
  * @return checksum (as u16_t) to be saved directly in the protocol header
  */
-u16_t
+u16_t ESP_IRAM_ATTR
 ip_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
        const ip_addr_t *src, const ip_addr_t *dest)
 {
index 04bf2181bb6e5dec6de16be2ed318295b04756c2..d2b9195a45f3d7a2bedb30f45dea1586978940ce 100755 (executable)
@@ -136,7 +136,7 @@ bool ip4_netif_exist(const ip4_addr_t *src, const ip4_addr_t *dest)
  * Source based IPv4 routing hook function. This function works only
  * when destination IP is broadcast IP.
  */
-struct netif *
+struct netif * ESP_IRAM_ATTR
 ip4_route_src_hook(const ip4_addr_t *dest, const ip4_addr_t *src)
 {
   struct netif *netif = NULL;
@@ -162,7 +162,7 @@ ip4_route_src_hook(const ip4_addr_t *dest, const ip4_addr_t *src)
  * Source based IPv4 routing must be fully implemented in
  * LWIP_HOOK_IP4_ROUTE_SRC(). This function only provides the parameters.
  */
-struct netif *
+struct netif * ESP_IRAM_ATTR
 ip4_route_src(const ip4_addr_t *dest, const ip4_addr_t *src)
 {
   if (src != NULL) {
@@ -189,7 +189,7 @@ ip4_route_src(const ip4_addr_t *dest, const ip4_addr_t *src)
  * @param dest the destination IP address for which to find the route
  * @return the netif on which to send to reach dest
  */
-struct netif *
+struct netif * ESP_IRAM_ATTR
 ip4_route(const ip4_addr_t *dest)
 {
   struct netif *netif;
@@ -410,7 +410,7 @@ return_noroute:
  * @return ERR_OK if the packet was processed (could return ERR_* if it wasn't
  *         processed, but currently always returns ERR_OK)
  */
-err_t
+err_t ESP_IRAM_ATTR
 ip4_input(struct pbuf *p, struct netif *inp)
 {
   struct ip_hdr *iphdr;
@@ -818,7 +818,7 @@ ip4_output_if_opt(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
  * Same as ip_output_if() but 'src' address is not replaced by netif address
  * when it is 'any'.
  */
-err_t
+err_t ESP_IRAM_ATTR
 ip4_output_if_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
              u8_t ttl, u8_t tos,
              u8_t proto, struct netif *netif)
@@ -831,7 +831,7 @@ ip4_output_if_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
  * Same as ip_output_if_opt() but 'src' address is not replaced by netif address
  * when it is 'any'.
  */
-err_t
+err_t ESP_IRAM_ATTR
 ip4_output_if_opt_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
        u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options,
        u16_t optlen)
index 0501b84e5fb9b2c80a4386175257c986efcda234..b143f36e293521e10c34a17d7dd7133b91d93e22 100755 (executable)
@@ -55,7 +55,7 @@ const ip_addr_t ip_addr_broadcast = IPADDR4_INIT(IPADDR_BROADCAST);
  * @param netif the network interface against which the address is checked
  * @return returns non-zero if the address is a broadcast address
  */
-u8_t
+u8_t ESP_IRAM_ATTR
 ip4_addr_isbroadcast_u32(u32_t addr, const struct netif *netif)
 {
   ip4_addr_t ipaddr;
index 8c2eb05a54658f3eb76fbd9f01eae51e92f341ae..a80a002a4643c98d75ee0e312bc519fe3e90cdaa 100755 (executable)
@@ -513,7 +513,7 @@ pbuf_realloc(struct pbuf *p, u16_t new_len)
  * @return non-zero on failure, zero on success.
  *
  */
-static u8_t
+static u8_t ESP_IRAM_ATTR
 pbuf_header_impl(struct pbuf *p, s16_t header_size_increment, u8_t force)
 {
   u16_t type;
@@ -609,7 +609,7 @@ pbuf_header_impl(struct pbuf *p, s16_t header_size_increment, u8_t force)
  * @return non-zero on failure, zero on success.
  *
  */
-u8_t
+u8_t ESP_IRAM_ATTR
 pbuf_header(struct pbuf *p, s16_t header_size_increment)
 {
    return pbuf_header_impl(p, header_size_increment, 0);
index ef47b2e187c7649594806263a4c516bb893eec05..3358a6a54bb36b367b65c6d8ae9e01c99f88f785 100755 (executable)
@@ -535,7 +535,7 @@ sys_timeouts_sleeptime(void)
  * @param mbox the mbox to fetch the message from
  * @param msg the place to store the message
  */
-void
+void ESP_IRAM_ATTR
 sys_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg)
 {
   u32_t time_needed;
index 779dd9ad7f4ae2c07e993dc5e7d6f43f24ad5e1f..489e48705e08da232d71cc468dc8df28c9ffd34d 100755 (executable)
@@ -146,7 +146,7 @@ again:
  * @param broadcast 1 if his is an IPv4 broadcast (global or subnet-only), 0 otherwise (only used for IPv4)
  * @return 1 on match, 0 otherwise
  */
-static u8_t
+static u8_t ESP_IRAM_ATTR
 udp_input_local_match(struct udp_pcb *pcb, struct netif *inp, u8_t broadcast)
 {
   LWIP_UNUSED_ARG(inp);       /* in IPv6 only case */
@@ -210,7 +210,7 @@ udp_input_local_match(struct udp_pcb *pcb, struct netif *inp, u8_t broadcast)
  * @param inp network interface on which the datagram was received.
  *
  */
-void
+void ESP_IRAM_ATTR
 udp_input(struct pbuf *p, struct netif *inp)
 {
   struct udp_hdr *udphdr;
@@ -474,7 +474,7 @@ chkerr:
  *
  * @see udp_disconnect() udp_sendto()
  */
-err_t
+err_t ESP_IRAM_ATTR
 udp_send(struct udp_pcb *pcb, struct pbuf *p)
 {
   if ((pcb == NULL) || IP_IS_ANY_TYPE_VAL(pcb->remote_ip)) {
@@ -519,7 +519,7 @@ udp_send_chksum(struct udp_pcb *pcb, struct pbuf *p,
  *
  * @see udp_disconnect() udp_send()
  */
-err_t
+err_t ESP_IRAM_ATTR
 udp_sendto(struct udp_pcb *pcb, struct pbuf *p,
   const ip_addr_t *dst_ip, u16_t dst_port)
 {
@@ -615,7 +615,7 @@ udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip,
  *
  * @see udp_disconnect() udp_send()
  */
-err_t
+err_t ESP_IRAM_ATTR
 udp_sendto_if(struct udp_pcb *pcb, struct pbuf *p,
   const ip_addr_t *dst_ip, u16_t dst_port, struct netif *netif)
 {
@@ -681,7 +681,7 @@ udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_i
 }
 
 /** Same as udp_sendto_if(), but with source address */
-err_t
+err_t ESP_IRAM_ATTR
 udp_sendto_if_src(struct udp_pcb *pcb, struct pbuf *p,
   const ip_addr_t *dst_ip, u16_t dst_port, struct netif *netif, const ip_addr_t *src_ip)
 {
index 4652c6d4a86f4cf8c1292a4cfd69c8957fd943ed..3e8d26ee3b8cf5abbccb983281e635b5f4c86c48 100644 (file)
 #define ESP_LWIP_LOGI(...)              ESP_LOGI("lwip", __VA_ARGS__)
 #define ESP_PING                        1
 
+#if CONFIG_LWIP_IRAM_OPTIMIZATION
+#define ESP_IRAM_ATTR                   IRAM_ATTR
+#else
+#define ESP_IRAM_ATTR                   
+#endif
+
 #define TCP_WND_DEFAULT                 CONFIG_TCP_WND_DEFAULT
 #define TCP_SND_BUF_DEFAULT             CONFIG_TCP_SND_BUF_DEFAULT
 
index d9854dbb0b8ba57996987be730cd3534e4c779d5..54f7487ace064d8e35b76c4e88cc113a586cdcb5 100755 (executable)
@@ -404,7 +404,7 @@ etharp_find_entry(const ip4_addr_t *ipaddr, u8_t flags, struct netif* netif)
  * @params dst the destination MAC address to be copied into the ethernet header
  * @return ERR_OK if the packet was sent, any other err_t on failure
  */
-static err_t
+static err_t ESP_IRAM_ATTR
 etharp_send_ip(struct netif *netif, struct pbuf *p, struct eth_addr *src, const struct eth_addr *dst)
 {
   struct eth_hdr *ethhdr = (struct eth_hdr *)p->payload;
@@ -872,7 +872,7 @@ etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p)
 /** Just a small helper function that sends a pbuf to an ethernet address
  * in the arp_table specified by the index 'arp_idx'.
  */
-static err_t
+static err_t ESP_IRAM_ATTR
 etharp_output_to_arp_index(struct netif *netif, struct pbuf *q, u8_t arp_idx)
 {
   LWIP_ASSERT("arp_table[arp_idx].state >= ETHARP_STATE_STABLE",
@@ -916,7 +916,7 @@ etharp_output_to_arp_index(struct netif *netif, struct pbuf *q, u8_t arp_idx)
  * - ERR_RTE No route to destination (no gateway to external networks),
  * or the return type of either etharp_query() or etharp_send_ip().
  */
-err_t
+err_t ESP_IRAM_ATTR
 etharp_output(struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr)
 {
   const struct eth_addr *dest;
index 90f62583bc748c3503f7f6c77ad8b2cbabbb4302..340ce0a51c09387101b780945bbdbde66b60ade4 100755 (executable)
@@ -63,7 +63,7 @@ const struct eth_addr ethzero = {{0,0,0,0,0,0}};
  * @param p the received packet, p->payload pointing to the ethernet header
  * @param netif the network interface on which the packet was received
  */
-err_t
+err_t ESP_IRAM_ATTR
 ethernet_input(struct pbuf *p, struct netif *netif)
 {
   struct eth_hdr* ethhdr;
index aa58f9bb17c6ae057410cdb19cddd46eb1fb91ab..77eaef0a118df3523b444ee67827a5831ff3971d 100755 (executable)
@@ -72,7 +72,7 @@ sys_mutex_new(sys_mutex_t *pxMutex)
 
 /** Lock a mutex
  * @param mutex the mutex to lock */
-void
+void ESP_IRAM_ATTR
 sys_mutex_lock(sys_mutex_t *pxMutex)
 {
   while (xSemaphoreTake(*pxMutex, portMAX_DELAY) != pdPASS);
@@ -87,7 +87,7 @@ sys_mutex_trylock(sys_mutex_t *pxMutex)
 
 /** Unlock a mutex
  * @param mutex the mutex to unlock */
-void
+void ESP_IRAM_ATTR
 sys_mutex_unlock(sys_mutex_t *pxMutex)
 {
   xSemaphoreGive(*pxMutex);
@@ -127,7 +127,7 @@ sys_sem_new(sys_sem_t *sem, u8_t count)
 
 /*-----------------------------------------------------------------------------------*/
 // Signals a semaphore
-void
+void ESP_IRAM_ATTR
 sys_sem_signal(sys_sem_t *sem)
 {
     xSemaphoreGive(*sem);
@@ -149,7 +149,7 @@ sys_sem_signal(sys_sem_t *sem)
   Notice that lwIP implements a function with a similar name,
   sys_sem_wait(), that uses the sys_arch_sem_wait() function.
 */
-u32_t
+u32_t ESP_IRAM_ATTR
 sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout)
 {
   portTickType StartTime, EndTime, Elapsed;
@@ -228,14 +228,14 @@ sys_mbox_new(sys_mbox_t *mbox, int size)
 
 /*-----------------------------------------------------------------------------------*/
 //   Posts the "msg" to the mailbox.
-void
+void ESP_IRAM_ATTR
 sys_mbox_post(sys_mbox_t *mbox, void *msg)
 {
   while (xQueueSendToBack((*mbox)->os_mbox, &msg, portMAX_DELAY) != pdTRUE);
 }
 
 /*-----------------------------------------------------------------------------------*/
-err_t
+err_t ESP_IRAM_ATTR
 sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
 {
   err_t xReturn;
@@ -266,7 +266,7 @@ sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
   Note that a function with a similar name, sys_mbox_fetch(), is
   implemented by lwIP.
 */
-u32_t
+u32_t ESP_IRAM_ATTR
 sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout)
 {
   void *dummyptr;
index 7f145624b50bddd98c75efcfd65f89a84792c10e..7c60b6934916dd9e99f388c820242a9d34afc03a 100644 (file)
@@ -101,7 +101,7 @@ low_level_init(struct netif *netif)
  *       to become availale since the stack doesn't retry to send a packet
  *       dropped because of memory failure (except for the TCP timers).
  */
-static err_t
+static err_t ESP_IRAM_ATTR
 low_level_output(struct netif *netif, struct pbuf *p)
 {
   wifi_interface_t wifi_if = tcpip_adapter_get_esp_if(netif);
@@ -139,7 +139,7 @@ low_level_output(struct netif *netif, struct pbuf *p)
  *
  * @param netif the lwip network interface structure for this ethernetif
  */
-void
+void ESP_IRAM_ATTR
 wlanif_input(struct netif *netif, void *buffer, u16_t len, void* eb)
 {
   struct pbuf *p;
index 897e637d0794ef2262611a4f246e0ec5080d505b..7b80cabf8fb1ff99cde5559aead3f84d4609e555 100755 (executable)
@@ -26,8 +26,11 @@ import os
 import re
 import struct
 import sys
+import hashlib
+import binascii
 
 MAX_PARTITION_LENGTH = 0xC00   # 3K for partition data (96 entries) leaves 1K in a 4K sector for signature
+MD5_PARTITION_BEGIN = b"\xEB\xEB" + b"\xFF" * 14 # The first 2 bytes are like magic numbers for MD5 sum
 
 __version__ = '1.0'
 
@@ -112,6 +115,7 @@ class PartitionTable(list):
 
     @classmethod
     def from_binary(cls, b):
+        md5 = hashlib.md5();
         result = cls()
         for o in range(0,len(b),32):
             data = b[o:o+32]
@@ -119,11 +123,19 @@ class PartitionTable(list):
                 raise InputError("Partition table length must be a multiple of 32 bytes")
             if data == b'\xFF'*32:
                 return result  # got end marker
+            if data[:2] == MD5_PARTITION_BEGIN[:2]: #check only the magic number part
+                if data[16:] == md5.digest():
+                    continue # the next iteration will check for the end marker
+                else:
+                    raise InputError("MD5 checksums don't match! (computed: 0x%s, parsed: 0x%s)" % (md5.hexdigest(), binascii.hexlify(data[16:])))
+            else:
+                md5.update(data)
             result.append(PartitionDefinition.from_binary(data))
         raise InputError("Partition table is missing an end-of-table marker")
 
     def to_binary(self):
         result = b"".join(e.to_binary() for e in self)
+        result += MD5_PARTITION_BEGIN + hashlib.md5(result).digest()
         if len(result )>= MAX_PARTITION_LENGTH:
             raise InputError("Binary partition table length (%d) longer than max" % len(result))
         result += b"\xFF" * (MAX_PARTITION_LENGTH - len(result))  # pad the sector, for signing
index 46fe45c228516b8b0560b2d1e8013ec84709d67b..4919a53d8784447ce1401d51ff9dd11dc93e168f 100755 (executable)
@@ -37,6 +37,10 @@ LONGER_BINARY_TABLE += b"\xAA\x50\x10\x00" + \
                        b"\x00\x10\x00\x00" + \
                        b"second" + (b"\0"*10) + \
                        b"\x00\x00\x00\x00"
+# MD5 checksum
+LONGER_BINARY_TABLE += b"\xEB\xEB" + b"\xFF" * 14
+LONGER_BINARY_TABLE += b'\xf9\xbd\x06\x1b\x45\x68\x6f\x86\x57\x1a\x2c\xd5\x2a\x1d\xa6\x5b'
+# empty partition
 LONGER_BINARY_TABLE += b"\xFF" * 32
 
 
@@ -168,12 +172,14 @@ first, 0x30, 0xEE, 0x100400, 0x300000
 """
         t = PartitionTable.from_csv(csv)
         tb = _strip_trailing_ffs(t.to_binary())
-        self.assertEqual(len(tb), 64)
+        self.assertEqual(len(tb), 64+32)
         self.assertEqual(b'\xAA\x50', tb[0:2]) # magic
         self.assertEqual(b'\x30\xee', tb[2:4]) # type, subtype
         eo, es = struct.unpack("<LL", tb[4:12])
         self.assertEqual(eo, 0x100400) # offset
         self.assertEqual(es, 0x300000) # size
+        self.assertEqual(b"\xEB\xEB" + b"\xFF" * 14, tb[32:48])
+        self.assertEqual(b'\x43\x03\x3f\x33\x40\x87\x57\x51\x69\x83\x9b\x40\x61\xb1\x27\x26', tb[48:64])
 
     def test_multiple_entries(self):
         csv = """
@@ -182,7 +188,7 @@ second,0x31, 0xEF,         , 0x100000
 """
         t = PartitionTable.from_csv(csv)
         tb = _strip_trailing_ffs(t.to_binary())
-        self.assertEqual(len(tb), 96)
+        self.assertEqual(len(tb), 96+32)
         self.assertEqual(b'\xAA\x50', tb[0:2])
         self.assertEqual(b'\xAA\x50', tb[32:34])
 
index a55440225ef9f2aacec5c6cf9ed86d884cf784a7..594e46976753141304a7e14a3eb70e1ab384ebf7 100644 (file)
@@ -82,6 +82,17 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card)
     memcpy(&card->host, config, sizeof(*config));
     const bool is_spi = host_is_spi(card);
 
+    if ( !is_spi ) {
+        //check HOST flags compatible with slot configuration.
+        int slot_bit_width = config->get_bus_width(config->slot);        
+        if ( slot_bit_width == 1 && (config->flags & (SDMMC_HOST_FLAG_4BIT|SDMMC_HOST_FLAG_8BIT))) {
+            ESP_LOGW(TAG, "HOST slot only enables 1-bit.");
+            card->host.flags = ((card->host.flags&(~(SDMMC_HOST_FLAG_4BIT|SDMMC_HOST_FLAG_8BIT)))|SDMMC_HOST_FLAG_1BIT);
+        } else if ( slot_bit_width == 4  && (config->flags & SDMMC_HOST_FLAG_8BIT)){
+            ESP_LOGW(TAG, "HOST slot only enables 4-bit.");
+            card->host.flags = ((card->host.flags&(~(SDMMC_HOST_FLAG_1BIT|SDMMC_HOST_FLAG_8BIT)))|SDMMC_HOST_FLAG_4BIT);            
+        }
+    }
     /* GO_IDLE_STATE (CMD0) command resets the card */
     esp_err_t err = sdmmc_send_cmd_go_idle_state(card);
     if (err != ESP_OK) {
@@ -218,7 +229,7 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card)
     /* If the host has been initialized with 4-bit bus support, and the card
      * supports 4-bit bus, switch to 4-bit bus now.
      */
-    if ((config->flags & SDMMC_HOST_FLAG_4BIT) &&
+    if ((card->host.flags & SDMMC_HOST_FLAG_4BIT) &&
         (card->scr.bus_width & SCR_SD_BUS_WIDTHS_4BIT)) {
         ESP_LOGD(TAG, "switching to 4-bit bus mode");
         err = sdmmc_send_cmd_set_bus_width(card, 4);
index 3f2ce01344914b0f9bc8004f81035c2769d7812d..0fbe6c5aee0206becc7c88e1a324b3e2383d5856 100644 (file)
@@ -36,7 +36,7 @@ TEST_CASE("MMC_RSP_BITS", "[sd]")
     TEST_ASSERT_EQUAL_HEX32(0x11,  MMC_RSP_BITS(data, 59, 5));
 }
 
-TEST_CASE("can probe SD", "[sd][test_env=UT_T1_SDMODE][ignore]")
+TEST_CASE("can probe SD (4-bit)", "[sd][test_env=UT_T1_SDMODE]")
 {
     sdmmc_host_t config = SDMMC_HOST_DEFAULT();
     sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
@@ -50,8 +50,37 @@ TEST_CASE("can probe SD", "[sd][test_env=UT_T1_SDMODE][ignore]")
     free(card);
 }
 
+TEST_CASE("can probe SD (1-bit)", "[sd][test_env=UT_T1_SDMODE]")
+{
+    //the card DAT3 should be connected to high in SD 1-bit mode
+    //do it by our own GPIO.
+    gpio_config_t conf = {
+        .pin_bit_mask = GPIO_SEL_13,
+        .mode = GPIO_MODE_OUTPUT,
+        .pull_up_en = 1,
+        .pull_down_en = 0,
+        .intr_type = GPIO_INTR_DISABLE,
+    };
+    gpio_config(&conf);
+    gpio_set_level(GPIO_NUM_13, 1);
+
+    sdmmc_host_t config = SDMMC_HOST_DEFAULT();
+    config.flags = SDMMC_HOST_FLAG_1BIT;
+    sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
+    slot_config.width=1;
+    TEST_ESP_OK(sdmmc_host_init());
+    TEST_ESP_OK(sdmmc_host_init_slot(SDMMC_HOST_SLOT_1, &slot_config));
+    sdmmc_card_t* card = malloc(sizeof(sdmmc_card_t));
+    TEST_ASSERT_NOT_NULL(card);
+    TEST_ESP_OK(sdmmc_card_init(&config, card));
+    sdmmc_card_print_info(stdout, card);
+    TEST_ESP_OK(sdmmc_host_deinit());
+    free(card);
+}
+
+
 
-TEST_CASE("can probe SD(using SPI)", "[sdspi][test_env=UT_T1_SPIMODE][ignore]")
+TEST_CASE("can probe SD(using SPI)", "[sdspi][test_env=UT_T1_SPIMODE]")
 {
     sdmmc_host_t config = SDSPI_HOST_DEFAULT();
     sdspi_slot_config_t slot_config = SDSPI_SLOT_CONFIG_DEFAULT();
index 2598989d096d70c4cd91e3e7652fc13990f7833d..5516e59834c5126dde057ce5ddb7de9e3c2634cc 100644 (file)
@@ -124,18 +124,21 @@ static inline void __attribute__ ((deprecated)) PIN_PULLDWN_EN(uint32_t PIN_NAME
 #define CLK_OUT1_S                                  0
 
 #define PERIPHS_IO_MUX_GPIO0_U            (DR_REG_IO_MUX_BASE +0x44)
+#define IO_MUX_GPIO0_REG                  PERIPHS_IO_MUX_GPIO0_U
 #define FUNC_GPIO0_EMAC_TX_CLK                      5
 #define FUNC_GPIO0_GPIO0                            2
 #define FUNC_GPIO0_CLK_OUT1                         1
 #define FUNC_GPIO0_GPIO0_0                          0
 
 #define PERIPHS_IO_MUX_U0TXD_U            (DR_REG_IO_MUX_BASE +0x88)
+#define IO_MUX_GPIO1_REG                  PERIPHS_IO_MUX_U0TXD_U
 #define FUNC_U0TXD_EMAC_RXD2                        5
 #define FUNC_U0TXD_GPIO1                            2
 #define FUNC_U0TXD_CLK_OUT3                         1
 #define FUNC_U0TXD_U0TXD                            0
 
 #define PERIPHS_IO_MUX_GPIO2_U            (DR_REG_IO_MUX_BASE +0x40)
+#define IO_MUX_GPIO2_REG                  PERIPHS_IO_MUX_GPIO2_U
 #define FUNC_GPIO2_SD_DATA0                         4
 #define FUNC_GPIO2_HS2_DATA0                        3
 #define FUNC_GPIO2_GPIO2                            2
@@ -143,11 +146,13 @@ static inline void __attribute__ ((deprecated)) PIN_PULLDWN_EN(uint32_t PIN_NAME
 #define FUNC_GPIO2_GPIO2_0                          0
 
 #define PERIPHS_IO_MUX_U0RXD_U            (DR_REG_IO_MUX_BASE +0x84)
+#define IO_MUX_GPIO3_REG                    PERIPHS_IO_MUX_U0RXD_U
 #define FUNC_U0RXD_GPIO3                            2
 #define FUNC_U0RXD_CLK_OUT2                         1
 #define FUNC_U0RXD_U0RXD                            0
 
 #define PERIPHS_IO_MUX_GPIO4_U            (DR_REG_IO_MUX_BASE +0x48)
+#define IO_MUX_GPIO4_REG                    PERIPHS_IO_MUX_GPIO4_U
 #define FUNC_GPIO4_EMAC_TX_ER                       5
 #define FUNC_GPIO4_SD_DATA1                         4
 #define FUNC_GPIO4_HS2_DATA1                        3
@@ -156,6 +161,7 @@ static inline void __attribute__ ((deprecated)) PIN_PULLDWN_EN(uint32_t PIN_NAME
 #define FUNC_GPIO4_GPIO4_0                          0
 
 #define PERIPHS_IO_MUX_GPIO5_U            (DR_REG_IO_MUX_BASE +0x6c)
+#define IO_MUX_GPIO5_REG                   PERIPHS_IO_MUX_GPIO5_U
 #define FUNC_GPIO5_EMAC_RX_CLK                      5
 #define FUNC_GPIO5_HS1_DATA6                        3
 #define FUNC_GPIO5_GPIO5                            2
@@ -163,6 +169,7 @@ static inline void __attribute__ ((deprecated)) PIN_PULLDWN_EN(uint32_t PIN_NAME
 #define FUNC_GPIO5_GPIO5_0                          0
 
 #define PERIPHS_IO_MUX_SD_CLK_U           (DR_REG_IO_MUX_BASE +0x60)
+#define IO_MUX_GPIO6_REG                    PERIPHS_IO_MUX_SD_CLK_U
 #define FUNC_SD_CLK_U1CTS                           4
 #define FUNC_SD_CLK_HS1_CLK                         3
 #define FUNC_SD_CLK_GPIO6                           2
@@ -170,6 +177,7 @@ static inline void __attribute__ ((deprecated)) PIN_PULLDWN_EN(uint32_t PIN_NAME
 #define FUNC_SD_CLK_SD_CLK                          0
 
 #define PERIPHS_IO_MUX_SD_DATA0_U         (DR_REG_IO_MUX_BASE +0x64)
+#define IO_MUX_GPIO7_REG                    PERIPHS_IO_MUX_SD_DATA0_U
 #define FUNC_SD_DATA0_U2RTS                         4
 #define FUNC_SD_DATA0_HS1_DATA0                     3
 #define FUNC_SD_DATA0_GPIO7                         2
@@ -177,6 +185,7 @@ static inline void __attribute__ ((deprecated)) PIN_PULLDWN_EN(uint32_t PIN_NAME
 #define FUNC_SD_DATA0_SD_DATA0                      0
 
 #define PERIPHS_IO_MUX_SD_DATA1_U         (DR_REG_IO_MUX_BASE +0x68)
+#define IO_MUX_GPIO8_REG                    PERIPHS_IO_MUX_SD_DATA1_U
 #define FUNC_SD_DATA1_U2CTS                         4
 #define FUNC_SD_DATA1_HS1_DATA1                     3
 #define FUNC_SD_DATA1_GPIO8                         2
@@ -184,6 +193,7 @@ static inline void __attribute__ ((deprecated)) PIN_PULLDWN_EN(uint32_t PIN_NAME
 #define FUNC_SD_DATA1_SD_DATA1                      0
 
 #define PERIPHS_IO_MUX_SD_DATA2_U         (DR_REG_IO_MUX_BASE +0x54)
+#define IO_MUX_GPIO9_REG                    PERIPHS_IO_MUX_SD_DATA2_U
 #define FUNC_SD_DATA2_U1RXD                         4
 #define FUNC_SD_DATA2_HS1_DATA2                     3
 #define FUNC_SD_DATA2_GPIO9                         2
@@ -191,6 +201,7 @@ static inline void __attribute__ ((deprecated)) PIN_PULLDWN_EN(uint32_t PIN_NAME
 #define FUNC_SD_DATA2_SD_DATA2                      0
 
 #define PERIPHS_IO_MUX_SD_DATA3_U         (DR_REG_IO_MUX_BASE +0x58)
+#define IO_MUX_GPIO10_REG                   PERIPHS_IO_MUX_SD_DATA3_U
 #define FUNC_SD_DATA3_U1TXD                         4
 #define FUNC_SD_DATA3_HS1_DATA3                     3
 #define FUNC_SD_DATA3_GPIO10                        2
@@ -198,6 +209,7 @@ static inline void __attribute__ ((deprecated)) PIN_PULLDWN_EN(uint32_t PIN_NAME
 #define FUNC_SD_DATA3_SD_DATA3                      0
 
 #define PERIPHS_IO_MUX_SD_CMD_U           (DR_REG_IO_MUX_BASE +0x5c)
+#define IO_MUX_GPIO11_REG                   PERIPHS_IO_MUX_SD_CMD_U
 #define FUNC_SD_CMD_U1RTS                           4
 #define FUNC_SD_CMD_HS1_CMD                         3
 #define FUNC_SD_CMD_GPIO11                          2
@@ -205,6 +217,7 @@ static inline void __attribute__ ((deprecated)) PIN_PULLDWN_EN(uint32_t PIN_NAME
 #define FUNC_SD_CMD_SD_CMD                          0
 
 #define PERIPHS_IO_MUX_MTDI_U             (DR_REG_IO_MUX_BASE +0x34)
+#define IO_MUX_GPIO12_REG                    PERIPHS_IO_MUX_MTDI_U
 #define FUNC_MTDI_EMAC_TXD3                         5
 #define FUNC_MTDI_SD_DATA2                          4
 #define FUNC_MTDI_HS2_DATA2                         3
@@ -213,6 +226,7 @@ static inline void __attribute__ ((deprecated)) PIN_PULLDWN_EN(uint32_t PIN_NAME
 #define FUNC_MTDI_MTDI                              0
 
 #define PERIPHS_IO_MUX_MTCK_U             (DR_REG_IO_MUX_BASE +0x38)
+#define IO_MUX_GPIO13_REG                    PERIPHS_IO_MUX_MTCK_U
 #define FUNC_MTCK_EMAC_RX_ER                        5
 #define FUNC_MTCK_SD_DATA3                          4
 #define FUNC_MTCK_HS2_DATA3                         3
@@ -221,6 +235,7 @@ static inline void __attribute__ ((deprecated)) PIN_PULLDWN_EN(uint32_t PIN_NAME
 #define FUNC_MTCK_MTCK                              0
 
 #define PERIPHS_IO_MUX_MTMS_U             (DR_REG_IO_MUX_BASE +0x30)
+#define IO_MUX_GPIO14_REG                    PERIPHS_IO_MUX_MTMS_U
 #define FUNC_MTMS_EMAC_TXD2                         5
 #define FUNC_MTMS_SD_CLK                            4
 #define FUNC_MTMS_HS2_CLK                           3
@@ -229,6 +244,7 @@ static inline void __attribute__ ((deprecated)) PIN_PULLDWN_EN(uint32_t PIN_NAME
 #define FUNC_MTMS_MTMS                              0
 
 #define PERIPHS_IO_MUX_MTDO_U             (DR_REG_IO_MUX_BASE +0x3c)
+#define IO_MUX_GPIO15_REG                    PERIPHS_IO_MUX_MTDO_U
 #define FUNC_MTDO_EMAC_RXD3                         5
 #define FUNC_MTDO_SD_CMD                            4
 #define FUNC_MTDO_HS2_CMD                           3
@@ -237,6 +253,7 @@ static inline void __attribute__ ((deprecated)) PIN_PULLDWN_EN(uint32_t PIN_NAME
 #define FUNC_MTDO_MTDO                              0
 
 #define PERIPHS_IO_MUX_GPIO16_U           (DR_REG_IO_MUX_BASE +0x4c)
+#define IO_MUX_GPIO16_REG                    PERIPHS_IO_MUX_GPIO16_U
 #define FUNC_GPIO16_EMAC_CLK_OUT                    5
 #define FUNC_GPIO16_U2RXD                           4
 #define FUNC_GPIO16_HS1_DATA4                       3
@@ -244,6 +261,7 @@ static inline void __attribute__ ((deprecated)) PIN_PULLDWN_EN(uint32_t PIN_NAME
 #define FUNC_GPIO16_GPIO16_0                        0
 
 #define PERIPHS_IO_MUX_GPIO17_U           (DR_REG_IO_MUX_BASE +0x50)
+#define IO_MUX_GPIO17_REG                    PERIPHS_IO_MUX_GPIO17_U
 #define FUNC_GPIO17_EMAC_CLK_OUT_180                5
 #define FUNC_GPIO17_U2TXD                           4
 #define FUNC_GPIO17_HS1_DATA5                       3
@@ -251,12 +269,14 @@ static inline void __attribute__ ((deprecated)) PIN_PULLDWN_EN(uint32_t PIN_NAME
 #define FUNC_GPIO17_GPIO17_0                        0
 
 #define PERIPHS_IO_MUX_GPIO18_U           (DR_REG_IO_MUX_BASE +0x70)
+#define IO_MUX_GPIO18_REG                    PERIPHS_IO_MUX_GPIO18_U
 #define FUNC_GPIO18_HS1_DATA7                       3
 #define FUNC_GPIO18_GPIO18                          2
 #define FUNC_GPIO18_VSPICLK                         1
 #define FUNC_GPIO18_GPIO18_0                        0
 
 #define PERIPHS_IO_MUX_GPIO19_U           (DR_REG_IO_MUX_BASE +0x74)
+#define IO_MUX_GPIO19_REG                    PERIPHS_IO_MUX_GPIO19_U
 #define FUNC_GPIO19_EMAC_TXD0                       5
 #define FUNC_GPIO19_U0CTS                           3
 #define FUNC_GPIO19_GPIO19                          2
@@ -264,16 +284,19 @@ static inline void __attribute__ ((deprecated)) PIN_PULLDWN_EN(uint32_t PIN_NAME
 #define FUNC_GPIO19_GPIO19_0                        0
 
 #define PERIPHS_IO_MUX_GPIO20_U           (DR_REG_IO_MUX_BASE +0x78)
+#define IO_MUX_GPIO20_REG                    PERIPHS_IO_MUX_GPIO20_U
 #define FUNC_GPIO20_GPIO20                          2
 #define FUNC_GPIO20_GPIO20_0                        0
 
 #define PERIPHS_IO_MUX_GPIO21_U           (DR_REG_IO_MUX_BASE +0x7c)
+#define IO_MUX_GPIO21_REG                    PERIPHS_IO_MUX_GPIO21_U
 #define FUNC_GPIO21_EMAC_TX_EN                      5
 #define FUNC_GPIO21_GPIO21                          2
 #define FUNC_GPIO21_VSPIHD                          1
 #define FUNC_GPIO21_GPIO21_0                        0
 
 #define PERIPHS_IO_MUX_GPIO22_U           (DR_REG_IO_MUX_BASE +0x80)
+#define IO_MUX_GPIO22_REG                    PERIPHS_IO_MUX_GPIO22_U
 #define FUNC_GPIO22_EMAC_TXD1                       5
 #define FUNC_GPIO22_U0RTS                           3
 #define FUNC_GPIO22_GPIO22                          2
@@ -281,59 +304,72 @@ static inline void __attribute__ ((deprecated)) PIN_PULLDWN_EN(uint32_t PIN_NAME
 #define FUNC_GPIO22_GPIO22_0                        0
 
 #define PERIPHS_IO_MUX_GPIO23_U           (DR_REG_IO_MUX_BASE +0x8c)
+#define IO_MUX_GPIO23_REG                    PERIPHS_IO_MUX_GPIO23_U
 #define FUNC_GPIO23_HS1_STROBE                      3
 #define FUNC_GPIO23_GPIO23                          2
 #define FUNC_GPIO23_VSPID                           1
 #define FUNC_GPIO23_GPIO23_0                        0
 
 #define PERIPHS_IO_MUX_GPIO24_U           (DR_REG_IO_MUX_BASE +0x90)
+#define IO_MUX_GPIO24_REG                    PERIPHS_IO_MUX_GPIO24_U
 #define FUNC_GPIO24_GPIO24                          2
 #define FUNC_GPIO24_GPIO24_0                        0
 
 #define PERIPHS_IO_MUX_GPIO25_U           (DR_REG_IO_MUX_BASE +0x24)
+#define IO_MUX_GPIO25_REG                    PERIPHS_IO_MUX_GPIO25_U
 #define FUNC_GPIO25_EMAC_RXD0                       5
 #define FUNC_GPIO25_GPIO25                          2
 #define FUNC_GPIO25_GPIO25_0                        0
 
 #define PERIPHS_IO_MUX_GPIO26_U           (DR_REG_IO_MUX_BASE +0x28)
+#define IO_MUX_GPIO26_REG                    PERIPHS_IO_MUX_GPIO26_U
 #define FUNC_GPIO26_EMAC_RXD1                       5
 #define FUNC_GPIO26_GPIO26                          2
 #define FUNC_GPIO26_GPIO26_0                        0
 
 #define PERIPHS_IO_MUX_GPIO27_U           (DR_REG_IO_MUX_BASE +0x2c)
+#define IO_MUX_GPIO27_REG                    PERIPHS_IO_MUX_GPIO27_U
 #define FUNC_GPIO27_EMAC_RX_DV                      5
 #define FUNC_GPIO27_GPIO27                          2
 #define FUNC_GPIO27_GPIO27_0                        0
 
 #define PERIPHS_IO_MUX_GPIO32_U           (DR_REG_IO_MUX_BASE +0x1c)
+#define IO_MUX_GPIO32_REG                    PERIPHS_IO_MUX_GPIO32_U
 #define FUNC_GPIO32_GPIO32                          2
 #define FUNC_GPIO32_GPIO32_0                        0
 
 #define PERIPHS_IO_MUX_GPIO33_U           (DR_REG_IO_MUX_BASE +0x20)
+#define IO_MUX_GPIO33_REG                    PERIPHS_IO_MUX_GPIO33_U
 #define FUNC_GPIO33_GPIO33                          2
 #define FUNC_GPIO33_GPIO33_0                        0
 
 #define PERIPHS_IO_MUX_GPIO34_U           (DR_REG_IO_MUX_BASE +0x14)
+#define IO_MUX_GPIO34_REG                    PERIPHS_IO_MUX_GPIO34_U
 #define FUNC_GPIO34_GPIO34                          2
 #define FUNC_GPIO34_GPIO34_0                        0
 
 #define PERIPHS_IO_MUX_GPIO35_U           (DR_REG_IO_MUX_BASE +0x18)
+#define IO_MUX_GPIO35_REG                    PERIPHS_IO_MUX_GPIO35_U
 #define FUNC_GPIO35_GPIO35                          2
 #define FUNC_GPIO35_GPIO35_0                        0
 
 #define PERIPHS_IO_MUX_GPIO36_U           (DR_REG_IO_MUX_BASE +0x04)
+#define IO_MUX_GPIO36_REG                    PERIPHS_IO_MUX_GPIO36_U
 #define FUNC_GPIO36_GPIO36                          2
 #define FUNC_GPIO36_GPIO36_0                        0
 
 #define PERIPHS_IO_MUX_GPIO37_U           (DR_REG_IO_MUX_BASE +0x08)
+#define IO_MUX_GPIO37_REG                    PERIPHS_IO_MUX_GPIO37_U
 #define FUNC_GPIO37_GPIO37                          2
 #define FUNC_GPIO37_GPIO37_0                        0
 
 #define PERIPHS_IO_MUX_GPIO38_U           (DR_REG_IO_MUX_BASE +0x0c)
+#define IO_MUX_GPIO38_REG                    PERIPHS_IO_MUX_GPIO38_U
 #define FUNC_GPIO38_GPIO38                          2
 #define FUNC_GPIO38_GPIO38_0                        0
 
 #define PERIPHS_IO_MUX_GPIO39_U           (DR_REG_IO_MUX_BASE +0x10)
+#define IO_MUX_GPIO39_REG                    PERIPHS_IO_MUX_GPIO39_U
 #define FUNC_GPIO39_GPIO39                          2
 #define FUNC_GPIO39_GPIO39_0                        0
 
index 1f7a196d729bbe11a55e1dfcf80ee1e6e15417bf..697f2b68bd8f556d826db18a7470ec89671d3742 100644 (file)
@@ -5,16 +5,14 @@ config SPIFFS_MAX_PARTITIONS
     default 3
     range 1 10
     help
-        Define maximum number of partitions 
-        that can be mounted.
+        Define maximum number of partitions that can be mounted.
 
 menu "SPIFFS Cache Configuration"
 config SPIFFS_CACHE
     bool "Enable SPIFFS Cache"
     default "y"
     help
-        Enables/disable memory read 
-        caching of nucleus file system 
+        Enables/disable memory read caching of nucleus file system 
         operations.
 
 config SPIFFS_CACHE_WR
@@ -22,16 +20,14 @@ config SPIFFS_CACHE_WR
     default "y"
     depends on SPIFFS_CACHE
     help
-        Enables memory write caching for 
-        file descriptors in hydrogen.
+        Enables memory write caching for file descriptors in hydrogen.
 
 config SPIFFS_CACHE_STATS
     bool "Enable SPIFFS Cache Statistics"
     default "n"
     depends on SPIFFS_CACHE
     help
-        Enable/disable statistics on caching. 
-        Debug/test purpose only.
+        Enable/disable statistics on caching. Debug/test purpose only.
 
 endmenu
 
@@ -39,44 +35,54 @@ config SPIFFS_PAGE_CHECK
     bool "Enable SPIFFS Page Check"
     default "y"
     help
-        Always check header of each 
-        accessed page to ensure consistent state.
-        If enabled it will increase number 
-        of reads, will increase flash.
+        Always check header of each accessed page to ensure consistent state.
+        If enabled it will increase number of reads from flash, especially
+        if cache is disabled.
 
 config SPIFFS_GC_MAX_RUNS
     int "Set Maximum GC Runs"
     default 10
     range 1 255
     help
-        Define maximum number of gc runs to 
-        perform to reach desired free pages.
+        Define maximum number of GC runs to perform to reach desired free pages.
 
 config SPIFFS_GC_STATS
     bool "Enable SPIFFS GC Statistics"
     default "n"
     help
-        Enable/disable statistics on gc. 
-        Debug/test purpose only.
+        Enable/disable statistics on gc. Debug/test purpose only.
+
+config SPIFFS_PAGE_SIZE
+       int "SPIFFS logical page size"
+       default 256
+       range 256 1024
+       help
+               Logical page size of SPIFFS partition, in bytes. Must be multiple
+               of flash page size (which is usually 256 bytes).
+               Larger page sizes reduce overhead when storing large files, and
+               improve filesystem performance when reading large files.
+               Smaller page sizes reduce overhead when storing small (< page size)
+               files.
 
 config SPIFFS_OBJ_NAME_LEN
     int "Set SPIFFS Maximum Name Length"
     default 32
     range 1 256
     help
-        Object name maximum length. Note that this length 
-        include the zero-termination character, 
-        meaning maximum string of characters can at most be 
-        SPIFFS_OBJ_NAME_LEN - 1.
+        Object name maximum length. Note that this length include the 
+        zero-termination character, meaning maximum string of characters
+        can at most be SPIFFS_OBJ_NAME_LEN - 1.
+        
+        SPIFFS_OBJ_NAME_LEN + SPIFFS_META_LENGTH should not exceed
+        SPIFFS_PAGE_SIZE - 64.
 
 config SPIFFS_USE_MAGIC
     bool "Enable SPIFFS Filesystem Magic"
     default "y"
     help
         Enable this to have an identifiable spiffs filesystem. 
-        This will look for a magic in all sectors 
-        to determine if this is a valid spiffs system 
-        or not on mount point.
+        This will look for a magic in all sectors to determine if this
+        is a valid spiffs system or not at mount time.
 
 config SPIFFS_USE_MAGIC_LENGTH
     bool "Enable SPIFFS Filesystem Length Magic"
@@ -96,6 +102,9 @@ config SPIFFS_META_LENGTH
         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.
+        
+        SPIFFS_OBJ_NAME_LEN + SPIFFS_META_LENGTH should not exceed
+        SPIFFS_PAGE_SIZE - 64.
 
 config SPIFFS_USE_MTIME
     bool "Save file modification time"
@@ -113,45 +122,39 @@ config SPIFFS_DBG
     bool "Enable general SPIFFS debug"
     default "n"
     help
-        Enabling this option will print 
-        general debug mesages to the console
+        Enabling this option will print general debug mesages to the console.
 
 config SPIFFS_API_DBG
     bool "Enable SPIFFS API debug"
     default "n"
     help
-        Enabling this option will print 
-        API debug mesages to the console
+        Enabling this option will print API debug mesages to the console.
 
 config SPIFFS_GC_DBG
     bool "Enable SPIFFS Garbage Cleaner debug"
     default "n"
     help
-        Enabling this option will print 
-        GC debug mesages to the console
+        Enabling this option will print GC debug mesages to the console.
 
 config SPIFFS_CACHE_DBG
     bool "Enable SPIFFS Cache debug"
     default "n"
     depends on SPIFFS_CACHE
     help
-        Enabling this option will print 
-        Cache debug mesages to the console
+        Enabling this option will print cache debug mesages to the console.
 
 config SPIFFS_CHECK_DBG
     bool "Enable SPIFFS Filesystem Check debug"
     default "n"
     help
-        Enabling this option will print 
-        Filesystem Check debug mesages 
-        to the console
+        Enabling this option will print Filesystem Check debug mesages
+        to the console.
 
 config SPIFFS_TEST_VISUALISATION
     bool "Enable SPIFFS Filesystem Visualization"
     default "n"
     help
-        Enable this option to enable SPIFFS_vis function 
-        in the api.
+        Enable this option to enable SPIFFS_vis function in the API.
 
 endmenu
 
index 2c454e9dd35833b6ba335f2536619c721e89f833..c344e577648022807dd09ebddd805890741d5a99 100644 (file)
@@ -222,6 +222,14 @@ static esp_err_t esp_spiffs_init(const esp_vfs_spiffs_conf_t* conf)
         return ESP_ERR_INVALID_STATE;
     }
 
+    uint32_t flash_page_size = g_rom_flashchip.page_size;
+    uint32_t log_page_size = CONFIG_SPIFFS_PAGE_SIZE;
+    if (log_page_size % flash_page_size != 0) {
+        ESP_LOGE(TAG, "SPIFFS_PAGE_SIZE is not multiple of flash chip page size (%d)",
+                flash_page_size);
+        return ESP_ERR_INVALID_ARG;
+    }
+
     esp_partition_subtype_t subtype = conf->partition_label ?
             ESP_PARTITION_SUBTYPE_ANY : ESP_PARTITION_SUBTYPE_DATA_SPIFFS;
     const esp_partition_t* partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, 
@@ -247,7 +255,7 @@ static esp_err_t esp_spiffs_init(const esp_vfs_spiffs_conf_t* conf)
     efs->cfg.hal_read_f        = spiffs_api_read;
     efs->cfg.hal_write_f       = spiffs_api_write;
     efs->cfg.log_block_size    = g_rom_flashchip.sector_size;
-    efs->cfg.log_page_size     = g_rom_flashchip.page_size;
+    efs->cfg.log_page_size     = log_page_size;
     efs->cfg.phys_addr         = 0;
     efs->cfg.phys_erase_block  = g_rom_flashchip.sector_size;
     efs->cfg.phys_size         = partition->size;
@@ -349,8 +357,12 @@ esp_err_t esp_spiffs_info(const char* partition_label, size_t *total_bytes, size
 
 esp_err_t esp_spiffs_format(const char* partition_label)
 {
-    bool mount_on_success = false;
+    bool partition_was_mounted = false;
     int index;
+    /* If the partition is not mounted, need to create SPIFFS structures
+     * and mount the partition, unmount, format, delete SPIFFS structures.
+     * See SPIFFS wiki for the reason why.
+     */
     esp_err_t err = esp_spiffs_by_label(partition_label, &index);
     if (err != ESP_OK) {
         esp_vfs_spiffs_conf_t conf = {
@@ -363,23 +375,28 @@ esp_err_t esp_spiffs_format(const char* partition_label)
             return err;
         }
         err = esp_spiffs_by_label(partition_label, &index);
-        if (err != ESP_OK) {
-            return err;
-        }
-        esp_spiffs_free(&_efs[index]);
-        return ESP_OK;
+        assert(err == ESP_OK && "failed to get index of the partition just mounted");
     } else if (SPIFFS_mounted(_efs[index]->fs)) {
-        SPIFFS_unmount(_efs[index]->fs);
-        mount_on_success = true;
+        partition_was_mounted = true;
     }
+
+    SPIFFS_unmount(_efs[index]->fs);
+
     s32_t res = SPIFFS_format(_efs[index]->fs);
     if (res != SPIFFS_OK) {
         ESP_LOGE(TAG, "format failed, %i", SPIFFS_errno(_efs[index]->fs));
         SPIFFS_clearerr(_efs[index]->fs);
+        /* If the partition was previously mounted, but format failed, don't
+         * try to mount the partition back (it will probably fail). On the
+         * other hand, if it was not mounted, need to clean up.
+         */
+        if (!partition_was_mounted) {
+            esp_spiffs_free(&_efs[index]);
+        }
         return ESP_FAIL;
     }
 
-    if (mount_on_success) {
+    if (partition_was_mounted) {
         res = SPIFFS_mount(_efs[index]->fs, &_efs[index]->cfg, _efs[index]->work,
                             _efs[index]->fds, _efs[index]->fds_sz, _efs[index]->cache,
                             _efs[index]->cache_sz, spiffs_api_check);
@@ -388,6 +405,8 @@ esp_err_t esp_spiffs_format(const char* partition_label)
             SPIFFS_clearerr(_efs[index]->fs);
             return ESP_FAIL;
         }
+    } else {
+        esp_spiffs_free(&_efs[index]);
     }
     return ESP_OK;
 }
index 28414facf9f302226a82a064a86f28ffbd833726..a382ba6f9568b2c5ea6b1cf7a3eac87fbad2b34a 100755 (executable)
@@ -153,12 +153,15 @@ extern void spiffs_api_unlock(struct spiffs_t *fs);
 // changes the on-disk format, so the change is not backward-compatible.
 //
 // Do note: the meta length must never exceed
-// logical_page_size - (SPIFFS_OBJ_NAME_LEN + 64)
+// logical_page_size - (SPIFFS_OBJ_NAME_LEN + SPIFFS_PAGE_EXTRA_SIZE)
 //
 // 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             (CONFIG_SPIFFS_META_LENGTH)
+#define SPIFFS_PAGE_EXTRA_SIZE          (64)
+_Static_assert(SPIFFS_OBJ_META_LEN + SPIFFS_OBJ_NAME_LEN + SPIFFS_PAGE_EXTRA_SIZE
+        <= CONFIG_SPIFFS_PAGE_SIZE, "SPIFFS_OBJ_META_LEN or SPIFFS_OBJ_NAME_LEN too long");
 
 // Size of buffer allocated on stack used when copying data.
 // Lower value generates more read/writes. No meaning having it bigger
index b428de4cebcae3228ff1743a44365c7dfba1817b..fdd3d241a6390bc6db46a82f586c4c52115c08f3 100644 (file)
@@ -407,7 +407,7 @@ static void test_teardown()
     TEST_ESP_OK(esp_vfs_spiffs_unregister(spiffs_test_partition_label));
 }
 
-TEST_CASE("can format partition", "[spiffs]")
+TEST_CASE("can initialize SPIFFS in erased partition", "[spiffs]")
 {
     const esp_partition_t* part = get_test_data_partition();
     TEST_ASSERT_NOT_NULL(part);
@@ -420,6 +420,44 @@ TEST_CASE("can format partition", "[spiffs]")
     test_teardown();
 }
 
+TEST_CASE("can format mounted partition", "[spiffs]")
+{
+    // Mount SPIFFS, create file, format, check that the file does not exist.
+    const esp_partition_t* part = get_test_data_partition();
+    TEST_ASSERT_NOT_NULL(part);
+    test_setup();
+    const char* filename = "/spiffs/hello.txt";
+    test_spiffs_create_file_with_text(filename, spiffs_test_hello_str);
+    esp_spiffs_format(part->label);
+    FILE* f = fopen(filename, "r");
+    TEST_ASSERT_NULL(f);
+    test_teardown();
+}
+
+TEST_CASE("can format unmounted partition", "[spiffs]")
+{
+    // Mount SPIFFS, create file, unmount. Format. Mount again, check that
+    // the file does not exist.
+    const esp_partition_t* part = get_test_data_partition();
+    TEST_ASSERT_NOT_NULL(part);
+    test_setup();
+    const char* filename = "/spiffs/hello.txt";
+    test_spiffs_create_file_with_text(filename, spiffs_test_hello_str);
+    test_teardown();
+    esp_spiffs_format(part->label);
+    // Don't use test_setup here, need to mount without formatting
+    esp_vfs_spiffs_conf_t conf = {
+        .base_path = "/spiffs",
+        .partition_label = spiffs_test_partition_label,
+        .max_files = 5,
+        .format_if_mount_failed = false
+    };
+    TEST_ESP_OK(esp_vfs_spiffs_register(&conf));
+    FILE* f = fopen(filename, "r");
+    TEST_ASSERT_NULL(f);
+    test_teardown();
+}
+
 TEST_CASE("can create and write file", "[spiffs]")
 {
     test_setup();
diff --git a/docs/_static/esp32-devkitc-dimensions-back.jpg b/docs/_static/esp32-devkitc-dimensions-back.jpg
new file mode 100644 (file)
index 0000000..a19fdce
Binary files /dev/null and b/docs/_static/esp32-devkitc-dimensions-back.jpg differ
diff --git a/docs/_static/esp32-devkitc-functional-overview.jpg b/docs/_static/esp32-devkitc-functional-overview.jpg
new file mode 100644 (file)
index 0000000..0aec635
Binary files /dev/null and b/docs/_static/esp32-devkitc-functional-overview.jpg differ
index 6a5c67669138a7cba12ee82723958df29d714bdc..ee88631232a15d21b8ad6cd21b8fc98d621a9252 100644 (file)
@@ -6,7 +6,7 @@ Overview
 
 A single ESP32's flash can contain multiple apps, as well as many different kinds of data (calibration data, filesystems, parameter storage, etc). For this reason a partition table is flashed to offset 0x8000 in the flash.
 
-Partition table length is 0xC00 bytes (maximum 95 partition table entries). If the partition table is signed due to `secure boot`, the signature is appended after the table data.
+Partition table length is 0xC00 bytes (maximum 95 partition table entries). An MD5 checksum is appended after the table data. If the partition table is signed due to `secure boot`, the signature is appended after the partition table.
 
 Each entry in the partition table has a name (label), type (app, data, or something else), subtype and the offset in flash where the partition is loaded.
 
@@ -148,6 +148,11 @@ To display the contents of a binary partition table on stdout (this is how the s
 
 ``gen_esp32part.py`` takes one optional argument, ``--verify``, which will also verify the partition table during conversion (checking for overlapping partitions, unaligned partitions, etc.)
 
+MD5 checksum
+~~~~~~~~~~~~
+
+The binary format of the partition table contains an MD5 checksum computed based on the partition table. This checksum is used for checking the integrity of the partition table during the boot.
+
 Flashing the partition table
 ----------------------------
 
index 6f123d672985f2b27f6fe3d55a373c71573d1073..bc042ed654b754012a2cf9eba5e35469d5686fbe 100755 (executable)
@@ -857,7 +857,7 @@ Convenience macros for peripheral registers access
 ULP source files are passed through C preprocessor before the assembler. This allows certain macros to be used to facilitate access to peripheral registers.\r
 \r
 Some existing macros are defined in ``soc/soc_ulp.h`` header file. These macros allow access to the fields of peripheral registers by their names.\r
-Peripheral registers names which can be used with these macros are the ones defined in ``soc/rtc_cntl_reg.h``, ``soc/rtc_io_reg.h``, ``soc/sens_reg.h``, and ``soc_rtc_i2c_reg.h``.\r
+Peripheral registers names which can be used with these macros are the ones defined in ``soc/rtc_cntl_reg.h``, ``soc/rtc_io_reg.h``, ``soc/sens_reg.h``, and ``soc/rtc_i2c_reg.h``.\r
 \r
 READ_RTC_REG(rtc_reg, low_bit, bit_width)\r
   Read up to 16 bits from rtc_reg[low_bit + bit_width - 1 : low_bit] into R0. For example::\r
diff --git a/docs/get-started/get-started-devkitc-v2.rst b/docs/get-started/get-started-devkitc-v2.rst
new file mode 100644 (file)
index 0000000..8303c0d
--- /dev/null
@@ -0,0 +1,79 @@
+ESP32-DevKitC V2 Getting Started Guide\r
+======================================\r
+\r
+This user guide shows how to get started with ESP32-DevKitC development board.\r
+\r
+\r
+What You Need\r
+-------------\r
+\r
+* 1 Ã— :ref:`ESP32-DevKitC V2 board <get-started-esp32-devkitc-v2-board-front>`\r
+* 1 Ã— USB A / micro USB B cable \r
+* 1 Ã— PC loaded with Windows, Linux or Mac OS\r
+\r
+\r
+Overview\r
+--------\r
+\r
+ESP32-DevKitC is a small-sized ESP32-based development board produced by `Espressif <https://espressif.com>`_. Most of the I/O pins are broken out to the pin headers on both sides for easy interfacing. Developers can connect these pins to peripherals as needed. Standard headers also make development easy and convenient when using a breadboard.\r
+\r
+\r
+Functional Description\r
+----------------------\r
+\r
+The following list and figure below describe key components, interfaces and controls of ESP32-DevKitC board.\r
+\r
+ESP-WROOM-32\r
+    Standard `ESP-WROOM-32 <https://www.espressif.com/sites/default/files/documentation/esp-wroom-32_datasheet_en.pdf>`_ module soldered to the ESP32-DevKitC board.\r
+EN\r
+    Reset button: pressing this button resets the system.\r
+Boot\r
+    Download 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.\r
+USB\r
+    USB interface. It functions as the power supply for the board and the communication interface between PC and ESP-WROOM-32.\r
+I/O\r
+    Most of the pins on the ESP-WROOM-32 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.\r
+\r
+.. _get-started-esp32-devkitc-v2-board-front:\r
+\r
+.. figure:: ../_static/esp32-devkitc-v2-functional-overview.png\r
+    :align: center\r
+    :alt: ESP32-DevKitC V2 board layout\r
+    :figclass: align-center\r
+\r
+    ESP32-DevKitC V2 board layout\r
+\r
+\r
+Power Supply Options\r
+--------------------\r
+\r
+There following options are available to provide power supply to this board:\r
+\r
+1. Micro USB port, this is default power supply connection\r
+2. 5V / GND header pins\r
+3. 3V3 / GND header pins\r
+\r
+.. warning::\r
+\r
+    Above options are mutually exclusive, i.e. the power supply may be provided using only one of the above options. Attempt to power the board using more than one connection at a time may damage the board and/or the power supply source.\r
+\r
+\r
+Start Application Development\r
+------------------------------\r
+\r
+Before powering up the ESP32-DevKitC, please make sure that the board has been received in good condition with no obvious signs of damage.\r
+\r
+To start development of applications, proceed to section :doc:`index`, that will walk you through the following steps:\r
+\r
+* :ref:`get-started-setup-toolchain` in your PC to develop applications for ESP32 in C language\r
+* :ref:`get-started-connect` the module to the PC and verify if it is accessible\r
+* :ref:`get-started-build-flash` an example application to the ESP32\r
+* :ref:`get-started-build-monitor` instantly what the application is doing\r
+\r
+\r
+Related Documents\r
+-----------------\r
+\r
+* `ESP32-DevKitC schematic <https://dl.espressif.com/dl/schematics/ESP32-Core-Board-V2_sch.pdf>`_ (PDF)\r
+* `ESP32 Datasheet <https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf>`_ (PDF)\r
+* `ESP-WROOM-32 Datasheet <https://espressif.com/sites/default/files/documentation/esp-wroom-32_datasheet_en.pdf>`_ (PDF)\r
index b5f14ab10e03b3a93ffda96ad8f40744429cd341..1c9772bbf8a68e82dcb5889ec56b7b062e5bea6d 100644 (file)
@@ -15,7 +15,9 @@ What You Need
 Overview\r
 --------\r
 \r
-ESP32-DevKitC is a small-sized ESP32-based development board produced by `Espressif <https://espressif.com>`_. Most of the I/O pins are broken out to the pin headers on both sides for easy interfacing. Developers can connect these pins to peripherals as needed. Standard headers also make development easy and convenient when using a breadboard.\r
+ESP32-DevKitC is a small-sized ESP32-based development board produced by `Espressif <https://espressif.com>`_. Most of the I/O pins are broken out to the pin headers on both sides for easy interfacing. Developers can connect these pins to peripherals as needed. Standard headers also make development easy and convenient when using a breadboard. \r
+\r
+The board comes in two versions, either with :ref:`esp-modules-and-boards-esp-wroom-32` or :ref:`esp-modules-and-boards-esp32-wrover` module soldered.\r
 \r
 \r
 Functional Description\r
@@ -24,30 +26,45 @@ Functional Description
 The following list and figure below describe key components, interfaces and controls of ESP32-DevKitC board.\r
 \r
 ESP-WROOM-32\r
-    Standard `ESP-WROOM-32 <https://www.espressif.com/sites/default/files/documentation/esp-wroom-32_datasheet_en.pdf>`_ module soldered to the ESP32-DevKitC board.\r
-EN\r
-    Reset button: pressing this button resets the system.\r
+    :ref:`esp-modules-and-boards-esp-wroom-32` module soldered to the ESP32-DevKitC board.\r
+ESP32-WROVER\r
+    Optionally :ref:`esp-modules-and-boards-esp32-wrover` module may be soldered instead of the ESP-WROOM-32.\r
+USB-UART Bridge\r
+    A single chip USB-UART bridge provides up to 3 Mbps transfers rates.\r
 Boot\r
     Download 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.\r
-USB\r
-    USB interface. It functions as the power supply for the board and the communication interface between PC and ESP-WROOM-32.\r
+Micro USB Port\r
+    USB interface. It functions as the power supply for the board and the communication interface between PC and the ESP module.\r
+5V Power On LED\r
+    This LED lights when the USB or an external 5V power supply is applied to the board. For details see schematic in `Related Documents`_.\r
+EN\r
+    Reset button: pressing this button resets the system.\r
 I/O\r
-    Most of the pins on the ESP-WROOM-32 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.\r
+    Most of the pins on the ESP module 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.\r
+\r
+    .. note::\r
+\r
+        Some of broken out pins are used internally be the ESP32 module to communicate with SPI memory. They are grouped on one side of the board besides the USB connector and labeled D0, D1, D2, D3, CMD and CLK. In general these pins should be left unconnected or access to the SPI flash memory / SPI RAM may be disturbed. \r
+\r
+    .. note::\r
+\r
+        GPIO16 and 17 are used internally by the ESP32-WROVER module. They are broken out and avialable for use only for boards that have the ESP-WROOM-32 module installed.\r
+\r
 \r
 .. _get-started-esp32-devkitc-board-front:\r
 \r
-.. figure:: ../_static/esp32-devkitc-functional-overview.png\r
+.. figure:: ../_static/esp32-devkitc-functional-overview.jpg\r
     :align: center\r
-    :alt: ESP32-DevKitC board layout\r
+    :alt: ESP32-DevKitC with ESP-WROOM-32 module soldered\r
     :figclass: align-center\r
 \r
-    ESP32-DevKitC board layout\r
+    ESP32-DevKitC with ESP-WROOM-32 module soldered\r
 \r
 \r
 Power Supply Options\r
 --------------------\r
 \r
-There following options are available to provide power supply to the ESP32-PICO-KIT V4:\r
+There following options are available to provide power supply to this board:\r
 \r
 1. Micro USB port, this is default power supply connection\r
 2. 5V / GND header pins\r
@@ -71,9 +88,26 @@ To start development of applications, proceed to section :doc:`index`, that will
 * :ref:`get-started-build-monitor` instantly what the application is doing\r
 \r
 \r
+Board Dimensions\r
+----------------\r
+\r
+.. figure:: ../_static/esp32-devkitc-dimensions-back.jpg\r
+    :align: center\r
+    :alt: ESP32 DevKitC board dimensions - back\r
+    :figclass: align-center\r
+\r
+    ESP32 DevKitC board dimensions - back\r
+\r
+\r
 Related Documents\r
 -----------------\r
 \r
-* `ESP32-DevKitC schematic <https://dl.espressif.com/dl/schematics/ESP32-Core-Board-V2_sch.pdf>`_ (PDF)\r
+* `ESP32-DevKitC schematic <https://dl.espressif.com/dl/schematics/esp32_devkitc_v4-sch.pdf>`_ (PDF)\r
 * `ESP32 Datasheet <https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf>`_ (PDF)\r
 * `ESP-WROOM-32 Datasheet <https://espressif.com/sites/default/files/documentation/esp-wroom-32_datasheet_en.pdf>`_ (PDF)\r
+* `ESP32-WROVER Datasheet <https://espressif.com/sites/default/files/documentation/esp32-wrover_datasheet_en.pdf>`_ (PDF)\r
+\r
+.. toctree::\r
+    :hidden:\r
+\r
+    get-started-devkitc-v2
\ No newline at end of file
index 95a968804196fabc1a9f56cb7417fcdb7f6af9d9..41f7642502844372e8b86954b91fe276bf89d7d0 100644 (file)
@@ -30,6 +30,28 @@ Documentation
 * `ESP32-PICO-D4 Datasheet <http://espressif.com/sites/default/files/documentation/esp32-pico-d4_datasheet_en.pdf>`_ (PDF)
 
 
+.. _esp-modules-and-boards-esp32-devkitc-v2:
+
+ESP32 Core Board V2 / ESP32 DevKitC
+-----------------------------------
+
+Small and convenient development board with :ref:`esp-modules-and-boards-esp-wroom-32` module installed, break out pin headers and minimum additional components. Includes USB to serial programming interface, that also provides power supply for the board. Has pushbuttons to reset the board and put it in upload mode. 
+
+.. figure:: https://dl.espressif.com/dl/schematics/pictures/esp32-core-board-v2.png
+    :align: center
+    :alt: ESP32 Core Board V2 / ESP32 DevKitC board
+    :width: 50%
+
+    ESP32 Core Board V2 / ESP32 DevKitC board
+
+Documentation
+"""""""""""""
+
+* :doc:`../get-started/get-started-devkitc-v2`
+* `ESP32 DevKitC V2 Schematic <https://dl.espressif.com/dl/schematics/ESP32-Core-Board-V2_sch.pdf>`__ (PDF)
+* `CP210x USB to UART Bridge VCP Drivers <https://www.silabs.com/products/development-tools/software/usb-to-uart-bridge-vcp-drivers>`_
+
+
 .. _esp-modules-and-boards-esp-wrover-kit-v1:
 
 ESP-WROVER-KIT V1 / ESP32 DevKitJ V1
index b300a56688fbbaf5c3194eb5c9a3aa52a3cbb303..c1d8cd08d1e251f6fb01c7f30d84c08d91163347 100644 (file)
@@ -104,6 +104,7 @@ Documentation
 
 * `ESP32-WROVER Datasheet <https://espressif.com/sites/default/files/documentation/esp32-wrover_datasheet_en.pdf>`__ (PDF)
 * `ESP-PSRAM32 Datasheet <https://espressif.com/sites/default/files/documentation/esp-psram32_datasheet_en.pdf>`__ (PDF)
+* `ESP32-WROVER Reference Design <http://www.espressif.com/sites/default/files/documentation/esp32-wrover_reference_design.zip>`_ PDF containing OrCAD schematic, PCB layout, gerbers and BOM
 
 
 .. _esp-modules-and-boards-esp32-pico-pit-v4:
@@ -138,26 +139,31 @@ Previous Versions
 
 .. _esp-modules-and-boards-esp32-devkitc:
    
-ESP32 Core Board V2 / ESP32 DevKitC
-===================================
+ESP32 DevKitC V4
+================
 
-Small and convenient development board with :ref:`esp-modules-and-boards-esp-wroom-32` module installed, break out pin headers and minimum additional components. Includes USB to serial programming interface, that also provides power supply for the board. Has pushbuttons to reset the board and put it in upload mode. 
+Small and convenient development board with :ref:`esp-modules-and-boards-esp-wroom-32` module installed, break out pin headers and minimum additional components. Includes USB to serial programming interface, that also provides power supply for the board. Has pushbuttons to reset the board and put it in upload mode. Comparing to the previous :ref:`esp-modules-and-boards-esp32-devkitc-v2`, instead of ESP-WROOM-32 it can accommodate :ref:`esp-modules-and-boards-esp32-wrover` module and has CP2102N chip that supports faster baud rates.
 
-.. figure:: https://dl.espressif.com/dl/schematics/pictures/esp32-core-board-v2.png
+.. figure:: https://dl.espressif.com/dl/schematics/pictures/esp32-devkitc-v4-front.jpg
     :align: center
-    :alt: ESP32 Core Board V2 / ESP32 DevKitC board
+    :alt: ESP32 DevKitC V4 board
     :width: 50%
 
-    ESP32 Core Board V2 / ESP32 DevKitC board
+    ESP32 DevKitC V4 board
 
 Documentation
 -------------
 
 * :doc:`../get-started/get-started-devkitc`
-* `ESP32 DevKitC Schematic <https://dl.espressif.com/dl/schematics/ESP32-Core-Board-V2_sch.pdf>`__ (PDF)
-* `ESP32 Development Board Reference Design <https://espressif.com/sites/default/files/documentation/esp32_development_board_reference_design.zip>`_ (ZIP) containing OrCAD schematic, PCB layout, gerbers and BOM
+* `ESP32-DevKitC schematic <https://dl.espressif.com/dl/schematics/esp32_devkitc_v4-sch.pdf>`_ (PDF)
+* `ESP32-DevKitC Reference Design <http://www.espressif.com/sites/default/files/documentation/esp32-devkitc-v4_reference_design_0.zip>`_ (ZIP) containing OrCAD schematic, PCB layout, gerbers and BOM
 * `CP210x USB to UART Bridge VCP Drivers <https://www.silabs.com/products/development-tools/software/usb-to-uart-bridge-vcp-drivers>`_
 
+Previous Versions
+-----------------
+
+* :ref:`esp-modules-and-boards-esp32-devkitc-v2`
+
 
 .. _esp-modules-and-boards-esp-wrover-kit-v3:
 
index e58457cbcb12c618bb2f4f2fbc0878c3fd7b5ce2..80cadb400f444fa53fbcac3592ac5b754bf7b9bb 100644 (file)
@@ -110,7 +110,8 @@ esp_err_t iperf_start_report(void)
 {
     int ret;
 
-    ret = xTaskCreate(iperf_report_task, IPERF_REPORT_TASK_NAME, IPERF_REPORT_TASK_STACK, NULL, IPERF_REPORT_TASK_PRIORITY, NULL);
+    ret = xTaskCreatePinnedToCore(iperf_report_task, IPERF_REPORT_TASK_NAME, IPERF_REPORT_TASK_STACK, NULL, IPERF_REPORT_TASK_PRIORITY, NULL, portNUM_PROCESSORS-1);
+
     if (ret != pdPASS)  {
         ESP_LOGE(TAG, "create task %s failed", IPERF_REPORT_TASK_NAME);
         return ESP_FAIL;
@@ -191,7 +192,7 @@ esp_err_t iperf_run_tcp_server(void)
     return ESP_OK;
 }
 
-esp_err_t iperf_run_udp_server(void)
+esp_err_t IRAM_ATTR iperf_run_udp_server(void)
 {
     socklen_t addr_len = sizeof(struct sockaddr_in);
     struct sockaddr_in addr;
@@ -321,7 +322,8 @@ esp_err_t iperf_run_udp_client(void)
 
 esp_err_t iperf_run_tcp_client(void)
 {
-    struct sockaddr_in addr;
+    struct sockaddr_in local_addr;
+    struct sockaddr_in remote_addr;
     int actual_send = 0;
     int want_send = 0;
     uint8_t *buffer;
@@ -336,19 +338,21 @@ esp_err_t iperf_run_tcp_client(void)
 
     setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
 
-    addr.sin_family = AF_INET;
-    addr.sin_port = 0;
-    addr.sin_addr.s_addr = s_iperf_ctrl.cfg.sip;
-    if (bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
+    memset(&local_addr, 0, sizeof(local_addr));
+    local_addr.sin_family = AF_INET;
+    local_addr.sin_port = 0;
+    local_addr.sin_addr.s_addr = s_iperf_ctrl.cfg.sip;
+    if (bind(sockfd, (struct sockaddr*)&local_addr, sizeof(local_addr)) != 0) {
         iperf_show_socket_error_reason("tcp client bind", sockfd);
         return ESP_FAIL;
     }
 
 
-    addr.sin_family = AF_INET;
-    addr.sin_port = htons(s_iperf_ctrl.cfg.dport);
-    addr.sin_addr.s_addr = s_iperf_ctrl.cfg.dip;
-    if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+    memset(&remote_addr, 0, sizeof(remote_addr));
+    remote_addr.sin_family = AF_INET;
+    remote_addr.sin_port = htons(s_iperf_ctrl.cfg.dport);
+    remote_addr.sin_addr.s_addr = s_iperf_ctrl.cfg.dip;
+    if (connect(sockfd, (struct sockaddr *)&remote_addr, sizeof(remote_addr)) < 0) {
         iperf_show_socket_error_reason("tcp client connect", sockfd);
         return ESP_FAIL;
     }
@@ -359,7 +363,8 @@ esp_err_t iperf_run_tcp_client(void)
     while (!s_iperf_ctrl.finish) {
         actual_send = send(sockfd, buffer, want_send, 0);
         if (actual_send <= 0) {
-            vTaskDelay(1);
+            iperf_show_socket_error_reason("tcp client send", sockfd);
+            break;
         } else {
             s_iperf_ctrl.total_len += actual_send;
         }
@@ -427,8 +432,9 @@ esp_err_t iperf_start(iperf_cfg_t *cfg)
         ESP_LOGE(TAG, "create buffer: out of memory");
         return ESP_FAIL;
     }
+    memset(s_iperf_ctrl.buffer, 0, s_iperf_ctrl.buffer_len);
 
-    ret = xTaskCreate(iperf_task_traffic, IPERF_TRAFFIC_TASK_NAME, IPERF_TRAFFIC_TASK_STACK, NULL, IPERF_TRAFFIC_TASK_PRIORITY, NULL);
+    ret = xTaskCreatePinnedToCore(iperf_task_traffic, IPERF_TRAFFIC_TASK_NAME, IPERF_TRAFFIC_TASK_STACK, NULL, IPERF_TRAFFIC_TASK_PRIORITY, NULL, portNUM_PROCESSORS-1);
     if (ret != pdPASS)  {
         ESP_LOGE(TAG, "create task %s failed", IPERF_TRAFFIC_TASK_NAME);
         free(s_iperf_ctrl.buffer);
index 5d19e1d6d8a1248c3874ea530f9ea81c8280259b..1bbdaf18d3d7ea71c19381b6e902a2c9df2fd367 100644 (file)
@@ -24,17 +24,17 @@ extern "C" {
 #define IPERF_DEFAULT_TIME     30
 
 #define IPERF_TRAFFIC_TASK_NAME       "iperf_traffic"
-#define IPERF_TRAFFIC_TASK_PRIORITY   19
+#define IPERF_TRAFFIC_TASK_PRIORITY   10
 #define IPERF_TRAFFIC_TASK_STACK      4096
 #define IPERF_REPORT_TASK_NAME        "iperf_report"
-#define IPERF_REPORT_TASK_PRIORITY    10
+#define IPERF_REPORT_TASK_PRIORITY    20
 #define IPERF_REPORT_TASK_STACK       4096
 #define IPERF_REPORT_TASK_NAME        "iperf_report"
 
 #define IPERF_UDP_TX_LEN     (1472)
-#define IPERF_UDP_RX_LEN     (32<<10)
-#define IPERF_TCP_TX_LEN     (32<<10)
-#define IPERF_TCP_RX_LEN     (32<<10)
+#define IPERF_UDP_RX_LEN     (16<<10)
+#define IPERF_TCP_TX_LEN     (16<<10)
+#define IPERF_TCP_RX_LEN     (16<<10)
 
 #define IPERF_MAX_DELAY      64
 
index 2ec55c5eeda39a17745f19197c14dcce5dfe6d64..52b0cbaef6414c18efdd0d40e38b2862474770d6 100644 (file)
@@ -6,9 +6,9 @@ CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=16
 CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=64
 CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM=64
 CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED=y
-CONFIG_ESP32_WIFI_TX_BA_WIN=12
+CONFIG_ESP32_WIFI_TX_BA_WIN=32
 CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y
-CONFIG_ESP32_WIFI_RX_BA_WIN=12
+CONFIG_ESP32_WIFI_RX_BA_WIN=32
 
 CONFIG_FREERTOS_UNICORE=
 CONFIG_FREERTOS_HZ=1000
@@ -23,3 +23,7 @@ CONFIG_UDP_RECVMBOX_SIZE=64
 CONFIG_TCPIP_RECVMBOX_SIZE=64
 CONFIG_LWIP_ETHARP_TRUST_IP_MAC=
 
+CONFIG_FLASHMODE_QIO=y
+CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
+CONFIG_LWIP_IRAM_OPTIMIZATION=y
+
index d3972aa5d5a4ac3e6e3882e2e55ec844da6542f3..789d4e844feddcac878109a339b0683e2f200fef 100644 (file)
@@ -3,6 +3,7 @@ components/bt/lib                                   @GENERAL_MIRROR_SERVER@/idf/
 components/aws_iot/aws-iot-device-sdk-embedded-C    @GENERAL_MIRROR_SERVER@/idf/aws-iot-device-sdk-embedded-C.git   ALLOW_TO_SYNC_FROM_PUBLIC
 components/coap/libcoap                             @GENERAL_MIRROR_SERVER@/idf/libcoap.git                         ALLOW_TO_SYNC_FROM_PUBLIC
 components/esptool_py/esptool                       @GENERAL_MIRROR_SERVER@/idf/esptool.git                         ALLOW_TO_SYNC_FROM_PUBLIC
+components/json/cJSON                               @GENERAL_MIRROR_SERVER@/idf/cJSON.git                           ALLOW_TO_SYNC_FROM_PUBLIC
 components/libsodium/libsodium                      @GENERAL_MIRROR_SERVER@/idf/libsodium.git                       ALLOW_TO_SYNC_FROM_PUBLIC
 components/micro-ecc/micro-ecc                      @GENERAL_MIRROR_SERVER@/idf/micro-ecc.git                       ALLOW_TO_SYNC_FROM_PUBLIC
 components/nghttp/nghttp2                           @GENERAL_MIRROR_SERVER@/idf/nghttp2.git                         ALLOW_TO_SYNC_FROM_PUBLIC
index ccaddffa138a7cef6dfc6b8562313f916cf9fc2e..f0dcd6f7c6caccc0596ee5e2248992cf50b2c463 100755 (executable)
@@ -443,13 +443,25 @@ class Monitor(object):
         with self:  # disable console control
             sys.stderr.write(ANSI_NORMAL)
             try:
-                subprocess.call(["%sgdb" % self.toolchain_prefix,
+                process = subprocess.Popen(["%sgdb" % self.toolchain_prefix,
                                 "-ex", "set serial baud %d" % self.serial.baudrate,
                                 "-ex", "target remote %s" % self.serial.port,
                                 "-ex", "interrupt",  # monitor has already parsed the first 'reason' command, need a second
                                 self.elf_file], cwd=".")
+                process.wait()
             except KeyboardInterrupt:
                 pass  # happens on Windows, maybe other OSes
+            finally:
+                try:
+                    # on Linux, maybe other OSes, gdb sometimes seems to be alive even after wait() returns...
+                    process.terminate()
+                except:
+                    pass
+                try:
+                    # also on Linux, maybe other OSes, gdb sometimes exits uncleanly and breaks the tty mode
+                    subprocess.call(["stty", "sane"])
+                except:
+                    pass  # don't care if there's no stty, we tried...
             self.prompt_next_action("gdb exited")
 
     def output_enable(self, enable):
@@ -567,6 +579,17 @@ if os.name == 'nt':
             self.handle = GetStdHandle(STD_ERROR_HANDLE if self.output == sys.stderr else STD_OUTPUT_HANDLE)
             self.matched = b''
 
+        def _output_write(self, data):
+            # Windows 10 bug since the Fall Creators Update, sometimes writing to console randomly fails
+            # (but usually succeeds afterwards, it seems.)
+            # Ref https://github.com/espressif/esp-idf/issues/1136
+            for tries in range(3):
+                try:
+                    self.output.write(data)
+                    return
+                except IOError:
+                    pass
+
         def write(self, data):
             for b in data:
                 l = len(self.matched)
@@ -585,18 +608,10 @@ if os.name == 'nt':
                                 color |= FOREGROUND_INTENSITY
                             SetConsoleTextAttribute(self.handle, color)
                         else:
-                            self.output.write(self.matched) # not an ANSI color code, display verbatim
+                            self._output_write(self.matched) # not an ANSI color code, display verbatim
                         self.matched = b''
                 else:
-                    try:
-                        self.output.write(b)
-                    except IOError:
-                        # Windows 10 bug since the Fall Creators Update, sometimes writing to console randomly fails
-                        # (but usually succeeds the second time, it seems.) Ref https://github.com/espressif/esp-idf/issues/1136
-                        try:
-                            self.output.write(b)
-                        except IOError:
-                            pass
+                    self._output_write(b)
                     self.matched = b''
 
         def flush(self):
index 1cd36131310aa65e9dd0916167a0ae2d8307d931..f13a803e440d714d719cbe16ae982eb996d79408 100644 (file)
@@ -22,147 +22,16 @@ import sys
 import re
 import argparse
 
-import yaml
-
 test_fw_path = os.getenv("TEST_FW_PATH")
 if test_fw_path:
     sys.path.insert(0, test_fw_path)
 
-from Utility import CaseConfig, SearchCases, GitlabCIJob
-
-
-class Group(object):
-
-    MAX_EXECUTION_TIME = 30
-    MAX_CASE = 15
-    SORT_KEYS = ["env_tag"]
-
-    def __init__(self, case):
-        self.execution_time = 0
-        self.case_list = [case]
-        self.filters = dict(zip(self.SORT_KEYS, [case.case_info[x] for x in self.SORT_KEYS]))
-
-    def accept_new_case(self):
-        """
-        check if allowed to add any case to this group
-
-        :return: True or False
-        """
-        max_time = (sum([x.case_info["execution_time"] for x in self.case_list]) < self.MAX_EXECUTION_TIME)
-        max_case = (len(self.case_list) < self.MAX_CASE)
-        return max_time and max_case
-
-    def add_case(self, case):
-        """
-        add case to current group
-
-        :param case: test case
-        :return: True if add succeed, else False
-        """
-        added = False
-        if self.accept_new_case():
-            for key in self.filters:
-                if case.case_info[key] != self.filters[key]:
-                    break
-            else:
-                self.case_list.append(case)
-                added = True
-        return added
-
-    def output(self):
-        """
-        output data for job configs
-
-        :return: {"Filter": case filter, "CaseConfig": list of case configs for cases in this group}
-        """
-        output_data = {
-            "Filter": self.filters,
-            "CaseConfig": [{"name": x.case_info["name"]} for x in self.case_list],
-        }
-        return output_data
-
+from Utility.CIAssignTest import AssignTest
 
-class AssignTest(object):
-    """
-    Auto assign tests to CI jobs.
-
-    :param test_case: path of test case file(s)
-    :param ci_config_file: path of ``.gitlab-ci.yml``
-    """
 
+class CIExampleAssignTest(AssignTest):
     CI_TEST_JOB_PATTERN = re.compile(r"^example_test_.+")
 
-    def __init__(self, test_case, ci_config_file):
-        self.test_cases = self._search_cases(test_case)
-        self.jobs = self._parse_gitlab_ci_config(ci_config_file)
-
-    def _parse_gitlab_ci_config(self, ci_config_file):
-
-        with open(ci_config_file, "r") as f:
-            ci_config = yaml.load(f)
-
-        job_list = list()
-        for job_name in ci_config:
-            if self.CI_TEST_JOB_PATTERN.search(job_name) is not None:
-                job_list.append(GitlabCIJob.Job(ci_config[job_name], job_name))
-        return job_list
-
-    @staticmethod
-    def _search_cases(test_case, case_filter=None):
-        """
-        :param test_case: path contains test case folder
-        :param case_filter: filter for test cases
-        :return: filtered test case list
-        """
-        test_methods = SearchCases.Search.search_test_cases(test_case)
-        return CaseConfig.filter_test_cases(test_methods, case_filter if case_filter else dict())
-
-    def _group_cases(self):
-        """
-        separate all cases into groups according group rules. each group will be executed by one CI job.
-
-        :return: test case groups.
-        """
-        groups = []
-        for case in self.test_cases:
-            for group in groups:
-                # add to current group
-                if group.add_case(case):
-                    break
-            else:
-                # create new group
-                groups.append(Group(case))
-        return groups
-
-    def assign_cases(self):
-        """
-        separate test cases to groups and assign test cases to CI jobs.
-
-        :raise AssertError: if failed to assign any case to CI job.
-        :return: None
-        """
-        failed_to_assign = []
-        test_groups = self._group_cases()
-        for group in test_groups:
-            for job in self.jobs:
-                if job.match_group(group):
-                    job.assign_group(group)
-                    break
-            else:
-                failed_to_assign.append(group)
-        assert not failed_to_assign
-
-    def output_configs(self, output_path):
-        """
-
-        :param output_path: path to output config files for each CI job
-        :return: None
-        """
-        if not os.path.exists(output_path):
-            os.makedirs(output_path)
-        for job in self.jobs:
-            job.output_config(output_path)
-
 
 if __name__ == '__main__':
     parser = argparse.ArgumentParser()
@@ -174,6 +43,6 @@ if __name__ == '__main__':
                         help="output path of config files")
     args = parser.parse_args()
 
-    assign_test = AssignTest(args.test_case, args.ci_config_file)
+    assign_test = CIExampleAssignTest(args.test_case, args.ci_config_file)
     assign_test.assign_cases()
     assign_test.output_configs(args.output_path)
diff --git a/tools/tiny-test-fw/CIAssignUnitTest.py b/tools/tiny-test-fw/CIAssignUnitTest.py
new file mode 100644 (file)
index 0000000..a621eb8
--- /dev/null
@@ -0,0 +1,121 @@
+"""
+Command line tool to assign unit tests to CI test jobs.
+"""
+
+import re
+import os
+import sys
+import argparse
+
+import yaml
+
+test_fw_path = os.getenv("TEST_FW_PATH")
+if test_fw_path:
+    sys.path.insert(0, test_fw_path)
+
+from Utility import CIAssignTest
+
+
+class Group(CIAssignTest.Group):
+    SORT_KEYS = ["Test App", "SDK", "test environment"]
+    MAX_CASE = 30
+    ATTR_CONVERT_TABLE = {
+        "execution_time": "execution time"
+    }
+
+    @staticmethod
+    def _get_case_attr(case, attr):
+        if attr in Group.ATTR_CONVERT_TABLE:
+            attr = Group.ATTR_CONVERT_TABLE[attr]
+        return case[attr]
+
+    @staticmethod
+    def _get_ut_config(test_app):
+        # we format test app "UT_ + config" when parsing test cases
+        # now we need to extract config
+        assert test_app[:3] == "UT_"
+        return test_app[3:]
+
+    def _create_extra_data(self):
+        case_data = []
+        for case in self.case_list:
+            if self._get_case_attr(case, "cmd set") == "multiple_devices_case":
+                case_data.append({
+                    "config": self._get_ut_config(self._get_case_attr(case, "Test App")),
+                    "name": self._get_case_attr(case, "summary"),
+                    "child case num": self._get_case_attr(case, "child case num")
+                })
+            else:
+                case_data.append({
+                    "config": self._get_ut_config(self._get_case_attr(case, "Test App")),
+                    "name": self._get_case_attr(case, "summary"),
+                    "reset": self._get_case_attr(case, "reset") ,
+                })
+        return case_data
+
+    def output(self):
+        """
+        output data for job configs
+
+        :return: {"Filter": case filter, "CaseConfig": list of case configs for cases in this group}
+        """
+        output_data = {
+            # we don't need filter for test function, as UT uses a few test functions for all cases
+            "CaseConfig": [
+                {
+                    "name": self.case_list[0]["cmd set"] if isinstance(self.case_list[0]["cmd set"], str) else self.case_list[0]["cmd set"][0],
+                    "extra_data": self._create_extra_data(),
+                }
+            ]
+        }
+        return output_data
+
+
+class UnitTestAssignTest(CIAssignTest.AssignTest):
+    CI_TEST_JOB_PATTERN = re.compile(r"^UT_.+")
+
+    def __init__(self, test_case_path, ci_config_file):
+        CIAssignTest.AssignTest.__init__(self, test_case_path, ci_config_file, case_group=Group)
+
+    @staticmethod
+    def _search_cases(test_case_path, case_filter=None):
+        """
+        For unit test case, we don't search for test functions.
+        The unit test cases is stored in a yaml file which is created in job build-idf-test.
+        """
+
+        with open(test_case_path, "r") as f:
+            raw_data = yaml.load(f)
+        test_cases = raw_data["test cases"]
+        if case_filter:
+            for key in case_filter:
+                filtered_cases = []
+                for case in test_cases:
+                    try:
+                        # bot converts string to lower case
+                        if isinstance(case[key], str):
+                            _value = case[key].lower()
+                        else:
+                            _value = case[key]
+                        if _value in case_filter[key]:
+                            filtered_cases.append(case)
+                    except KeyError:
+                        # case don't have this key, regard as filter success
+                        filtered_cases.append(case)
+                test_cases = filtered_cases
+        return test_cases
+
+
+if __name__ == '__main__':
+    parser = argparse.ArgumentParser()
+    parser.add_argument("test_case",
+                        help="test case folder or file")
+    parser.add_argument("ci_config_file",
+                        help="gitlab ci config file")
+    parser.add_argument("output_path",
+                        help="output path of config files")
+    args = parser.parse_args()
+
+    assign_test = UnitTestAssignTest(args.test_case, args.ci_config_file)
+    assign_test.assign_cases()
+    assign_test.output_configs(args.output_path)
index 1c6526709b4271a9b0da42a863422a23e307376d..1cfd2c7d531872cf2ada018940ceaa2a816ec160 100644 (file)
@@ -316,7 +316,7 @@ class BaseDUT(object):
         if flush:
             self.data_cache.flush()
         # do write if cache
-        if data:
+        if data is not None:
             self._port_write(data + eol if eol else data)
 
     @_expect_lock
@@ -557,9 +557,9 @@ class SerialDUT(BaseDUT):
         :return: formatted data (str)
         """
         timestamp = time.time()
-        timestamp = "{}:{}".format(time.strftime("%m-%d %H:%M:%S", time.localtime(timestamp)),
-                                   str(timestamp % 1)[2:5])
-        formatted_data = "[{}]:\r\n{}\r\n".format(timestamp, _decode_data(data))
+        timestamp = "[{}:{}]".format(time.strftime("%m-%d %H:%M:%S", time.localtime(timestamp)),
+                                     str(timestamp % 1)[2:5])
+        formatted_data = timestamp.encode() + b"\r\n" + data + b"\r\n"
         return formatted_data
 
     def _port_open(self):
@@ -571,11 +571,13 @@ class SerialDUT(BaseDUT):
     def _port_read(self, size=1):
         data = self.port_inst.read(size)
         if data:
-            with open(self.log_file, "a+") as _log_file:
+            with open(self.log_file, "ab+") as _log_file:
                 _log_file.write(self._format_data(data))
         return data
 
     def _port_write(self, data):
+        if isinstance(data, str):
+            data = data.encode()
         self.port_inst.write(data)
 
     @classmethod
index 3828277ed860b42ba1fdf5dbb07d1d861c373f80..4bf667f64bf55711ee869cf9acac3a8e4ffe42cf 100644 (file)
@@ -144,11 +144,28 @@ class Example(IDFApp):
 
 class UT(IDFApp):
     def get_binary_path(self, app_path):
-        if app_path:
-            # specified path, join it and the idf path
-            path = os.path.join(self.idf_path, app_path)
-        else:
-            path = os.path.join(self.idf_path, "tools", "unit-test-app", "build")
+        """
+        :param app_path: app path or app config
+        :return: binary path
+        """
+        if not app_path:
+            app_path = "default"
+
+        path = os.path.join(self.idf_path, app_path)
+        if not os.path.exists(path):
+            while True:
+                # try to get by config
+                if app_path == "default":
+                    # it's default config, we first try to get form build folder of unit-test-app
+                    path = os.path.join(self.idf_path, "tools", "unit-test-app", "build")
+                    if os.path.exists(path):
+                        # found, use bin in build path
+                        break
+                # ``make ut-build-all-configs`` or ``make ut-build-CONFIG`` will copy binary to output folder
+                path = os.path.join(self.idf_path, "tools", "unit-test-app", "output", app_path)
+                if os.path.exists(path):
+                    break
+                raise OSError("Failed to get unit-test-app binary path")
         return path
 
 
diff --git a/tools/tiny-test-fw/Utility/CIAssignTest.py b/tools/tiny-test-fw/Utility/CIAssignTest.py
new file mode 100644 (file)
index 0000000..ff1bf99
--- /dev/null
@@ -0,0 +1,206 @@
+# Copyright 2015-2017 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.
+
+"""
+Common logic to assign test cases to CI jobs.
+
+Some background knowledge about Gitlab CI and use flow in esp-idf:
+
+* Gitlab CI jobs are static in ``.gitlab-ci.yml``. We can't dynamically create test jobs
+* For test job running on DUT, we use ``tags`` to select runners with different test environment
+* We have ``assign_test`` stage, will collect cases, and then assign them to correct test jobs
+* ``assign_test`` will fail if failed to assign any cases
+* with ``assign_test``, we can:
+    * dynamically filter test case we want to test
+    * alert user if they forget to add CI jobs and guide how to add test jobs
+* the last step of ``assign_test`` is to output config files, then test jobs will run these cases
+
+The Basic logic to assign test cases is as follow:
+
+1. do search all the cases
+2. do filter case (if filter is specified by @bot)
+3. put cases to different groups according to rule of ``Group``
+    * try to put them in existed groups
+    * if failed then create a new group and add this case
+4. parse and filter the test jobs from CI config file
+5. try to assign all groups to jobs according to tags
+6. output config files for jobs
+
+"""
+
+import os
+import re
+import json
+
+import yaml
+
+from Utility import (CaseConfig, SearchCases, GitlabCIJob)
+
+
+class Group(object):
+
+    MAX_EXECUTION_TIME = 30
+    MAX_CASE = 15
+    SORT_KEYS = ["env_tag"]
+
+    def __init__(self, case):
+        self.execution_time = 0
+        self.case_list = [case]
+        self.filters = dict(zip(self.SORT_KEYS, [self._get_case_attr(case, x) for x in self.SORT_KEYS]))
+
+    @staticmethod
+    def _get_case_attr(case, attr):
+        # we might use different type for case (dict or test_func)
+        # this method will do get attribute form cases
+        return case.case_info[attr]
+
+    def accept_new_case(self):
+        """
+        check if allowed to add any case to this group
+
+        :return: True or False
+        """
+        max_time = (sum([self._get_case_attr(x, "execution_time") for x in self.case_list])
+                    < self.MAX_EXECUTION_TIME)
+        max_case = (len(self.case_list) < self.MAX_CASE)
+        return max_time and max_case
+
+    def add_case(self, case):
+        """
+        add case to current group
+
+        :param case: test case
+        :return: True if add succeed, else False
+        """
+        added = False
+        if self.accept_new_case():
+            for key in self.filters:
+                if self._get_case_attr(case, key) != self.filters[key]:
+                    break
+            else:
+                self.case_list.append(case)
+                added = True
+        return added
+
+    def output(self):
+        """
+        output data for job configs
+
+        :return: {"Filter": case filter, "CaseConfig": list of case configs for cases in this group}
+        """
+        output_data = {
+            "Filter": self.filters,
+            "CaseConfig": [{"name": self._get_case_attr(x, "name")} for x in self.case_list],
+        }
+        return output_data
+
+
+class AssignTest(object):
+    """
+    Auto assign tests to CI jobs.
+
+    :param test_case_path: path of test case file(s)
+    :param ci_config_file: path of ``.gitlab-ci.yml``
+    """
+    # subclass need to rewrite CI test job pattern, to filter all test jobs
+    CI_TEST_JOB_PATTERN = re.compile(r"^test_.+")
+
+    def __init__(self, test_case_path, ci_config_file, case_group=Group):
+        self.test_case_path = test_case_path
+        self.test_cases = []
+        self.jobs = self._parse_gitlab_ci_config(ci_config_file)
+        self.case_group = case_group
+
+    def _parse_gitlab_ci_config(self, ci_config_file):
+
+        with open(ci_config_file, "r") as f:
+            ci_config = yaml.load(f)
+
+        job_list = list()
+        for job_name in ci_config:
+            if self.CI_TEST_JOB_PATTERN.search(job_name) is not None:
+                job_list.append(GitlabCIJob.Job(ci_config[job_name], job_name))
+        return job_list
+
+    @staticmethod
+    def _search_cases(test_case_path, case_filter=None):
+        """
+        :param test_case_path: path contains test case folder
+        :param case_filter: filter for test cases
+        :return: filtered test case list
+        """
+        test_methods = SearchCases.Search.search_test_cases(test_case_path)
+        return CaseConfig.filter_test_cases(test_methods, case_filter if case_filter else dict())
+
+    def _group_cases(self):
+        """
+        separate all cases into groups according group rules. each group will be executed by one CI job.
+
+        :return: test case groups.
+        """
+        groups = []
+        for case in self.test_cases:
+            for group in groups:
+                # add to current group
+                if group.add_case(case):
+                    break
+            else:
+                # create new group
+                groups.append(self.case_group(case))
+        return groups
+
+    @staticmethod
+    def _apply_bot_filter():
+        """
+        we support customize CI test with bot.
+        here we process from and return the filter which ``_search_cases`` accepts.
+
+        :return: filter for search test cases
+        """
+        bot_filter = os.getenv("BOT_CASE_FILTER")
+        if bot_filter:
+            bot_filter = json.loads(bot_filter)
+        else:
+            bot_filter = dict()
+        return bot_filter
+
+    def assign_cases(self):
+        """
+        separate test cases to groups and assign test cases to CI jobs.
+
+        :raise AssertError: if failed to assign any case to CI job.
+        :return: None
+        """
+        failed_to_assign = []
+        case_filter = self._apply_bot_filter()
+        self.test_cases = self._search_cases(self.test_case_path, case_filter)
+        test_groups = self._group_cases()
+        for group in test_groups:
+            for job in self.jobs:
+                if job.match_group(group):
+                    job.assign_group(group)
+                    break
+            else:
+                failed_to_assign.append(group)
+        assert not failed_to_assign
+
+    def output_configs(self, output_path):
+        """
+        :param output_path: path to output config files for each CI job
+        :return: None
+        """
+        if not os.path.exists(output_path):
+            os.makedirs(output_path)
+        for job in self.jobs:
+            job.output_config(output_path)
index 1fe5df42bad71dc6ccdc18ba7a8edf098bf85549..af013ec282955c6897d86b61c245e6133b5b6a8f 100644 (file)
@@ -51,6 +51,20 @@ import yaml
 import TestCase
 
 
+def _convert_to_lower_case(item):
+    """
+    bot filter is always lower case string.
+    this function will convert to all string to lower case.
+    """
+    if isinstance(item, (tuple, list)):
+        output = [_convert_to_lower_case(v) for v in item]
+    elif isinstance(item, str):
+        output = item.lower()
+    else:
+        output = item
+    return output
+
+
 def _filter_one_case(test_method, case_filter):
     """ Apply filter for one case (the filter logic is the same as described in ``filter_test_cases``) """
     filter_result = True
@@ -58,7 +72,8 @@ def _filter_one_case(test_method, case_filter):
         if key in test_method.case_info:
             # the filter key is both in case and filter
             # we need to check if they match
-            filter_item, accepted_item = case_filter[key], test_method.case_info[key]
+            filter_item = _convert_to_lower_case(case_filter[key])
+            accepted_item = _convert_to_lower_case(test_method.case_info[key])
 
             if isinstance(filter_item, (tuple, list)) \
                     and isinstance(accepted_item, (tuple, list)):
@@ -91,6 +106,7 @@ def filter_test_cases(test_methods, case_filter):
             * if one is list/tuple, the other one is string/int, then check if string/int is in list/tuple
             * if both are list/tuple, then check if they have common item
         2. if only case attribute or filter have the key, filter succeed
+        3. will do case insensitive compare for string
 
     for example, the following are match succeed scenarios
     (the rule is symmetric, result is same if exchange values for user filter and case attribute):
index 05f6393c66e02e75698764398f4c2d62ac0e9d3a..9d1223c94cd668dbe281c9f643ff1e6d78a8d54d 100644 (file)
@@ -70,4 +70,4 @@ class Job(dict):
         file_name = os.path.join(file_path, self["name"] + ".yml")
         if "case group" in self:
             with open(file_name, "w") as f:
-                yaml.dump(self["case group"].output(), f)
+                yaml.dump(self["case group"].output(), f, default_flow_style=False)
index fd979f0dae3de202e34fc99b381a9ff143c261b3..318e89f42ab5cc1894d57f60b7779722e4e58f9d 100644 (file)
@@ -342,6 +342,12 @@ void unity_run_menu()
                 print_test_menu();
             }
         }
+        /*use '-' to show test history. Need to do it before UNITY_BEGIN cleanup history */
+        if (cmdline[0] == '-')
+        {
+            UNITY_END();
+            continue;
+        }
 
         UNITY_BEGIN();
 
index c296077d310b9f995a16b288b81677fe2c41b6f9..f3ca496dc4f44796afba2505a4e77904a4ab12de 100644 (file)
@@ -20,7 +20,8 @@ TEST_CASE_PATTERN = {
     "version": "v1 (2016-12-06)",
     "test environment": "UT_T1_1",
     "reset": "",
-    "expected result": "1. set succeed"
+    "expected result": "1. set succeed",
+    "cmd set": "test_unit_test_case",
 }
 
 CONFIG_FILE_PATTERN = {
@@ -78,10 +79,10 @@ class Parser(object):
                 name_addr = table.get_unsigned_int(section, test_addr, 4)
                 desc_addr = table.get_unsigned_int(section, test_addr + 4, 4)
                 file_name_addr = table.get_unsigned_int(section, test_addr + 12, 4)
+                function_count = table.get_unsigned_int(section, test_addr+20, 4)
                 name = table.get_string("any", name_addr)
                 desc = table.get_string("any", desc_addr)
                 file_name = table.get_string("any", file_name_addr)
-
                 tc = self.parse_one_test_case(name, desc, file_name, app_name)
 
                 # check if duplicated case names
@@ -100,7 +101,13 @@ class Parser(object):
                         self.test_env_tags[tc["test environment"]].append(tc["ID"])
                     else:
                         self.test_env_tags.update({tc["test environment"]: [tc["ID"]]})
-                    # only add cases need to be executed
+
+                    if function_count > 1:
+                        tc.update({"cmd set": "multiple_devices_case",
+                                   "child case num": function_count})
+                        del tc['reset']
+
+                    # only add  cases need to be executed
                     test_cases.append(tc)
 
         os.remove("section_table.tmp")
@@ -178,7 +185,6 @@ class Parser(object):
         test_case.update({"Test App": self.APP_NAME_PREFIX + app_name,
                           "module": self.module_map[prop["module"]]['module'],
                           "CI ready": "No" if prop["ignore"] == "Yes" else "Yes",
-                          "cmd set": ["IDFUnitTest/UnitTest", [name]],
                           "ID": tc_id,
                           "test point 2": prop["module"],
                           "steps": name,
@@ -262,4 +268,3 @@ def main():
 
 if __name__ == '__main__':
     main()
-
diff --git a/tools/unit-test-app/unit_test.py b/tools/unit-test-app/unit_test.py
new file mode 100644 (file)
index 0000000..bb6cf74
--- /dev/null
@@ -0,0 +1,384 @@
+"""
+Test script for unit test case.
+"""
+
+import re
+import os
+import sys
+import time
+
+import threading
+
+# if we want to run test case outside `tiny-test-fw` folder,
+# we need to insert tiny-test-fw path into sys path
+test_fw_path = os.getenv("TEST_FW_PATH")
+if test_fw_path and test_fw_path not in sys.path:
+    sys.path.insert(0, test_fw_path)
+
+import TinyFW
+import IDF
+import Utility
+from DUT import ExpectTimeout
+from IDF.IDFApp import UT
+
+
+UT_APP_BOOT_UP_DONE = "Press ENTER to see the list of tests."
+UT_TIMEOUT = 30
+
+def format_test_case_config(test_case_data):
+    """
+    convert the test case data to unified format.
+    We need to following info to run unit test cases:
+
+    1. unit test app config
+    2. test case name
+    3. test case reset info
+
+    the formatted case config is a dict, with ut app config as keys. The value is a list of test cases.
+    Each test case is a dict with "name" and "reset" as keys. For example::
+
+    case_config = {
+        "default": [{"name": "restart from PRO CPU", "reset": "SW_CPU_RESET"}, {...}],
+        "psram": [{"name": "restart from PRO CPU", "reset": "SW_CPU_RESET"}],
+    }
+
+    If config is not specified for test case, then
+
+    :param test_case_data: string, list, or a dictionary list
+    :return: formatted data
+    """
+
+    case_config = dict()
+
+    def parse_case(one_case_data):
+        """ parse and format one case """
+
+        def process_reset_list(reset_list):
+            # strip space and remove white space only items
+            _output = list()
+            for _r in reset_list:
+                _data = _r.strip(" ")
+                if _data:
+                    _output.append(_data)
+            return _output
+
+        _case = dict()
+        if isinstance(one_case_data, str):
+            _temp = one_case_data.split(" [reset=")
+            _case["name"] = _temp[0]
+            try:
+                _case["reset"] = process_reset_list(_temp[1][0:-1].split(","))
+            except IndexError:
+                _case["reset"] = list()
+        elif isinstance(one_case_data, dict):
+            _case = one_case_data.copy()
+            assert "name" in _case
+            if "reset" not in _case:
+                _case["reset"] = list()
+            else:
+                if isinstance(_case["reset"], str):
+                    _case["reset"] = process_reset_list(_case["reset"].split(","))
+        else:
+            raise TypeError("Not supported type during parsing unit test case")
+
+        if "config" not in _case:
+            _case["config"] = "default"
+
+        return _case
+
+    if not isinstance(test_case_data, list):
+        test_case_data = [test_case_data]
+
+    for case_data in test_case_data:
+        parsed_case = parse_case(case_data)
+        try:
+            case_config[parsed_case["config"]].append(parsed_case)
+        except KeyError:
+            case_config[parsed_case["config"]] = [parsed_case]
+
+    return case_config
+
+
+@TinyFW.test_method(app=UT, dut=IDF.IDFDUT, chip="ESP32", module="unit_test",
+                    execution_time=1, env_tag="UT_T1_1")
+def test_unit_test_case(env, extra_data):
+    """
+    extra_data can be three types of value
+    1. as string:
+               1. "case_name"
+               2. "case_name [reset=RESET_REASON]"
+    2. as dict:
+               1. with key like {"name": "Intr_alloc test, shared ints"}
+               2. with key like {"name": "restart from PRO CPU", "reset": "SW_CPU_RESET", "config": "psram"}
+    3. as list of string or dict:
+               [case1, case2, case3, {"name": "restart from PRO CPU", "reset": "SW_CPU_RESET"}, ...]
+
+    :param extra_data: the case name or case list or case dictionary
+    :return: None
+    """
+
+    case_config = format_test_case_config(extra_data)
+
+    # compile the patterns for expect only once
+    reset_pattern = re.compile(r"(ets [\w]{3}\s+[\d]{1,2} [\d]{4} [\d]{2}:[\d]{2}:[\d]{2}[^()]*\([\w].*?\))")
+    exception_pattern = re.compile(r"(Guru Meditation Error: Core\s+\d panic'ed \([\w].*?\))")
+    abort_pattern = re.compile(r"(abort\(\) was called at PC 0x[a-eA-E\d]{8} on core \d)")
+    finish_pattern = re.compile(r"1 Tests (\d) Failures (\d) Ignored")
+
+    # we don't want stop on failed case (unless some special scenarios we can't handle)
+    # this flag is used to log if any of the case failed during executing
+    # Before exit test function this flag is used to log if the case fails
+    failed_cases = []
+
+    for ut_config in case_config:
+        dut = env.get_dut("unit-test-app", app_path=ut_config)
+        dut.start_app()
+
+        for one_case in case_config[ut_config]:
+            dut.reset()
+            # esptool ``run`` cmd takes quite long time.
+            # before reset finish, serial port is closed. therefore DUT could already bootup before serial port opened.
+            # this could cause checking bootup print failed.
+            # now we input cmd `-`, and check either bootup print or test history,
+            # to determine if DUT is ready to test.
+            dut.write("-", flush=False)
+            dut.expect_any(UT_APP_BOOT_UP_DONE,
+                           "0 Tests 0 Failures 0 Ignored")
+
+            # run test case
+            dut.write("\"{}\"".format(one_case["name"]))
+            dut.expect("Running " + one_case["name"] + "...")
+
+            exception_reset_list = []
+
+            # we want to set this flag in callbacks (inner functions)
+            # use list here so we can use append to set this flag
+            test_finish = list()
+
+            # expect callbacks
+            def one_case_finish(result):
+                """ one test finished, let expect loop break and log result """
+                test_finish.append(True)
+                if result:
+                    Utility.console_log("Success: " + one_case["name"], color="green")
+                else:
+                    failed_cases.append(one_case["name"])
+                    Utility.console_log("Failed: " + one_case["name"], color="red")
+
+            def handle_exception_reset(data):
+                """
+                just append data to exception list.
+                exception list will be checked in ``handle_reset_finish``, once reset finished.
+                """
+                exception_reset_list.append(data[0])
+
+            def handle_test_finish(data):
+                """ test finished without reset """
+                # in this scenario reset should not happen
+                assert not exception_reset_list
+                if int(data[1]):
+                    # case ignored
+                    Utility.console_log("Ignored: " + one_case["name"], color="orange")
+                one_case_finish(not int(data[0]))
+
+            def handle_reset_finish(data):
+                """ reset happened and reboot finished """
+                assert exception_reset_list  # reboot but no exception/reset logged. should never happen
+                result = False
+                if len(one_case["reset"]) == len(exception_reset_list):
+                    for i, exception in enumerate(exception_reset_list):
+                        if one_case["reset"][i] not in exception:
+                            break
+                    else:
+                        result = True
+                if not result:
+                    Utility.console_log("""Reset Check Failed: \r\n\tExpected: {}\r\n\tGet: {}"""
+                                        .format(one_case["reset"], exception_reset_list),
+                                        color="orange")
+                one_case_finish(result)
+
+            while not test_finish:
+                try:
+                    dut.expect_any((reset_pattern, handle_exception_reset),  # reset pattern
+                                   (exception_pattern, handle_exception_reset),  # exception pattern
+                                   (abort_pattern, handle_exception_reset),  # abort pattern
+                                   (finish_pattern, handle_test_finish),  # test finish pattern
+                                   (UT_APP_BOOT_UP_DONE, handle_reset_finish),  # reboot finish pattern
+                                   timeout=UT_TIMEOUT)
+                except ExpectTimeout:
+                    Utility.console_log("Timeout in expect", color="orange")
+                    one_case_finish(False)
+                    break
+
+    # raise exception if any case fails
+    if failed_cases:
+        Utility.console_log("Failed Cases:", color="red")
+        for _case_name in failed_cases:
+            Utility.console_log("\t" + _case_name, color="red")
+        raise AssertionError("Unit Test Failed")
+
+
+class Handler(threading.Thread):
+
+    WAIT_SIGNAL_PATTERN = re.compile(r'Waiting for signal: \[(.+)\]!')
+    SEND_SIGNAL_PATTERN = re.compile(r'Send signal: \[(.+)\]!')
+    FINISH_PATTERN = re.compile(r"1 Tests (\d) Failures (\d) Ignored")
+
+    def __init__(self, dut, sent_signal_list, lock, parent_case_name, child_case_index, timeout=30):
+        self.dut = dut
+        self.sent_signal_list = sent_signal_list
+        self.lock = lock
+        self.parent_case_name = parent_case_name
+        self.child_case_name = ""
+        self.child_case_index = child_case_index + 1
+        self.finish = False
+        self.result = False
+        self.fail_name = None
+        self.timeout = timeout
+        threading.Thread.__init__(self, name="{} Handler".format(dut))
+
+    def run(self):
+        def get_child_case_name(data):
+            self.child_case_name = data[0]
+            time.sleep(1)
+            self.dut.write(str(self.child_case_index))
+
+        def one_device_case_finish(result):
+            """ one test finished, let expect loop break and log result """
+            self.finish = True
+            self.result = result
+            if not result:
+                self.fail_name = self.child_case_name
+
+        def device_wait_action(data):
+            start_time = time.time()
+            expected_signal = data[0]
+            while 1:
+                if time.time() > start_time + self.timeout:
+                    Utility.console_log("Timeout in device for function: %s"%self.child_case_name, color="orange")
+                    break
+                with self.lock:
+                    if expected_signal in self.sent_signal_list:
+                        self.dut.write(" ")
+                        self.sent_signal_list.remove(expected_signal)
+                        break
+                time.sleep(0.01)
+
+        def device_send_action(data):
+            with self.lock:
+                self.sent_signal_list.append(data[0].encode('utf-8'))
+
+        def handle_device_test_finish(data):
+            """ test finished without reset """
+            # in this scenario reset should not happen
+            if int(data[1]):
+                # case ignored
+                Utility.console_log("Ignored: " + self.child_case_name, color="orange")
+            one_device_case_finish(not int(data[0]))
+
+        self.dut.reset()
+        self.dut.write("-", flush=False)
+        self.dut.expect_any(UT_APP_BOOT_UP_DONE, "0 Tests 0 Failures 0 Ignored")
+        time.sleep(1)
+        self.dut.write("\"{}\"".format(self.parent_case_name))
+        self.dut.expect("Running " + self.parent_case_name + "...")
+
+        while not self.finish:
+            try:
+                self.dut.expect_any((re.compile('\(' + str(self.child_case_index) + '\)\s"(\w+)"'), get_child_case_name),
+                                    (self.WAIT_SIGNAL_PATTERN, device_wait_action),  # wait signal pattern
+                                    (self.SEND_SIGNAL_PATTERN, device_send_action),  # send signal pattern
+                                    (self.FINISH_PATTERN, handle_device_test_finish),  # test finish pattern
+                                    timeout=UT_TIMEOUT)
+            except ExpectTimeout:
+                Utility.console_log("Timeout in expect", color="orange")
+                one_device_case_finish(False)
+                break
+
+
+def get_case_info(one_case):
+    parent_case = one_case["name"]
+    child_case_num = one_case["child case num"]
+    return parent_case, child_case_num
+
+
+def get_dut(duts, env, name, ut_config):
+    if name in duts:
+        dut = duts[name]
+    else:
+        dut = env.get_dut(name, app_path=ut_config)
+        duts[name] = dut
+        dut.start_app()
+    return dut
+
+
+def case_run(duts, ut_config, env, one_case, failed_cases):
+    lock = threading.RLock()
+    threads = []
+    send_signal_list = []
+    failed_device = []
+    result = True
+    parent_case, case_num = get_case_info(one_case)
+    for i in range(case_num):
+        dut = get_dut(duts, env, "dut%d" % i, ut_config)
+        threads.append(Handler(dut, send_signal_list, lock,
+                               parent_case, i))
+    for thread in threads:
+        thread.setDaemon(True)
+        thread.start()
+    for thread in threads:
+        thread.join()
+        result = result and thread.result
+        if not thread.result:
+            failed_device.append(thread.fail_name)
+    if result:
+        Utility.console_log("Success: " + one_case["name"], color="green")
+    else:
+        failed_cases.append(one_case["name"])
+        Utility.console_log("Failed: " + one_case["name"], color="red")
+
+
+@TinyFW.test_method(app=UT, dut=IDF.IDFDUT, chip="ESP32", module="master_slave_test_case", execution_time=1,
+                    env_tag="UT_T2_1")
+def multiple_devices_case(env, extra_data):
+    """
+     extra_data can be two types of value
+     1. as dict:
+            e.g.
+                {"name":  "gpio master/slave test example",
+                "child case num": 2,
+                "config": "release",
+                "env_tag": "UT_T2_1"}
+     2. as list dict:
+            e.g.
+               [{"name":  "gpio master/slave test example1",
+                "child case num": 2,
+                "config": "release",
+                "env_tag": "UT_T2_1"},
+               {"name":  "gpio master/slave test example2",
+                "child case num": 2,
+                "config": "release",
+                "env_tag": "UT_T2_1"}]
+
+    """
+    failed_cases = []
+    case_config = format_test_case_config(extra_data)
+    DUTS = {}
+    for ut_config in case_config:
+        for one_case in case_config[ut_config]:
+            case_run(DUTS, ut_config, env, one_case, failed_cases)
+
+    if failed_cases:
+        Utility.console_log("Failed Cases:", color="red")
+        for _case_name in failed_cases:
+            Utility.console_log("\t" + _case_name, color="red")
+        raise AssertionError("Unit Test Failed")
+
+if __name__ == '__main__':
+    multiple_devices_case(extra_data={"name":  "gpio master/slave test example",
+                                      "child case num": 2,
+                                      "config": "release",
+                                      "env_tag": "UT_T2_1"})
+
+
+