]> granicus.if.org Git - esp-idf/commitdiff
Merge branch 'bugfix/btdm_gattc_get_db_prop_bug' into 'master'
authorJiang Jiang Jian <jack@espressif.com>
Sat, 30 Sep 2017 07:42:45 +0000 (15:42 +0800)
committerJiang Jiang Jian <jack@espressif.com>
Sat, 30 Sep 2017 07:42:45 +0000 (15:42 +0800)
component/bt: Fiexd the bug of the gattc cannot get the db properties.

See merge request !1284

112 files changed:
components/app_trace/app_trace.c
components/app_trace/gcov/gcov_rtio.c
components/app_trace/host_file_io.c
components/bt/Kconfig
components/bt/bluedroid/bta/gatt/bta_gattc_cache.c
components/bt/bluedroid/stack/sdp/sdp_server.c
components/bt/bt.c
components/bt/include/bt.h
components/bt/lib
components/driver/include/driver/sigmadelta.h
components/driver/include/driver/uart.h
components/driver/rtc_module.c
components/driver/sdspi_host.c
components/driver/spi_master.c
components/driver/test/test_spi_master.c
components/esp32/Kconfig
components/esp32/clk.c
components/esp32/cpu_start.c
components/esp32/dport_access.c
components/esp32/dport_panic_highint_hdl.S
components/esp32/include/esp_spiram.h
components/esp32/include/esp_task_wdt.h
components/esp32/include/esp_wifi.h
components/esp32/include/esp_wifi_types.h
components/esp32/intr_alloc.c
components/esp32/lib
components/esp32/sleep_modes.c
components/esp32/spiram.c
components/esp32/system_api.c
components/esp32/task_wdt.c
components/freertos/include/freertos/portmacro.h
components/freertos/include/freertos/task.h
components/freertos/readme_smp.txt [deleted file]
components/freertos/tasks.c
components/heap/heap_caps.c
components/heap/heap_caps_init.c
components/heap/include/esp_heap_caps.h
components/heap/include/esp_heap_caps_init.h
components/heap/multi_heap.c
components/heap/multi_heap_platform.h
components/heap/multi_heap_poisoning.c
components/heap/test/test_malloc.c
components/idf_test/integration_test/KnownIssues
components/log/log.c
components/lwip/Kconfig
components/lwip/apps/dhcpserver.c
components/lwip/core/tcp_out.c
components/lwip/include/lwip/port/lwipopts.h
components/nvs_flash/include/nvs_flash.h
components/nvs_flash/src/nvs_api.cpp
components/nvs_flash/test/test_nvs.c
components/nvs_flash/test_nvs_host/spi_flash_emulation.h
components/soc/esp32/include/soc/soc.h
components/soc/esp32/rtc_clk.c
components/soc/esp32/soc_memory_layout.c
components/soc/include/soc/soc_memory_layout.h
components/spi_flash/flash_mmap.c
components/spiffs/esp_spiffs.c
components/spiffs/include/esp_spiffs.h
components/spiffs/include/spiffs_config.h
components/spiffs/spiffs
components/tcpip_adapter/tcpip_adapter_lwip.c
docs/_static/freertos-ready-task-list-smp-pxIndex.png [new file with mode: 0644]
docs/_static/freertos-ready-task-list-smp.png [new file with mode: 0644]
docs/_static/freertos-ready-task-list.png [new file with mode: 0644]
docs/api-guides/external-ram.rst [new file with mode: 0644]
docs/api-guides/freertos-smp.rst [new file with mode: 0644]
docs/api-guides/index.rst
docs/api-reference/peripherals/sigmadelta.rst
docs/api-reference/system/heap_debug.rst
docs/api-reference/system/wdts.rst
docs/get-started/eclipse-setup-windows.rst
docs/get-started/index.rst
docs/get-started/windows-setup-scratch.rst
docs/get-started/windows-setup.rst
examples/bluetooth/a2dp_sink/main/main.c
examples/bluetooth/ble_adv/main/app_bt.c
examples/peripherals/sigmadelta/README.md
examples/peripherals/sigmadelta/main/sigmadelta_example_main.c
examples/protocols/openssl_client/main/openssl_client_example.h
examples/protocols/openssl_client/main/openssl_client_example_main.c
examples/protocols/openssl_server/main/openssl_server_example.h
examples/protocols/openssl_server/main/openssl_server_example_main.c
examples/system/task_watchdog/Makefile [new file with mode: 0644]
examples/system/task_watchdog/README.md [new file with mode: 0644]
examples/system/task_watchdog/main/component.mk [new file with mode: 0644]
examples/system/task_watchdog/main/task_watchdog_example_main.c [new file with mode: 0644]
examples/wifi/README.md
examples/wifi/espnow/Makefile [new file with mode: 0644]
examples/wifi/espnow/README.md [new file with mode: 0644]
examples/wifi/espnow/main/Kconfig.projbuild [new file with mode: 0644]
examples/wifi/espnow/main/component.mk [new file with mode: 0644]
examples/wifi/espnow/main/espnow_example.h [new file with mode: 0644]
examples/wifi/espnow/main/espnow_example_main.c [new file with mode: 0644]
examples/wifi/iperf/Makefile [new file with mode: 0755]
examples/wifi/iperf/README.md [new file with mode: 0644]
examples/wifi/iperf/components/component.mk [new file with mode: 0755]
examples/wifi/iperf/components/iperf.c [new file with mode: 0644]
examples/wifi/iperf/components/iperf.h [new file with mode: 0644]
examples/wifi/iperf/main/cmd_decl.h [new file with mode: 0644]
examples/wifi/iperf/main/cmd_wifi.c [new file with mode: 0644]
examples/wifi/iperf/main/component.mk [new file with mode: 0644]
examples/wifi/iperf/main/main.c [new file with mode: 0644]
examples/wifi/iperf/sdkconfig.defaults [new file with mode: 0644]
examples/wifi/power_save/main/power_save.c
make/component_wrapper.mk
tools/ci/test_build_system.sh
tools/kconfig/Makefile
tools/kconfig/lxdialog/check-lxdialog.sh
tools/unit-test-app/components/unity/unity.c
tools/unit-test-app/components/unity/unity_platform.c
tools/windows/windows_install_prerequisites.sh

index 293f210e2b3e626e77f844290000e97df38900d7..c46f71b5afe9c655f22023f31d5661d1442d39e7 100644 (file)
@@ -864,6 +864,8 @@ esp_err_t esp_apptrace_init()
 
     if (!s_trace_buf.inited) {
         memset(&s_trace_buf, 0, sizeof(s_trace_buf));
+        // disabled by default
+        esp_apptrace_rb_init(&s_trace_buf.rb_down, NULL, 0);
         res = esp_apptrace_lock_initialize(&s_trace_buf.lock);
         if (res != ESP_OK) {
             ESP_APPTRACE_LOGE("Failed to init log lock (%d)!", res);
@@ -884,9 +886,6 @@ esp_err_t esp_apptrace_init()
     esp_apptrace_trax_init();
 #endif
 
-    // disabled by default
-    esp_apptrace_rb_init(&s_trace_buf.rb_down, NULL, 0);
-
     s_trace_buf.inited |= 1 << xPortGetCoreID(); // global and this CPU-specific data are inited
 
     return ESP_OK;
index c03bd6b35c592206440f3fc402e5606a8c7e6726..7556c9f7fac9fb205bfdbe0bf8116936b6031b4a 100644 (file)
@@ -14,9 +14,9 @@
 
 // This module implements runtime file I/O API for GCOV.
 
-#include "esp_task_wdt.h"
 #include "freertos/FreeRTOS.h"
 #include "freertos/task.h"
+#include "esp_task_wdt.h"
 #include "soc/cpu.h"
 #include "soc/timer_group_struct.h"
 #include "soc/timer_group_reg.h"
@@ -49,6 +49,7 @@ void esp_gcov_dump()
     }
 
     if (s_gcov_exit) {
+        esp_apptrace_down_buffer_config(s_gcov_down_buf, sizeof(s_gcov_down_buf));
         s_gcov_exit();
     }
 
@@ -61,7 +62,6 @@ void esp_gcov_dump()
 int gcov_rtio_atexit(void (*function)(void))
 {
     s_gcov_exit = function;
-    esp_apptrace_down_buffer_config(s_gcov_down_buf, sizeof(s_gcov_down_buf));
     return 0;
 }
 
index d16144a32c289bbb9412677781513011d2ff37e5..d3cf2447b8ddd659cc3f0120bbd2dad524d6e3f4 100644 (file)
@@ -155,14 +155,14 @@ void *esp_apptrace_fopen(esp_apptrace_dest_t dest, const char *path, const char
     }
 
     // now read the answer
-    uint8_t resp[sizeof(void *)];
-    ret = esp_apptrace_file_rsp_recv(dest, resp, sizeof(resp));
+    void *resp;
+    ret = esp_apptrace_file_rsp_recv(dest, (uint8_t *)&resp, sizeof(resp));
     if (ret != ESP_OK) {
         ESP_LOGE(TAG, "Failed to read response (%d)!", ret);
         return NULL;
     }
 
-    return *((void **)resp);
+    return resp;
 }
 
 static void esp_apptrace_fclose_args_prepare(uint8_t *buf, void *priv)
@@ -185,14 +185,14 @@ int esp_apptrace_fclose(esp_apptrace_dest_t dest, void *stream)
     }
 
     // now read the answer
-    uint8_t resp[sizeof(int)];
-    ret = esp_apptrace_file_rsp_recv(dest, resp, sizeof(resp));
+    int resp;
+    ret = esp_apptrace_file_rsp_recv(dest, (uint8_t *)&resp, sizeof(resp));
     if (ret != ESP_OK) {
         ESP_LOGE(TAG, "Failed to read response (%d)!", ret);
         return EOF;
     }
 
-    return *((int *)resp);
+    return resp;
 }
 
 static void esp_apptrace_fwrite_args_prepare(uint8_t *buf, void *priv)
@@ -218,14 +218,14 @@ size_t esp_apptrace_fwrite(esp_apptrace_dest_t dest, const void *ptr, size_t siz
     }
 
     // now read the answer
-    uint8_t resp[sizeof(size_t)];
-    ret = esp_apptrace_file_rsp_recv(dest, resp, sizeof(resp));
+    size_t resp;
+    ret = esp_apptrace_file_rsp_recv(dest, (uint8_t *)&resp, sizeof(resp));
     if (ret != ESP_OK) {
         ESP_LOGE(TAG, "Failed to read response (%d)!", ret);
         return 0;
     }
 
-    return *((size_t *)resp);
+    return resp;
 }
 
 static void esp_apptrace_fread_args_prepare(uint8_t *buf, void *priv)
@@ -250,20 +250,20 @@ size_t esp_apptrace_fread(esp_apptrace_dest_t dest, void *ptr, size_t size, size
     }
 
     // now read the answer
-    uint8_t resp[sizeof(size_t)];
-    ret = esp_apptrace_file_rsp_recv(dest, resp, sizeof(resp));
+    size_t resp;
+    ret = esp_apptrace_file_rsp_recv(dest, (uint8_t *)&resp, sizeof(resp));
     if (ret != ESP_OK) {
         ESP_LOGE(TAG, "Failed to read response (%d)!", ret);
         return 0;
     }
-    if (*((size_t *)resp) > 0) {
-        ret = esp_apptrace_file_rsp_recv(dest, ptr, *((size_t *)resp));
+    if (resp > 0) {
+        ret = esp_apptrace_file_rsp_recv(dest, ptr, resp);
         if (ret != ESP_OK) {
             ESP_LOGE(TAG, "Failed to read file data (%d)!", ret);
             return 0;
         }
     }
-    return *((size_t *)resp);
+    return resp;
 }
 
 static void esp_apptrace_fseek_args_prepare(uint8_t *buf, void *priv)
@@ -288,14 +288,14 @@ int esp_apptrace_fseek(esp_apptrace_dest_t dest, void *stream, long offset, int
     }
 
     // now read the answer
-    uint8_t resp[sizeof(int)];
-    ret = esp_apptrace_file_rsp_recv(dest, resp, sizeof(resp));
+    int resp;
+    ret = esp_apptrace_file_rsp_recv(dest, (uint8_t *)&resp, sizeof(resp));
     if (ret != ESP_OK) {
         ESP_LOGE(TAG, "Failed to read response (%d)!", ret);
         return -1;
     }
 
-    return *((int *)resp);
+    return resp;
 }
 
 static void esp_apptrace_ftell_args_prepare(uint8_t *buf, void *priv)
@@ -318,14 +318,14 @@ int esp_apptrace_ftell(esp_apptrace_dest_t dest, void *stream)
     }
 
     // now read the answer
-    uint8_t resp[sizeof(int)];
-    ret = esp_apptrace_file_rsp_recv(dest, resp, sizeof(resp));
+    int resp;
+    ret = esp_apptrace_file_rsp_recv(dest, (uint8_t *)&resp, sizeof(resp));
     if (ret != ESP_OK) {
         ESP_LOGE(TAG, "Failed to read response (%d)!", ret);
         return -1;
     }
 
-    return *((int *)resp);
+    return resp;
 }
 
 int esp_apptrace_fstop(esp_apptrace_dest_t dest)
index fd805bd7f691c5a8afd19edb6aad59b0368e890e..174b2deefad468806591e2d93ecb3543f06839bd 100644 (file)
@@ -31,15 +31,6 @@ config CLASSIC_BT_ENABLED
     help
         For now this option needs "SMP_ENABLE" to be set to yes
 
-config BT_DRAM_RELEASE
-    bool "Release DRAM from Classic BT controller"
-    depends on BT_ENABLED && (!BLUEDROID_ENABLED || (BLUEDROID_ENABLED && !CLASSIC_BT_ENABLED))
-    default n
-    help
-        This option should only be used when BLE only.
-        Enabling this option will release about 30K DRAM from Classic BT.
-        The released DRAM will be used as system heap memory.
-
 config GATTS_ENABLE
     bool "Include GATT server module(GATTS)"
     depends on BLUEDROID_ENABLED
index 5e0943a9bb80955f5812882aab44f6389f44bb45..d9f67ef2092258bea7715d45ce3e4b4d2d567f0a 100644 (file)
@@ -187,6 +187,7 @@ tBTA_GATT_STATUS bta_gattc_init_cache(tBTA_GATTC_SERV *p_srvc_cb)
         p_srvc_cb->cur_srvc_idx = 0;
         p_srvc_cb->cur_char_idx = 0;
         p_srvc_cb->next_avail_idx = 0;
+        p_srvc_cb->total_attr = 0;
     }
 
     return BTA_GATT_OK;
index b88134a74d58f773a5545e20a7d4becfc0be89b4..84333b2b0b53393e9efff630b07e37ff68625308 100644 (file)
@@ -222,7 +222,7 @@ static void process_service_search (tCONN_CB *p_ccb, UINT16 trans_num,
         }
         BE_STREAM_TO_UINT16 (cont_offset, p_req);
 
-        if (cont_offset != p_ccb->cont_offset) {
+        if (cont_offset != p_ccb->cont_offset || num_rsp_handles < cont_offset) {
             sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE,
                                      SDP_TEXT_BAD_CONT_INX);
             return;
index 61e8792da0c41597a596843d6b7d01ff08a61ab6..12e1d4407454a92c28fae7064adb9f1cb6d5c036 100644 (file)
@@ -17,6 +17,7 @@
 #include <stdio.h>
 #include <string.h>
 
+#include "esp_heap_caps_init.h"
 #include "freertos/FreeRTOS.h"
 #include "freertos/task.h"
 #include "freertos/queue.h"
 #include "esp_attr.h"
 #include "esp_phy_init.h"
 #include "bt.h"
+#include "esp_err.h"
+#include "esp_log.h"
 
 #if CONFIG_BT_ENABLED
 
+#define BTDM_LOG_TAG                        "BTDM_INIT"
+
 #define BTDM_INIT_PERIOD                    (5000)    /* ms */
 
 /* Bluetooth system and controller config */
-#define BTDM_CFG_BT_EM_RELEASE              (1<<0)
-#define BTDM_CFG_BT_DATA_RELEASE            (1<<1)
-#define BTDM_CFG_HCI_UART                   (1<<2)
-#define BTDM_CFG_CONTROLLER_RUN_APP_CPU     (1<<3)
+#define BTDM_CFG_BT_DATA_RELEASE            (1<<0)
+#define BTDM_CFG_HCI_UART                   (1<<1)
+#define BTDM_CFG_CONTROLLER_RUN_APP_CPU     (1<<2)
 /* Other reserved for future */
 
 /* not for user call, so don't put to include file */
@@ -48,6 +52,7 @@ extern int btdm_controller_init(uint32_t config_mask, esp_bt_controller_config_t
 extern int btdm_controller_deinit(void);
 extern int btdm_controller_enable(esp_bt_mode_t mode);
 extern int btdm_controller_disable(esp_bt_mode_t mode);
+extern uint8_t btdm_controller_get_mode(void);
 extern void btdm_rf_bb_init(void);
 
 /* VHCI function interface */
@@ -63,6 +68,13 @@ extern void API_vhci_host_register_callback(const vhci_host_callback_t *callback
 extern int ble_txpwr_set(int power_type, int power_level);
 extern int ble_txpwr_get(int power_type);
 
+extern char _bss_start_btdm;
+extern char _bss_end_btdm;
+extern char _data_start_btdm;
+extern char _data_end_btdm;
+extern uint32_t _data_start_btdm_rom;
+extern uint32_t _data_end_btdm_rom;
+
 #define BT_DEBUG(...)
 #define BT_API_CALL_CHECK(info, api_call, ret) \
 do{\
@@ -75,6 +87,27 @@ do{\
 
 #define OSI_FUNCS_TIME_BLOCKING  0xffffffff
 
+typedef struct {
+    esp_bt_mode_t mode;
+    intptr_t start;
+    intptr_t end;
+} btdm_dram_available_region_t;
+
+/* the mode column will be modifid by release function to indicate the available region */
+static btdm_dram_available_region_t btdm_dram_available_region[] = {
+    //following is .data 
+    {ESP_BT_MODE_BTDM,          0x3ffae6e0, 0x3ffaff10},
+    //following is memory which HW will use
+    {ESP_BT_MODE_BTDM,          0x3ffb0000, 0x3ffb09a8},
+    {ESP_BT_MODE_BLE,           0x3ffb09a8, 0x3ffb1ddc},
+    {ESP_BT_MODE_BTDM,          0x3ffb1ddc, 0x3ffb2730},
+    {ESP_BT_MODE_CLASSIC_BT,    0x3ffb2730, 0x3ffb8000},
+    //following is .bss
+    {ESP_BT_MODE_BTDM,          0x3ffb8000, 0x3ffbbb28},
+    {ESP_BT_MODE_CLASSIC_BT,    0x3ffbbb28, 0x3ffbdb28},
+    {ESP_BT_MODE_BTDM,          0x3ffbdb28, 0x3ffc0000},
+};
+
 struct osi_funcs_t {
     xt_handler (*_set_isr)(int n, xt_handler f, void *arg);
     void (*_ints_on)(unsigned int mask);
@@ -303,9 +336,10 @@ static uint32_t btdm_config_mask_load(void)
 {
     uint32_t mask = 0x0;
 
-#ifdef CONFIG_BT_DRAM_RELEASE
-    mask |= (BTDM_CFG_BT_EM_RELEASE | BTDM_CFG_BT_DATA_RELEASE);
-#endif
+    if (btdm_dram_available_region[0].mode == ESP_BT_MODE_BLE) {
+        mask |= BTDM_CFG_BT_DATA_RELEASE;
+    }
+
 #ifdef CONFIG_BT_HCI_UART
     mask |= BTDM_CFG_HCI_UART;
 #endif
@@ -315,6 +349,76 @@ static uint32_t btdm_config_mask_load(void)
     return mask;
 }
 
+static void btdm_controller_mem_init(void)
+{
+    /* initialise .bss, .data and .etc section */
+    memcpy(&_data_start_btdm, (void *)_data_start_btdm_rom, &_data_end_btdm - &_data_start_btdm);
+    ESP_LOGD(BTDM_LOG_TAG, ".data initialise [0x%08x] <== [0x%08x]\n", (uint32_t)&_data_start_btdm, _data_start_btdm_rom);
+
+    for (int i = 1; i < sizeof(btdm_dram_available_region)/sizeof(btdm_dram_available_region_t); i++) {
+        if (btdm_dram_available_region[i].mode != ESP_BT_MODE_IDLE) {
+            memset((void *)btdm_dram_available_region[i].start, 0x0, btdm_dram_available_region[i].end - btdm_dram_available_region[i].start);
+            ESP_LOGD(BTDM_LOG_TAG, ".bss initialise [0x%08x] - [0x%08x]\n", btdm_dram_available_region[i].start, btdm_dram_available_region[i].end);
+        }
+    }
+}
+
+esp_err_t esp_bt_controller_mem_release(esp_bt_mode_t mode)
+{
+    bool update = true;
+    intptr_t mem_start, mem_end;
+
+    //get the mode which can be released, skip the mode which is running
+    mode &= ~btdm_controller_get_mode();
+    if (mode == 0x0) {
+        return ESP_ERR_INVALID_ARG;
+    }
+
+    //already relesed
+    if (!(mode & btdm_dram_available_region[0].mode)) {
+        return ESP_ERR_INVALID_STATE;
+    }
+
+    for (int i = 0; i < sizeof(btdm_dram_available_region)/sizeof(btdm_dram_available_region_t); i++) {
+        //skip the share mode, idle mode and other mode
+        if (btdm_dram_available_region[i].mode == ESP_BT_MODE_IDLE
+                || (mode & btdm_dram_available_region[i].mode) != btdm_dram_available_region[i].mode) {
+            //clear the bit of the mode which will be released
+            btdm_dram_available_region[i].mode &= ~mode;
+            continue;
+        } else {
+            //clear the bit of the mode which will be released
+            btdm_dram_available_region[i].mode &= ~mode;
+        }
+
+        if (update) {
+            mem_start = btdm_dram_available_region[i].start;
+            mem_end = btdm_dram_available_region[i].end;
+            update = false;
+        }
+
+        if (i < sizeof(btdm_dram_available_region)/sizeof(btdm_dram_available_region_t) - 1) {
+            mem_end = btdm_dram_available_region[i].end;
+            if (btdm_dram_available_region[i+1].mode != ESP_BT_MODE_IDLE
+                    && (mode & btdm_dram_available_region[i+1].mode) == btdm_dram_available_region[i+1].mode
+                    && mem_end == btdm_dram_available_region[i+1].start) {
+                continue;
+            } else {
+                ESP_LOGD(BTDM_LOG_TAG, "Release DRAM [0x%08x] - [0x%08x]\n", mem_start, mem_end);
+                ESP_ERROR_CHECK( heap_caps_add_region(mem_start, mem_end));
+                update = true;
+            }
+        } else {
+            mem_end = btdm_dram_available_region[i].end;
+            ESP_LOGD(BTDM_LOG_TAG, "Release DRAM [0x%08x] - [0x%08x]\n", mem_start, mem_end);
+            ESP_ERROR_CHECK( heap_caps_add_region(mem_start, mem_end));
+            update = true;
+        }
+    }
+
+    return ESP_OK;
+}
+
 esp_err_t esp_bt_controller_init(esp_bt_controller_config_t *cfg)
 {
     BaseType_t ret;
@@ -324,6 +428,11 @@ esp_err_t esp_bt_controller_init(esp_bt_controller_config_t *cfg)
         return ESP_ERR_INVALID_STATE;
     }
 
+    //if all the bt available memory was already released, cannot initialize bluetooth controller
+    if (btdm_dram_available_region[0].mode == ESP_BT_MODE_IDLE) {
+        return ESP_ERR_INVALID_STATE;
+    }
+
     if (cfg == NULL) {
         return ESP_ERR_INVALID_ARG;
     }
@@ -335,6 +444,8 @@ esp_err_t esp_bt_controller_init(esp_bt_controller_config_t *cfg)
 
     btdm_osi_funcs_register(&osi_funcs);
 
+    btdm_controller_mem_init();
+
     btdm_cfg_mask = btdm_config_mask_load();
 
     ret = btdm_controller_init(btdm_cfg_mask, cfg);
@@ -356,7 +467,7 @@ esp_err_t esp_bt_controller_deinit(void)
         return ESP_ERR_NO_MEM;
     }
 
-    btdm_controller_status = ESP_BT_CONTROLLER_STATUS_IDLE;
+    btdm_controller_status = ESP_BT_CONTROLLER_STATUS_SHUTDOWN;
     return ESP_OK;
 }
 
@@ -368,7 +479,8 @@ esp_err_t esp_bt_controller_enable(esp_bt_mode_t mode)
         return ESP_ERR_INVALID_STATE;
     }
 
-    if (mode != ESP_BT_MODE_BTDM) {
+    //check the mode is available mode
+    if (mode & ~btdm_dram_available_region[0].mode) {
         return ESP_ERR_INVALID_ARG;
     }
 
@@ -389,7 +501,7 @@ esp_err_t esp_bt_controller_enable(esp_bt_mode_t mode)
     return ESP_OK;
 }
 
-esp_err_t esp_bt_controller_disable(esp_bt_mode_t mode)
+esp_err_t esp_bt_controller_disable(void)
 {
     int ret;
 
@@ -397,11 +509,7 @@ esp_err_t esp_bt_controller_disable(esp_bt_mode_t mode)
         return ESP_ERR_INVALID_STATE;
     }
 
-    if (mode != ESP_BT_MODE_BTDM) {
-        return ESP_ERR_INVALID_ARG;
-    }
-
-    ret = btdm_controller_disable(mode);
+    ret = btdm_controller_disable(btdm_controller_get_mode());
     if (ret < 0) {
         return ESP_ERR_INVALID_STATE;
     }
@@ -435,5 +543,4 @@ esp_power_level_t esp_ble_tx_power_get(esp_ble_power_type_t power_type)
     return (esp_power_level_t)ble_txpwr_get(power_type);
 }
 
-
-#endif
+#endif /*  CONFIG_BT_ENABLED */
index a11cd3eeceaa9272ed0faa67bc4d4d6f8143bd0c..99769604427ba4a61fa201ff5756ca799e53b4d9 100644 (file)
@@ -78,6 +78,7 @@ typedef enum {
     ESP_BT_CONTROLLER_STATUS_IDLE = 0,
     ESP_BT_CONTROLLER_STATUS_INITED,
     ESP_BT_CONTROLLER_STATUS_ENABLED,
+    ESP_BT_CONTROLLER_STATUS_SHUTDOWN,
     ESP_BT_CONTROLLER_STATUS_NUM,
 } esp_bt_controller_status_t;
 
@@ -158,20 +159,20 @@ esp_err_t esp_bt_controller_init(esp_bt_controller_config_t *cfg);
 esp_err_t esp_bt_controller_deinit(void);
 
 /**
- * @brief Enable BT controller
+ * @brief Enable BT controller.
+ *               Due to a known issue, you cannot call esp_bt_controller_enable() a second time
+ *               to change the controller mode dynamically. To change controller mode, call
+ *               esp_bt_controller_disable() and then call esp_bt_controller_enable() with the new mode.
  * @param mode : the mode(BLE/BT/BTDM) to enable.
- *               Now only support BTDM.
  * @return       ESP_OK - success, other - failed
  */
 esp_err_t esp_bt_controller_enable(esp_bt_mode_t mode);
 
 /**
  * @brief  Disable BT controller
- * @param mode : the mode(BLE/BT/BTDM) to disable.
- *               Now only support BTDM.
  * @return       ESP_OK - success, other - failed
  */
-esp_err_t esp_bt_controller_disable(esp_bt_mode_t mode);
+esp_err_t esp_bt_controller_disable(void);
 
 /**
  * @brief  Get BT controller is initialised/de-initialised/enabled/disabled
@@ -207,6 +208,36 @@ void esp_vhci_host_send_packet(uint8_t *data, uint16_t len);
  */
 void esp_vhci_host_register_callback(const esp_vhci_host_callback_t *callback);
 
+/** @brief esp_bt_controller_mem_release
+ * release the memory by mode, if never use the bluetooth mode
+ * it can release the .bbs, .data and other section to heap.
+ * The total size is about 70k bytes.
+ *
+ * If esp_bt_controller_enable(mode) has already been called, calling
+ * esp_bt_controller_mem_release(ESP_BT_MODE_BTDM) will automatically
+ * release all memory which is not needed for the currently enabled
+ * Bluetooth controller mode.
+ *
+ * For example, calling esp_bt_controller_enable(ESP_BT_MODE_BLE) then
+ * esp_bt_controller_mem_release(ESP_BT_MODE_BTDM) will enable BLE modes
+ * and release memory only used by BT Classic. Also, call esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT)
+ * is the same.
+ *
+ * Note that once BT controller memory is released, the process cannot be reversed.
+ * If your firmware will later upgrade the Bluetooth controller mode (BLE -> BT Classic or disabled -> enabled)
+ * then do not call this function.
+ *
+ * If user never use bluetooth controller, could call esp_bt_controller_mem_release(ESP_BT_MODE_BTDM)
+ * before esp_bt_controller_init or after esp_bt_controller_deinit.
+ *
+ * For example, user only use bluetooth to config SSID and PASSWORD of WIFI, after config, will never use bluetooth.
+ * Then, could call esp_bt_controller_mem_release(ESP_BT_MODE_BTDM) after esp_bt_controller_deinit.
+ *
+ * @param mode : the mode want to release memory
+ * @return ESP_OK - success, other - failed
+ */
+esp_err_t esp_bt_controller_mem_release(esp_bt_mode_t mode);
+
 #ifdef __cplusplus
 }
 #endif
index d41e3512971612a4903e1ea1189968408811a1ae..2d33374ca531f92859640f1d055fc4f28d60dfdc 160000 (submodule)
@@ -1 +1 @@
-Subproject commit d41e3512971612a4903e1ea1189968408811a1ae
+Subproject commit 2d33374ca531f92859640f1d055fc4f28d60dfdc
index 61a35c21f9139d28a3408f83ad09db52b92408c3..76237c193d3574fabb56969a0c11784d13b6e37a 100644 (file)
@@ -27,14 +27,14 @@ extern "C" {
  * @brief Sigma-delta channel list
  */
 typedef enum{
-    SIGMADELTA_CHANNEL_0 = 0,         /*!< Sigma-delta channel0 */
-    SIGMADELTA_CHANNEL_1 = 1,         /*!< Sigma-delta channel1 */
-    SIGMADELTA_CHANNEL_2 = 2,         /*!< Sigma-delta channel2 */
-    SIGMADELTA_CHANNEL_3 = 3,         /*!< Sigma-delta channel3 */
-    SIGMADELTA_CHANNEL_4 = 4,         /*!< Sigma-delta channel4 */
-    SIGMADELTA_CHANNEL_5 = 5,         /*!< Sigma-delta channel5 */
-    SIGMADELTA_CHANNEL_6 = 6,         /*!< Sigma-delta channel6 */
-    SIGMADELTA_CHANNEL_7 = 7,         /*!< Sigma-delta channel7 */
+    SIGMADELTA_CHANNEL_0 = 0,         /*!< Sigma-delta channel 0 */
+    SIGMADELTA_CHANNEL_1 = 1,         /*!< Sigma-delta channel 1 */
+    SIGMADELTA_CHANNEL_2 = 2,         /*!< Sigma-delta channel 2 */
+    SIGMADELTA_CHANNEL_3 = 3,         /*!< Sigma-delta channel 3 */
+    SIGMADELTA_CHANNEL_4 = 4,         /*!< Sigma-delta channel 4 */
+    SIGMADELTA_CHANNEL_5 = 5,         /*!< Sigma-delta channel 5 */
+    SIGMADELTA_CHANNEL_6 = 6,         /*!< Sigma-delta channel 6 */
+    SIGMADELTA_CHANNEL_7 = 7,         /*!< Sigma-delta channel 7 */
     SIGMADELTA_CHANNEL_MAX,
 } sigmadelta_channel_t;
 
@@ -64,7 +64,8 @@ esp_err_t sigmadelta_config(const sigmadelta_config_t *config);
  *
  *        This function is used to set Sigma-delta channel duty,
  *        If you add a capacitor between the output pin and ground,
- *        the average output voltage Vdc = VDDIO / 256 * duty + VDDIO/2, VDDIO is power supply voltage.
+ *        the average output voltage will be Vdc = VDDIO / 256 * duty + VDDIO/2,
+ *        where VDDIO is the power supply voltage.
  *
  * @param channel Sigma-delta channel number
  * @param duty Sigma-delta duty of one channel, the value ranges from -128 to 127, recommended range is -90 ~ 90.
index de1b619f93059b9164fd4ae078bf786e2b4324f8..5cf3d08ec0434e613cee2dc73917b1bf01bc5a10 100644 (file)
@@ -601,7 +601,7 @@ int uart_write_bytes_with_break(uart_port_t uart_num, const char* src, size_t si
 int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickType_t ticks_to_wait);
 
 /**
- * @brief UART ring buffer flush
+ * @brief UART ring buffer flush. This will discard all data in the UART RX buffer.
  *
  * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2
  *
index 0804721e4d95ba933b7ac91c1176d70d07c5c483..39df20cfa74db7f42ddda614dd0e4e2fb47dda4f 100644 (file)
@@ -100,7 +100,7 @@ const rtc_gpio_desc_t rtc_gpio_desc[GPIO_PIN_COUNT] = {
     {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1},                                                                                                                                            //23
     {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1},                                                                                                                                            //24
     {RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_MUX_SEL_M, RTC_IO_PDAC1_FUN_SEL_S, RTC_IO_PDAC1_FUN_IE_M, RTC_IO_PDAC1_RUE_M, RTC_IO_PDAC1_RDE_M, RTC_IO_PDAC1_SLP_SEL_M, RTC_IO_PDAC1_SLP_IE_M, RTC_IO_PDAC1_HOLD_M, RTC_CNTL_PDAC1_HOLD_FORCE_M, RTC_IO_PDAC1_DRV_V, RTC_IO_PDAC1_DRV_S, RTCIO_GPIO25_CHANNEL},                           //25
-    {RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_MUX_SEL_M, RTC_IO_PDAC2_FUN_SEL_S, RTC_IO_PDAC2_FUN_IE_M, RTC_IO_PDAC2_RUE_M, RTC_IO_PDAC2_RDE_M, RTC_IO_PDAC2_SLP_SEL_M, RTC_IO_PDAC2_SLP_IE_M, RTC_IO_PDAC2_HOLD_M, RTC_CNTL_PDAC1_HOLD_FORCE_M, RTC_IO_PDAC2_DRV_V, RTC_IO_PDAC2_DRV_S, RTCIO_GPIO26_CHANNEL},                           //26
+    {RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_MUX_SEL_M, RTC_IO_PDAC2_FUN_SEL_S, RTC_IO_PDAC2_FUN_IE_M, RTC_IO_PDAC2_RUE_M, RTC_IO_PDAC2_RDE_M, RTC_IO_PDAC2_SLP_SEL_M, RTC_IO_PDAC2_SLP_IE_M, RTC_IO_PDAC2_HOLD_M, RTC_CNTL_PDAC2_HOLD_FORCE_M, RTC_IO_PDAC2_DRV_V, RTC_IO_PDAC2_DRV_S, RTCIO_GPIO26_CHANNEL},                           //26
     {RTC_IO_TOUCH_PAD7_REG, RTC_IO_TOUCH_PAD7_MUX_SEL_M, RTC_IO_TOUCH_PAD7_FUN_SEL_S, RTC_IO_TOUCH_PAD7_FUN_IE_M, RTC_IO_TOUCH_PAD7_RUE_M, RTC_IO_TOUCH_PAD7_RDE_M, RTC_IO_TOUCH_PAD7_SLP_SEL_M, RTC_IO_TOUCH_PAD7_SLP_IE_M, RTC_IO_TOUCH_PAD7_HOLD_M, RTC_CNTL_TOUCH_PAD7_HOLD_FORCE_M, RTC_IO_TOUCH_PAD7_DRV_V, RTC_IO_TOUCH_PAD7_DRV_S, RTCIO_GPIO27_CHANNEL}, //27
     {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1},                                                                                                                                            //28
     {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1},                                                                                                                                            //29
index 11f702e52892ccf987f9212bbe99b3c644aad425..57cf3f99359d0a9b99f2c53266a9267858875950 100644 (file)
@@ -172,10 +172,11 @@ static void release_bus(int slot)
 /// Clock out 80 cycles (10 bytes) before GO_IDLE command\r
 static void go_idle_clockout(int slot)\r
 {\r
-    uint8_t data[10];\r
+    //actually we need 10, declare 12 to meet requirement of RXDMA\r
+    uint8_t data[12];\r
     memset(data, 0xff, sizeof(data));\r
     spi_transaction_t t = {\r
-        .length = sizeof(data) * 8,\r
+        .length = 10*8,\r
         .tx_buffer = data,\r
         .rx_buffer = data,\r
     };\r
@@ -428,22 +429,21 @@ static esp_err_t start_command_default(int slot, int flags, sdspi_hw_cmd_t *cmd)
 static esp_err_t poll_busy(int slot, spi_transaction_t* t)\r
 {\r
     uint8_t t_rx;\r
-    uint8_t rd_data;\r
     *t = (spi_transaction_t) {\r
         .tx_buffer = &t_rx,\r
-        .rx_buffer = &rd_data,\r
+        .flags = SPI_TRANS_USE_RXDATA,  //data stored in rx_data\r
         .length = 8,\r
     };\r
     esp_err_t ret;\r
 \r
     for (int i = 0; i < SDSPI_RETRY_COUNT; i++) {\r
         t_rx = SDSPI_MOSI_IDLE_VAL;\r
-        rd_data = 0;\r
+        t->rx_data[0] = 0;\r
         ret = spi_device_transmit(spi_handle(slot), t);\r
         if (ret != ESP_OK) {\r
             return ret;\r
         }\r
-        if (rd_data != 0) {\r
+        if (t->rx_data[0] != 0) {\r
             if (i < SDSPI_RETRY_COUNT - 2) {\r
                 i = SDSPI_RETRY_COUNT - 2;\r
             }\r
@@ -456,28 +456,27 @@ static esp_err_t poll_busy(int slot, spi_transaction_t* t)
 static esp_err_t poll_response_token(int slot, spi_transaction_t* t)\r
 {\r
     uint8_t t_rx;\r
-    uint8_t rd_data;\r
     *t = (spi_transaction_t) {\r
         .tx_buffer = &t_rx,\r
-        .rx_buffer = &rd_data,\r
+        .flags = SPI_TRANS_USE_RXDATA,\r
         .length = 8,\r
     };\r
     esp_err_t ret;\r
 \r
     for (int retry = 0; retry < SDSPI_RETRY_COUNT; retry++) {\r
         t_rx = SDSPI_MOSI_IDLE_VAL;\r
-        rd_data = 0;\r
+        t->rx_data[0] = 0;\r
         ret = spi_device_transmit(spi_handle(slot), t);\r
         if (ret != ESP_OK) {\r
             return ret;\r
         }\r
-        if ((rd_data & TOKEN_RSP_MASK) == TOKEN_RSP_OK) {\r
+        if ((t->rx_data[0] & TOKEN_RSP_MASK) == TOKEN_RSP_OK) {\r
             break;\r
         }\r
-        if ((rd_data & TOKEN_RSP_MASK) == TOKEN_RSP_CRC_ERR) {\r
+        if ((t->rx_data[0] & TOKEN_RSP_MASK) == TOKEN_RSP_CRC_ERR) {\r
             return ESP_ERR_INVALID_CRC;\r
         }\r
-        if ((rd_data & TOKEN_RSP_MASK) == TOKEN_RSP_WRITE_ERR) {\r
+        if ((t->rx_data[0] & TOKEN_RSP_MASK) == TOKEN_RSP_WRITE_ERR) {\r
             return ESP_ERR_INVALID_RESPONSE;\r
         }\r
         if (retry == SDSPI_RETRY_COUNT - 1) {\r
index dc073dc28836f4442029c143953506c0abe8a845..465f0689949b8f29d351c02ad63887621f56423e 100644 (file)
@@ -620,8 +620,8 @@ esp_err_t spi_device_queue_trans(spi_device_handle_t handle, spi_transaction_t *
         trans_buf.buffer_to_rcv = trans_desc->rx_buffer;
     }
     if ( trans_buf.buffer_to_rcv && handle->host->dma_chan && (!esp_ptr_dma_capable( trans_buf.buffer_to_rcv ) || ((int)trans_buf.buffer_to_rcv%4!=0)) ) {
-        //if rxbuf in the desc not DMA-capable, malloc a new one
-        trans_buf.buffer_to_rcv = heap_caps_malloc((trans_desc->rxlength+7)/8, MALLOC_CAP_DMA);
+        //if rxbuf in the desc not DMA-capable, malloc a new one. The rx buffer need to be length of multiples of 32 bits to avoid heap corruption.
+        trans_buf.buffer_to_rcv = heap_caps_malloc((trans_desc->rxlength+31)/8, MALLOC_CAP_DMA);
         if ( trans_buf.buffer_to_rcv==NULL ) return ESP_ERR_NO_MEM;
     }
     
index 5d35f4eee68a19200605ac4ad13e5bbe892237bc..c9280363542cd7cc788781da84e6713c24c2a0f4 100644 (file)
@@ -261,62 +261,62 @@ TEST_CASE("SPI Master test, interaction of multiple devs", "[spi][ignore]") {
 
 TEST_CASE("SPI Master no response when switch from host1 (HSPI) to host2 (VSPI)", "[spi]")
 {
-       //spi config
-       spi_bus_config_t bus_config;
-       spi_device_interface_config_t device_config;
-       spi_device_handle_t spi;
-       spi_host_device_t host;
-       int dma = 1;
-
-       memset(&bus_config, 0, sizeof(spi_bus_config_t));
-       memset(&device_config, 0, sizeof(spi_device_interface_config_t));
-
-       bus_config.miso_io_num = -1;
-       bus_config.mosi_io_num = 26;
-       bus_config.sclk_io_num = 25;
-       bus_config.quadwp_io_num = -1;
-       bus_config.quadhd_io_num = -1;
-
-       device_config.clock_speed_hz = 50000;
-       device_config.mode = 0;
-       device_config.spics_io_num = -1;
-       device_config.queue_size = 1;
-       device_config.flags = SPI_DEVICE_TXBIT_LSBFIRST | SPI_DEVICE_RXBIT_LSBFIRST;
-
-       struct spi_transaction_t transaction = {
-               .flags = SPI_TRANS_USE_TXDATA | SPI_TRANS_USE_RXDATA,
-               .length = 16,
-               .tx_buffer = NULL,
-               .rx_buffer = NULL,
-               .tx_data = {0x04, 0x00}
-       };
-
-
-       //initialize for first host
-       host = 1;
-
-       assert(spi_bus_initialize(host, &bus_config, dma) == ESP_OK);
-       assert(spi_bus_add_device(host, &device_config, &spi) == ESP_OK);
-
-       printf("before first xmit\n");
-       assert(spi_device_transmit(spi, &transaction) == ESP_OK);
-       printf("after first xmit\n");
-
-       assert(spi_bus_remove_device(spi) == ESP_OK);
-       assert(spi_bus_free(host) == ESP_OK);
-
-
-       //for second host and failed before
-       host = 2;
-
-       assert(spi_bus_initialize(host, &bus_config, dma) == ESP_OK);
-       assert(spi_bus_add_device(host, &device_config, &spi) == ESP_OK);
-
-       printf("before second xmit\n");
-       // the original version (bit mis-written) stucks here.
-       assert(spi_device_transmit(spi, &transaction) == ESP_OK);
-       // test case success when see this.
-       printf("after second xmit\n");
+    //spi config
+    spi_bus_config_t bus_config;
+    spi_device_interface_config_t device_config;
+    spi_device_handle_t spi;
+    spi_host_device_t host;
+    int dma = 1;
+
+    memset(&bus_config, 0, sizeof(spi_bus_config_t));
+    memset(&device_config, 0, sizeof(spi_device_interface_config_t));
+
+    bus_config.miso_io_num = -1;
+    bus_config.mosi_io_num = 26;
+    bus_config.sclk_io_num = 25;
+    bus_config.quadwp_io_num = -1;
+    bus_config.quadhd_io_num = -1;
+
+    device_config.clock_speed_hz = 50000;
+    device_config.mode = 0;
+    device_config.spics_io_num = -1;
+    device_config.queue_size = 1;
+    device_config.flags = SPI_DEVICE_TXBIT_LSBFIRST | SPI_DEVICE_RXBIT_LSBFIRST;
+
+    struct spi_transaction_t transaction = {
+        .flags = SPI_TRANS_USE_TXDATA | SPI_TRANS_USE_RXDATA,
+        .length = 16,
+        .rx_buffer = NULL,
+        .tx_data = {0x04, 0x00}
+    };
+
+    //initialize for first host
+    host = 1;
+
+    TEST_ASSERT(spi_bus_initialize(host, &bus_config, dma) == ESP_OK);
+    TEST_ASSERT(spi_bus_add_device(host, &device_config, &spi) == ESP_OK);
+
+    printf("before first xmit\n");
+    TEST_ASSERT(spi_device_transmit(spi, &transaction) == ESP_OK);
+    printf("after first xmit\n");
+
+    TEST_ASSERT(spi_bus_remove_device(spi) == ESP_OK);
+    TEST_ASSERT(spi_bus_free(host) == ESP_OK);
+
+    //for second host and failed before
+    host = 2;
+
+    TEST_ASSERT(spi_bus_initialize(host, &bus_config, dma) == ESP_OK);
+    TEST_ASSERT(spi_bus_add_device(host, &device_config, &spi) == ESP_OK);
+
+    printf("before second xmit\n");
+    // the original version (bit mis-written) stucks here.
+    TEST_ASSERT(spi_device_transmit(spi, &transaction) == ESP_OK);
+    // test case success when see this.
+    printf("after second xmit\n");
+
+    TEST_ASSERT(spi_bus_remove_device(spi) == ESP_OK);
+    TEST_ASSERT(spi_bus_free(host) == ESP_OK);
 }
 
 IRAM_ATTR  static uint32_t data_iram[320];
@@ -355,11 +355,10 @@ TEST_CASE("SPI Master DMA test, TX and RX in different regions", "[spi]")
     };
     //Initialize the SPI bus
     ret=spi_bus_initialize(HSPI_HOST, &buscfg, 1);
-    assert(ret==ESP_OK);
+    TEST_ASSERT(ret==ESP_OK);
     //Attach the LCD to the SPI bus
     ret=spi_bus_add_device(HSPI_HOST, &devcfg, &spi);
-    assert(ret==ESP_OK);
-
+    TEST_ASSERT(ret==ESP_OK);
 
     static spi_transaction_t trans[6];
     int x;
@@ -392,17 +391,101 @@ TEST_CASE("SPI Master DMA test, TX and RX in different regions", "[spi]")
     trans[5].length = 8*4;
     trans[5].flags = SPI_TRANS_USE_RXDATA | SPI_TRANS_USE_TXDATA;
 
-
     //Queue all transactions.
     for (x=0; x<6; x++) {
         ret=spi_device_queue_trans(spi,&trans[x], portMAX_DELAY);
-        assert(ret==ESP_OK);
+        TEST_ASSERT(ret==ESP_OK);
     }
 
     for (x=0; x<6; x++) {
         spi_transaction_t* ptr;
         ret=spi_device_get_trans_result(spi,&ptr, portMAX_DELAY);
-        assert(ret==ESP_OK);
-        assert(ptr = trans+x);
+        TEST_ASSERT(ret==ESP_OK);
+        TEST_ASSERT(ptr = trans+x);
+    }
+
+    TEST_ASSERT(spi_bus_remove_device(spi) == ESP_OK);
+    TEST_ASSERT(spi_bus_free(HSPI_HOST) == ESP_OK);
+}
+
+
+static inline void int_connect( uint32_t gpio, uint32_t sigo, uint32_t sigi ) 
+{
+    gpio_matrix_out( gpio, sigo, false, false );
+    gpio_matrix_in( gpio, sigi, false );
+}
+
+//this part tests 3 DMA issues in master mode, full-duplex in IDF2.1
+// 1. RX buffer not aligned (start and end)
+// 2. not setting rx_buffer
+// 3. setting rx_length != length
+TEST_CASE("SPI Master DMA test: length, start, not aligned", "[spi]")
+{
+    uint8_t tx_buf[320]={0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0xaa, 0xcc, 0xff, 0xee, 0x55, 0x77, 0x88, 0x43};
+    uint8_t rx_buf[320];
+
+    esp_err_t ret;
+    spi_device_handle_t spi;
+    spi_bus_config_t buscfg={
+        .miso_io_num=PIN_NUM_MISO, 
+        .mosi_io_num=PIN_NUM_MOSI,
+        .sclk_io_num=PIN_NUM_CLK,
+        .quadwp_io_num=-1,
+        .quadhd_io_num=-1
+    };
+    spi_device_interface_config_t devcfg={
+        .clock_speed_hz=10*1000*1000,               //Clock out at 10 MHz
+        .mode=0,                                //SPI mode 0
+        .spics_io_num=PIN_NUM_CS,               //CS pin
+        .queue_size=7,                          //We want to be able to queue 7 transactions at a time
+        .pre_cb=NULL,  
+    };
+    //Initialize the SPI bus
+    ret=spi_bus_initialize(HSPI_HOST, &buscfg, 1);
+    TEST_ASSERT(ret==ESP_OK);
+    //Attach the LCD to the SPI bus
+    ret=spi_bus_add_device(HSPI_HOST, &devcfg, &spi);
+    TEST_ASSERT(ret==ESP_OK);
+
+    //do internal connection
+    int_connect( PIN_NUM_MOSI, HSPID_OUT_IDX, HSPIQ_IN_IDX );
+
+    memset(rx_buf, 0x66, 320);
+    
+    for ( int i = 0; i < 8; i ++ ) {
+        memset( rx_buf, 0x66, sizeof(rx_buf));
+
+        spi_transaction_t t = {};
+        t.length = 8*(i+1);
+        t.rxlength = 0;
+        t.tx_buffer = tx_buf+2*i;  
+        t.rx_buffer = rx_buf + i;
+
+        if ( i == 1 ) {
+            //test set no start
+            t.rx_buffer = NULL;
+        } else if ( i == 2 ) {
+            //test rx length != tx_length
+            t.rxlength = t.length - 8;
+        } 
+        spi_device_transmit( spi, &t );
+
+        for( int i = 0; i < 16; i ++ ) {
+            printf("%02X ", rx_buf[i]);
+        }
+        printf("\n");
+
+        if ( i == 1 ) {
+            // no rx, skip check
+        } else if ( i == 2 ) {
+            //test rx length = tx length-1
+            TEST_ASSERT( memcmp(t.tx_buffer, t.rx_buffer, t.length/8-1)==0 );
+        } else {
+            //normal check
+            TEST_ASSERT( memcmp(t.tx_buffer, t.rx_buffer, t.length/8)==0 );
+        }  
     }
+
+    TEST_ASSERT(spi_bus_remove_device(spi) == ESP_OK);
+    TEST_ASSERT(spi_bus_free(HSPI_HOST) == ESP_OK);
 }
index 65f88dd916e6cb7b2b564144903622314c9e5c3d..0870a731bca062923e877b26e41766727adaebc3 100644 (file)
@@ -47,7 +47,7 @@ config SPIRAM_BOOT_INIT
 
 choice SPIRAM_USE
     prompt "SPI RAM access method"
-    default SPIRAM_USE_MEMMAP
+    default SPIRAM_USE_MALLOC
     help
         The SPI RAM can be accessed in multiple methods: by just having it available as an unmanaged
         memory region in the ESP32 memory map, by integrating it in the ESP32s heap as 'special' memory
@@ -59,8 +59,7 @@ config SPIRAM_USE_MEMMAP
 config SPIRAM_USE_CAPS_ALLOC
     bool "Make RAM allocatable using heap_caps_malloc(..., MALLOC_CAP_SPIRAM)"
 config SPIRAM_USE_MALLOC
-    bool "Make RAM allocatable using malloc as well"
-    depends on TO_BE_DONE
+    bool "Make RAM allocatable using malloc() as well"
 endchoice
 
 choice SPIRAM_TYPE
@@ -118,6 +117,46 @@ config SPIRAM_CACHE_WORKAROUND
         with the workaround and located in flash instead.
 
 
+config SPIRAM_MALLOC_ALWAYSINTERNAL
+    int "Maximum malloc() size, in bytes, to always put in internal memory"
+    depends on SPIRAM_USE_MALLOC
+    default 16384
+    range 0 131072
+    help
+        If malloc() is capable of also allocating SPI-connected ram, its allocation strategy will prefer to allocate chunks less
+        than this size in internal memory, while allocations larger than this will be done from external RAM.
+        If allocation from the preferred region fails, an attempt is made to allocate from the non-preferred
+        region instead, so malloc() will not suddenly fail when either internal or external memory is full.
+
+config SPIRAM_MALLOC_RESERVE_INTERNAL
+    int "Reserve this amount of bytes for data that specifically needs to be in DMA or internal memory"
+    depends on SPIRAM_USE_MALLOC
+    default 32768
+    range 0 131072
+    help
+        Because the external/internal RAM allocation strategy is not always perfect, it sometimes may happen
+        that the internal memory is entirely filled up. This causes allocations that are specifically done in
+        internal memory, for example the stack for new tasks or memory to service DMA or have memory that's 
+        also available when SPI cache is down, to fail. This option reserves a pool specifically for requests 
+        like that; the memory in this pool is not given out when a normal malloc() is called.
+        
+        Set this to 0 to disable this feature.
+        
+        Note that because FreeRTOS stacks are forced to internal memory, they will also use this memory pool;
+        be sure to keep this in mind when adjusting this value.
+
+config SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY
+    bool "Allow external memory as an argument to xTaskCreateStatic"
+    default n
+    depends on SPIRAM_USE_MALLOC
+    help
+        Because some bits of the ESP32 code environment cannot be recompiled with the cache workaround, normally
+        tasks cannot be safely run with their stack residing in external memory; for this reason xTaskCreate and
+        friends always allocate stack in internal memory and xTaskCreateStatic will check if the memory passed
+        to it is in internal memory. If you have a task that needs a large amount of stack and does not call on
+        ROM code in any way (no direct calls, but also no Bluetooth/WiFi), you can try to disable this and use
+        xTaskCreateStatic to create the tasks stack in external memory.
+
 endmenu
 
 config MEMMAP_TRACEMEM
@@ -488,10 +527,11 @@ config INT_WDT_CHECK_CPU1
         Also detect if interrupts on CPU 1 are disabled for too long.
 
 config TASK_WDT
-    bool "Task watchdog"
+    bool "Initialize Task Watchdog on startup"
     default y
     help
         This watchdog timer can be used to make sure individual tasks are still running.
+        The Task Watchdog timer can be initialized at run time as well
 
 config TASK_WDT_PANIC
     bool "Invoke panic handler when Task Watchdog is triggered"
@@ -500,33 +540,46 @@ config TASK_WDT_PANIC
     help
         Normally, the Task Watchdog will only print out a warning if it detects it has not
         been fed. If this is enabled, it will invoke the panic handler instead, which
-        can then halt or reboot the chip.
+        can then halt or reboot the chip. This can also be configured at run time
+        by reinitializing the task watchdog.
 
 config TASK_WDT_TIMEOUT_S
-    int "Task watchdog timeout (seconds)"
+    int "Task watchdog timeout (ms)"
     depends on TASK_WDT
-    range 1 60
-    default 5
+    range 1 60000
+    default 5000
     help
-        Timeout for the task WDT, in seconds.
+        Timeout for the task WDT, in ms.
 
-config TASK_WDT_CHECK_IDLE_TASK
-    bool "Task watchdog watches CPU0 idle task"
+config TASK_WDT_CHECK_IDLE_TASK_CPU0
+    bool "Add CPU0 idle task to task watchdog on startup"
     depends on TASK_WDT
     default y
     help
-        With this turned on, the task WDT can detect if the idle task is not called within the task
-        watchdog timeout period. The idle task not being called usually is a symptom of another
+        With this turned on, the CPU0 idle task will be added to the task watchdog
+        on startup. Adding the idle task to the task watchdog allows for the detection
+        of CPU starvation. The idle task not being called is usually a symptom of another
         task hoarding the CPU. It is also a bad thing because FreeRTOS household tasks depend on the
-        idle task getting some runtime every now and then. Take Care: With this disabled, this
-        watchdog will trigger if no tasks register themselves within the timeout value.
+        idle task getting some runtime every now and then.
 
 config TASK_WDT_CHECK_IDLE_TASK_CPU1
-    bool "Task watchdog also watches CPU1 idle task"
-    depends on TASK_WDT_CHECK_IDLE_TASK && !FREERTOS_UNICORE
+    bool "Add CPU0 idle task to task watchdog on startup"
+    depends on TASK_WDT && !FREERTOS_UNICORE
     default y
     help
-        Also check the idle task that runs on CPU1.
+        With this turned on, the CPU1 idle task will also be added to the task watchdog
+        on startup.
+
+config TASK_WDT_LEGACY_BEHAVIOR
+    bool "Use legacy behavior for task watchdog"
+    depends on TASK_WDT
+    default n
+    help
+        Task wdt legacy behavior will add a task to the wdt list on its first
+        call to esp_task_wdt_feed(). Furthermore, tasks can only remove 
+        themselves from the wdt task list when calling esp_task_wdt_delete().
+        Therefore esp_task_wdt_delete() should be called with no parameters
+        when legacy behavior is enabled.
 
 #The brownout detector code is disabled (by making it depend on a nonexisting symbol) because the current revision of ESP32
 #silicon has a bug in the brown-out detector, rendering it unusable for resetting the CPU.
@@ -776,6 +829,7 @@ config ESP32_WIFI_STATIC_TX_BUFFER
     bool "STATIC"
 config ESP32_WIFI_DYNAMIC_TX_BUFFER
     bool "DYNAMIC"
+    depends on !SPIRAM_USE_MALLOC
 endchoice
 
 config ESP32_WIFI_TX_BUFFER_TYPE
index 4472629e8eb248bfc10a6fa5241c028eac795f33..319b31fbcd829ab7fa5e44436f95463a2e7eca58 100644 (file)
@@ -27,6 +27,7 @@
 #include "soc/rtc_cntl_reg.h"
 #include "soc/dport_reg.h"
 #include "soc/i2s_reg.h"
+#include "xtensa/core-macros.h"
 
 /* Number of cycles to wait from the 32k XTAL oscillator to consider it running.
  * Larger values increase startup delay. Smaller values may cause false positive
@@ -35,6 +36,8 @@
 #define XTAL_32K_DETECT_CYCLES  32
 #define SLOW_CLK_CAL_CYCLES     CONFIG_ESP32_RTC_CLK_CAL_CYCLES
 
+#define MHZ (1000000)
+
 static void select_rtc_slow_clk(rtc_slow_freq_t slow_clk);
 
 static const char* TAG = "clk";
@@ -77,7 +80,14 @@ void esp_clk_init(void)
     // Wait for UART TX to finish, otherwise some UART output will be lost
     // when switching APB frequency
     uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM);
+    
+    uint32_t freq_before = rtc_clk_cpu_freq_value(rtc_clk_cpu_freq_get()) / MHZ ;
+    
     rtc_clk_cpu_freq_set(freq);
+
+    // Re calculate the ccount to make time calculation correct. 
+    uint32_t freq_after = CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ;
+    XTHAL_SET_CCOUNT( XTHAL_GET_CCOUNT() * freq_after / freq_before );
 }
 
 void IRAM_ATTR ets_update_cpu_frequency(uint32_t ticks_per_us)
index 25e31de45f6b81e869884c8e3ce21b0890f5e2f1..9834938390a305085864a1b257552b99949007b9 100644 (file)
@@ -262,6 +262,16 @@ void start_cpu0_default(void)
         ESP_EARLY_LOGE(TAG, "External RAM could not be added to heap!");
         abort();
     }
+#if CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL
+    r=esp_spiram_reserve_dma_pool(CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL);
+    if (r != ESP_OK) {
+        ESP_EARLY_LOGE(TAG, "Could not reserve internal/DMA pool!");
+        abort();
+    }
+#endif
+#if CONFIG_SPIRAM_USE_MALLOC
+    heap_caps_malloc_extmem_enable(CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL);
+#endif
 #endif
 
 //Enable trace memory and immediately start trace.
@@ -313,9 +323,6 @@ void start_cpu0_default(void)
     do_global_ctors();
 #if CONFIG_INT_WDT
     esp_int_wdt_init();
-#endif
-#if CONFIG_TASK_WDT
-    esp_task_wdt_init();
 #endif
     esp_cache_err_int_init();
     esp_crosscore_int_init();
@@ -390,6 +397,30 @@ static void main_task(void* args)
 #endif
     //Enable allocation in region where the startup stacks were located.
     heap_caps_enable_nonos_stack_heaps();
+
+    //Initialize task wdt
+#ifdef CONFIG_TASK_WDT
+#ifdef CONFIG_TASK_WDT_PANIC
+    esp_task_wdt_init(CONFIG_TASK_WDT_TIMEOUT_S, true);
+#else
+    esp_task_wdt_init(CONFIG_TASK_WDT_TIMEOUT_S, false);
+#endif
+#endif
+    //Add IDLE 0 to task wdt
+#ifdef CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0
+    TaskHandle_t idle_0 = xTaskGetIdleTaskHandleForCPU(0);
+    if(idle_0 != NULL){
+        esp_task_wdt_add(idle_0);
+    }
+#endif
+    //Add IDLE 1 to task wdt
+#ifdef CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1
+    TaskHandle_t idle_1 = xTaskGetIdleTaskHandleForCPU(1);
+    if(idle_1 != NULL){
+        esp_task_wdt_add(idle_1);
+    }
+#endif
+
     app_main();
     vTaskDelete(NULL);
 }
index 6abc51dbdef3fa4cf615ef12be872b4599f61f18..2281eea34315b52bd44f4d0e245a5ceecac194fe 100644 (file)
@@ -41,6 +41,7 @@
 
 #include "xtensa/core-macros.h"
 
+#ifndef CONFIG_FREERTOS_UNICORE
 static portMUX_TYPE g_dport_mux = portMUX_INITIALIZER_UNLOCKED;
 
 #define DPORT_CORE_STATE_IDLE        0
@@ -61,9 +62,9 @@ static uint32_t ccount_margin[portNUM_PROCESSORS][DPORT_ACCESS_BENCHMARK_STORE_N
 static uint32_t ccount_margin_cnt;
 #endif
 
-#ifndef CONFIG_FREERTOS_UNICORE
+
 static BaseType_t oldInterruptLevel[2];
-#endif
+#endif // CONFIG_FREERTOS_UNICORE
 
 /* stall other cpu that this cpu is pending to access dport register start */
 void IRAM_ATTR esp_dport_access_stall_other_cpu_start(void)
@@ -153,17 +154,17 @@ void IRAM_ATTR esp_dport_access_stall_other_cpu_end_wrap(void)
     DPORT_STALL_OTHER_CPU_END();
 }
 
+#ifndef CONFIG_FREERTOS_UNICORE
 static void dport_access_init_core(void *arg)
 {
     int core_id = 0;
     uint32_t intr_source = ETS_FROM_CPU_INTR2_SOURCE;
 
-#ifndef CONFIG_FREERTOS_UNICORE
+
     core_id = xPortGetCoreID();
     if (core_id == 1) {
         intr_source = ETS_FROM_CPU_INTR3_SOURCE;
     }
-#endif
 
     ESP_INTR_DISABLE(ETS_DPORT_INUM);
     intr_matrix_set(core_id, intr_source, ETS_DPORT_INUM);
@@ -176,40 +177,43 @@ static void dport_access_init_core(void *arg)
 
     vTaskDelete(NULL);
 }
+#endif
 
 /*  Defer initialisation until after scheduler is running */
 void esp_dport_access_int_init(void)
 {
+#ifndef CONFIG_FREERTOS_UNICORE
     portBASE_TYPE res = xTaskCreatePinnedToCore(&dport_access_init_core, "dport", configMINIMAL_STACK_SIZE, NULL, 5, NULL, xPortGetCoreID());
     assert(res == pdTRUE);
+#endif
 }
 
 void IRAM_ATTR esp_dport_access_int_pause(void)
 {
+#ifndef CONFIG_FREERTOS_UNICORE
     portENTER_CRITICAL_ISR(&g_dport_mux);
     dport_core_state[0] = DPORT_CORE_STATE_IDLE;
-#ifndef CONFIG_FREERTOS_UNICORE
     dport_core_state[1] = DPORT_CORE_STATE_IDLE;
-#endif
     portEXIT_CRITICAL_ISR(&g_dport_mux);
+#endif
 }
 
 //Used in panic code: the enter_critical stuff may be messed up so we just stop everything without checking the mux.
 void IRAM_ATTR esp_dport_access_int_abort(void)
 {
-    dport_core_state[0] = DPORT_CORE_STATE_IDLE;
 #ifndef CONFIG_FREERTOS_UNICORE
+    dport_core_state[0] = DPORT_CORE_STATE_IDLE;
     dport_core_state[1] = DPORT_CORE_STATE_IDLE;
 #endif
 }
 
 void IRAM_ATTR esp_dport_access_int_resume(void)
 {
+#ifndef CONFIG_FREERTOS_UNICORE
     portENTER_CRITICAL_ISR(&g_dport_mux);
     dport_core_state[0] = DPORT_CORE_STATE_RUNNING;
-#ifndef CONFIG_FREERTOS_UNICORE
     dport_core_state[1] = DPORT_CORE_STATE_RUNNING;
-#endif
     portEXIT_CRITICAL_ISR(&g_dport_mux);
+#endif
 }
 
index e921e258f1f0970fb43b397fac7a3a2e4e21c3d5..ffd3b45f11342a629d59ea23af5cc49d36756644 100644 (file)
@@ -45,10 +45,12 @@ _l4_intr_stack:
     .align      4
 xt_highint4:
 
+#ifndef CONFIG_FREERTOS_UNICORE
     /* See if we're here for the dport access interrupt */
     rsr     a0, INTERRUPT
     extui   a0, a0, ETS_DPORT_INUM, 1
     bnez    a0, .handle_dport_access_int
+#endif // CONFIG_FREERTOS_UNICORE
 
     /* Allocate exception frame and save minimal context. */
     mov     a0, sp
@@ -129,6 +131,7 @@ xt_highint4:
 
 
 
+#ifndef CONFIG_FREERTOS_UNICORE
 
     .align      4
 .handle_dport_access_int:
@@ -187,6 +190,7 @@ xt_highint4:
     rsr     a0, EXCSAVE_4                   /* restore a0 */
     rfi     4
 
+#endif // CONFIG_FREERTOS_UNICORE
 
 /* The linker has no reason to link in this file; all symbols it exports are already defined
    (weakly!) in the default int handler. Define a symbol here so we can use it to have the 
index 9854035295cd9069c7c971025c73527e00a7addf..2dbff1afc2cca85276d445b7c06047d11d5e3485 100644 (file)
@@ -64,4 +64,16 @@ void esp_spiram_writeback_cache();
 
 
 
+/**
+ * @brief Reserve a pool of internal memory for specific DMA/internal allocations
+ *
+ * @param size Size of reserved pool in bytes
+ *
+ * @return
+ *          - ESP_OK on success
+ *          - ESP_ERR_NO_MEM when no memory available for pool
+ */
+esp_err_t esp_spiram_reserve_dma_pool(size_t size);
+
+
 #endif
\ No newline at end of file
index eb773770096c5b58083c6de188454c0c4249b9b0..e2200faefb3849fbd5dd258fc40eaa14415fcea6 100644 (file)
 #ifndef __ESP_TASK_WDT_H
 #define __ESP_TASK_WDT_H
 
+#include "freertos/task.h"
+#include "esp_err.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-
-/** \defgroup Watchdog_APIs Watchdog APIs
-  * @brief Watchdog APIs
-  */
-
-/** @addtogroup Watchdog_APIs
-  * @{
-  */
-
 /*
-This routine enables a more general-purpose task watchdog: tasks can individually
-feed the watchdog and the watchdog will bark if one or more tasks haven't fed the
-watchdog within the specified time. Optionally, the idle tasks can also configured
-to feed the watchdog in a similar fashion, to detect CPU starvation.
-
-This uses the TIMERG0 WDT.
+This routine enables a more general-purpose task watchdog which uses the TIMERG0
+WDT: All tasks subscribed to the task wdt must feed the wdt at least once
+before the wdt times out or else the wdt will bark. The Idle tasks can also be
+subscribed to the wdt. This allows for the detection of CPU starvation.
+
+To enable/disable legacy behavior, toggle the CONFIG_TASK_WDT_LEGACY_BEHAVIOR
+option in menuconfig. Under legacy behavior, a task will subscribe to the
+wdt on its first call of esp_task_wdt_feed(). esp_task_wdt_add() SHOULD NOT BE
+CALLED in an application if legacy behavior is enabled. esp_task_wdt_delete()
+will only allow tasks to unsubscribe themselves from the task wdt under legacy
+behavior. Enabling legacy behavior will also force the task wdt to be enabled on
+startup.
 */
 
-
 /**
-  * @brief  Initialize the task watchdog. This is called in the init code, if the
-  *         task watchdog is enabled in menuconfig.
+  * @brief  Initialize the task watchdog timer
   *
-  */
-void esp_task_wdt_init();
-
-/**
-  * @brief  Feed the watchdog. After the first feeding session, the watchdog will expect the calling
-  *         task to keep feeding the watchdog until task_wdt_delete() is called.
+  * This function will initialize the task watchdog timer. If the wdt
+  * has already been initialized, calling this function again will change the
+  * timeout and panic configuration, then reset the timer count. Once the watchdog
+  * timer has been initialized, tasks can subscribe to the watchdog timer.
   *
+  * @param[in]  timeout     Timeout(ms) period of watchdog timer
+  * @param[in]  panic       Flag that if set, will cause the wdt to trigger the
+  *                         panic handler when it times out
+  * @return
+  *     - ESP_OK:           Initialization was successful
+  *     - ESP_ERR_NO_MEM:   Initialization was unsuccessful due to lack of
+  *                         memory
+  *
+  * @note   esp_task_wdt_init() must only be called after the scheduler
+  *         started
   */
+esp_err_t esp_task_wdt_init(uint32_t timeout, bool panic);
+
 
-void esp_task_wdt_feed();
+/**
+ * @brief   Deinitialize the task watchdog timer
+ *
+ * This function will deinitialize the task watchdog timer. This function should
+ * be called when all tasks have unsubscribed form the watchdog timer.
+ *
+ * @return
+ *      - ESP_OK:                 Watchdog timer successfully deinitialized
+ *      - ESP_ERR_INVALID_STATE:  Error due to wdt not being initialized, or
+ *                                if tasks are still subscribed to the wdt
+ */
+esp_err_t esp_task_wdt_deinit();
 
 
 /**
-  * @brief  Delete the watchdog for the current task.
+  * @brief  Subscribes a tasks to the task watchdog timer
   *
+  * This function subscribes a task to the task watchdog timer. Note that once
+  * subscribed, a task must periodically feed the watchdog timer. If the task
+  * is an IDLE task, this function will enable feeding from the idle hook
+  * automatically.
+  *
+  * @param[in]  handle            Handle of the task to be added. Input NULL
+  *                               to add the current running task
+  *
+  * @return
+  *     - ESP_OK:                 Successfully subscribed the task to the wdt
+  *     - ESP_ERR_INVALID_ARG:    Failed as task is already subscribed
+  *     - ESP_ERR_INVALID_STATE:  Failed as wdt has not been initialized
+  *     - ESP_ERR_NO_MEM:         Failed due to lack of memory
+  *
+  * @note   This function should not be called from application code if
+  *         CONFIG_TASK_WDT_LEGACY_BEHAVIOR has been enabled. Legacy behavior
+  *         uses esp_task_wdt_feed() to subscribe tasks to the wdt.
   */
-void esp_task_wdt_delete();
+esp_err_t esp_task_wdt_add(TaskHandle_t handle);
 
 /**
-  * @}
+  * @brief  Feed the current running task
+  *
+  * This function will feed the current running task if it has subscribed to the
+  * task watchdog timer. All non-idle tasks subscribed to the wdt must call this
+  * function at least once per watchdog timeout period. Idle tasks will feed
+  * the task watchdog automatically from their idle hooks.
+  *
+  * @return
+  *     - ESP_OK:                 Successfully fed the current running task
+  *     - ESP_ERR_INVALID_STATE:  Failed to feed current running task as the
+  *                               wdt has not been initialized, or the
+  *                               current running task has not subscribed
+  *
+  * @note   If CONFIG_TASK_WDT_LEGACY_BEHAVIOR is enabled, tasks will subscribe
+  *         to the watchdog on their first call of esp_task_wdt_feed().
   */
+esp_err_t esp_task_wdt_feed();
 
+/**
+  * @brief  Unsubscribes a task from the task watchdog timer
+  *
+  * This function unsubscribes a task from the task watchdog. After unsubscribing,
+  * the task should no longer feed the wdt. If the unsubscribing task is an idle
+  * task, this function will disable feeding from the idle hook automatically.
+  *
+  * @param[in]  handle              Handle of the task to be deleted. Input NULL
+  *                                 if deleting the current running task.
+  *
+  * @return
+  *     - ESP_OK:                 Successfully unsubscribed task form the wdt
+  *     - ESP_ERR_INVALID_ARG:    Failed to unsubscribe task as it was not
+  *                               subscribed to begin with
+  *     - ESP_ERR_INVALID_STATE:  Failed to unsubscribe task as wdt has not
+  *                               been initialized yet
+  *
+  * @note   Legacy behavior only allows tasks to unsubscribe from the wdt on their
+  *         own behalf. Therefore, if CONFIG_TASK_WDT_LEGACY_BEHAVIOR is
+  *         enabled, this esp_task_wdt_delete() will accept no parameters and
+  *         unsubscribe the calling task from the wdt.
+  */
+#ifdef CONFIG_TASK_WDT_LEGACY_BEHAVIOR
+esp_err_t esp_task_wdt_delete();
+#else
+esp_err_t esp_task_wdt_delete(TaskHandle_t handle);
+#endif      //CONFIG_TASK_WDT_LEGACY_BEHAVIOR
 
 #ifdef __cplusplus
 }
 #endif
 
-
-
-#endif
\ No newline at end of file
+#endif      //__ESP_TASK_WDT_H
index 7bfa96379ee0efe60774b5e35272df8f72c9a797..0ca0dd95abf95f13de57f7eb4c0a8ca7d26f599a 100755 (executable)
@@ -517,23 +517,34 @@ esp_err_t esp_wifi_set_channel(uint8_t primary, wifi_second_chan_t second);
 esp_err_t esp_wifi_get_channel(uint8_t *primary, wifi_second_chan_t *second);
 
 /**
-  * @brief     Set country code
-  *            The default value is WIFI_COUNTRY_CN
+  * @brief     configure country info
   *
-  * @param     country  country type
+  * @attention 1. The default country is {.cc="CN", .schan=1, .nchan=13, policy=WIFI_COUNTRY_POLICY_AUTO}
+  * @attention 2. When the country policy is WIFI_COUNTRY_POLICY_AUTO, use the country info of AP to which 
+  *               the station is connected. E.g. if the configured country info is {.cc="USA", .schan=1, .nchan=11}, 
+  *               the country info of the AP to which the station is connected is {.cc="JP", .schan=1, .nchan=14},
+  *               then our country info is {.cc="JP", .schan=1, .nchan=14}. If the station disconnected
+  *               from the AP, the country info back to {.cc="USA", .schan=1, .nchan=11} again.
+  * @attention 3. When the country policy is WIFI_COUNTRY_POLICY_MANUAL, always use the configured country info.
+  * @attention 4. When the country info is changed because of configuration or because the station connects to a different
+  *               external AP, the country IE in probe response/beacon of the soft-AP is changed also.
+  * @attention 5. The country configuration is not stored into flash
+  * @attention 6. This API doesn't validate the per-country rules, it's up to the user to fill in all fields according to 
+  *               local regulations.
+  *
+  * @param     country   the configured country info
   *
   * @return
   *    - ESP_OK: succeed
   *    - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by esp_wifi_init
   *    - ESP_ERR_WIFI_ARG: invalid argument
-  *    - others: refer to error code in esp_err.h
   */
-esp_err_t esp_wifi_set_country(wifi_country_t country);
+esp_err_t esp_wifi_set_country(wifi_country_t *country);
 
 /**
-  * @brief     Get country code
+  * @brief     get the current country info
   *
-  * @param     country  store current country
+  * @param     country  country info
   *
   * @return
   *    - ESP_OK: succeed
@@ -542,6 +553,7 @@ esp_err_t esp_wifi_set_country(wifi_country_t country);
   */
 esp_err_t esp_wifi_get_country(wifi_country_t *country);
 
+
 /**
   * @brief     Set MAC address of the ESP32 WiFi station or the soft-AP interface.
   *
index 2410b73ce1c9c66a7dd2804d79668416c2f5e7bc..e91031a5b906b97d29c230189cc6313c33406e36 100755 (executable)
@@ -40,11 +40,15 @@ typedef esp_interface_t wifi_interface_t;
 #define WIFI_IF_AP  ESP_IF_WIFI_AP
 
 typedef enum {
-    WIFI_COUNTRY_CN = 0, /**< country China, channel range [1, 14] */
-    WIFI_COUNTRY_JP,     /**< country Japan, channel range [1, 14] */
-    WIFI_COUNTRY_US,     /**< country USA, channel range [1, 11] */
-    WIFI_COUNTRY_EU,     /**< country Europe, channel range [1, 13] */
-    WIFI_COUNTRY_MAX
+    WIFI_COUNTRY_POLICY_AUTO,   /**< Country policy is auto, use the country info of AP to which the station is connected */
+    WIFI_COUNTRY_POLICY_MANUAL, /**< Country policy is manual, always use the configured country info */
+} wifi_country_policy_t;
+
+typedef struct {
+    char                  cc[3];   /**< country code string */
+    uint8_t               schan;   /**< start channel */
+    uint8_t               nchan;   /**< total channel number */
+    wifi_country_policy_t policy;  /**< country policy */
 } wifi_country_t;
 
 typedef enum {
@@ -121,6 +125,16 @@ typedef struct {
     wifi_scan_time_t scan_time;  /**< scan time per channel */
 } wifi_scan_config_t;
 
+typedef enum {
+    WIFI_CIPHER_TYPE_NONE = 0,   /**< the cipher type is none */
+    WIFI_CIPHER_TYPE_WEP40,      /**< the cipher type is WEP40 */
+    WIFI_CIPHER_TYPE_WEP104,     /**< the cipher type is WEP104 */
+    WIFI_CIPHER_TYPE_TKIP,       /**< the cipher type is TKIP */
+    WIFI_CIPHER_TYPE_CCMP,       /**< the cipher type is CCMP */
+    WIFI_CIPHER_TYPE_TKIP_CCMP,  /**< the cipher type is TKIP and CCMP */
+    WIFI_CIPHER_TYPE_UNKNOWN,    /**< the cipher type is unknown */
+} wifi_cipher_type_t;
+
 typedef struct {
     uint8_t bssid[6];                     /**< MAC address of AP */
     uint8_t ssid[33];                     /**< SSID of AP */
@@ -128,10 +142,26 @@ typedef struct {
     wifi_second_chan_t second;            /**< second channel of AP */
     int8_t  rssi;                         /**< signal strength of AP */
     wifi_auth_mode_t authmode;            /**< authmode of AP */
-    uint32_t low_rate_enable:1;           /**< bit: 0 flag to identify if low rate is enabled or not */
-    uint32_t reserved:31;                 /**< bit: 1..31 reserved */
+    wifi_cipher_type_t pairwise_cipher;   /**< pairwise cipher of AP */
+    wifi_cipher_type_t group_cipher;      /**< group cipher of AP */
+    uint32_t phy_11b:1;                   /**< bit: 0 flag to identify if 11b mode is enabled or not */
+    uint32_t phy_11g:1;                   /**< bit: 1 flag to identify if 11g mode is enabled or not */
+    uint32_t phy_11n:1;                   /**< bit: 2 flag to identify if 11n mode is enabled or not */
+    uint32_t phy_lr:1;                    /**< bit: 3 flag to identify if low rate is enabled or not */
+    uint32_t wps:1;                       /**< bit: 4 flag to identify if WPS is supported or not */
+    uint32_t reserved:27;                 /**< bit: 5..31 reserved */
 } wifi_ap_record_t;
 
+typedef enum {
+    WIFI_FAST_SCAN = 0,                   /**< Do fast scan, scan will end after find SSID match AP */
+    WIFI_ALL_CHANNEL_SCAN,                /**< All channel scan, scan will end after scan all the channel */
+}wifi_scan_method_t;
+
+typedef enum {
+    WIFI_CONNECT_AP_BY_SIGNAL = 0,        /**< Sort match AP in scan list by RSSI */
+    WIFI_CONNECT_AP_BY_SECURITY,          /**< Sort match AP in scan list by security mode */
+}wifi_sort_method_t;
+
 typedef enum {
     WIFI_PS_NONE,    /**< No power save */
     WIFI_PS_MODEM,   /**< Modem power save */
@@ -161,9 +191,11 @@ typedef struct {
 typedef struct {
     uint8_t ssid[32];      /**< SSID of target AP*/
     uint8_t password[64];  /**< password of target AP*/
+    wifi_scan_method_t scan_method;    /**< do all channel scan or fast scan */
     bool bssid_set;        /**< whether set MAC address of target AP or not. Generally, station_config.bssid_set needs to be 0; and it needs to be 1 only when users need to check the MAC address of the AP.*/
     uint8_t bssid[6];     /**< MAC address of target AP*/
     uint8_t channel;       /**< channel of target AP. Set to 1~13 to scan starting from the specified channel before connecting to AP. If the channel of AP is unknown, set it to 0.*/
+    wifi_sort_method_t sort_method;    /**< sort the connect AP in the list by rssi or security mode */
 } wifi_sta_config_t;
 
 typedef union {
index b38fc994eda37fcc7faf962d171b55b2f63157bf..c01ecf33c811d20e4e1ea6ac35ccd2354a71399c 100644 (file)
@@ -222,7 +222,7 @@ static vector_desc_t *get_desc_for_int(int intno, int cpu)
 {
     vector_desc_t *vd=find_desc_for_int(intno, cpu);
     if (vd==NULL) {
-        vector_desc_t *newvd=malloc(sizeof(vector_desc_t));
+        vector_desc_t *newvd=heap_caps_malloc(sizeof(vector_desc_t), MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT);
         if (newvd==NULL) return NULL;
         memset(newvd, 0, sizeof(vector_desc_t));
         newvd->intno=intno;
@@ -574,7 +574,7 @@ esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusre
     if (source==ETS_INTERNAL_PROFILING_INTR_SOURCE) force=ETS_INTERNAL_PROFILING_INTR_NO;
 
     //Allocate a return handle. If we end up not needing it, we'll free it later on.
-    ret=malloc(sizeof(intr_handle_data_t));
+    ret=heap_caps_malloc(sizeof(intr_handle_data_t), MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT);
     if (ret==NULL) return ESP_ERR_NO_MEM;
 
     portENTER_CRITICAL(&spinlock);
index a3c8f349fe3c9e5be6cf74b813a1dc505fe38dc1..70e346552061b635d26079709348676446999cf9 160000 (submodule)
@@ -1 +1 @@
-Subproject commit a3c8f349fe3c9e5be6cf74b813a1dc505fe38dc1
+Subproject commit 70e346552061b635d26079709348676446999cf9
index e305b96eb4a9c4cdf4f7d5cb7b758889a7191965..6924b2ec28458f01025958230db1258b32a6a87b 100644 (file)
@@ -197,7 +197,10 @@ static void rtc_wdt_disable()
  * Helper function which handles entry to and exit from light sleep
  * Placed into IRAM as flash may need some time to be powered on.
  */
-static esp_err_t IRAM_ATTR esp_light_sleep_inner(uint32_t pd_flags,
+static esp_err_t esp_light_sleep_inner(uint32_t pd_flags,
+        rtc_cpu_freq_t cpu_freq, uint32_t flash_enable_time_us) IRAM_ATTR __attribute__((noinline));
+
+static esp_err_t esp_light_sleep_inner(uint32_t pd_flags,
         rtc_cpu_freq_t cpu_freq, uint32_t flash_enable_time_us)
 {
     // Enter sleep
index aff3b9ce86c0a9b0dc52ced3ad46de6dd314f60f..6eae6c98e743dc89d41382e57b199c570ccce916 100644 (file)
@@ -124,11 +124,24 @@ esp_err_t esp_spiram_init()
 
 esp_err_t esp_spiram_add_to_heapalloc()
 {
+    ESP_EARLY_LOGI(TAG, "Adding pool of %dK of external SPI memory to heap allocator", CONFIG_SPIRAM_SIZE/1024);
     //Add entire external RAM region to heap allocator. Heap allocator knows the capabilities of this type of memory, so there's
     //no need to explicitly specify them.
     return heap_caps_add_region((intptr_t)SOC_EXTRAM_DATA_LOW, (intptr_t)SOC_EXTRAM_DATA_LOW + CONFIG_SPIRAM_SIZE-1);
 }
 
+
+static uint8_t *dma_heap;
+
+esp_err_t esp_spiram_reserve_dma_pool(size_t size) {
+    if (size==0) return ESP_OK; //no-op
+    ESP_EARLY_LOGI(TAG, "Reserving pool of %dK of internal memory for DMA/internal allocations", size/1024);
+    dma_heap=heap_caps_malloc(size, MALLOC_CAP_DMA|MALLOC_CAP_INTERNAL);
+    if (!dma_heap) return ESP_ERR_NO_MEM;
+    uint32_t caps[]={MALLOC_CAP_DMA|MALLOC_CAP_INTERNAL, 0, MALLOC_CAP_8BIT|MALLOC_CAP_32BIT};
+    return heap_caps_add_region_with_caps(caps, dma_heap, dma_heap+size-1);
+}
+
 size_t esp_spiram_get_size()
 {
     return CONFIG_SPIRAM_SIZE;
index 82010595e31e67af8da54d97c55ffd200c276630..37958db40bd85f235b5dd234574afca5ca73f1ae 100644 (file)
@@ -360,12 +360,12 @@ void system_restore(void)
 
 uint32_t esp_get_free_heap_size( void )
 {
-    return heap_caps_get_free_size( MALLOC_CAP_8BIT );
+    return heap_caps_get_free_size( MALLOC_CAP_DEFAULT );
 }
 
 uint32_t esp_get_minimum_free_heap_size( void )
 {
-    return heap_caps_get_minimum_free_size( MALLOC_CAP_8BIT );
+    return heap_caps_get_minimum_free_size( MALLOC_CAP_DEFAULT );
 }
 
 uint32_t system_get_free_heap_size(void) __attribute__((alias("esp_get_free_heap_size")));
index eaed32b4caf51bb7cb011292922b739ae96b032e..bb45b6a4afbc2e18dbd5b45941c2367072a9f3d3 100644 (file)
@@ -20,6 +20,7 @@
 #include <string.h>
 #include <stdbool.h>
 #include "sdkconfig.h"
+#include "freertos/FreeRTOSConfig.h"
 #include "freertos/FreeRTOS.h"
 #include "freertos/task.h"
 #include "freertos/queue.h"
@@ -28,6 +29,7 @@
 #include "esp_err.h"
 #include "esp_intr.h"
 #include "esp_intr_alloc.h"
+#include "esp_ipc.h"
 #include "esp_attr.h"
 #include "esp_freertos_hooks.h"
 #include "soc/timer_group_struct.h"
 
 #include "esp_task_wdt.h"
 
-#if CONFIG_TASK_WDT
-
-static const char* TAG = "task_wdt";
+//Assertion macro that exits from critical section and returns 'ret'
+#define ASSERT_EXIT_CRIT_RET_ERR(cond, ret)  ({                             \
+            if(!(cond)){                                                    \
+                portEXIT_CRITICAL(&taskwdt_spinlock);                       \
+                return ret;                                                 \
+            }                                                               \
+})
 
 typedef struct wdt_task_t wdt_task_t;
 struct wdt_task_t {
     TaskHandle_t task_handle;
-    bool fed_watchdog;
+    bool fed;
     wdt_task_t *next;
 };
 
-static wdt_task_t *wdt_task_list=NULL;
+typedef struct task_wdt_config_t task_wdt_config_t;
+struct task_wdt_config_t {
+    wdt_task_t *list;
+    uint32_t timeout;
+    bool panic;
+    bool idle_enable[portNUM_PROCESSORS];
+    TaskHandle_t idle_handles[portNUM_PROCESSORS];
+    intr_handle_t intr_handle;
+};
+
+static task_wdt_config_t *wdt_config = NULL;
 static portMUX_TYPE taskwdt_spinlock = portMUX_INITIALIZER_UNLOCKED;
 
+/*
+ * Internal function that checks the list for target task. Returns the list item
+ * if found and returns null if not found. Also checks if the all the other
+ * tasks have checked in. Called within critical.
+ */
+static wdt_task_t *check_list(TaskHandle_t handle, bool *checked)
+{
+    wdt_task_t *target = NULL;
+    *checked = true;
+    for(wdt_task_t *task = wdt_config->list; task != NULL; task = task->next){
+        if(task->task_handle == handle){
+            target = task;   //Get pointer to target task list member
+        }else{
+            if(task->fed == false){     //If a task has yet to check in
+                *checked = false;
+            }
+        }
+    }
+    return target;
+}
 
-static void task_wdt_isr(void *arg) {
+/*
+ * Resets the wdt and fed flags of each task on the list. Called within critical
+ */
+static void feed_wdt()
+{
+    //All tasks have checked in; time to feed the hw watchdog.
+    TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
+    TIMERG0.wdt_feed=1;
+    TIMERG0.wdt_wprotect=0;
+    //Reset fed flags on all tasks in list
+    for (wdt_task_t *task = wdt_config->list; task != NULL; task = task->next){
+        task->fed=false;
+    }
+}
+
+/*
+ * ISR for when wdt expires. Checks for which tasks have not fed. Trigger panic
+ * if configured to do so in initialization
+ */
+static void task_wdt_isr(void *arg)
+{
+    portENTER_CRITICAL(&taskwdt_spinlock);
     wdt_task_t *wdttask;
     const char *cpu;
-    //Feed the watchdog so we do not reset
+    //Feed the watchdog so we don't reset
     TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
     TIMERG0.wdt_feed=1;
     TIMERG0.wdt_wprotect=0;
@@ -65,16 +122,14 @@ static void task_wdt_isr(void *arg) {
     //bad thing, possibly (temporarily) hanging up the 2nd core and stopping FreeRTOS. In this case,
     //something bad already happened and reporting this is considered more important
     //than the badness caused by a spinlock here.
-    portENTER_CRITICAL(&taskwdt_spinlock);
-    if (!wdt_task_list) {
-        //No task on list. Maybe none registered yet.
+    if (!wdt_config->list) {       //No tasks on list
         portEXIT_CRITICAL(&taskwdt_spinlock);
         return;
     }
     //Watchdog got triggered because at least one task did not report in.
     ets_printf("Task watchdog got triggered. The following tasks did not feed the watchdog in time:\n");
-    for (wdttask=wdt_task_list; wdttask!=NULL; wdttask=wdttask->next) {
-        if (!wdttask->fed_watchdog) {
+    for (wdttask=wdt_config->list; wdttask!=NULL; wdttask=wdttask->next) {
+        if (!wdttask->fed) {
             cpu=xTaskGetAffinity(wdttask->task_handle)==0?DRAM_STR("CPU 0"):DRAM_STR("CPU 1");
             if (xTaskGetAffinity(wdttask->task_handle)==tskNO_AFFINITY) cpu=DRAM_STR("CPU 0/1");
             ets_printf(" - %s (%s)\n", pcTaskGetTaskName(wdttask->task_handle), cpu);
@@ -85,125 +140,243 @@ static void task_wdt_isr(void *arg) {
         ets_printf("CPU %d: %s\n", x, pcTaskGetTaskName(xTaskGetCurrentTaskHandleForCPU(x)));
     }
 
-#if CONFIG_TASK_WDT_PANIC
-    ets_printf("Aborting.\n");
-    abort();
-#endif
+    if (wdt_config->panic){
+        ets_printf("Aborting.\n");
+        portEXIT_CRITICAL(&taskwdt_spinlock);
+        abort();
+    }
+
     portEXIT_CRITICAL(&taskwdt_spinlock);
 }
 
+/*
+ * Idle hook for Idle Task to feed the wdt. Will only call feed if the idle task
+ * of that core has been added to wdt task list.
+ */
+static bool idle_hook(void)
+{
+    uint32_t core_id = xPortGetCoreID();
+    if (wdt_config->idle_enable[core_id]){
+        esp_task_wdt_feed();
+    }
+    return true;
+}
 
-void esp_task_wdt_feed() {
-    wdt_task_t *wdttask=wdt_task_list;
-    bool found_task=false, do_feed_wdt=true;
-    TaskHandle_t handle=xTaskGetCurrentTaskHandle();
+/*
+ * Initializes the task watchdog timer by allocating memory for the config data
+ * structure, obtaining the idle task handles/registering idle hooks, and
+ * setting the hardware timer registers. If reconfiguring, it will just modify
+ * wdt_config and reset the hardware timer.
+ */
+esp_err_t esp_task_wdt_init(uint32_t timeout, bool panic)
+{
     portENTER_CRITICAL(&taskwdt_spinlock);
+    if(wdt_config == NULL){        //wdt not initialized yet
+       //Allocate memory for wdt_config
+        wdt_config = calloc(1, sizeof(task_wdt_config_t));
+        ASSERT_EXIT_CRIT_RET_ERR((wdt_config != NULL), ESP_ERR_NO_MEM);
 
-    //Walk the linked list of wdt tasks to find this one, as well as see if we need to feed
-    //the real watchdog timer.
-    for (wdttask=wdt_task_list; wdttask!=NULL; wdttask=wdttask->next) {
-        //See if we are at the current task.
-        if (wdttask->task_handle == handle) {
-            wdttask->fed_watchdog=true;
-            found_task=true;
-        }
-        //If even one task in the list doesn't have the do_feed_wdt var set, we do not feed the watchdog.
-        if (!wdttask->fed_watchdog) do_feed_wdt=false;
-    }
-    
-    if (!found_task) {
-        //This is the first time the task calls the task_wdt_feed function. Create a new entry for it in
-        //the linked list.
-        wdt_task_t *newtask=malloc(sizeof(wdt_task_t));
-        memset(newtask, 0, sizeof(wdt_task_t));
-        newtask->task_handle=handle;
-        newtask->fed_watchdog=true;
-        if (wdt_task_list == NULL) {
-            wdt_task_list=newtask;
-        } else {
-            for (wdttask=wdt_task_list; wdttask->next!=NULL; wdttask=wdttask->next) ;
-            wdttask->next=newtask;
+        wdt_config->list = NULL;
+        wdt_config->timeout = timeout;
+        wdt_config->panic = panic;
+
+        //Get idle task handles
+        for(int i = 0; i <  portNUM_PROCESSORS; i++){
+            wdt_config->idle_handles[i] = xTaskGetIdleTaskHandleForCPU( i );
+            wdt_config->idle_enable[i] = false;
         }
-    }
-    if (do_feed_wdt) {
-        //All tasks have checked in; time to feed the hw watchdog.
-        TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
+
+        //Register Idle Hook and Interrupts
+        esp_register_freertos_idle_hook(idle_hook);
+        ESP_ERROR_CHECK(esp_intr_alloc(ETS_TG0_WDT_LEVEL_INTR_SOURCE, 0, task_wdt_isr, NULL, &wdt_config->intr_handle))
+
+        //Configure hardware timer
+        TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;               //Disable write protection
+        TIMERG0.wdt_config0.sys_reset_length=7;                 //3.2uS
+        TIMERG0.wdt_config0.cpu_reset_length=7;                 //3.2uS
+        TIMERG0.wdt_config0.level_int_en=1;
+        TIMERG0.wdt_config0.stg0=TIMG_WDT_STG_SEL_INT;          //1st stage timeout: interrupt
+        TIMERG0.wdt_config0.stg1=TIMG_WDT_STG_SEL_RESET_SYSTEM; //2nd stage timeout: reset system
+        TIMERG0.wdt_config1.clk_prescale=80*500;                //Prescaler: wdt counts in ticks of 0.5mS
+        TIMERG0.wdt_config2=wdt_config->timeout*2;      //Set timeout before interrupt
+        TIMERG0.wdt_config3=wdt_config->timeout*4;      //Set timeout before reset
+        TIMERG0.wdt_config0.en=1;
         TIMERG0.wdt_feed=1;
-        TIMERG0.wdt_wprotect=0;
-        //Reset fed_watchdog status
-        for (wdttask=wdt_task_list; wdttask->next!=NULL; wdttask=wdttask->next) wdttask->fed_watchdog=false;
+        TIMERG0.wdt_wprotect=0;                         //Enable write protection
+
+    }else{      //wdt_config previously initialized
+        //Reconfigure task wdt
+        wdt_config->panic = panic;
+        wdt_config->timeout = timeout;
+
+        //Reconfigure hardware timer
+        TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;   //Disable write protection
+        TIMERG0.wdt_config0.en=0;                   //Disable timer
+        TIMERG0.wdt_config2=wdt_config->timeout*2;           //Set timeout before interrupt
+        TIMERG0.wdt_config3=wdt_config->timeout*4;           //Set timeout before reset
+        TIMERG0.wdt_config0.en=1;                   //Renable timer
+        TIMERG0.wdt_feed=1;                         //Reset timer
+        TIMERG0.wdt_wprotect=0;                     //Enable write protection
     }
     portEXIT_CRITICAL(&taskwdt_spinlock);
+    return ESP_OK;
 }
 
-void esp_task_wdt_delete() {
-    TaskHandle_t handle=xTaskGetCurrentTaskHandle();
-    wdt_task_t *wdttask=wdt_task_list;
+esp_err_t esp_task_wdt_deinit()
+{
     portENTER_CRITICAL(&taskwdt_spinlock);
+    //wdt must already be initialized
+    ASSERT_EXIT_CRIT_RET_ERR((wdt_config != NULL), ESP_ERR_INVALID_STATE);
+    //Task list must be empty
+    ASSERT_EXIT_CRIT_RET_ERR((wdt_config->list == NULL), ESP_ERR_INVALID_STATE);
 
-    //Wdt task list can't be empty
-    if (!wdt_task_list) {
-        ESP_LOGE(TAG, "task_wdt_delete: No tasks in list?");
-        portEXIT_CRITICAL(&taskwdt_spinlock);
-        return;
+    //Disable hardware timer
+    TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;   //Disable write protection
+    TIMERG0.wdt_config0.en=0;                   //Disable timer
+    TIMERG0.wdt_wprotect=0;                     //Enable write protection
+
+    esp_deregister_freertos_idle_hook(idle_hook);   //deregister idle hook
+    ESP_ERROR_CHECK(esp_intr_free(wdt_config->intr_handle))  //Unregister interrupt
+    free(wdt_config);                      //Free wdt_config
+    wdt_config = NULL;
+    portEXIT_CRITICAL(&taskwdt_spinlock);
+    return ESP_OK;
+}
+
+esp_err_t esp_task_wdt_add(TaskHandle_t handle)
+{
+    portENTER_CRITICAL(&taskwdt_spinlock);      //Nested critical in Legacy (called from feed)
+#ifdef CONFIG_TASK_WDT_LEGACY_BEHAVIOR
+    wdt_task_t *target_task;
+#else
+    //Return error if wdt has not been initialized
+    ASSERT_EXIT_CRIT_RET_ERR((wdt_config != NULL), ESP_ERR_INVALID_STATE);
+
+    wdt_task_t *target_task;
+    bool all_fed;
+    if (handle == NULL){    //Get handle of current task if none is provided
+        handle = xTaskGetCurrentTaskHandle();
     }
-    if (handle==wdt_task_list) {
-        //Current task is first on list.
-        wdt_task_list=wdt_task_list->next;
-        free(wdttask);
-    } else {
-        //Find current task in list
-        if (wdt_task_list->task_handle==handle) {
-            //Task is the very first one.
-            wdt_task_t *freeme=wdt_task_list;
-            wdt_task_list=wdt_task_list->next;
-            free(freeme);
-            portEXIT_CRITICAL(&taskwdt_spinlock);
-            return;
-        }
-        while (wdttask->next!=NULL && wdttask->next->task_handle!=handle) wdttask=wdttask->next;
-        if (!wdttask->next) {
-            ESP_LOGE(TAG, "task_wdt_delete: Task never called task_wdt_feed!");
-            portEXIT_CRITICAL(&taskwdt_spinlock);
-            return;
+
+    //Check if tasks exists in task list, and if all other tasks have checked in
+    target_task = check_list(handle, &all_fed);
+    //Return error if task has already been added
+    ASSERT_EXIT_CRIT_RET_ERR((target_task == NULL), ESP_ERR_INVALID_ARG);
+#endif      //CONFIG_TASK_WDT_LEGACY_BEHAVIOR
+    //Add task to wdt list
+    target_task = calloc(1,sizeof(wdt_task_t));
+    //Return error if calloc failed
+    ASSERT_EXIT_CRIT_RET_ERR((target_task != NULL), ESP_ERR_NO_MEM);
+
+    target_task->task_handle = handle;
+    target_task->fed = true;
+    target_task->next = NULL;
+    if (wdt_config->list == NULL) {    //Adding to empty list
+        wdt_config->list = target_task;
+    } else {    //Adding to tail of list
+        wdt_task_t *task;
+        for (task = wdt_config->list; task->next != NULL; task = task->next){
+            ;   //point task to current tail of wdt task list
         }
-        wdt_task_t *freeme=wdttask->next;
-        wdttask->next=wdttask->next->next;
-        free(freeme);
+        task->next = target_task;
     }
-    portEXIT_CRITICAL(&taskwdt_spinlock);
-}
 
+    //If idle task, set idle_enable flag
+    for(int i = 0; i < portNUM_PROCESSORS; i++){
+        if(handle == wdt_config->idle_handles[i]){
+            wdt_config->idle_enable[i] = true;
+            break;
+        }
+    }
 
-#if CONFIG_TASK_WDT_CHECK_IDLE_TASK
-static bool idle_hook(void) {
-#if !CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1
-    if (xPortGetCoreID()!=0) return true;
+#ifndef CONFIG_TASK_WDT_LEGACY_BEHAVIOR
+    if(all_fed){     //Reset if all other tasks in list have checked in
+        feed_wdt();
+    }
 #endif
-    esp_task_wdt_feed();
-    return true;
+    portEXIT_CRITICAL(&taskwdt_spinlock);              //Nested critical if Legacy
+    return ESP_OK;
 }
-#endif
 
+esp_err_t esp_task_wdt_feed()
+{
+    portENTER_CRITICAL(&taskwdt_spinlock);
+    //Return error if wdt has not been initialized
+    ASSERT_EXIT_CRIT_RET_ERR((wdt_config != NULL), ESP_ERR_INVALID_STATE);
 
-void esp_task_wdt_init() {
-    TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
-    TIMERG0.wdt_config0.sys_reset_length=7;                 //3.2uS
-    TIMERG0.wdt_config0.cpu_reset_length=7;                 //3.2uS
-    TIMERG0.wdt_config0.level_int_en=1;
-    TIMERG0.wdt_config0.stg0=TIMG_WDT_STG_SEL_INT;          //1st stage timeout: interrupt
-    TIMERG0.wdt_config0.stg1=TIMG_WDT_STG_SEL_RESET_SYSTEM; //2nd stage timeout: reset system
-    TIMERG0.wdt_config1.clk_prescale=80*500;                //Prescaler: wdt counts in ticks of 0.5mS
-    TIMERG0.wdt_config2=CONFIG_TASK_WDT_TIMEOUT_S*2000;     //Set timeout before interrupt
-    TIMERG0.wdt_config3=CONFIG_TASK_WDT_TIMEOUT_S*4000;     //Set timeout before reset
-    TIMERG0.wdt_config0.en=1;
-    TIMERG0.wdt_feed=1;
-    TIMERG0.wdt_wprotect=0;
-#if CONFIG_TASK_WDT_CHECK_IDLE_TASK
-    esp_register_freertos_idle_hook(idle_hook);
-#endif
-    ESP_ERROR_CHECK( esp_intr_alloc(ETS_TG0_WDT_LEVEL_INTR_SOURCE, 0, task_wdt_isr, NULL, NULL) );
+    TaskHandle_t handle = xTaskGetCurrentTaskHandle();
+    wdt_task_t *target_task;
+    bool all_fed;
+
+    //Check if tasks exists in task list, and if all other tasks have checked in
+    target_task = check_list(handle, &all_fed);
+
+    if(target_task == NULL){
+#ifdef CONFIG_TASK_WDT_LEGACY_BEHAVIOR
+        //Add task to wdt task list if it doesn't exist, return error if failed to add
+        ASSERT_EXIT_CRIT_RET_ERR((esp_task_wdt_add(handle) == ESP_OK), ESP_ERR_NO_MEM);
+#else
+        portEXIT_CRITICAL(&taskwdt_spinlock);
+        return ESP_ERR_INVALID_STATE;                  //Return error if task does not exist
+#endif      //CONFIG_TASK_WDT_LEGACY_BEHAVIOR
+    }else{
+        target_task->fed = true;   //Feed the task
+    }
+
+    if(all_fed){     //Reset if all other tasks in list have checked in
+        feed_wdt();
+    }
+
+    portEXIT_CRITICAL(&taskwdt_spinlock);
+    return ESP_OK;
 }
 
+#ifdef CONFIG_TASK_WDT_LEGACY_BEHAVIOR
+esp_err_t esp_task_wdt_delete()
+{
+    TaskHandle_t handle = xTaskGetCurrentTaskHandle();
+#else
+esp_err_t esp_task_wdt_delete(TaskHandle_t handle)
+{
+    if(handle == NULL){
+        handle = xTaskGetCurrentTaskHandle();
+    }
+#endif      //CONFIG_TASK_WDT_LEGACY_BEHAVIOR
+    portENTER_CRITICAL(&taskwdt_spinlock);
+    //Return error if wdt has not been initialized
+    ASSERT_EXIT_CRIT_RET_ERR((wdt_config != NULL), ESP_ERR_INVALID_STATE);
+    wdt_task_t *target_task;
+    bool all_fed;
+
+    target_task = check_list(handle, &all_fed);
+
+    //Task doesn't exist on list. Return error
+    ASSERT_EXIT_CRIT_RET_ERR((target_task != NULL), ESP_ERR_INVALID_ARG);
+
+    if(target_task == wdt_config->list){     //target_task is head of list. Delete
+        wdt_config->list = target_task->next;
+        free(target_task);
+    }else{                                      //target_task not head of list. Delete
+        wdt_task_t *prev;
+        for (prev = wdt_config->list; prev->next != target_task; prev = prev->next){
+            ;   //point prev to task preceding target_task
+        }
+        prev->next = target_task->next;
+        free(target_task);
+    }
+
+    //If idle task, disable idle_enable flag
+    for(int i = 0; i < portNUM_PROCESSORS; i++){
+        if(handle == wdt_config->idle_handles[i]){
+            wdt_config->idle_enable[i] = false;
+        }
+    }
+
+    if(all_fed){     //Reset timer if all remaining tasks have checked in
+        feed_wdt();
+    }
+
+    portEXIT_CRITICAL(&taskwdt_spinlock);
+    return ESP_OK;
+}
 
-#endif
index d398ba5da68ef1cb20befdefa95964d370f4727f..fbfe9828f9ef432889bc041bea35861937315376 100644 (file)
@@ -82,6 +82,9 @@ extern "C" {
 #include "esp_crosscore_int.h"
 
 
+#include <esp_heap_caps.h>
+#include "soc/soc_memory_layout.h"
+
 //#include "xtensa_context.h"
 
 /*-----------------------------------------------------------
@@ -245,6 +248,18 @@ static inline unsigned portENTER_CRITICAL_NESTED() { unsigned state = XTOS_SET_I
 #define portSET_INTERRUPT_MASK_FROM_ISR()            portENTER_CRITICAL_NESTED()
 #define portCLEAR_INTERRUPT_MASK_FROM_ISR(state)     portEXIT_CRITICAL_NESTED(state)
 
+//Because the ROM routines don't necessarily handle a stack in external RAM correctly, we force
+//the stack memory to always be internal.
+#define pvPortMallocTcbMem(size) heap_caps_malloc(size, MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)
+#define pvPortMallocStackMem(size)  heap_caps_malloc(size, MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)
+
+//xTaskCreateStatic uses these functions to check incoming memory.
+#define portVALID_TCB_MEM(ptr) (esp_ptr_internal(ptr) && esp_ptr_byte_accessible(ptr))
+#ifndef CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY
+#define portVALID_STACK_MEM(ptr) esp_ptr_byte_accessible(ptr)
+#else
+#define portVALID_STACK_MEM(ptr) (esp_ptr_internal(ptr) && esp_ptr_byte_accessible(ptr))
+#endif
 
 /*
  * Wrapper for the Xtensa compare-and-set instruction. This subroutine will atomically compare
index 2438d960001ba7be8d22e8b90f6ec8be51c68f99..073267445f6915e18ecc9a6b43a185d4e6a06323 100644 (file)
@@ -1382,6 +1382,15 @@ BaseType_t xTaskCallApplicationTaskHook( TaskHandle_t xTask, void *pvParameter )
  */
 TaskHandle_t xTaskGetIdleTaskHandle( void );
 
+/**
+ * xTaskGetIdleTaskHandleForCPU() is only available if
+ * INCLUDE_xTaskGetIdleTaskHandle is set to 1 in FreeRTOSConfig.h.
+ *
+ * Simply returns the idle task handle of a given cpu. It is not valid to call
+ * xTaskGetIdleTaskHandleForCPU() before the scheduler has been started.
+ */
+TaskHandle_t xTaskGetIdleTaskHandleForCPU( UBaseType_t cpuid );
+
 /**
  * configUSE_TRACE_FACILITY must be defined as 1 in FreeRTOSConfig.h for
  * uxTaskGetSystemState() to be available.
diff --git a/components/freertos/readme_smp.txt b/components/freertos/readme_smp.txt
deleted file mode 100644 (file)
index fdd9b14..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-This version of FreeRTOS has been modified by Espressif to be SMP-aware. The 
-API is similar to the original FreeRTOS API, with the following changes:
-
-- The xTaskCreate() function now creates tasks that will run on the first 
-core only, for backwards compatibility. To schedule tasks on another core,
-use xTaskCreatePinnedToCore(), which will accept a core ID as the last
-argument. If this is the constant tskNO_AFFINITY, the task will be dynamically
-scheduled on whichever core has time.
-
-- vTaskSuspendAll/vTaskResumeAll in non-SMP FreeRTOS will suspend the scheduler
-so no other tasks than the current one will run. In this SMP version, it will
-only suspend the scheduler ON THE CURRENT CORE. That is, tasks scheduled to
-run on the other core(s) or without a specific CPU affinity, will still be
-able to run.
-
-- Enabling and disabling interrupts will only affect the current core. 
-Disabling the interrupts will not disallow other tasks to run as
-it would on a single-core system: the other core still will keep on 
-executing all it's own. Use a mux, queue or semaphore to protect your
-structures instead.
-
-- This FreeRTOS version has the task local storage backported from the 8.2.x
-versions. It, however, has an addition: you can also set a callback when you 
-set the pointer. This callback will be called by the idle task, with the 
-pointer as an argument, when the thread is destroyed. This depends on the idle
-task getting CPU time; when a thread is hogging the CPU without yielding,
-the idle thread won't be called and the delete callback won't be called either.
index 9126f862e0e2ba42091812a829f9029a1586f899..9fe36dbd9abd6df1cffcfaa97204cc39978ef67c 100644 (file)
@@ -677,8 +677,8 @@ void taskYIELD_OTHER_CORE( BaseType_t xCoreID, UBaseType_t uxPriority )
        TCB_t *pxNewTCB;
        TaskHandle_t xReturn;
 
-               configASSERT( puxStackBuffer != NULL );
-               configASSERT( pxTaskBuffer != NULL );
+               configASSERT( portVALID_TCB_MEM(pxTaskBuffer) );
+               configASSERT( portVALID_STACK_MEM(puxStackBuffer) );
                configASSERT( (xCoreID>=0 && xCoreID<portNUM_PROCESSORS) || (xCoreID==tskNO_AFFINITY) );
 
                if( ( pxTaskBuffer != NULL ) && ( puxStackBuffer != NULL ) )
@@ -724,7 +724,7 @@ void taskYIELD_OTHER_CORE( BaseType_t xCoreID, UBaseType_t uxPriority )
                        /* Allocate space for the TCB.  Where the memory comes from depends
                        on the implementation of the port malloc function and whether or
                        not static allocation is being used. */
-                       pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
+                       pxNewTCB = ( TCB_t * ) pvPortMallocTcbMem( sizeof( TCB_t ) );
 
                        if( pxNewTCB != NULL )
                        {
@@ -777,14 +777,14 @@ void taskYIELD_OTHER_CORE( BaseType_t xCoreID, UBaseType_t uxPriority )
                        /* Allocate space for the TCB.  Where the memory comes from depends on
                        the implementation of the port malloc function and whether or not static
                        allocation is being used. */
-                       pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
+                       pxNewTCB = ( TCB_t * ) pvPortMallocTcbMem( sizeof( TCB_t ) );
 
                        if( pxNewTCB != NULL )
                        {
                                /* Allocate space for the stack used by the task being created.
                                The base of the stack memory stored in the TCB so the task can
                                be deleted later if required. */
-                               pxNewTCB->pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
+                               pxNewTCB->pxStack = ( StackType_t * ) pvPortMallocStackMem( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
 
                                if( pxNewTCB->pxStack == NULL )
                                {
@@ -799,12 +799,12 @@ void taskYIELD_OTHER_CORE( BaseType_t xCoreID, UBaseType_t uxPriority )
                StackType_t *pxStack;
 
                        /* Allocate space for the stack used by the task being created. */
-                       pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
+                       pxStack = ( StackType_t * ) pvPortMallocStackMem( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
 
                        if( pxStack != NULL )
                        {
                                /* Allocate space for the TCB. */
-                               pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); /*lint !e961 MISRA exception as the casts are only redundant for some paths. */
+                               pxNewTCB = ( TCB_t * ) pvPortMallocTcbMem( sizeof( TCB_t ) ); /*lint !e961 MISRA exception as the casts are only redundant for some paths. */
 
                                if( pxNewTCB != NULL )
                                {
@@ -2371,6 +2371,18 @@ UBaseType_t uxTaskGetNumberOfTasks( void )
                return xIdleTaskHandle[ xPortGetCoreID() ];
        }
 
+       TaskHandle_t xTaskGetIdleTaskHandleForCPU( UBaseType_t cpuid )
+       {
+           TaskHandle_t xReturn = NULL;
+           /* If xTaskGetIdleTaskHandleForCPU() is called before the scheduler has been
+        started, then xIdleTaskHandle will be NULL. */
+           if (cpuid < portNUM_PROCESSORS) {
+               configASSERT( ( xIdleTaskHandle[ cpuid ] != NULL ) );
+               xReturn = xIdleTaskHandle[ cpuid ];
+           }
+           return xReturn;
+       }
+
 #endif /* INCLUDE_xTaskGetIdleTaskHandle */
 /*----------------------------------------------------------*/
 
index 2edf1dbc4ad06d5b00e8760d5755b8a5af05f89c..f8b9ca029e795e60a7b758d5d8f7ab3fec47b064 100644 (file)
@@ -133,12 +133,36 @@ IRAM_ATTR void *heap_caps_malloc( size_t size, uint32_t caps )
     return NULL;
 }
 
+
+#define MALLOC_DISABLE_EXTERNAL_ALLOCS -1
+//Dual-use: -1 (=MALLOC_DISABLE_EXTERNAL_ALLOCS) disables allocations in external memory, >=0 sets the limit for allocations preferring internal memory.
+static int malloc_alwaysinternal_limit=MALLOC_DISABLE_EXTERNAL_ALLOCS;
+
+void heap_caps_malloc_extmem_enable(size_t limit)
+{
+    malloc_alwaysinternal_limit=limit;
+}
+
 /*
  Default memory allocation implementation. Should return standard 8-bit memory. malloc() essentially resolves to this function.
 */
 IRAM_ATTR void *heap_caps_malloc_default( size_t size )
 {
-    return heap_caps_malloc( size, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL );
+    if (malloc_alwaysinternal_limit==MALLOC_DISABLE_EXTERNAL_ALLOCS) {
+        return heap_caps_malloc( size, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL);
+    } else {
+        void *r;
+        if (size <= malloc_alwaysinternal_limit) {
+            r=heap_caps_malloc( size, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL );
+        } else {
+            r=heap_caps_malloc( size, MALLOC_CAP_DEFAULT | MALLOC_CAP_SPIRAM );
+        }
+        if (r==NULL) {
+            //try again while being less picky
+            r=heap_caps_malloc( size, MALLOC_CAP_DEFAULT );
+        }
+        return r;
+    }
 }
 
 /*
@@ -147,7 +171,21 @@ IRAM_ATTR void *heap_caps_malloc_default( size_t size )
  */
 IRAM_ATTR void *heap_caps_realloc_default( void *ptr, size_t size )
 {
-    return heap_caps_realloc( ptr, size, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL );
+    if (malloc_alwaysinternal_limit==MALLOC_DISABLE_EXTERNAL_ALLOCS) {
+        return heap_caps_realloc( ptr, size, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL );
+    } else {
+        void *r;
+        if (size <= malloc_alwaysinternal_limit) {
+            r=heap_caps_realloc( ptr, size, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL );
+        } else {
+            r=heap_caps_realloc( ptr, size, MALLOC_CAP_DEFAULT | MALLOC_CAP_SPIRAM );
+        }
+        if (r==NULL && size>0) {
+            //We needed to allocate memory, but we didn't. Try again while being less picky.
+            r=heap_caps_realloc( ptr, size, MALLOC_CAP_DEFAULT );
+        }
+        return r;
+    }
 }
 
 
index 9f88c87d51990206d2d60a44f4d9a17b030e2585..c964c655e8086b5f696d580be63aab3e2069ccc9 100644 (file)
@@ -213,7 +213,8 @@ esp_err_t heap_caps_add_region(intptr_t start, intptr_t end)
 
     for (int i = 0; i < soc_memory_region_count; i++) {
         const soc_memory_region_t *region = &soc_memory_regions[i];
-        if (region->start <= start && (region->start + region->size) > end) {
+        // Test requested start only as 'end' may be in a different region entry, assume 'end' has same caps
+        if (region->start <= start && (region->start + region->size) > start) {
             const uint32_t *caps = soc_memory_types[region->type].caps;
             return heap_caps_add_region_with_caps(caps, start, end);
         }
@@ -229,6 +230,14 @@ esp_err_t heap_caps_add_region_with_caps(const uint32_t caps[], intptr_t start,
         return ESP_ERR_INVALID_ARG;
     }
 
+    //Check if region overlaps the start and/or end of an existing region. If so, the
+    //region is invalid (or maybe added twice)
+    heap_t *heap;
+    SLIST_FOREACH(heap, &registered_heaps, next) {
+        if ( start <= heap->start &&  heap->start <=end ) return ESP_FAIL;
+        if ( start <= heap->end &&  heap->end <=end ) return ESP_FAIL;
+    }
+
     heap_t *p_new = malloc(sizeof(heap_t));
     if (p_new == NULL) {
         err = ESP_ERR_NO_MEM;
index 022ae669a49c5431b04b3708703a1f7721040ac1..6dd1b9b5a6b9a67734ab589ecbdc6eb71809c82f 100644 (file)
@@ -32,6 +32,7 @@
 #define MALLOC_CAP_PID7             (1<<9)  ///< Memory must be mapped to PID7 memory space (PIDs are not currently used)
 #define MALLOC_CAP_SPIRAM           (1<<10) ///< Memory must be in SPI RAM
 #define MALLOC_CAP_INTERNAL         (1<<11) ///< Memory must be internal; specifically it should not disappear when flash/spiram cache is switched off
+#define MALLOC_CAP_DEFAULT          (1<<12) ///< Memory can be returned in a non-capability-specific memory allocation (e.g. malloc(), calloc()) call
 #define MALLOC_CAP_INVALID          (1<<31) ///< Memory can't be used / list end marker
 
 /**
@@ -172,3 +173,18 @@ void heap_caps_print_heap_info( uint32_t caps );
  * @return True if all heaps are valid, False if at least one heap is corrupt.
  */
 bool heap_caps_check_integrity(uint32_t caps, bool print_errors);
+
+
+
+/**
+ * @brief Enable malloc() in external memory and set limit below which 
+ *        malloc() attempts are placed in internal memory.
+ *
+ * When external memory is in use, the allocation strategy is to initially try to
+ * satisfy smaller allocation requests with internal memory and larger requests
+ * with external memory. This sets the limit between the two, as well as generally
+ * enabling allocation in external memory.
+ *
+ * @param limit       Limit, in bytes.
+ */
+void heap_caps_malloc_extmem_enable(size_t limit);
index b29e785262002e1e06589e5d69356090cd447162..5cfb8d82b51a65b2a1ec9c0f6470efca307577f9 100644 (file)
@@ -73,8 +73,11 @@ esp_err_t heap_caps_add_region(intptr_t start, intptr_t end);
  * @param start Start address of new region.
  * @param end End address of new region.
  *
- * @return ESP_OK on success, ESP_ERR_INVALID_ARG if a parameter is invalid, ESP_ERR_NO_MEM if no
- * memory to register new heap.
+ * @return 
+ *         - ESP_OK on success
+ *         - ESP_ERR_INVALID_ARG if a parameter is invalid
+ *         - ESP_ERR_NO_MEM if no memory to register new heap.
+ *         - ESP_FAIL if region overlaps the start and/or end of an existing region
  */
 esp_err_t heap_caps_add_region_with_caps(const uint32_t caps[], intptr_t start, intptr_t end);
 
index 723d534079c2f6fdbd29343d1d41d45995222904..1d2607913eabeaabb60b4ac1bb8081449d28ea65 100644 (file)
@@ -144,12 +144,14 @@ static inline size_t block_data_size(const heap_block_t *block)
 /* Check a block is valid for this heap. Used to verify parameters. */
 static void assert_valid_block(const heap_t *heap, const heap_block_t *block)
 {
-    assert(block >= &heap->first_block && block <= heap->last_block); /* block should be in heap */
+    MULTI_HEAP_ASSERT(block >= &heap->first_block && block <= heap->last_block,
+                      block); // block not in heap
     if (heap < (const heap_t *)heap->last_block) {
         const heap_block_t *next = get_next_block(block);
-        assert(next >= &heap->first_block && next <= heap->last_block);
+        MULTI_HEAP_ASSERT(next >= &heap->first_block && next <= heap->last_block, block); // Next block not in heap
         if (is_free(block)) {
-            assert(block->next_free >= &heap->first_block && block->next_free <= heap->last_block);
+            // Check block->next_free is valid
+            MULTI_HEAP_ASSERT(block->next_free >= &heap->first_block && block->next_free <= heap->last_block, &block->next_free);
         }
     }
 }
@@ -168,10 +170,11 @@ static heap_block_t *get_prev_free_block(heap_t *heap, const heap_block_t *block
     assert(block != &heap->first_block); /* can't look for a block before first_block */
 
     for (heap_block_t *b = &heap->first_block; b != NULL && b < block; b = b->next_free) {
-        assert(is_free(b));
+        MULTI_HEAP_ASSERT(is_free(b), b); // Block should be free
         if (b->next_free == NULL || b->next_free >= block) {
             if (is_free(block)) {
-                assert(b->next_free == block); /* if block is on freelist, 'b' should be the item before it. */
+                 /* if block is on freelist, 'b' should be the item before it. */
+                MULTI_HEAP_ASSERT(b->next_free == block, &b->next_free);
             }
             return b; /* b is the last free block before 'block' */
         }
@@ -199,7 +202,7 @@ static heap_block_t *merge_adjacent(heap_t *heap, heap_block_t *a, heap_block_t
         return b;
     }
 
-    assert(get_next_block(a) == b);
+    MULTI_HEAP_ASSERT(get_next_block(a) == b, a); // Blocks should be in order
 
     bool free = is_free(a) && is_free(b); /* merging two free blocks creates a free block */
     if (!free && (is_free(a) || is_free(b))) {
@@ -208,18 +211,20 @@ static heap_block_t *merge_adjacent(heap_t *heap, heap_block_t *a, heap_block_t
          */
         heap_block_t *free_block = is_free(a) ? a : b;
         heap_block_t *prev_free = get_prev_free_block(heap, free_block);
-        assert(free_block->next_free > prev_free);
+        MULTI_HEAP_ASSERT(free_block->next_free > prev_free, &free_block->next_free); // Next free block should be after prev one
         prev_free->next_free = free_block->next_free;
 
         heap->free_bytes -= block_data_size(free_block);
     }
 
     a->header = b->header & NEXT_BLOCK_MASK;
-    assert(a->header != 0);
+    MULTI_HEAP_ASSERT(a->header != 0, a);
     if (free) {
         a->header |= BLOCK_FREE_FLAG;
-        assert(b->next_free == NULL || b->next_free > a);
-        assert(b->next_free == NULL || b->next_free > b);
+        if (b->next_free != NULL) {
+            MULTI_HEAP_ASSERT(b->next_free > a, &b->next_free);
+            MULTI_HEAP_ASSERT(b->next_free > b, &b->next_free);
+        }
         a->next_free = b->next_free;
 
         /* b's header can be put into the pool of free bytes */
@@ -245,8 +250,8 @@ static heap_block_t *merge_adjacent(heap_t *heap, heap_block_t *a, heap_block_t
 */
 static void split_if_necessary(heap_t *heap, heap_block_t *block, size_t size, heap_block_t *prev_free_block)
 {
-    assert(!is_free(block)); /* split_if_necessary doesn't expect a free block */
-    assert(size <= block_data_size(block)); /* can't grow a block this way! */
+    MULTI_HEAP_ASSERT(!is_free(block), block); // split block shouldn't be free
+    MULTI_HEAP_ASSERT(size <= block_data_size(block), block); // size should be valid
     size = ALIGN_UP(size);
 
     /* can't split the head or tail block */
@@ -266,7 +271,9 @@ static void split_if_necessary(heap_t *heap, heap_block_t *block, size_t size, h
     if (prev_free_block == NULL) {
         prev_free_block = get_prev_free_block(heap, block);
     }
-    assert(prev_free_block->next_free > new_block); /* prev_free_block should point to a free block after new_block */
+    /* prev_free_block should point to a free block after new_block */
+    MULTI_HEAP_ASSERT(prev_free_block->next_free > new_block,
+                      &prev_free_block->next_free); // free blocks should be in order
     new_block->next_free = prev_free_block->next_free;
     prev_free_block->next_free = new_block;
     heap->free_bytes += block_data_size(new_block);
@@ -277,7 +284,7 @@ size_t multi_heap_get_allocated_size_impl(multi_heap_handle_t heap, void *p)
     heap_block_t *pb = get_block(p);
 
     assert_valid_block(heap, pb);
-    assert(!is_free(pb));
+    MULTI_HEAP_ASSERT(!is_free(pb), pb); // block should be free
     return block_data_size(pb);
 }
 
@@ -339,7 +346,8 @@ void *multi_heap_malloc_impl(multi_heap_handle_t heap, size_t size)
     /* Find best free block to perform the allocation in */
     prev = &heap->first_block;
     for (heap_block_t *b = heap->first_block.next_free; b != NULL; b = b->next_free) {
-        assert(is_free(b));
+        MULTI_HEAP_ASSERT(b > prev, &prev->next_free); // free blocks should be ascending in address
+        MULTI_HEAP_ASSERT(is_free(b), b); // block should be free
         size_t bs = block_data_size(b);
         if (bs >= size && bs < best_size) {
             best_block = b;
@@ -384,15 +392,16 @@ void multi_heap_free_impl(multi_heap_handle_t heap, void *p)
     MULTI_HEAP_LOCK(heap->lock);
 
     assert_valid_block(heap, pb);
-    assert(!is_free(pb));
-    assert(!is_last_block(pb));
-    assert(pb != &heap->first_block);
+    MULTI_HEAP_ASSERT(!is_free(pb), pb); // block should not be free
+    MULTI_HEAP_ASSERT(!is_last_block(pb), pb); // block should not be last block
+    MULTI_HEAP_ASSERT(pb != &heap->first_block, pb); // block should not be first block
 
     heap_block_t *next = get_next_block(pb);
 
     /* Update freelist pointers */
     heap_block_t *prev_free = get_prev_free_block(heap, pb);
-    assert(prev_free->next_free == NULL || prev_free->next_free > pb);
+    // freelist validity check
+    MULTI_HEAP_ASSERT(prev_free->next_free == NULL || prev_free->next_free > pb, &prev_free->next_free);
     pb->next_free = prev_free->next_free;
     prev_free->next_free = pb;
 
@@ -428,7 +437,8 @@ void *multi_heap_realloc_impl(multi_heap_handle_t heap, void *p, size_t size)
     }
 
     assert_valid_block(heap, pb);
-    assert(!is_free(pb) && "realloc arg should be allocated");
+    // non-null realloc arg should be allocated
+    MULTI_HEAP_ASSERT(!is_free(pb), pb);
 
     if (size == 0) {
         /* note: calling multi_free_impl() here as we've already been
@@ -646,7 +656,8 @@ void multi_heap_get_info_impl(multi_heap_handle_t heap, multi_heap_info_t *info)
     }
 
     info->minimum_free_bytes = heap->minimum_free_bytes;
-    assert(info->total_free_bytes == heap->free_bytes);
+    // heap has wrong total size (address printed here is not indicative of the real error)
+    MULTI_HEAP_ASSERT(info->total_free_bytes == heap->free_bytes, heap);
 
     MULTI_HEAP_UNLOCK(heap->lock);
 
index 2c4d9f96f635ba765fe67d109317a481cac6bcc1..874fd535bd7bf9d8a8019412e8c6c95215715bf4 100644 (file)
@@ -18,6 +18,7 @@
 #include <freertos/FreeRTOS.h>
 #include <freertos/task.h>
 #include <rom/ets_sys.h>
+#include <assert.h>
 
 /* Because malloc/free can happen inside an ISR context,
    we need to use portmux spinlocks here not RTOS mutexes */
 #define MULTI_HEAP_PRINTF ets_printf
 #define MULTI_HEAP_STDERR_PRINTF(MSG, ...) ets_printf(MSG, __VA_ARGS__)
 
-#else
+inline static void multi_heap_assert(bool condition, const char *format, int line, intptr_t address)
+{
+    /* Can't use libc assert() here as it calls printf() which can cause another malloc() for a newlib lock.
+
+       Also, it's useful to be able to print the memory address where corruption was detected.
+    */
+#ifndef NDEBUG
+    if(!condition) {
+#ifndef CONFIG_OPTIMIZATION_ASSERTIONS_SILENT
+        ets_printf(format, line, address);
+#endif  // CONFIG_OPTIMIZATION_ASSERTIONS_SILENT
+        abort();
+    }
+#else // NDEBUG
+    (void) condition;
+#endif // NDEBUG
+}
+
+#define MULTI_HEAP_ASSERT(CONDITION, ADDRESS) \
+    multi_heap_assert((CONDITION), "CORRUPT HEAP: multi_heap.c:%d detected at 0x%08x\n", \
+                      __LINE__, (intptr_t)(ADDRESS))
+
+#else // ESP_PLATFORM
+
+#include <assert.h>
 
 #define MULTI_HEAP_PRINTF printf
 #define MULTI_HEAP_STDERR_PRINTF(MSG, ...) fprintf(stderr, MSG, __VA_ARGS__)
 #define MULTI_HEAP_LOCK(PLOCK)
 #define MULTI_HEAP_UNLOCK(PLOCK)
 
+#define MULTI_HEAP_ASSERT(CONDITION, ADDRESS) assert((CONDITION) && "Heap corrupt")
 #endif
index 2ca17e0e2c29b5491db2dbe4e317fbe2daa759e8..a91446f09820d02e78e4d9f956ce0748f047eb3a 100644 (file)
@@ -92,7 +92,7 @@ static poison_head_t *verify_allocated_region(void *data, bool print_errors)
     /* check if the beginning of the data was overwritten */
     if (head->head_canary != HEAD_CANARY_PATTERN) {
         if (print_errors) {
-            printf("CORRUPT HEAP: Bad head at %p. Expected 0x%08x got 0x%08x\n", &head->head_canary,
+            MULTI_HEAP_STDERR_PRINTF("CORRUPT HEAP: Bad head at %p. Expected 0x%08x got 0x%08x\n", &head->head_canary,
                    HEAD_CANARY_PATTERN, head->head_canary);
         }
         return NULL;
@@ -142,7 +142,7 @@ static bool verify_fill_pattern(void *data, size_t size, bool print_errors, bool
         while (size >= 4) {
             if (*p != EXPECT_WORD) {
                 if (print_errors) {
-                    printf("Invalid data at %p. Expected 0x%08x got 0x%08x\n", p, EXPECT_WORD, *p);
+                    MULTI_HEAP_STDERR_PRINTF("CORRUPT HEAP: Invalid data at %p. Expected 0x%08x got 0x%08x\n", p, EXPECT_WORD, *p);
                 }
                 valid = false;
             }
@@ -159,7 +159,7 @@ static bool verify_fill_pattern(void *data, size_t size, bool print_errors, bool
     for (int i = 0; i < size; i++) {
         if (p[i] != (uint8_t)EXPECT_WORD) {
             if (print_errors) {
-                printf("Invalid data at %p. Expected 0x%02x got 0x%02x\n", p, (uint8_t)EXPECT_WORD, *p);
+                MULTI_HEAP_STDERR_PRINTF("CORRUPT HEAP: Invalid data at %p. Expected 0x%02x got 0x%02x\n", p, (uint8_t)EXPECT_WORD, *p);
             }
             valid = false;
         }
@@ -315,7 +315,7 @@ bool multi_heap_internal_check_block_poisoning(void *start, size_t size, bool is
             /* block can be bigger than alloc_size, for reasons of alignment & fragmentation,
                but block can never be smaller than head->alloc_size... */
             if (print_errors) {
-                printf("CORRUPT HEAP: Size at %p expected <=0x%08x got 0x%08x\n", &head->alloc_size,
+                MULTI_HEAP_STDERR_PRINTF("CORRUPT HEAP: Size at %p expected <=0x%08x got 0x%08x\n", &head->alloc_size,
                        size - POISON_OVERHEAD, head->alloc_size);
             }
             return false;
index 2ef74965a18fece8db6a9b712409a83416e2413c..23eef9d8b7403b14e4184644203a4e205a38e0e9 100644 (file)
 #include "soc/uart_reg.h"
 #include "soc/dport_reg.h"
 #include "soc/io_mux_reg.h"
+#include "esp_heap_caps.h"
 
 #include "esp_panic.h"
+#include "sdkconfig.h"
+
+
+static int **allocatedMem;
+static int noAllocated;
+
 
 static int tryAllocMem() {
-    int **mem;
-    int i, noAllocated, j;
+    int i, j;
+    const int allocateMaxK=1024*5; //try to allocate a max of 5MiB
 
-    mem=malloc(sizeof(int *)*1024);
-    if (!mem) return 0;
+    allocatedMem=malloc(sizeof(int *)*allocateMaxK);
+    if (!allocatedMem) return 0;
 
-    for (i=0; i<1024; i++) {
-        mem[i]=malloc(1024);
-        if (mem[i]==NULL) break;
-        for (j=0; j<1024/4; j++) mem[i][j]=(0xdeadbeef);
+    for (i=0; i<allocateMaxK; i++) {
+        allocatedMem[i]=malloc(1024);
+        if (allocatedMem[i]==NULL) break;
+        for (j=0; j<1024/4; j++) allocatedMem[i][j]=(0xdeadbeef);
     }
-
     noAllocated=i;
+    return i;
+}
+
 
+static void tryAllocMemFree() {
+    int i, j;
     for (i=0; i<noAllocated; i++) {
         for (j=0; j<1024/4; j++) {
-            TEST_ASSERT(mem[i][j]==(0xdeadbeef));
+            TEST_ASSERT(allocatedMem[i][j]==(0xdeadbeef));
         }
-        free(mem[i]);
+        free(allocatedMem[i]);
     }
-    free(mem);
-    return noAllocated;
+    free(allocatedMem);
 }
 
 
@@ -48,8 +58,33 @@ TEST_CASE("Malloc/overwrite, then free all available DRAM", "[heap]")
 {
     int m1=0, m2=0;
     m1=tryAllocMem();
+    tryAllocMemFree();
     m2=tryAllocMem();
+    tryAllocMemFree();
     printf("Could allocate %dK on first try, %dK on 2nd try.\n", m1, m2);
     TEST_ASSERT(m1==m2);
 }
 
+
+#if CONFIG_SPIRAM_USE_MALLOC
+
+#if (CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL > 1024)
+TEST_CASE("Check if reserved DMA pool still can allocate even when malloc()'ed memory is exhausted", "[heap]")
+{
+    char** dmaMem=malloc(sizeof(char*)*512);
+    assert(dmaMem);
+    int m=tryAllocMem();
+    int i=0;
+    for (i=0; i<512; i++) {
+        dmaMem[i]=heap_caps_malloc(1024, MALLOC_CAP_DMA);
+        if (dmaMem[i]==NULL) break;
+    }
+    for (int j=0; j<i; j++) free(dmaMem[j]);
+    free(dmaMem);
+    tryAllocMemFree();
+    printf("Could allocate %dK of DMA memory after allocating all of %dK of normal memory.\n", i, m);
+    TEST_ASSERT(i);
+}
+#endif
+
+#endif
\ No newline at end of file
index 8e7f77040b3186cacb355e2fb55cc864cea7ed3b..ef818e51f283b551023181b5fe094cb2cd96fd35 100644 (file)
@@ -234,7 +234,6 @@ BTSTK_GATT_25011
 BTSTK_GATT_25012
 BTSTK_GATT_25013
 BTSTK_GATT_25014
-BTSTK_GATT_20002
 
 # BT cases that might fail
 BTSTK_GAP_01003
@@ -245,7 +244,12 @@ BTSTK_GAP_06001
 BTSTK_GAP_06002
 BTSTK_GAP_06004
 BTSTK_GAP_06005
-BTSTK_GATT_20002
+
+BTSTK_GAP_03002
+BTSTK_GAP_03004
+BTSTK_GAP_09003
+BTSTK_GAP_09002
+
 BTSTK_GATT_21002
 BTSTK_GATT_21006
 BTSTK_GATT_21007
@@ -265,6 +269,13 @@ BTSTK_GATT_23003
 BTSTK_GATT_25005
 BTSTK_GATT_25007
 BTSTK_GATT_25014
+
+BTSTK_GATT_20001
+BTSTK_GATT_20002
+BTSTK_GATT_23007
+BTSTK_GATT_24003
+BTSTK_GATT_26006
+
 BTSTK_COEXIST_0101
 BTSTK_COEXIST_0102
 BTSTK_COEXIST_0103
@@ -272,3 +283,17 @@ BTSTK_COEXIST_0201
 BTSTK_COEXIST_0202
 BTSTK_COEXIST_0203
 BTSTK_COEXIST_0301
+
+# new failed caused by GATTC can't get correct attribute handle
+BTSTK_GATT_22001
+BTSTK_GATT_22002
+BTSTK_GATT_22014
+BTSTK_GATT_22013
+BTSTK_GATT_22012
+BTSTK_GATT_22011
+BTSTK_GATT_22017
+BTSTK_GATT_22016
+BTSTK_GATT_22015
+BTSTK_GATT_22019
+BTSTK_GATT_22018
+
index c43df97e48d2464bebe6157cb2aced74847f37e6..038e8e16357b2d89c0e1eb0ea0c30e474de8d2af 100644 (file)
@@ -310,10 +310,13 @@ static inline void heap_swap(int i, int j)
 #define ATTR
 #endif // BOOTLOADER_BUILD
 
+//the variable defined in ROM is the cpu frequency in MHz.
+//as a workaround before the interface for this variable
+extern uint32_t g_ticks_per_us_pro;
 
 uint32_t ATTR esp_log_early_timestamp()
 {
-    return xthal_get_ccount() / (CPU_CLK_FREQ_ROM / 1000);
+    return xthal_get_ccount() / (g_ticks_per_us_pro * 1000);
 }
 
 #ifndef BOOTLOADER_BUILD
@@ -324,7 +327,7 @@ uint32_t IRAM_ATTR esp_log_timestamp()
         return esp_log_early_timestamp();
     }
     static uint32_t base = 0;
-    if (base == 0) {
+    if (base == 0 && xPortGetCoreID() == 0) {
         base = esp_log_early_timestamp();
     }
     return base + xTaskGetTickCount() * (1000 / configTICK_RATE_HZ);
@@ -351,7 +354,7 @@ void esp_log_buffer_hex_internal(const char *tag, const void *buffer, uint16_t b
         } else {
             bytes_cur_line = buff_len;
         }
-        if ( !esp_ptr_byte_accesible(buffer) ) {
+        if ( !esp_ptr_byte_accessible(buffer) ) {
             //use memcpy to get around alignment issue
             memcpy( temp_buffer, buffer, (bytes_cur_line+3)/4*4 );
             ptr_line = temp_buffer;
@@ -383,7 +386,7 @@ void esp_log_buffer_char_internal(const char *tag, const void *buffer, uint16_t
         } else {
             bytes_cur_line = buff_len;
         }
-        if ( !esp_ptr_byte_accesible(buffer) ) {
+        if ( !esp_ptr_byte_accessible(buffer) ) {
             //use memcpy to get around alignment issue
             memcpy( temp_buffer, buffer, (bytes_cur_line+3)/4*4 );
             ptr_line = temp_buffer;
@@ -418,7 +421,7 @@ void esp_log_buffer_hexdump_internal( const char *tag, const void *buffer, uint1
         } else {
             bytes_cur_line = buff_len;
         }
-        if ( !esp_ptr_byte_accesible(buffer) ) {
+        if ( !esp_ptr_byte_accessible(buffer) ) {
             //use memcpy to get around alignment issue
             memcpy( temp_buffer, buffer, (bytes_cur_line+3)/4*4 );
             ptr_line = temp_buffer;
index 65c76017c4ed9787693b03cb621b6ea0548222ba..c9c796f87f3fc0ab385182927ba9f8f860c9b4d0 100644 (file)
@@ -68,6 +68,36 @@ config LWIP_IP_REASSEMBLY
     help
         Enabling this option allows reassemblying incoming fragmented IP packets.
 
+config LWIP_STATS
+    bool "Enable LWIP statistics"
+    default n
+    help
+        Enabling this option allows LWIP statistics
+
+config LWIP_ETHARP_TRUST_IP_MAC
+    bool "Enable LWIP ARP trust"
+    default y
+    help
+        Enabling this option allows ARP table to be updated.
+
+        If this option is enabled, the incoming IP packets cause the ARP table to be
+        updated with the source MAC and IP addresses supplied in the packet.
+        You may want to disable this if you do not trust LAN peers to have the
+        correct addresses, or as a limited approach to attempt to handle
+        spoofing. If disabled, lwIP will need to make a new ARP request if
+        the peer is not already in the ARP table, adding a little latency.
+        The peer *is* in the ARP table if it requested our address before.
+        Also notice that this slows down input processing of every IP packet!
+
+config TCPIP_RECVMBOX_SIZE
+    int "TCPIP receive mail box size"
+    default 32
+    range 6 64
+    help
+        Set TCPIP receive mail box size. Generally bigger value means higher throughput
+        but more memory. The value should be bigger than UDP/TCP mail box size.
+
 menu "TCP"
 
 config TCP_MAXRTX
@@ -131,7 +161,7 @@ config TCP_WND_DEFAULT
 config TCP_RECVMBOX_SIZE
     int "Default TCP receive mail box size"
     default 6
-    range 6 32
+    range 6 64
     help
         Set TCP receive mail box size. Generally bigger value means higher throughput
         but more memory. The recommended value is: TCP_WND_DEFAULT/TCP_MSS + 2, e.g. if 
@@ -188,7 +218,7 @@ menu "UDP"
 config UDP_RECVMBOX_SIZE
     int "Default UDP receive mail box size"
     default 6
-    range 6 32
+    range 6 64
     help
         Set UDP receive mail box size. The recommended value is 6.
 
index c5ce7a8adebaa0c66684c8960bcb244fd3c8e526..bc3cfda734e133bc05506dd51b86176c7cfbd62c 100644 (file)
@@ -1074,7 +1074,7 @@ void dhcps_start(struct netif *netif, ip4_addr_t ip)
 
     client_address_plus.addr = dhcps_poll.start_ip.addr;
 
-    udp_bind(pcb_dhcps, IP_ADDR_ANY, DHCPS_SERVER_PORT);
+    udp_bind(pcb_dhcps, &netif->ip_addr, DHCPS_SERVER_PORT);
     udp_recv(pcb_dhcps, handle_dhcp, NULL);
 #if DHCPS_DEBUG
     DHCPS_LOG("dhcps:dhcps_start->udp_recv function Set a receive callback handle_dhcp for UDP_PCB pcb_dhcps\n");
index 5ea0ffa76f0f2e367a1ad8c9b6e86781f9ff7ade..aa638273df0b646bd7624d196403063e59fc026e 100755 (executable)
@@ -1066,6 +1066,9 @@ tcp_output(struct tcp_pcb *pcb)
 
 #if TCP_OVERSIZE_DBGCHECK
     seg->oversize_left = 0;
+    if (seg->next == NULL) {
+      pcb->unsent_oversize = 0;
+    }
 #endif /* TCP_OVERSIZE_DBGCHECK */
     err = tcp_output_segment(seg, pcb);
     if ((err != ERR_OK) && (err != ERR_RTE)) {
index 7bc4021e5a9f1b52001791ab02740a27381c9fdd..89d09d2d71f8c6eecdcc3eeb70a90a7b212d6990 100644 (file)
  * The queue size value itself is platform-dependent, but is passed to
  * sys_mbox_new() when tcpip_init is called.
  */
-#define TCPIP_MBOX_SIZE                 32
+#define TCPIP_MBOX_SIZE                 CONFIG_TCPIP_RECVMBOX_SIZE
 
 /**
  * DEFAULT_UDP_RECVMBOX_SIZE: The mailbox size for the incoming packets on a
    ---------- Statistics options ----------
    ----------------------------------------
 */
+
 /**
  * LWIP_STATS==1: Enable statistics collection in lwip_stats.
  */
-#define LWIP_STATS                      0
+#define LWIP_STATS                      CONFIG_LWIP_STATS
+
+#if LWIP_STATS
+
+/**
+ * LWIP_STATS_DISPLAY==1: Compile in the statistics output functions.
+ */
+#define LWIP_STATS_DISPLAY              CONFIG_LWIP_STATS
+#endif
+
 
 /*
    ---------------------------------
  * The peer *is* in the ARP table if it requested our address before.
  * Also notice that this slows down input processing of every IP packet!
  */
-#define ETHARP_TRUST_IP_MAC             1
+#define ETHARP_TRUST_IP_MAC             CONFIG_LWIP_ETHARP_TRUST_IP_MAC
 
 
 /* Enable all Espressif-only options */
 #define ESP_IP4_ATON                    1
 #define ESP_LIGHT_SLEEP                 1
 #define ESP_L2_TO_L3_COPY               CONFIG_L2_TO_L3_COPY
-#define ESP_STATS_MEM                   0
-#define ESP_STATS_DROP                  0
+#define ESP_STATS_MEM                   CONFIG_LWIP_STATS
+#define ESP_STATS_DROP                  CONFIG_LWIP_STATS
 #define ESP_STATS_TCP                   0
 #define ESP_DHCP_TIMER                  1
 #define ESP_LWIP_LOGI(...)              ESP_LOGI("lwip", __VA_ARGS__)
index c9e4a72d742498a591eff8f73012a554d85643c1..a7ef7f4511efc564bd978a7a969618d8ccb4a0e5 100644 (file)
@@ -24,7 +24,7 @@ extern "C" {
  * @brief Initialize the default NVS partition.
  *
  * This API initialises the default NVS partition. The default NVS partition
- * is the one that is labelled "nvs" in the partition table.
+ * is the one that is labeled "nvs" in the partition table.
  *
  * @return
  *      - ESP_OK if storage was successfully initialized.
@@ -38,7 +38,7 @@ esp_err_t nvs_flash_init(void);
 /**
  * @brief Initialize NVS flash storage for the specified partition.
  *
- * @param[in]  partition_name    Name (label) of the partition. Note that internally a reference to
+ * @param[in]  partition_label   Label of the partition. Note that internally a reference to
  *                               passed value is kept and it should be accessible for future operations
  *
  * @return
@@ -48,7 +48,30 @@ esp_err_t nvs_flash_init(void);
  *      - ESP_ERR_NOT_FOUND if specified partition is not found in the partition table
  *      - one of the error codes from the underlying flash storage driver
  */
-esp_err_t nvs_flash_init_partition(const char *partition_name);
+esp_err_t nvs_flash_init_partition(const char *partition_label);
+
+/**
+ * @brief Deinitialize NVS storage for the default NVS partition
+ *
+ * Default NVS partition is the partition with "nvs" label in the partition table.
+ *
+ * @return
+ *      - ESP_OK on success (storage was deinitialized)
+ *      - ESP_ERR_NVS_NOT_INITIALIZED if the storage was not initialized prior to this call
+ */
+esp_err_t nvs_flash_deinit(void);
+
+/**
+ * @brief Deinitialize NVS storage for the given NVS partition
+ *
+ * @param[in]  partition_label   Label of the partition
+ *
+ * @return
+ *      - ESP_OK on success
+ *      - ESP_ERR_NVS_NOT_INITIALIZED if the storage for given partition was not
+ *        initialized prior to this call
+ */
+esp_err_t nvs_flash_deinit_partition(const char* partition_label);
 
 /**
  * @brief Erase the default NVS partition
index e5c2f5adcd4e1e1b3fe6b81b59362ae9636eb2f8..3502c6ca106178b66eb7b4e0c50843dc9956325d 100644 (file)
@@ -88,15 +88,22 @@ extern "C" void nvs_dump(const char *partName)
 extern "C" esp_err_t nvs_flash_init_custom(const char *partName, uint32_t baseSector, uint32_t sectorCount)
 {
     ESP_LOGD(TAG, "nvs_flash_init_custom partition=%s start=%d count=%d", partName, baseSector, sectorCount);
-    nvs::Storage* mStorage;
-
-    mStorage = lookup_storage_from_name(partName);
-    if (mStorage == NULL) {
-        mStorage = new nvs::Storage((const char *)partName);
-        s_nvs_storage_list.push_back(mStorage);
+    nvs::Storage* new_storage = NULL;
+    nvs::Storage* storage = lookup_storage_from_name(partName);
+    if (storage == NULL) {
+        new_storage = new nvs::Storage((const char *)partName);
+        storage = new_storage;
     }
 
-    return mStorage->init(baseSector, sectorCount);
+    esp_err_t err = storage->init(baseSector, sectorCount);
+    if (new_storage != NULL) {
+        if (err == ESP_OK) {
+            s_nvs_storage_list.push_back(new_storage);
+        } else {
+            delete new_storage;
+        }
+    }
+    return err;
 }
 
 #ifdef ESP_PLATFORM
@@ -126,6 +133,42 @@ extern "C" esp_err_t nvs_flash_init(void)
     return nvs_flash_init_partition(NVS_DEFAULT_PART_NAME);
 }
 
+extern "C" esp_err_t nvs_flash_deinit_partition(const char* partition_name)
+{
+    Lock::init();
+    Lock lock;
+
+    nvs::Storage* storage = lookup_storage_from_name(partition_name);
+    if (!storage) {
+        return ESP_ERR_NVS_NOT_INITIALIZED;
+    }
+
+    /* Clean up handles related to the storage being deinitialized */
+    auto it = s_nvs_handles.begin();
+    auto next = it;
+    while(it != s_nvs_handles.end()) {
+        next++;
+        if (it->mStoragePtr == storage) {
+            ESP_LOGD(TAG, "Deleting handle %d (ns=%d) related to partition \"%s\" (missing call to nvs_close?)",
+                    it->mHandle, it->mNsIndex, partition_name);
+            s_nvs_handles.erase(it);
+            delete static_cast<HandleEntry*>(it);
+        }
+        it = next;
+    }
+
+    /* Finally delete the storage itself */
+    s_nvs_storage_list.erase(storage);
+    delete storage;
+
+    return ESP_OK;
+}
+
+extern "C" esp_err_t nvs_flash_deinit(void)
+{
+    return nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME);
+}
+
 extern "C" esp_err_t nvs_flash_erase_partition(const char *part_name)
 {
     const esp_partition_t* partition = esp_partition_find_first(
index 07d01db468f1d66f3391e470d6d35d2f516fc9ac..ed0884a7d8661b15a1ce2e20961c0b372b277de8 100644 (file)
@@ -59,5 +59,9 @@ TEST_CASE("various nvs tests", "[nvs]")
     TEST_ASSERT_EQUAL_INT32(0, strcmp(buf, str));
 
     nvs_close(handle_1);
+
+    // check that deinit does not leak memory if some handles are still open
+    nvs_flash_deinit();
+
     nvs_close(handle_2);
 }
index 14e56bab6e46bfce1a1fc9e8b957e341b7877eac..d8099322d500958890a4905fd6433bcca20a3ea7 100644 (file)
@@ -148,10 +148,20 @@ public:
         fseek(f, 0, SEEK_END);
         off_t size = ftell(f);
         assert(size % SPI_FLASH_SEC_SIZE == 0);
-        mData.resize(size);
+        mData.resize(size / sizeof(uint32_t));
         fseek(f, 0, SEEK_SET);
         auto s = fread(mData.data(), SPI_FLASH_SEC_SIZE, size / SPI_FLASH_SEC_SIZE, f);
         assert(s == static_cast<size_t>(size / SPI_FLASH_SEC_SIZE));
+        fclose(f);
+    }
+    
+    void save(const char* filename)
+    {
+        FILE* f = fopen(filename, "wb");
+        auto n_sectors = mData.size() * sizeof(uint32_t) / SPI_FLASH_SEC_SIZE;
+        auto s = fwrite(mData.data(), SPI_FLASH_SEC_SIZE, n_sectors, f);
+        assert(s == n_sectors);
+        fclose(f);
     }
 
     void clearStats()
index b889f5fa5607ec46d750cc61e0392dad06c826e2..0e633821fdb6aa36cca330344d0bed38eb9d6208 100644 (file)
 #define SOC_DMA_LOW  0x3FFAE000
 #define SOC_DMA_HIGH 0x40000000
 
-// Region of memory that is byte-accessible. See esp_ptr_byte_accesible().
+// Region of memory that is byte-accessible. See esp_ptr_byte_accessible().
 #define SOC_BYTE_ACCESSIBLE_LOW     0x3FFAE000
 #define SOC_BYTE_ACCESSIBLE_HIGH    0x40000000
 
+//Region of memory that is internal, as in on the same silicon die as the ESP32 CPUs (excluding RTC data region, that's checked separately.) See esp_ptr_internal().
+#define SOC_MEM_INTERNAL_LOW        0x3F400000
+#define SOC_MEM_INTERNAL_HIGH       0x400C2000
+
+
 //Interrupt hardware source table
 //This table is decided by hardware, don't touch this.
 #define ETS_WIFI_MAC_INTR_SOURCE                0/**< interrupt of WiFi MAC, level*/
index 39ea41f40d5f09467f66457c44231b1e51386806..e3e007898df55e69efa2cb1ec240a077ce7a84a9 100644 (file)
@@ -29,6 +29,7 @@
 #include "i2c_rtc_clk.h"
 #include "soc_log.h"
 #include "sdkconfig.h"
+#include "xtensa/core-macros.h"
 
 
 #define MHZ (1000000)
@@ -510,6 +511,8 @@ uint32_t rtc_clk_apb_freq_get()
 
 void rtc_clk_init(rtc_clk_config_t cfg)
 {
+    rtc_cpu_freq_t cpu_source_before = rtc_clk_cpu_freq_get();
+    
     /* If we get a TG WDT system reset while running at 240MHz,
      * DPORT_CPUPERIOD_SEL register will be reset to 0 resulting in 120MHz
      * APB and CPU frequencies after reset. This will cause issues with XTAL
@@ -569,6 +572,11 @@ void rtc_clk_init(rtc_clk_config_t cfg)
     rtc_clk_apb_freq_update(xtal_freq * MHZ);
     /* Set CPU frequency */
     rtc_clk_cpu_freq_set(cfg.cpu_freq);
+    
+    /* Re-calculate the ccount to make time calculation correct. */    
+    uint32_t freq_before = rtc_clk_cpu_freq_value(cpu_source_before) / MHZ;
+    uint32_t freq_after = rtc_clk_cpu_freq_value(cfg.cpu_freq) / MHZ;
+    XTHAL_SET_CCOUNT( XTHAL_GET_CCOUNT() * freq_after / freq_before );
 
     /* Slow & fast clocks setup */
     if (cfg.slow_freq == RTC_SLOW_FREQ_32K_XTAL) {
index dce4ff8b4cffd87924b7eb2e0bfb60c8cefa3564..737216c84f06f185d7bfb115724b2c952f4043e0 100644 (file)
@@ -31,7 +31,7 @@ Each type contains an array of prioritised capabilities; types with later entrie
 ones can't fulfill the memory request.
 
 The prioritised capabilities work roughly like this:
-- For a normal malloc (MALLOC_CAP_8BIT), give away the DRAM-only memory first, then pass off any dual-use IRAM regions,
+- For a normal malloc (MALLOC_CAP_DEFAULT), give away the DRAM-only memory first, then pass off any dual-use IRAM regions,
   finally eat into the application memory.
 - For a malloc where 32-bit-aligned-only access is okay, first allocate IRAM, then DRAM, finally application IRAM.
 - Application mallocs (PIDx) will allocate IRAM first, if possible, then DRAM.
@@ -40,10 +40,10 @@ The prioritised capabilities work roughly like this:
 */
 const soc_memory_type_desc_t soc_memory_types[] = {
     //Type 0: Plain ole D-port RAM
-    { "DRAM", { MALLOC_CAP_DMA|MALLOC_CAP_8BIT|MALLOC_CAP_INTERNAL, MALLOC_CAP_32BIT, 0 }, false, false},
+    { "DRAM", { MALLOC_CAP_8BIT|MALLOC_CAP_DEFAULT, MALLOC_CAP_INTERNAL|MALLOC_CAP_DMA|MALLOC_CAP_32BIT, 0 }, false, false},
     //Type 1: Plain ole D-port RAM which has an alias on the I-port
     //(This DRAM is also the region used by ROM during startup)
-    { "D/IRAM", { 0, MALLOC_CAP_DMA|MALLOC_CAP_8BIT|MALLOC_CAP_INTERNAL, MALLOC_CAP_32BIT|MALLOC_CAP_EXEC }, true, true},
+    { "D/IRAM", { 0, MALLOC_CAP_DMA|MALLOC_CAP_8BIT|MALLOC_CAP_INTERNAL|MALLOC_CAP_DEFAULT, MALLOC_CAP_32BIT|MALLOC_CAP_EXEC }, true, true},
     //Type 2: IRAM
     { "IRAM", { MALLOC_CAP_EXEC|MALLOC_CAP_32BIT|MALLOC_CAP_INTERNAL, 0, 0 }, false, false},
     //Type 3-8: PID 2-7 IRAM
@@ -54,14 +54,14 @@ const soc_memory_type_desc_t soc_memory_types[] = {
     { "PID6IRAM", { MALLOC_CAP_PID6|MALLOC_CAP_INTERNAL, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, false, false},
     { "PID7IRAM", { MALLOC_CAP_PID7|MALLOC_CAP_INTERNAL, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, false, false},
     //Type 9-14: PID 2-7 DRAM
-    { "PID2DRAM", { MALLOC_CAP_PID2|MALLOC_CAP_INTERNAL, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, false, false},
-    { "PID3DRAM", { MALLOC_CAP_PID3|MALLOC_CAP_INTERNAL, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, false, false},
-    { "PID4DRAM", { MALLOC_CAP_PID4|MALLOC_CAP_INTERNAL, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, false, false},
-    { "PID5DRAM", { MALLOC_CAP_PID5|MALLOC_CAP_INTERNAL, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, false, false},
-    { "PID6DRAM", { MALLOC_CAP_PID6|MALLOC_CAP_INTERNAL, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, false, false},
-    { "PID7DRAM", { MALLOC_CAP_PID7|MALLOC_CAP_INTERNAL, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT }, false, false},
+    { "PID2DRAM", { MALLOC_CAP_PID2|MALLOC_CAP_INTERNAL, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT|MALLOC_CAP_DEFAULT }, false, false},
+    { "PID3DRAM", { MALLOC_CAP_PID3|MALLOC_CAP_INTERNAL, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT|MALLOC_CAP_DEFAULT }, false, false},
+    { "PID4DRAM", { MALLOC_CAP_PID4|MALLOC_CAP_INTERNAL, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT|MALLOC_CAP_DEFAULT }, false, false},
+    { "PID5DRAM", { MALLOC_CAP_PID5|MALLOC_CAP_INTERNAL, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT|MALLOC_CAP_DEFAULT }, false, false},
+    { "PID6DRAM", { MALLOC_CAP_PID6|MALLOC_CAP_INTERNAL, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT|MALLOC_CAP_DEFAULT }, false, false},
+    { "PID7DRAM", { MALLOC_CAP_PID7|MALLOC_CAP_INTERNAL, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT|MALLOC_CAP_DEFAULT }, false, false},
     //Type 15: SPI SRAM data
-    { "SPIRAM", { MALLOC_CAP_SPIRAM, 0, MALLOC_CAP_DMA|MALLOC_CAP_8BIT|MALLOC_CAP_32BIT}, false, false},
+    { "SPIRAM", { MALLOC_CAP_SPIRAM|MALLOC_CAP_DEFAULT, 0, MALLOC_CAP_8BIT|MALLOC_CAP_32BIT}, false, false},
 };
 
 const size_t soc_memory_type_count = sizeof(soc_memory_types)/sizeof(soc_memory_type_desc_t);
@@ -150,16 +150,10 @@ const soc_reserved_region_t soc_reserved_regions[] = {
     { 0x3ffe4000, 0x3ffe4350 }, //Reserve ROM APP data region
 
 #if CONFIG_BT_ENABLED
-#if CONFIG_BT_DRAM_RELEASE
-    { 0x3ffb0000, 0x3ffb3000 }, //Reserve BT data region
-    { 0x3ffb8000, 0x3ffbbb28 }, //Reserve BT data region
-    { 0x3ffbdb28, 0x3ffc0000 }, //Reserve BT data region
-#else
     { 0x3ffb0000, 0x3ffc0000 }, //Reserve BT hardware shared memory & BT data region
-#endif
     { 0x3ffae000, 0x3ffaff10 }, //Reserve ROM data region, inc region needed for BT ROM routines
 #else
-    { 0x3ffae000, 0x3ffae2a0 }, //Reserve ROM data region
+    { 0x3ffae000, 0x3ffae6e0 }, //Reserve ROM data region
 #endif
 
 #if CONFIG_MEMMAP_TRACEMEM
index 0a282062e5fca19cc15f0b854a8d234bfc48d666..1c1415e3dc31b5253ee62b4667deb1e7d19b9954 100644 (file)
@@ -17,6 +17,8 @@
 #include <stdbool.h>
 
 #include "soc/soc.h"
+#include "sdkconfig.h"
+#include "esp_attr.h"
 
 #define SOC_MEMORY_TYPE_NO_PRIOS 3
 
@@ -58,12 +60,12 @@ typedef struct
 extern const soc_reserved_region_t soc_reserved_regions[];
 extern const size_t soc_reserved_region_count;
 
-inline static bool esp_ptr_dma_capable(const void *p)
+inline static bool IRAM_ATTR esp_ptr_dma_capable(const void *p)
 {
     return (intptr_t)p >= SOC_DMA_LOW && (intptr_t)p < SOC_DMA_HIGH;
 }
 
-inline static bool esp_ptr_executable(const void *p)
+inline static bool IRAM_ATTR esp_ptr_executable(const void *p)
 {
     intptr_t ip = (intptr_t) p;
     return (ip >= SOC_IROM_LOW && ip < SOC_IROM_HIGH)
@@ -71,8 +73,19 @@ inline static bool esp_ptr_executable(const void *p)
         || (ip >= SOC_RTC_IRAM_LOW && ip < SOC_RTC_IRAM_HIGH);
 }
 
-inline bool esp_ptr_byte_accesible(const void *p)
+inline static bool IRAM_ATTR esp_ptr_byte_accessible(const void *p)
 {
-    //currently only support DRAM, add PSRAM region in the future
-    return (intptr_t)p >= SOC_BYTE_ACCESSIBLE_LOW && (intptr_t)p < SOC_BYTE_ACCESSIBLE_HIGH;
+    bool r;
+    r = ((intptr_t)p >= SOC_BYTE_ACCESSIBLE_LOW && (intptr_t)p < SOC_BYTE_ACCESSIBLE_HIGH);
+#if CONFIG_SPIRAM_SUPPORT
+    r |= ((intptr_t)p >= SOC_EXTRAM_DATA_LOW && (intptr_t)p < SOC_EXTRAM_DATA_HIGH);
+#endif
+    return r;
+}
+
+inline static bool IRAM_ATTR esp_ptr_internal(const void *p) {
+    bool r;
+    r = ((intptr_t)p >= SOC_MEM_INTERNAL_LOW && (intptr_t)p < SOC_MEM_INTERNAL_HIGH);
+    r |= ((intptr_t)p >= SOC_RTC_DATA_LOW && (intptr_t)p < SOC_RTC_DATA_HIGH);
+    return r;
 }
index 8b98e2cb108f6463f18461167b0d433466eabf80..52e64c11a5c61fc1087e9f843a036fc187813545 100644 (file)
@@ -135,7 +135,7 @@ esp_err_t IRAM_ATTR spi_flash_mmap_pages(int *pages, size_t page_count, spi_flas
             return ESP_ERR_INVALID_ARG;
         }
     }
-    mmap_entry_t* new_entry = (mmap_entry_t*) malloc(sizeof(mmap_entry_t));
+    mmap_entry_t* new_entry = (mmap_entry_t*) heap_caps_malloc(sizeof(mmap_entry_t), MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT);
     if (new_entry == 0) {
         return ESP_ERR_NO_MEM;
     }
index f731f5b0058d25773c1e9114ac31a556b18e775e..3f81b151d56a93726a247c591025d319e4b7c60c 100644 (file)
@@ -498,7 +498,7 @@ static int spiffs_mode_conv(int m)
     } else if ((m & O_CREAT) && (m & O_TRUNC)) {
         res |= SPIFFS_O_CREAT | SPIFFS_O_TRUNC;
     } else if (m & O_APPEND) {
-        res |= SPIFFS_O_APPEND;
+        res |= SPIFFS_O_CREAT | SPIFFS_O_APPEND;
     }
     return res;
 }
index 9a1f12c437ee1f4dbb80cdc81a234c304177732c..ae1b9ad430614f6d93415184b32e152f30370fcc 100644 (file)
 #include <stdbool.h>
 #include "esp_err.h"
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 /**
  * @brief Configuration structure for esp_vfs_spiffs_register
  */
@@ -91,4 +95,8 @@ esp_err_t esp_spiffs_format(const char* partition_label);
  */
 esp_err_t esp_spiffs_info(const char* partition_label, size_t *total_bytes, size_t *used_bytes);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* _ESP_SPIFFS_H_ */
index e0c9d7f9d2f6b4e53d7fb6268debf635eeef5ce5..e412bfd0cfaa85c0dd2c8fddd3066ca871204350 100755 (executable)
 #define SPIFFS_TAG "SPIFFS"
 
 // Set generic spiffs debug output call.
-#if CONGIG_SPIFFS_DBG
+#if CONFIG_SPIFFS_DBG
 #define SPIFFS_DBG(...)             ESP_LOGD(SPIFFS_TAG, __VA_ARGS__)
 #else
 #define SPIFFS_DBG(...)
 #endif
-#if CONGIG_SPIFFS_API_DBG
+#if CONFIG_SPIFFS_API_DBG
 #define SPIFFS_API_DBG(...)         ESP_LOGD(SPIFFS_TAG, __VA_ARGS__)
 #else
 #define SPIFFS_API_DBG(...)
 #endif
-#if CONGIG_SPIFFS_DBG
+#if CONFIG_SPIFFS_DBG
 #define SPIFFS_GC_DBG(...)          ESP_LOGD(SPIFFS_TAG, __VA_ARGS__)
 #else
 #define SPIFFS_GC_DBG(...)
 #endif
-#if CONGIG_SPIFFS_CACHE_DBG
+#if CONFIG_SPIFFS_CACHE_DBG
 #define SPIFFS_CACHE_DBG(...)       ESP_LOGD(SPIFFS_TAG, __VA_ARGS__)
 #else
 #define SPIFFS_CACHE_DBG(...)
 #endif
-#if CONGIG_SPIFFS_CHECK_DBG
+#if CONFIG_SPIFFS_CHECK_DBG
 #define SPIFFS_CHECK_DBG(...)       ESP_LOGD(SPIFFS_TAG, __VA_ARGS__)
 #else
 #define SPIFFS_CHECK_DBG(...)
index 794f0478d2aa9c978c3844da6e97f14239a1e061..f5e26c4e933189593a71c6b82cda381a7b21e41c 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 794f0478d2aa9c978c3844da6e97f14239a1e061
+Subproject commit f5e26c4e933189593a71c6b82cda381a7b21e41c
index d385fd418006a69b23204f830ecb3361dea170b7..86fe8dbfb1631fde441c272a92437c7710011c31 100644 (file)
@@ -309,6 +309,8 @@ esp_err_t tcpip_adapter_down(tcpip_adapter_if_t tcpip_if)
         tcpip_adapter_start_ip_lost_timer(tcpip_if);
     }
 
+    tcpip_adapter_update_default_netif();
+
     return ESP_OK;
 }
 
diff --git a/docs/_static/freertos-ready-task-list-smp-pxIndex.png b/docs/_static/freertos-ready-task-list-smp-pxIndex.png
new file mode 100644 (file)
index 0000000..0834685
Binary files /dev/null and b/docs/_static/freertos-ready-task-list-smp-pxIndex.png differ
diff --git a/docs/_static/freertos-ready-task-list-smp.png b/docs/_static/freertos-ready-task-list-smp.png
new file mode 100644 (file)
index 0000000..820007a
Binary files /dev/null and b/docs/_static/freertos-ready-task-list-smp.png differ
diff --git a/docs/_static/freertos-ready-task-list.png b/docs/_static/freertos-ready-task-list.png
new file mode 100644 (file)
index 0000000..ea14895
Binary files /dev/null and b/docs/_static/freertos-ready-task-list.png differ
diff --git a/docs/api-guides/external-ram.rst b/docs/api-guides/external-ram.rst
new file mode 100644 (file)
index 0000000..5270436
--- /dev/null
@@ -0,0 +1,104 @@
+Support for external RAM
+************************
+
+.. toctree::
+   :maxdepth: 1
+
+Introduction
+============
+
+The ESP32 has a few hundred KiB of internal RAM, residing on the same die as the rest of the ESP32. For some purposes, this is insufficient, 
+and therefore the ESP32 incorporates the ability to also use up to 4MiB of external SPI RAM memory as memory. The external memory is incorporated 
+in the memory map and is, within certain restrictions, usable in the same way internal data RAM is.
+
+Hardware
+========
+
+The ESP32 supports SPI (P)SRAM connected in parallel with the SPI flash chip. While the ESP32 is capable of supporting several types
+of RAM chips, the ESP32 SDK at the moment only supports the ESP-PSRAM32 chip.
+
+The ESP-PSRAM32 chip is an 1.8V device, and can only be used in parallel with an 1.8V flash part. Make sure to either set the MTDI 
+pin to a high signal level on bootup, or program the fuses in the ESP32 to always use a VDD_SIO level of 1.8V. Not doing this risks
+damaging the PSRAM and/or flash chip.
+
+To connect the ESP-PSRAM chip to the ESP32D0W*, connect the following signals:
+ * PSRAM /CE (pin 1) - ESP32 GPIO 16
+ * PSRAM SO (pin 2) - flash DO
+ * PSRAM SIO[2] (pin 3) - flash WP
+ * PSRAM SI (pin 5) - flash DI
+ * PSRAM SCLK (pin 6) - ESP32 GPIO 17
+ * PSRAM SIO[3] (pin 7) - flash HOLD
+ * PSRAM Vcc (pin 8) - ESP32 VCC_SDIO
+
+Connections for the ESP32D2W* chips are TBD.
+
+.. NOTE::
+   Espressif sells an ESP-WROVER module which contains an ESP32, 1.8V flash and the ESP-PSRAM32 integrated in a module, ready for inclusion
+   on an end product PCB.
+
+Software
+========
+
+ESP-IDF fully supports integrating external memory use into your applications. ESP-IDF can be configured to handle external RAM in several ways:
+ * Only initialize RAM. This allows the application to manually place data here by dereferencing pointers pointed at the external RAM memory
+   region (0x3F800000 and up).
+ * Initialize RAM and add it to the capability allocator. This allows a program to specifically allocate a chunk of external RAM using
+   ``heap_caps_malloc(size, MALLOC_CAP_SPIRAM)``. This memory can be used and subsequently freed using a normal ``free()`` call.
+ * Initialize RAM, add it to the capability allocator and add memory to the pool of RAM that can be returned by ``malloc()``. This allows
+   any application to use the external RAM without having to rewrite the code to use ``heap_caps_malloc``.
+
+All these options can be selected from the menuconfig menu.
+
+Restrictions
+------------
+
+The use of external RAM has a few restrictions:
+ * When disabling flash cache (for example, because the flash is being written to), the external RAM also becomes inaccessible; any reads from or
+   writes to it will lead to an illegal cache access exception. This is also the reason that ESP-IDF will never allocate a tasks stack in external
+   RAM.
+ * External RAM cannot be used as a place to store DMA transaction descriptors or as a buffer for a DMA transfer to read from or write into. Any
+   buffers that will be used in combination with DMA must be allocated using ``heap_caps_malloc(size, MALLOC_CAP_DMA)`` (and can be freed using a
+   standard ``free()`` call.)
+ * External RAM uses the same cache region as the external flash. This means that often accessed variables in external RAM can be read and 
+   modified almost as quickly as in internal ram. However, when accessing large chunks of data (>32K), the cache can be insufficient and speeds 
+   will fall back to the access speed of the external RAM. Moreover, accessing large chunks of data can 'push out' cached flash, possibly making 
+   execution of code afterwards slower.
+ * External RAM cannot be used as task stack memory; because of this, xTaskCreate and similar functions will always allocate internal memory
+   for stack and task TCBs and xTaskCreateStatic-type functions will check if the buffers passed are internal. However, for tasks not calling
+   on code in ROM in any way, directly or indirectly, the menuconfig option :ref:`CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY` will eliminate
+   the check in xTaskCreateStatic, allowing task stack in external RAM. Using this is not advised, however.
+
+
+Because there are a fair few situations that have a specific need for internal memory, but it is also possible to use malloc() to exhaust
+internal memory, there is a pool reserved specifically for requests that cannot be resolved from external memory; allocating task
+stack, DMA buffers and memory that stays accessible when cache is disabled is drawn from this pool. The size of this pool is configurable
+in menuconfig.
+
+
+Chip revisions
+==============
+
+There are some issues with certain revisions of the ESP32 that have repercussions for use with external RAM. These are documented in the ESP32 
+ECO_ document. In particular, ESP-IDF handles the bugs mentioned in the following ways:
+
+ESP32 rev v0
+------------
+ESP-IDF has no workaround for the bugs in this revision of silicon, and it cannot be used to map external PSRAM into the ESP32s main memory map.
+
+ESP32 rev v1
+------------
+The bugs in this silicon revision introduce a hazard when certain sequences of machine instructions operate on external memory locations (ESP32 ECO 3.2). 
+To work around this, the gcc compiler to compile ESP-IDF has been expanded with a flag: ``-mfix-esp32-psram-cache-issue``. With this flag passed to gcc 
+on the command line, the compiler works around these sequences and only outputs code that can safely be executed.
+
+In ESP-IDF, this flag is enabled when you select :ref:`CONFIG_SPIRAM_CACHE_WORKAROUND`. ESP-IDF also takes other measures to make
+sure no combination of PSRAM access plus the offending instruction sets are used: it links to a version of Newlib recompiled with the gcc flag, doesn't use
+some ROM functions and allocates static memory for the WiFi stack.
+
+.. _ECO: https://www.espressif.com/sites/default/files/documentation/eco_and_workarounds_for_bugs_in_esp32_en.pdf
+
+
+
+
+
+
diff --git a/docs/api-guides/freertos-smp.rst b/docs/api-guides/freertos-smp.rst
new file mode 100644 (file)
index 0000000..7ccba1b
--- /dev/null
@@ -0,0 +1,369 @@
+ESP-IDF FreeRTOS SMP Changes
+============================
+
+Overview
+--------
+
+The vanilla FreeRTOS is designed to run on a single core. However the ESP32 is 
+dual core containing a Protocol CPU (known as **CPU 0** or **PRO_CPU**) and an 
+Application CPU (known as **CPU 1** or **APP_CPU**). The two cores are 
+identical in practice and share the same memory. This allows the two cores to 
+run tasks interchangeably between them.
+
+The ESP-IDF FreeRTOS is a modified version of vanilla FreeRTOS which supports 
+symmetric multiprocessing (SMP). ESP-IDF FreeRTOS is based on the Xtensa port 
+of FreeRTOS v8.2.0, however features such as static task creation and Thread 
+Local Storage Pointers have been backported from later versions of FreeRTOS. 
+This guide outlines the major differences between vanilla FreeRTOS and 
+ESP-IDF FreeRTOS. The API reference for vanilla FreeRTOS can be found 
+via http://www.freertos.org/a00106.html
+
+:ref:`tasks-and-task-creation`: Use ``xTaskCreatePinnedToCore()`` or 
+``xTaskCreateStaticPinnedToCore()`` to create tasks in ESP-IDF FreeRTOS. The 
+last parameter of the two functions is ``xCoreID``. This parameter specifies 
+which core the task is pinned to. Acceptable values are ``0`` for **PRO_CPU**, 
+``1`` for **APP_CPU**, or ``tskNO_AFFINITY`` which allows the task to run on
+both.
+        
+:ref:`round-robin-scheduling`: The ESP-IDF FreeRTOS scheduler will skip tasks when 
+implementing Round-Robin scheduling between multiple tasks in the Ready state 
+that are of the same priority. To avoid this behavior, ensure that those tasks either 
+enter a blocked state, or are distributed across a wider range of priorities.
+        
+:ref:`scheduler-suspension`: Suspending the scheduler in ESP-IDF FreeRTOS will only 
+affect the scheduler on the the calling core. In other words, calling 
+``vTaskSuspendAll()`` on **PRO_CPU** will not prevent **APP_CPU** from scheduling, and
+vice versa. Use critical sections or semaphores instead for simultaneous
+access protection.
+    
+:ref:`tick-interrupt-synchronicity`: Tick interrupts of **PRO_CPU** and **APP_CPU** 
+are not synchronized. Do not expect to use ``vTaskDelay`` or 
+``vTaskDelayUntil`` as an accurate method of synchronizing task execution 
+between the two cores. Use a counting semaphore instead as their context 
+switches are not tied to tick interrupts due to preemption.
+        
+:ref:`critical-sections`: In ESP-IDF FreeRTOS, critical sections are implemented using
+mutexes. Entering critical sections involve taking a mutex, then disabling the 
+scheduler and interrupts of the calling core. However the other core is left 
+unaffected. If the other core attemps to take same mutex, it will spin until
+the calling core has released the mutex by exiting the critical section.
+        
+:ref:`deletion-callbacks`: ESP-IDF FreeRTOS has 
+backported the Thread Local Storage Pointers feature. However they have the 
+extra feature of deletion callbacks. Deletion callbacks are used to
+automatically free memory used by Thread Local Storage Pointers during the task
+deletion. Call ``vTaskSetThreadLocalStoragePointerAndDelCallback()``
+to set Thread Local Storage Pointers and deletion callbacks.
+
+:ref:`esp-idf-freertos-configuration`: Several aspects of ESP-IDF FreeRTOS can be 
+configured using ``make meunconfig`` such as running ESP-IDF in Unicore Mode,
+or configuring the number of Thread Local Storage Pointers each task will have.
+
+
+.. _tasks-and-task-creation:
+
+Tasks and Task Creation
+-----------------------
+
+Tasks in ESP-IDF FreeRTOS are designed to run on a particular core, therefore 
+two new task creation functions have been added to ESP-IDF FreeRTOS by 
+appending ``PinnedToCore`` to the names of the task creation functions in 
+vanilla FreeRTOS. The vanilla FreeRTOS functions of ``xTaskCreate()``
+and ``xTaskCreateStatic()`` have led to the addition of 
+``xTaskCreatePinnedToCore()`` and ``xTaskCreateStaticPinnedToCore()`` in 
+ESP-IDF FreeRTOS.
+
+For more details see :component_file:`freertos/task.c`
+
+The ESP-IDF FreeRTOS task creation functions are nearly identical to their 
+vanilla counterparts with the exception of the extra parameter known as 
+``xCoreID``. This parameter specifies the core on which the task should run on 
+and can be one of the following values.
+
+    -  ``0`` pins the task to **PRO_CPU**
+    -  ``1`` pins the task to **APP_CPU**
+    -  ``tskNO_AFFINITY`` allows the task to be run on both CPUs
+
+For example ``xTaskCreatePinnedToCore(tsk_callback, “APP_CPU Task”, 1000, NULL, 10, NULL, 1)`` 
+creates a task of priority 10 that is pinned to **APP_CPU** with a stack size 
+of 1000 bytes. It should be noted that the ``uxStackDepth`` parameter in 
+vanilla FreeRTOS specifies a task’s stack depth in terms of the number of 
+words, whereas ESP-IDF FreeRTOS specifies the stack depth in terms of bytes.
+
+Note that the vanilla FreeRTOS functions ``xTaskCreate`` and 
+``xTaskCreateStatic`` have been macro defined in ESP-IDF FreeRTOS to call 
+``xTaskCreatePinnedToCore()`` and ``xTaskCreateStaticPinnedToCore()``
+respectively with ``tskNO_AFFINITY`` as the ``xCoreID`` value. 
+
+Each Task Control Block (TCB) in ESP-IDF stores the ``xCoreID`` as a member. 
+Hence when each core calls the scheduler to select a task to run, the 
+``xCoreID`` member will allow the scheduler to determine if a given task is  
+permitted to run on the core that called it.
+
+Scheduling
+----------
+
+The vanilla FreeRTOS implements scheduling in the ``vTaskSwitchContext()`` 
+function. This function is responsible for selecting the highest priority task
+to run from a list of tasks in the Ready state known as the Ready Tasks List 
+(described in the next section). In ESP-IDF FreeRTOS, each core will call 
+``vTaskSwitchContext()`` independently to select a task to run from the 
+Ready Tasks List which is shared between both cores. There are several 
+differences in scheduling behavior between vanilla and ESP-IDF FreeRTOS such as 
+differences in Round Robin scheduling, scheduler suspension, and tick interrupt 
+synchronicity. 
+
+.. _round-robin-scheduling:
+
+Round Robin Scheduling
+^^^^^^^^^^^^^^^^^^^^^^
+
+Given multiple tasks in the Ready state and of the same priority, vanilla 
+FreeRTOS implements Round Robin scheduling between each task. This will result
+in running those tasks in turn each time the scheduler is called 
+(e.g. every tick interrupt). On the other hand, the ESP-IDF FreeRTOS scheduler 
+may skip tasks when Round Robin scheduling multiple Ready state tasks of the 
+same priority.
+
+The issue of skipping tasks during Round Robin scheduling arises from the way 
+the Ready Tasks List is implemented in FreeRTOS. In vanilla FreeRTOS, 
+``pxReadyTasksList`` is used to store a list of tasks that are in the Ready 
+state. The list is implemented as an array of length ``configMAX_PRIORITIES`` 
+where each element of the array is a linked list. Each linked list is of type 
+``List_t`` and contains TCBs of tasks of the same priority that are in the 
+Ready state. The following diagram illustrates the ``pxReadyTasksList`` 
+structure.
+
+.. figure:: ../_static/freertos-ready-task-list.png
+    :align: center
+    :alt: Vanilla FreeRTOS Ready Task List Structure
+    
+    Illustration of FreeRTOS Ready Task List Data Structure 
+
+
+Each linked list also contains a ``pxIndex`` which points to the last TCB 
+returned when the list was queried. This index allows the ``vTaskSwitchContext()`` 
+to start traversing the list at the TCB immediately after ``pxIndex`` hence 
+implementing Round Robin Scheduling between tasks of the same priority.
+
+In ESP-IDF FreeRTOS, the Ready Tasks List is shared between cores hence 
+``pxReadyTasksList`` will contain tasks pinned to different cores. When a core 
+calls the scheduler, it is able to look at the ``xCoreID`` member of each TCB 
+in the list to determine if a task is allowed to run on calling the core. The 
+ESP-IDF FreeRTOS ``pxReadyTasksList`` is illustrated below.
+
+.. figure:: ../_static/freertos-ready-task-list-smp.png
+    :align: center
+    :alt: ESP-IDF FreeRTOS Ready Task List Structure
+    
+    Illustration of FreeRTOS Ready Task List Data Structure in ESP-IDF
+    
+Therefore when **PRO_CPU** calls the scheduler, it will only consider the tasks 
+in blue or purple. Whereas when **APP_CPU** calls the scheduler, it will only 
+consider the tasks in orange or purple.
+
+Although each TCB has an ``xCoreID`` in ESP-IDF FreeRTOS, the linked list of 
+each priority only has a single ``pxIndex``. Therefore when the scheduler is 
+called from a particular core and traverses the linked list, it will skip all 
+TCBs pinned to the other core and point the pxIndex at the selected task. If 
+the other core then calls the scheduler, it will traverse the linked list 
+starting at the TCB immediately after ``pxIndex``. Therefore, TCBs skipped on
+the previous scheduler call from the other core would not be considered on the 
+current scheduler call. This issue is demonstrated in the following 
+illustration.
+
+.. figure:: ../_static/freertos-ready-task-list-smp-pxIndex.png
+    :align: center
+    :alt: ESP-IDF pxIndex Behavior
+    
+    Illustration of pxIndex behavior in ESP-IDF FreeRTOS
+
+Referring to the illustration above, assume that priority 9 is the highest 
+priority, and none of the tasks in priority 9 will block hence will always be 
+either in the running or Ready state.
+
+1)     **PRO_CPU** calls the scheduler and selects Task A to run, hence moves 
+``pxIndex`` to point to Task A
+
+2)     **APP_CPU** calls the scheduler and starts traversing from the task after 
+``pxIndex`` which is Task B. However Task B is not selected to run as it is not 
+pinned to **APP_CPU** hence it is skipped and Task C is selected instead. 
+``pxIndex`` now points to Task C
+
+3)     **PRO_CPU** calls the scheduler and starts traversing from Task D. It skips 
+Task D and selects Task E to run and points ``pxIndex`` to Task E. Notice that 
+Task B isn’t traversed because it was skipped the last time **APP_CPU** called 
+the scheduler to traverse the list.
+
+4)     The same situation with Task D will occur if **APP_CPU** calls the 
+scheduler again as ``pxIndex`` now points to Task E
+
+One solution to the issue of task skipping is to ensure that every task will
+enter a blocked state so that they are removed from the Ready Task List.
+Another solution is to distribute tasks across multiple priorities such that 
+a given priority will not be assigned multiple tasks that are pinned to 
+different cores.
+
+.. _scheduler-suspension:
+
+Scheduler Suspension
+^^^^^^^^^^^^^^^^^^^^
+
+In vanilla FreeRTOS, suspending the scheduler via ``vTaskSuspendAll()`` will 
+prevent calls of ``vTaskSwitchContext()`` from context switching until the 
+scheduler has been resumed with ``vTaskResumeAll()``. However servicing ISRs 
+are still permitted. Therefore any changes in task states as a result from the
+current running task or ISRSs will not be executed until the scheduler is 
+resumed. Scheduler suspension in vanilla FreeRTOS is a common protection method 
+against simultaneous access of data shared between tasks, whilst still allowing 
+ISRs to be serviced.
+
+In ESP-IDF FreeRTOS, ``vTaskSuspendAll()`` will only prevent calls of 
+``vTaskSwitchContext()`` from switching contexts on the core that called for the
+suspension. Hence if **PRO_CPU** calls ``vTaskSuspendAll()``, **APP_CPU** will 
+still be able to switch contexts. If data is shared between tasks that are 
+pinned to different cores, scheduler suspension is **NOT** a valid method of 
+protection against simultaneous access. Consider using critical sections 
+(disables interrupts) or semaphores (does not disable interrupts) instead when 
+protecting shared resources in ESP-IDF FreeRTOS.
+
+.. _tick-interrupt-synchronicity:
+
+Tick Interrupt Synchronicity 
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+In ESP-IDF FreeRTOS, tasks on different cores that unblock on the same tick 
+count might not run at exactly the same time due to the scheduler calls from 
+each core being independent, and the tick interrupts to each core being 
+unsynchronized.
+
+In vanilla FreeRTOS the tick interrupt triggers a call to 
+``xTaskIncrementTick()`` which is responsible for incrementing the tick 
+counter, checking if tasks which have called ``vTaskDelay()`` have fulfilled 
+their delay period, and moving those tasks from the Delayed Task List to the 
+Ready Task List. The tick interrupt will then call the scheduler if a context 
+switch is necessary.
+
+In ESP-IDF FreeRTOS, delayed tasks are unblocked with reference to the tick 
+interrupt on PRO_CPU as PRO_CPU is responsible for incrementing the shared tick 
+count. However tick interrupts to each core might not be synchronized (same 
+frequency but out of phase) hence when PRO_CPU receives a tick interrupt, 
+APP_CPU might not have received it yet. Therefore if multiple tasks of the same 
+priority are unblocked on the same tick count, the task pinned to PRO_CPU will 
+run immediately whereas the task pinned to APP_CPU must wait until APP_CPU 
+receives its out of sync tick interrupt. Upon receiving the tick interrupt, 
+APP_CPU will then call for a context switch and finally switches contexts to
+the newly unblocked task.
+
+Therefore, task delays should **NOT** be used as a method of synchronization 
+between tasks in ESP-IDF FreeRTOS. Instead, consider using a counting semaphore 
+to unblock multiple tasks at the same time.
+
+.. _critical-sections:
+
+Critical Sections & Disabling Interrupts
+----------------------------------------
+
+Vanilla FreeRTOS implements critical sections in ``vTaskEnterCritical`` which 
+disables the scheduler and calls ``portDISABLE_INTERRUPTS``. This prevents 
+context switches and servicing of ISRs during a critical section. Therefore, 
+critical sections are used as a valid protection method against simultaneous 
+access in vanilla FreeRTOS.
+
+On the other hand, the ESP32 has no hardware method for cores to disable each 
+other’s interrupts. Calling ``portDISABLE_INTERRUPTS()`` will have no effect on 
+the interrupts of the other core. Therefore, disabling interrupts is **NOT** 
+a valid protection method against simultaneous access to shared data as it 
+leaves the other core free to access the data even if the current core has 
+disabled its own interrupts. 
+
+For this reason, ESP-IDF FreeRTOS implements critical sections using mutexes, 
+and calls to enter or exit a critical must provide a mutex that is associated 
+with a shared resource requiring access protection. When entering a critical 
+section in ESP-IDF FreeRTOS, the calling core will disable its scheduler and 
+interrupts similar to the vanilla FreeRTOS implementation. However, the calling 
+core will also take the mutex whilst the other core is left unaffected during 
+the critical section. If the other core attempts to take the same mutex, it 
+will spin until the mutex is released. Therefore, the ESP-IDF FreeRTOS 
+implementation of critical sections allows a core to have protected access to a
+shared resource without disabling the other core. The other core will only be 
+affected if it tries to concurrently access the same resource.
+
+The ESP-IDF FreeRTOS critical section functions have been modified as follows…
+
+ - ``taskENTER_CRITICAL(mux)``, ``taskENTER_CRITICAL_ISR(mux)``, 
+   ``portENTER_CRITICAL(mux)``, ``portENTER_CRITICAL_ISR(mux)`` are all macro 
+   defined to call ``vTaskEnterCritical()`` 
+
+ - ``taskEXIT_CRITICAL(mux)``, ``taskEXIT_CRITICAL_ISR(mux)``, 
+   ``portEXIT_CRITICAL(mux)``, ``portEXIT_CRITICAL_ISR(mux)`` are all macro 
+   defined to call ``vTaskExitCritical()``
+
+For more details see :component_file:`freertos/include/freertos/portmacro.h` 
+and :component_file:`freertos/task.c`
+
+It should be noted that when modifying vanilla FreeRTOS code to be ESP-IDF 
+FreeRTOS compatible, it is trivial to modify the type of critical section 
+called as they are all defined to call the same function. As long as the same 
+mutex is provided upon entering and exiting, the type of call should not 
+matter.
+
+.. _deletion-callbacks:
+
+Thread Local Storage Pointers & Deletion Callbacks
+--------------------------------------------------
+
+Thread Local Storage Pointers are pointers stored directly in the TCB which 
+allows each task to have a pointer to a data structure containing that is 
+specific to that task. However vanilla FreeRTOS provides no functionality to 
+free the memory pointed to by the Thread Local Storage Pointers. Therefore if 
+the memory pointed to by the Thread Local Storage Pointers is not explicitly 
+freed by the user before a task is deleted, memory leak will occur.
+
+ESP-IDF FreeRTOS provides the added feature of deletion callbacks. These 
+deletion callbacks are used to automatically free the memory pointed to by the 
+Thread Local Storage Pointers when a task is deleted. Each Thread Local Storage 
+Pointer can have its own call back, and these call backs are called when the 
+Idle tasks cleans up a deleted tasks.
+
+Vanilla FreeRTOS sets a Thread Local Storage Pointers using 
+``vTaskSetThreadLocalStoragePointer()`` whereas ESP-IDF FreeRTOS sets a Thread 
+Local Storage Pointers and Deletion Callbacks using 
+``vTaskSetThreadLocalStoragePointerAndDelCallback()`` which accepts a pointer 
+to the deletion call back as an extra parameter of type 
+```TlsDeleteCallbackFunction_t``. Calling the vanilla FreeRTOS API 
+``vTaskSetThreadLocalStoragePointer()`` is still valid however it is internally
+defined to call ``vTaskSetThreadLocalStoragePointerAndDelCallback()`` with a
+``NULL`` pointer as the deletion call back. This results in the selected Thread 
+Local Storage Pointer to have no deletion call back.
+
+For more details see :component_file:`freertos/include/freertos/task.h`
+
+.. _esp-idf-freertos-configuration:
+
+Configuring ESP-IDF FreeRTOS
+----------------------------
+
+The ESP-IDF FreeRTOS can be configured using ``make menuconfig`` under 
+``Component_Config/FreeRTOS``. The following section highlights some of the
+ESP-IDF FreeRTOS configuration options. For a full list of ESP-IDF
+FreeRTOS configurations, see :doc:`FreeRTOS <../api-reference/kconfig>`
+
+:ref:`CONFIG_FREERTOS_UNICORE` will run ESP-IDF FreeRTOS only
+on **PRO_CPU**. Note that this is **not equivalent to running vanilla 
+FreeRTOS**. Behaviors of multiple components in ESP-IDF will be modified such 
+as :component_file:`esp32/cpu_start.c`. For more details regarding the 
+effects of running ESP-IDF FreeRTOS on a single core, search for 
+occurences of ``CONFIG_FREERTOS_UNICORE`` in the ESP-IDF components.
+    
+:ref:`CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS` will define the 
+number of Thread Local Storage Pointers each task will have in ESP-IDF 
+FreeRTOS.
+
+:ref:`CONFIG_SUPPORT_STATIC_ALLOCATION` will enable the backported
+functionality of ``xTaskCreateStaticPinnedToCore()`` in ESP-IDF FreeRTOS
+    
+:ref:`CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION` will trigger a halt in
+particular functions in ESP-IDF FreeRTOS which have not been fully tested
+in an SMP context.
+    
\ No newline at end of file
index 0f155bafb8fadcb38cc6ed77564bb93286a63e25..662ab0e79156c8e7fdbd33de30fa6653a0704fc1 100644 (file)
@@ -9,6 +9,7 @@ API Guides
    Deep Sleep Wake Stubs <deep-sleep-stub>
    ESP32 Core Dump <core_dump>
    Flash Encryption <../security/flash-encryption>
+   FreeRTOS SMP Changes <freertos-smp>
    High Level Interrupts <hlinterrupts>
    JTAG Debugging <jtag-debugging/index>
    Partition Tables <partition-tables>
@@ -19,3 +20,4 @@ API Guides
    Console Component <console>
    ROM debug console <romconsole>
    WiFi Driver <wifi>
+   External SPI-connected RAM <external-ram>
index a70decea60d8154eb17f86902e32b4d73235867c..34d7c7aeb198d515a6d1c63b26873b7ef3365428 100644 (file)
@@ -1,11 +1,25 @@
 Sigma-delta Modulation
 ======================
 
-Overview
---------
+Introduction
+------------
 
-ESP32 has a second-order sigma-delta modulation module.
-This driver configures the channels of the sigma-delta module.
+ESP32 has a second-order sigma-delta modulation module. This driver configures the channels of the sigma-delta module.
+
+Functionality Overview 
+----------------------
+
+There are eight independent sigma-delta modulation channels identified with :cpp:type:`sigmadelta_channel_t`. Each channel is capable to output the binary, hardware generated signal with the sigma-delta modulation.
+
+Selected channel should be set up by providing configuration parameters in :cpp:type:`sigmadelta_config_t` and then applying this configuration with :cpp:func:`sigmadelta_config`.
+
+Another option is to call individual functions, that will configure all required parameters one by one:
+
+* **Prescaler** of the sigma-delta generator - :cpp:func:`sigmadelta_set_prescale`
+* **Duty** of the output signal - :cpp:func:`sigmadelta_set_duty`
+* **GPIO pin** to output modulated signal - :cpp:func:`sigmadelta_set_pin`
+
+The range of the 'duty' input parameter of :cpp:func:`sigmadelta_set_duty` is from -128 to 127 (eight bit signed integer). If zero value is set, then the output signal's duty will be about 50%, see description of :cpp:func:`sigmadelta_set_duty`.
 
 Application Example
 -------------------
@@ -16,4 +30,3 @@ API Reference
 -------------
 
 .. include:: /_build/inc/sigmadelta.inc
-
index 478f602faef7420b2bf7c9451035a44ff4b23b2b..b93a3272b5b190b92cfca9f451635f7aa1e5b421 100644 (file)
@@ -38,7 +38,9 @@ Assertions
 
 The heap implementation (``multi_heap.c``, etc.) includes a lot of assertions which will fail if the heap memory is corrupted. To detect heap corruption most effectively, ensure that assertions are enabled in ``make menuconfig`` under ``Compiler options``.
 
-It's also possible to manually check heap integrity by calling the ``heap_caps_check_integrity()`` function (see below). This function checks all of requested heap memory for integrity, and can be used even if assertions are disabled.
+If a heap integrity assertion fails, a line will be printed like ``CORRUPT HEAP: multi_heap.c:225 detected at 0x3ffbb71c``. The memory address which is printed is the address of the heap structure which has corrupt content.
+
+It's also possible to manually check heap integrity by calling the ``heap_caps_check_integrity()`` function (see below). This function checks all of requested heap memory for integrity, and can be used even if assertions are disabled. If the integrity check prints an error, it will contain the address(es) of corrupt heap structures.
 
 Configuration
 ^^^^^^^^^^^^^
@@ -81,7 +83,7 @@ Finding Heap Corruption
 
 Memory corruption can be one of the hardest classes of bugs to find and fix, as one area of memory can be corrupted from a totally different place. Some tips:
 
-- If you can find the address (in memory) which is being corrupted, you can set a watchpoint on this address via JTAG to have the CPU halt when it is written to.
+- Once you know the address (in memory) which is being corrupted, you can set a watchpoint on this address via JTAG to have the CPU halt when it is written to.
 - If you don't have JTAG, but you do know roughly when the corruption happens, then you can set a watchpoint in software. A fatal exception will occur when the watchpoint triggers. For example ``esp_set_watchpoint(0, (void *)addr, 4, ESP_WATCHPOINT_STORE``. Note that the watchpoint is set on the current running CPU only, so if you don't know which CPU is corrupting memory then you will need to call this function on both CPUs.
 - For buffer overflows, `heap tracing`_ in ``HEAP_TRACE_ALL`` mode lets you see which callers allocate the memory address(es) immediately before the address which is being corrupted. There is a strong chance this is the code which overflows the buffer.
 
index e15ff53dd7132db38ecd28d63ec8500259c89af0..98506cf1445fcf176ed4ef41488679bf10c9c8e2 100644 (file)
@@ -28,23 +28,40 @@ Task watchdog
 ^^^^^^^^^^^^^
 
 Any tasks can elect to be watched by the task watchdog. If such a task does not feed the watchdog within the time
-specified by the task watchdog timeout (which is configurable using ``make menuconfig``), the watchdog will
-print out a warning with information about which processes are running on the ESP32 CPUs and which processes
-failed to feed the watchdog.
+specified by the task watchdog timeout, the watchdog will print out a warning with information about which processes 
+are running on the ESP32 CPUs and which processes failed to feed the watchdog.
 
-By default, the task watchdog watches the idle tasks. The usual cause of idle tasks not feeding the watchdog 
+Ideally, the task watchdog should watch the idle tasks. The usual cause of the idle tasks not feeding the watchdog 
 is a higher-priority process looping without yielding to the lower-priority processes, and can be an indicator
 of badly-written code that spinloops on a peripheral or a task that is stuck in an infinite loop.
 
-Other task can elect to be watched by the task watchdog by calling ``esp_task_wdt_feed()``. Calling this routine
-for the first time will register the task to the task watchdog; calling it subsequent times will feed
-the watchdog. If a task does not want to be watched anymore (e.g. because it is finished and will call 
-``vTaskDelete()`` on itself), it needs to call ``esp_task_wdt_delete()``.
-
-The task watchdog is built around the hardware watchdog in timer group 0. If this watchdog for some reason
+Other task can elect to be watched by the task watchdog. If the watchdog watches multiple tasks, all the tasks
+must feed before the watchdog times out. If one or more tasks fails to feed, the watchdog timeout will trigger.
+The watchdog timer can be initialized using :cpp:func:`esp_task_wdt_init` which will configure the hardware timer and
+establish the hardware timer interrupts. In order for a task to be watched by the task watchdog, a task must subscribe to
+the task watchdog using :cpp:func:`esp_task_wdt_add`. Once added, each subscribed task must call :cpp:func:`esp_task_wdt_feed` 
+periodically to prevent the watchdog from timing out. If all tasks being watched have fed, the watchdog timer counter immediately resets
+and starts recounting to the timeout value from zero. To unsubscribe a task from the task watchdog, call :cpp:func:`esp_task_wdt_delete()`.
+Once all tasks have been unsubscribed form the task watchdog, :cpp:func:`esp_task_wdt_deinit()` can be called to deinitialize
+the task watchdog. Deinitialization will stop the hardware timer, deregister the hardware timer interrupts, and remove the idle hooks
+if idle tasks were being watched.
+
+By setting the task watchdog options in ``make menuconfig``, the task watchdog can be automatically initialized
+during startup by enabling the :ref:`CONFIG_TASK_WDT`. Moreover the two Idle tasks can also be subscibed to the 
+task watchdog on startup as well by enabling :ref:`CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0` and 
+:ref:`CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1`.
+
+The task watchdog is built around the hardware watchdog in timer group 0. If the watchdog for some reason
 cannot execute the interrupt handler that prints the task data (e.g. because IRAM is overwritten by garbage
 or interrupts are disabled entirely) it will hard-reset the SOC.
 
+Note: ``make menuconfig`` provides a :ref:`CONFIG_TASK_WDT_LEGACY_BEHAVIOR` option which will change the behavior of the 
+task watchdog to make it compatible with the legacy code. Note that the legacy behavior causes tasks to subscribe to the
+task watchdog on their first call to :cpp:func:`esp_task_wdt_feed`. Moreover, legacy behavior only allows
+tasks to unsubscribe on their own behalf when calling :cpp:func:`esp_task_wdt_delete()`. It is strongly recommended that 
+non-legacy behavior is used as additions/deletions/feeds are explicit.
+
+
 JTAG and watchdogs
 ^^^^^^^^^^^^^^^^^^
 
@@ -53,20 +70,27 @@ CPU. This makes it very hard to debug code; that is why the OpenOCD config will
 This does mean that you will not get any warnings or panics from either the task or interrupt watchdog when the ESP32
 is connected to OpenOCD via JTAG.
 
-API Reference
--------------
+Interrupt Watchdog API Reference
+--------------------------------
 
-Header Files
-^^^^^^^^^^^^
+Header File
+^^^^^^^^^^^
 
   * :component_file:`esp32/include/esp_int_wdt.h`
-  * :component_file:`esp32/include/esp_task_wdt.h`
 
 
 Functions
 ---------
-
 .. doxygenfunction:: esp_int_wdt_init
-.. doxygenfunction:: esp_task_wdt_init
-.. doxygenfunction:: esp_task_wdt_feed
-.. doxygenfunction:: esp_task_wdt_delete
+
+Task Watchdog API Reference
+----------------------------
+
+NOTE: Ensure that the :ref:`CONFIG_TASK_WDT_LEGACY_BEHAVIOR` configuraiton is disabled using ``make menuconfig``
+to prevent legacy behavior of the task watchdog.
+
+A full example using the Task Watchdog is available in esp-idf: :example:`system/task_watchdog`
+
+
+.. include:: /_build/inc/esp_task_wdt.inc
index f8bec31e661bdc682ee80660ba7beecf09313968..94ee30d34290ddd57578052e45de81c2ceb7c72a 100644 (file)
@@ -58,7 +58,7 @@ Project Properties
 
      * In the list of providers, click "CDT GCC Built-in Compiler Settings Cygwin". Under "Command to get compiler specs", replace the text ``${COMMAND}`` at the beginning of the line with ``xtensa-esp32-elf-gcc``. This means the full "Command to get compiler specs" should be ``xtensa-esp32-elf-gcc ${FLAGS} -E -P -v -dD "${INPUTS}"``.
 
-     * In the list of providers, click "CDT GCC Build Output Parser" and type ``xtensa-esp32-elf-`` at the beginning of the Compiler command pattern. This means the full Compiler command pattern should be ``xtensa-esp32-elf-(g?cc)|([gc]\+\+)|(clang)``
+     * In the list of providers, click "CDT GCC Build Output Parser" and type ``xtensa-esp32-elf-`` at the beginning of the Compiler command pattern, and wrap remaining part with brackets. This means the full Compiler command pattern should be ``xtensa-esp32-elf-((g?cc)|([gc]\+\+)|(clang))``
 
 
 Building in Eclipse
index 31cda9cf3df9010f9d7252f50eb39cc247ae32bf..a19b805e3d3c2aa5a52cfb5b95af9f3e84f8cb19 100644 (file)
@@ -284,6 +284,34 @@ Several lines below, after start up and diagnostic log, you should see "Hello wo
 \r
 To exit monitor use shortcut ``Ctrl+]``. To execute ``make flash`` and ``make monitor`` in one shoot type ``make flash monitor``. Check section :doc:`IDF Monitor <idf-monitor>` for handy shortcuts and more details on using this application.\r
 \r
+That's all what you need to get started with ESP32! \r
+\r
+Now you are ready to try some other :idf:`examples`, or go right to developing your own applications.\r
+\r
+\r
+Updating ESP-IDF\r
+================\r
+\r
+After some time of using ESP-IDF, you may want to update it to take advantage of new features or bug fixes. The simplest way to do so is by deleting existing ``esp-idf`` folder and cloning it again, exactly as when doing initial installation described in sections :ref:`get-started-get-esp-idf`.\r
+\r
+Another solution is to update only what has changed. This method is useful if you have slow connection to the GiHub. To do the update run the following commands::\r
+\r
+    cd ~/esp/esp-idf\r
+    git pull\r
+    git submodule update --init --recursive\r
+\r
+The ``git pull`` command is fetching and merging changes from ESP-IDF repository on GitHub. Then ``git submodule update --init --recursive`` is updating existing submodules or getting a fresh copy of new ones. On GitHub the submodules are represented as links to other repositories and require this additional command to get them onto your PC.\r
+\r
+If you would like to use specific release of ESP-IDF, e.g. `v2.1`, run::\r
+\r
+    cd ~/esp\r
+    git clone https://github.com/espressif/esp-idf.git esp-idf-v2.1\r
+    cd esp-idf-v2.1/\r
+    git checkout v2.1\r
+    git submodule update --init --recursive\r
+\r
+After that remember to :doc:`add-idf_path-to-profile`, so the toolchain scripts know where to find the ESP-IDF in it's release specific location.\r
+\r
 \r
 Related Documents\r
 =================\r
index b750a6c559aca2666ba0008320a7a8a9638a4454..9b5d4603fffb04459cf9545c213e5838f6f174a9 100644 (file)
@@ -28,12 +28,18 @@ This process involves installing MSYS2_, then installing the MSYS2_ and Python p
 
 * The ``windows_install_prerequisites.sh`` script will download and install packages for ESP-IDF support, and the ESP32 toolchain.
 
-* During the initial update step, MSYS may update itself into a state where it can no longer operate. You may see errors like the following::
+
+Troubleshooting
+~~~~~~~~~~~~~~~
+
+* While the install script runs, MSYS may update itself into a state where it can no longer operate. You may see errors like the following::
 
      *** fatal error - cygheap base mismatch detected - 0x612E5408/0x612E4408. This problem is probably due to using incompatible versions of the cygwin DLL.
 
   If you see errors like this, close the terminal window entirely (terminating the processes running there) and then re-open a new terminal. Re-run ``windows_install_prerequisites.sh`` (tip: use the up arrow key to see the last run command). The update process will resume after this step.
 
+* MSYS2 is a "rolling" distribution so running the installer script may install newer packages than what is used in the prebuilt environments. If you see any errors that appear to be related to installing MSYS2 packages, please check the `MSYS2-packages issues list`_ for known issues. If you don't see any relevant issues, please `raise an IDF issue`_.
+
 
 MSYS2 Mirrors in China
 ~~~~~~~~~~~~~~~~~~~~~~
@@ -88,5 +94,22 @@ Next Steps
 
 To carry on with development environment setup, proceed to section :ref:`get-started-get-esp-idf`.
 
+.. _updating-existing-windows-environment:
+
+Updating The Environment
+========================
+
+When IDF is updated, sometimes new toolchains are required or new system requirements are added to the Windows MSYS2 environment.
+
+Rather than setting up a new environment, you can update an existing Windows environment & toolchain:
+
+- Update IDF to the new version you want to use.
+- Run the ``tools/windows/windows_install_prerequisites.sh`` script inside IDF. This will install any new software packages that weren't previously installed, and download and replace the toolchain with the latest version.
+
+The script to update MSYS2 may also fail with the same errors mentioned under Troubleshooting_.
+
+If you need to support multiple IDF versions concurrently, you can have different independent MSYS2 environments in different directories. Alternatively you can download multiple toolchains and unzip these to different directories, then use the PATH environment variable to set which one is the default.
 
 .. _MSYS2: https://msys2.github.io/
+.. _MSYS2-packages issues list: https://github.com/Alexpux/MSYS2-packages/issues/
+.. _raise an IDF issue: https://github.com/espressif/esp-idf/issues/new
index b5523e21248b86f0d1e43efc6567af7fd6e53ee5..51e42b4db4b3715ce3bde49240d2d3ddb19a28ef 100644 (file)
@@ -11,7 +11,7 @@ Windows doesn't have a built-in "make" environment, so as well as installing the
 Toolchain Setup 
 ===============
 
-The quick setup is to download the Windows all-in-one toolchain & MSYS zip file from dl.espressif.com:
+The quick setup is to download the Windows all-in-one toolchain & MSYS2 zip file from dl.espressif.com:
 
 https://dl.espressif.com/dl/esp32_win32_msys2_environment_and_toolchain-20170918.zip
 
@@ -38,6 +38,20 @@ Next Steps
 
 To carry on with development environment setup, proceed to section :ref:`get-started-get-esp-idf`.
 
+Updating The Environment
+========================
+
+When IDF is updated, sometimes new toolchains are required or new requirements are added to the Windows MSYS2 environment. To move any data from an old version of the precompiled environment to a new one:
+
+- Take the old MSYS2 environment (ie ``C:\msys32``) and move/rename it to a different directory (ie ``C:\msys32_old``).
+- Download the new precompiled environment using the steps above.
+- Unzip the new MSYS2 environment to ``C:\msys32`` (or another location).
+- Find the old ``C:\msys32_old\home`` directory and move this into ``C:\msys32``.
+- You can now delete the ``C:\msys32_old`` directory if you no longer need it.
+
+You can have independent different MSYS2 environments on your system, as long as they are in different directories.
+
+There are :ref:`also steps to update the existing environment without downloading a new one <updating-existing-windows-environment>`, although this is more complex.
 
 Related Documents
 =================
index 3e97b4f3ec642e3ba9563ea971d0e993a504db14..e8d579c8b0a5ab2b9fde8175c2d05be86cef9043 100644 (file)
@@ -58,7 +58,7 @@ void app_main()
         return;
     }
 
-    if (esp_bt_controller_enable(ESP_BT_MODE_BTDM) != ESP_OK) {
+    if (esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT) != ESP_OK) {
         ESP_LOGE(BT_AV_TAG, "%s enable controller failed\n", __func__);
         return;
     }
index 6d0ef24595230c58d6775cc87eeb698ec109284e..f6c01a2dbac423088a8a1a0834f6d5d3979c0fe1 100644 (file)
@@ -227,16 +227,35 @@ void app_main()
     ESP_ERROR_CHECK( ret );
     esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
     
+    ret = esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
+    if (ret) {
+        ESP_LOGI(tag, "Bluetooth controller release classic bt memory failed");
+        return;
+    }
+
     if (esp_bt_controller_init(&bt_cfg) != ESP_OK) {
         ESP_LOGI(tag, "Bluetooth controller initialize failed");
         return;
     }
 
-    if (esp_bt_controller_enable(ESP_BT_MODE_BTDM) != ESP_OK) {
+    if (esp_bt_controller_enable(ESP_BT_MODE_BLE) != ESP_OK) {
         ESP_LOGI(tag, "Bluetooth controller enable failed");
         return;
     }
 
+    /*
+     * If call mem release here, also work. Input ESP_BT_MODE_CLASSIC_BT, the function will
+     * release the memory of classic bt mdoe.
+     * esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
+     *
+     */
+
+    /*
+     * If call mem release here, also work. Input ESP_BT_MODE_BTDM, the function will calculate
+     * that the BLE mode is already used, so it will release of only classic bt mode.
+     * esp_bt_controller_mem_release(ESP_BT_MODE_BTDM);
+     */
+
     xTaskCreatePinnedToCore(&bleAdvtTask, "bleAdvtTask", 2048, NULL, 5, NULL, 0);
 }
 
index e8cac2c87b8f5c4891e374c11fbf90b9525e75aa..669e65a0e5d25135ad56132f388d25697f8c5cdf 100644 (file)
@@ -1,7 +1,7 @@
 # Example: sigma_delta modulation
 
-This example uses the sigma_delta output modulation driver to generate modulated output on a GPIO.
+This example uses the sigma-delta driver to generate modulated output on a GPIO.
 
 By default the GPIO output is 4, however you can edit this in the `sigmadelta_init()` function inside `main/sigmadelta_test.c`.
 
-If you connect an LED to the output GPIO, you will see it blinking slowly.
+If you connect a LED to the output GPIO, you will see it blinking slowly.
index 5249d2dea76270470384e72c153ba68957d2ad32..594dbdb0c40688d67883326c98a2d9466790cf30 100644 (file)
 #include "freertos/task.h"
 #include "esp_system.h"
 #include "driver/sigmadelta.h"
-
 /*
- * This test code intended to configure sigma-delta and set GPIO4 as signal output pin.
- * If you connect this GPIO4 with an LED, you will see the LED blinking slowly.
+ * This test code will configure sigma-delta and set GPIO4 as a signal output pin.
+ * If you connect this GPIO4 with a LED, you will see the LED blinking slowly.
  */
 
-/**
- * @brief Sigma-delta initialization.
+/*
+ * Configure and initialize the sigma delta modulation
+ * on channel 0 to output signal on GPIO4
  */
 static void sigmadelta_example_init(void)
 {
-   sigmadelta_config_t sigmadelta_cfg = {
-       /* Sigma-delta channel0*/
-       .channel = SIGMADELTA_CHANNEL_0,
-       /* Sigma-delta set duty 10*/
-       .sigmadelta_duty = 10,
-       /* Set prescale 30 */
-       .sigmadelta_prescale = 80,
-       /*select GPIO4 as output_io */
-       .sigmadelta_gpio = 4,
-     };
-   sigmadelta_config(&sigmadelta_cfg);
+    sigmadelta_config_t sigmadelta_cfg = {
+        .channel = SIGMADELTA_CHANNEL_0,
+        .sigmadelta_prescale = 80,
+        .sigmadelta_duty = 0,
+        .sigmadelta_gpio = 4,
+    };
+    sigmadelta_config(&sigmadelta_cfg);
 }
 
-/**
- * @brief Sigma-delta test to change duty of output signal.
+/*
+ *  Perform the sigma-delta modulation test
+ *  by changing the duty of the output signal.
  */
 void app_main()
 {
     sigmadelta_example_init();
+
     int8_t duty = 0;
     int inc = 1;
-    while(1) {
+    while (1) {
         sigmadelta_set_duty(SIGMADELTA_CHANNEL_0, duty);
-        /*by changing delay time, you can change the blink frequency of LED. */
+        /* By changing delay time, you can change the blink frequency of LED */
         vTaskDelay(10 / portTICK_PERIOD_MS);
 
         duty += inc;
-        if(duty == 127 || duty == -127) inc = (-1) * inc;
+        if (duty == 127 || duty == -127) {
+            inc = (-1) * inc;
+        }
     }
 }
index 7b6ef8d40349a209ca757070c79e99d9afbc21a4..fbdecb0d277f378986cd0cf531de422935dfb906 100644 (file)
@@ -33,7 +33,7 @@
 
 #define OPENSSL_EXAMPLE_TASK_NAME        "openssl_example"
 #define OPENSSL_EXAMPLE_TASK_STACK_WORDS 10240
-#define OPENSSL_EXAMPLE_TASK_PRORIOTY    8
+#define OPENSSL_EXAMPLE_TASK_PRIORITY    8
 
 #define OPENSSL_EXAMPLE_RECV_BUF_LEN       1024
 
index cf9d14d41c8f2cbb5df7f66260951840ef8d9210..691dbde37cbfae946862d956661df2da32de18a7 100644 (file)
@@ -169,7 +169,7 @@ static void openssl_example_client_init(void)
                       OPENSSL_EXAMPLE_TASK_NAME,
                       OPENSSL_EXAMPLE_TASK_STACK_WORDS,
                       NULL,
-                      OPENSSL_EXAMPLE_TASK_PRORIOTY,
+                      OPENSSL_EXAMPLE_TASK_PRIORITY,
                       &openssl_handle);
 
     if (ret != pdPASS)  {
index bdb84bae5cdaee155b786ea59cca033e956bb272..8097b2b7417b9bace5ed85c81e5c71dfdf0a95d5 100755 (executable)
@@ -23,7 +23,7 @@
 
 #define OPENSSL_EXAMPLE_TASK_NAME        "openssl_example"
 #define OPENSSL_EXAMPLE_TASK_STACK_WORDS 10240
-#define OPENSSL_EXAMPLE_TASK_PRORIOTY    8
+#define OPENSSL_EXAMPLE_TASK_PRIORITY    8
 
 #define OPENSSL_EXAMPLE_RECV_BUF_LEN       1024
 
index 00d5c5ba7549e1bb5f2e4760334501f3a9fc0fcd..a765c35522729b9c8b4e00a8da679a78f68781df 100755 (executable)
@@ -203,7 +203,7 @@ static void openssl_server_init(void)
                       OPENSSL_EXAMPLE_TASK_NAME,
                       OPENSSL_EXAMPLE_TASK_STACK_WORDS,
                       NULL,
-                      OPENSSL_EXAMPLE_TASK_PRORIOTY,
+                      OPENSSL_EXAMPLE_TASK_PRIORITY,
                       &openssl_handle); 
 
     if (ret != pdPASS)  {
diff --git a/examples/system/task_watchdog/Makefile b/examples/system/task_watchdog/Makefile
new file mode 100644 (file)
index 0000000..fb4f26b
--- /dev/null
@@ -0,0 +1,9 @@
+#
+# This is a project Makefile. It is assumed the directory this Makefile resides in is a
+# project subdirectory.
+#
+
+PROJECT_NAME := task_watchdog
+
+include $(IDF_PATH)/make/project.mk
+
diff --git a/examples/system/task_watchdog/README.md b/examples/system/task_watchdog/README.md
new file mode 100644 (file)
index 0000000..92a3132
--- /dev/null
@@ -0,0 +1,12 @@
+# Example: task_watchdog
+
+This test code shows how to initialize the task watchdog, add tasks to the
+watchdog task list, feeding the tasks, deleting tasks from the watchdog task
+list, and deinitializing the task watchdog.
+
+
+### Test:
+
+Program should run without error. Comment out "esp_task_wdt_feed()" to observe
+a watchdog timeout.
+
diff --git a/examples/system/task_watchdog/main/component.mk b/examples/system/task_watchdog/main/component.mk
new file mode 100644 (file)
index 0000000..44bd2b5
--- /dev/null
@@ -0,0 +1,3 @@
+#
+# Main Makefile. This is basically the same as a component makefile.
+#
diff --git a/examples/system/task_watchdog/main/task_watchdog_example_main.c b/examples/system/task_watchdog/main/task_watchdog_example_main.c
new file mode 100644 (file)
index 0000000..072ac73
--- /dev/null
@@ -0,0 +1,98 @@
+/* Task_Watchdog Example
+
+   This example code is in the Public Domain (or CC0 licensed, at your option.)
+
+   Unless required by applicable law or agreed to in writing, this
+   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+   CONDITIONS OF ANY KIND, either express or implied.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/queue.h"
+#include "esp_task_wdt.h"
+
+#ifndef CONFIG_TASK_WDT_LEGACY_BEHAVIOR
+
+#define TIMEOUT 4000
+#define TASK_PERIOD 2000
+
+#define ASSERT_PRINT_ERROR(cond, str)    ({                                 \
+            if(!(cond)){                                                    \
+                printf("%s\n",str);                                         \
+            }                                                               \
+})
+
+void feeding_task(void *arg){
+    int core = xPortGetCoreID();
+    //Simple task that periodically feeds watchdog
+    while(1){
+        if(esp_task_wdt_feed() == ESP_OK){
+            printf("Feed Task %d\n", core);
+        }
+        vTaskDelay(pdMS_TO_TICKS(TASK_PERIOD));
+    }
+}
+
+void app_main(void)
+{
+    //Initialize task watchdog
+    if(esp_task_wdt_init(TIMEOUT, false) != ESP_OK){
+        printf("Error\n");
+    }
+
+    //Create tasks
+    TaskHandle_t handle_0;
+    TaskHandle_t handle_1;
+    xTaskCreatePinnedToCore(feeding_task, "Feed Task 0", 2000, NULL, 15, &handle_0, 0);
+    xTaskCreatePinnedToCore(feeding_task, "Feed Task 1", 2000, NULL, 15, &handle_1, 1);
+
+    //Add tasks to wdt
+    ASSERT_PRINT_ERROR(esp_task_wdt_add(handle_0) == ESP_OK, "Add failed");
+    ASSERT_PRINT_ERROR(esp_task_wdt_add(handle_1) == ESP_OK, "Add failed");
+
+#ifndef CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0
+    //Adding Idles. Idle hook will call feed automatically
+    TaskHandle_t idle_0 = xTaskGetIdleTaskHandleForCPU(0);
+    ASSERT_PRINT_ERROR(idle_0 != NULL, "Get Idle handle failed");
+    ASSERT_PRINT_ERROR(esp_task_wdt_add(idle_0) == ESP_OK, "Add failed");
+#endif
+
+#ifndef CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1
+    TaskHandle_t idle_1 = xTaskGetIdleTaskHandleForCPU(1);
+    ASSERT_PRINT_ERROR(idle_1 != NULL, "Get Idle handle failed");
+    ASSERT_PRINT_ERROR(esp_task_wdt_add(idle_1) == ESP_OK, "Add failed");
+#endif
+
+    //Wait 10 seconds
+    vTaskDelay(pdMS_TO_TICKS(10000));
+
+    //Remove tasks form wdt
+    ASSERT_PRINT_ERROR(esp_task_wdt_delete(handle_0) == ESP_OK, "Failed to delete");
+    ASSERT_PRINT_ERROR(esp_task_wdt_delete(handle_1) == ESP_OK, "Failed to delete");
+    //Delete tasks
+    vTaskDelete(handle_0);
+    vTaskDelete(handle_1);
+
+#ifndef CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0
+    ASSERT_PRINT_ERROR(esp_task_wdt_delete(idle_0) == ESP_OK, "Failed to delete");
+#endif
+
+#ifndef CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1
+    ASSERT_PRINT_ERROR(esp_task_wdt_delete(idle_1) == ESP_OK, "Failed to delete");
+#endif
+
+
+#ifndef CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0
+#ifndef CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1
+    //Deinit task watchdog. Can only do so once all tasks (including idle) are deleted from wdt
+    ASSERT_PRINT_ERROR(esp_task_wdt_deinit() == ESP_OK, "deinit failed");
+#endif
+#endif
+
+    printf("Complete\n");
+
+}
+
+#endif
index 21578e9d4e842b3e910cb5a87ecec015f0f2c364..ba9f2e0fa2bfbcdfad555d72cb36988b0eabda7c 100644 (file)
@@ -20,6 +20,12 @@ shows how to use wps(Wifi Protected Setup).
 \r
 See the [README.md](./wps/README.md) file in the project [wps](./wps/).\r
 \r
+## espnow\r
+\r
+shows how to use espnow.\r
+\r
+See the [README.md](./espnow/README.md) file in the project [espnow](./espnow/).\r
+\r
 # More\r
 \r
 See the [README.md](../README.md) file in the upper level [examples](../) directory for more information about examples.\r
diff --git a/examples/wifi/espnow/Makefile b/examples/wifi/espnow/Makefile
new file mode 100644 (file)
index 0000000..a98a511
--- /dev/null
@@ -0,0 +1,9 @@
+#
+# This is a project Makefile. It is assumed the directory this Makefile resides in is a
+# project subdirectory.
+#
+
+PROJECT_NAME := espnow_example
+
+include $(IDF_PATH)/make/project.mk
+
diff --git a/examples/wifi/espnow/README.md b/examples/wifi/espnow/README.md
new file mode 100644 (file)
index 0000000..595d51d
--- /dev/null
@@ -0,0 +1,30 @@
+# ESPNOW Example
+
+This example shows how to use ESPNOW of wifi. Example does the following steps:
+
+1. Start WiFi.
+
+2. Initialize ESPNOW.
+
+3. Register ESPNOW sending or receiving callback function.
+
+4. Add ESPNOW peer information.
+
+5. Send and receive ESPNOW data. 
+
+In order to get the MAC address of the other device, firstly send broadcast ESPNOW data to each other with 'state' set as 0. When receiving 
+broadcast ESPNOW data with 'state' as 0, add the device from which the data comes to the peer list. Then start sending broadcast ESPNOW 
+data with 'state' set as 1. When receiving broadcast ESPNOW data with 'state' as 1, compare the local magic number with that in the data. 
+If the local one is bigger than that one, stop sending broadcast ESPNOW data and start sending unicast ESPNOW data. If receive unicast 
+ESPNOW data, also stop sending broadcast ESPNOW data. That is what happens in this example. It shows how to send/receive broadcast/unicast 
+ESPNOW data. In practice, if the MAC address of the other device is known, it's not required to send/receive broadcast ESPNOW data first, 
+just add the device to the peer list and send/receive unicast ESPNOW data.
+
+There are a lot of "extras" on top of ESPNOW data, such as type, state, sequence number, CRC and magic in this example. These "extras" are 
+not required to use ESPNOW. They are only used to make this example to run correctly. However, it is recommended that users add some "extras" 
+to make ESPNOW data more safe and more reliable.
+
+*Note:* The two devices can be set as either station or softap or station+softap mode. If the receiving device is in station mode only
+and it connects to an AP, modem sleep should be disabled.
+
+More info in the code [espnow_example_main.c](./main/espnow_example_main.c).
diff --git a/examples/wifi/espnow/main/Kconfig.projbuild b/examples/wifi/espnow/main/Kconfig.projbuild
new file mode 100644 (file)
index 0000000..be6b6ff
--- /dev/null
@@ -0,0 +1,55 @@
+menu "Example Configuration"
+
+choice WIFI_MODE
+    prompt "WiFi mode"
+    default STATION_MODE
+    help
+        WiFi mode(station or softap).
+    
+config STATION_MODE
+    bool "Station"
+config SOFTAP_MODE
+    bool "Softap"
+endchoice
+      
+config ESPNOW_PMK
+    string "ESPNOW primary master key"
+    default "pmk1234567890123"
+    help
+        ESPNOW primary master for the example to use. The length of ESPNOW primary master must be 16 bytes.
+
+config ESPNOW_LMK
+    string "ESPNOW local master key"
+    default "lmk1234567890123"
+    help
+        ESPNOW local master for the example to use. The length of ESPNOW local master must be 16 bytes.
+        
+config ESPNOW_CHANNEL
+    int "Channel"
+    default 1
+    range 1 13
+    help
+        The channel on which sending and receiving ESPNOW data.
+
+config ESPNOW_SEND_COUNT
+    int "Send count"
+    default 100
+    range 1 65535
+    help
+        Total count of unicast ESPNOW data to be sent.
+        
+config ESPNOW_SEND_DELAY
+    int "Send delay"
+    default 1000
+    range 0 65535
+    help
+        Delay between sending two ESPNOW data, unit: ms.
+        
+config ESPNOW_SEND_LEN
+    int "Send len"
+    range 10 250
+    default 200
+    help
+        Length of ESPNOW data to be sent, unit: byte.
+
+endmenu
diff --git a/examples/wifi/espnow/main/component.mk b/examples/wifi/espnow/main/component.mk
new file mode 100644 (file)
index 0000000..7d7b29b
--- /dev/null
@@ -0,0 +1,7 @@
+#
+# "main" pseudo-component makefile.
+#
+# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
+
+
+
diff --git a/examples/wifi/espnow/main/espnow_example.h b/examples/wifi/espnow/main/espnow_example.h
new file mode 100644 (file)
index 0000000..b53e403
--- /dev/null
@@ -0,0 +1,82 @@
+/* ESPNOW Example
+
+   This example code is in the Public Domain (or CC0 licensed, at your option.)
+
+   Unless required by applicable law or agreed to in writing, this
+   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+   CONDITIONS OF ANY KIND, either express or implied.
+*/
+
+#ifndef ESPNOW_EXAMPLE_H
+#define ESPNOW_EXAMPLE_H
+
+/* ESPNOW can work in both station and softap mode. It is configured in menuconfig. */
+#if CONFIG_STATION_MODE
+#define ESPNOW_WIFI_MODE WIFI_MODE_STA
+#define ESPNOW_WIFI_IF   ESP_IF_WIFI_STA
+#else
+#define ESPNOW_WIFI_MODE WIFI_MODE_AP
+#define ESPNOW_WIFI_IF   ESP_IF_WIFI_AP
+#endif
+
+#define ESPNOW_QUEUE_SIZE           6
+
+#define IS_BROADCAST_ADDR(addr) (memcmp(addr, example_broadcast_mac, ESP_NOW_ETH_ALEN) == 0)
+
+typedef enum {
+    EXAMPLE_ESPNOW_SEND_CB,
+    EXAMPLE_ESPNOW_RECV_CB,
+} example_espnow_event_id_t;
+
+typedef struct {
+    uint8_t mac_addr[ESP_NOW_ETH_ALEN];
+    esp_now_send_status_t status;
+} example_espnow_event_send_cb_t;
+
+typedef struct {
+    uint8_t mac_addr[ESP_NOW_ETH_ALEN];
+    uint8_t *data;
+    int data_len;
+} example_espnow_event_recv_cb_t;
+
+typedef union {
+    example_espnow_event_send_cb_t send_cb;
+    example_espnow_event_recv_cb_t recv_cb;
+} example_espnow_event_info_t;
+
+/* When ESPNOW sending or receiving callback function is called, post event to ESPNOW task. */
+typedef struct {
+    example_espnow_event_id_t id;
+    example_espnow_event_info_t info;
+} example_espnow_event_t;
+
+enum {
+    EXAMPLE_ESPNOW_DATA_BROADCAST,
+    EXAMPLE_ESPNOW_DATA_UNICAST,
+    EXAMPLE_ESPNOW_DATA_MAX,
+};
+
+/* User defined field of ESPNOW data in this example. */
+typedef struct {
+    uint8_t type;                         //Broadcast or unicast ESPNOW data.
+    uint8_t state;                        //Indicate that if has received broadcast ESPNOW data or not.
+    uint16_t seq_num;                     //Sequence number of ESPNOW data.
+    uint16_t crc;                         //CRC16 value of ESPNOW data.
+    uint32_t magic;                       //Magic number which is used to determine which device to send unicast ESPNOW data.
+    uint8_t payload[0];                   //Real payload of ESPNOW data.
+} __attribute__((packed)) example_espnow_data_t;
+
+/* Parameters of sending ESPNOW data. */
+typedef struct {
+    bool unicast;                         //Send unicast ESPNOW data.
+    bool broadcast;                       //Send broadcast ESPNOW data.
+    uint8_t state;                        //Indicate that if has received broadcast ESPNOW data or not.
+    uint32_t magic;                       //Magic number which is used to determine which device to send unicast ESPNOW data.
+    uint16_t count;                       //Total count of unicast ESPNOW data to be sent.
+    uint16_t delay;                       //Delay between sending two ESPNOW data, unit: ms.
+    int len;                              //Length of ESPNOW data to be sent, unit: byte.
+    uint8_t *buffer;                      //Buffer pointing to ESPNOW data.
+    uint8_t dest_mac[ESP_NOW_ETH_ALEN];   //MAC address of destination device.
+} example_espnow_send_param_t;
+
+#endif
diff --git a/examples/wifi/espnow/main/espnow_example_main.c b/examples/wifi/espnow/main/espnow_example_main.c
new file mode 100644 (file)
index 0000000..68e56ff
--- /dev/null
@@ -0,0 +1,386 @@
+/* ESPNOW Example
+
+   This example code is in the Public Domain (or CC0 licensed, at your option.)
+
+   Unless required by applicable law or agreed to in writing, this
+   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+   CONDITIONS OF ANY KIND, either express or implied.
+*/
+
+/*
+   This example shows how to use ESPNOW.
+   Prepare two device, one for sending ESPNOW data and another for receiving
+   ESPNOW data.
+*/
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/semphr.h"
+#include "freertos/timers.h"
+#include "nvs_flash.h"
+#include "esp_event_loop.h"
+#include "tcpip_adapter.h"
+#include "esp_wifi.h"
+#include "esp_log.h"
+#include "esp_system.h"
+#include "esp_now.h"
+#include "rom/ets_sys.h"
+#include "rom/crc.h"
+#include "espnow_example.h"
+
+static const char *TAG = "espnow_example";
+
+static xQueueHandle example_espnow_queue;
+
+static uint8_t example_broadcast_mac[ESP_NOW_ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+static uint16_t s_example_espnow_seq[EXAMPLE_ESPNOW_DATA_MAX] = { 0, 0 };
+
+static void example_espnow_deinit(example_espnow_send_param_t *send_param);
+
+static esp_err_t example_event_handler(void *ctx, system_event_t *event)
+{
+    switch(event->event_id) {
+    case SYSTEM_EVENT_STA_START:
+        ESP_LOGI(TAG, "WiFi started");
+        break;
+    default:
+        break;
+    }
+    return ESP_OK;
+}
+
+/* WiFi should start before using ESPNOW */
+static void example_wifi_init(void)
+{
+    tcpip_adapter_init();
+    ESP_ERROR_CHECK( esp_event_loop_init(example_event_handler, NULL) );
+    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
+    ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
+    ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
+    ESP_ERROR_CHECK( esp_wifi_set_mode(ESPNOW_WIFI_MODE) );
+    ESP_ERROR_CHECK( esp_wifi_start());
+
+    /* In order to simplify example, channel is set after WiFi started.
+     * This is not necessary in real application if the two devices have
+     * been already on the same channel.
+     */
+    ESP_ERROR_CHECK( esp_wifi_set_channel(CONFIG_ESPNOW_CHANNEL, 0) );
+}
+
+/* ESPNOW sending or receiving callback function is called in WiFi task.
+ * Users should not do lengthy operations from this task. Instead, post
+ * necessary data to a queue and handle it from a lower priority task. */
+static void example_espnow_send_cb(const uint8_t *mac_addr, esp_now_send_status_t status)
+{
+    example_espnow_event_t evt;
+    example_espnow_event_send_cb_t *send_cb = &evt.info.send_cb;
+
+    if (mac_addr == NULL) {
+        ESP_LOGE(TAG, "Send cb arg error");
+        return;
+    }
+
+    evt.id = EXAMPLE_ESPNOW_SEND_CB;
+    memcpy(send_cb->mac_addr, mac_addr, ESP_NOW_ETH_ALEN);
+    send_cb->status = status;
+    if (xQueueSend(example_espnow_queue, &evt, portMAX_DELAY) != pdTRUE) {
+        ESP_LOGW(TAG, "Send send queue fail");
+    }
+}
+
+static void example_espnow_recv_cb(const uint8_t *mac_addr, const uint8_t *data, int len)
+{
+    example_espnow_event_t evt;
+    example_espnow_event_recv_cb_t *recv_cb = &evt.info.recv_cb;
+
+    if (mac_addr == NULL || data == NULL || len <= 0) {
+        ESP_LOGE(TAG, "Receive cb arg error");
+        return;
+    }
+
+    evt.id = EXAMPLE_ESPNOW_RECV_CB;
+    memcpy(recv_cb->mac_addr, mac_addr, ESP_NOW_ETH_ALEN);
+    recv_cb->data = malloc(len);
+    if (recv_cb->data == NULL) {
+        ESP_LOGE(TAG, "Malloc receive data fail");
+        return;
+    }
+    memcpy(recv_cb->data, data, len);
+    recv_cb->data_len = len;
+    if (xQueueSend(example_espnow_queue, &evt, portMAX_DELAY) != pdTRUE) {
+        ESP_LOGW(TAG, "Send receive queue fail");
+        free(recv_cb->data);
+    }
+}
+
+/* Parse received ESPNOW data. */
+int example_espnow_data_parse(uint8_t *data, uint16_t data_len, uint8_t *state, uint16_t *seq, int *magic)
+{
+    example_espnow_data_t *buf = (example_espnow_data_t *)data;
+    uint16_t crc, crc_cal = 0;
+
+    if (data_len < sizeof(example_espnow_data_t)) {
+        ESP_LOGE(TAG, "Receive ESPNOW data too short, len:%d", data_len);
+        return -1;
+    }
+
+    *state = buf->state;
+    *seq = buf->seq_num;
+    *magic = buf->magic;
+    crc = buf->crc;
+    buf->crc = 0;
+    crc_cal = crc16_le(UINT16_MAX, (uint8_t const *)buf, data_len);
+
+    if (crc_cal == crc) {
+        return buf->type;
+    }
+
+    return -1;
+}
+
+/* Prepare ESPNOW data to be sent. */
+void example_espnow_data_prepare(example_espnow_send_param_t *send_param)
+{
+    example_espnow_data_t *buf = (example_espnow_data_t *)send_param->buffer;
+    int i = 0;
+
+    assert(send_param->len >= sizeof(example_espnow_data_t));
+
+    buf->type = IS_BROADCAST_ADDR(send_param->dest_mac) ? EXAMPLE_ESPNOW_DATA_BROADCAST : EXAMPLE_ESPNOW_DATA_UNICAST;
+    buf->state = send_param->state;
+    buf->seq_num = s_example_espnow_seq[buf->type]++;
+    buf->crc = 0;
+    buf->magic = send_param->magic;
+    for (i = 0; i < send_param->len - sizeof(example_espnow_data_t); i++) {
+        buf->payload[i] = (uint8_t)esp_random();
+    }
+    buf->crc = crc16_le(UINT16_MAX, (uint8_t const *)buf, send_param->len);
+}
+
+static void example_espnow_task(void *pvParameter)
+{
+    example_espnow_event_t evt;
+    uint8_t recv_state = 0;
+    uint16_t recv_seq = 0;
+    int recv_magic = 0;
+    bool is_broadcast = false;
+    int ret;
+
+    vTaskDelay(5000 / portTICK_RATE_MS);
+    ESP_LOGI(TAG, "Start sending broadcast data");
+
+    /* Start sending broadcast ESPNOW data. */
+    example_espnow_send_param_t *send_param = (example_espnow_send_param_t *)pvParameter;
+    if (esp_now_send(send_param->dest_mac, send_param->buffer, send_param->len) != ESP_OK) {
+        ESP_LOGE(TAG, "Send error");
+        example_espnow_deinit(send_param);
+        vTaskDelete(NULL);
+    }
+
+    while (xQueueReceive(example_espnow_queue, &evt, portMAX_DELAY) == pdTRUE) {
+        switch (evt.id) {
+            case EXAMPLE_ESPNOW_SEND_CB:
+            {
+                example_espnow_event_send_cb_t *send_cb = &evt.info.send_cb;
+                is_broadcast = IS_BROADCAST_ADDR(send_cb->mac_addr);
+
+                ESP_LOGD(TAG, "Send data to "MACSTR", status1: %d", MAC2STR(send_cb->mac_addr), send_cb->status);
+
+                if (is_broadcast && (send_param->broadcast == false)) {
+                    break;
+                }
+
+                if (!is_broadcast) {
+                    send_param->count--;
+                    if (send_param->count == 0) {
+                        ESP_LOGI(TAG, "Send done");
+                        example_espnow_deinit(send_param);
+                        vTaskDelete(NULL);
+                    }
+                }
+
+                /* Delay a while before sending the next data. */
+                if (send_param->delay > 0) {
+                    vTaskDelay(send_param->delay/portTICK_RATE_MS);
+                }
+
+                ESP_LOGI(TAG, "send data to "MACSTR"", MAC2STR(send_cb->mac_addr));
+
+                memcpy(send_param->dest_mac, send_cb->mac_addr, ESP_NOW_ETH_ALEN);
+                example_espnow_data_prepare(send_param);
+
+                /* Send the next data after the previous data is sent. */
+                if (esp_now_send(send_param->dest_mac, send_param->buffer, send_param->len) != ESP_OK) {
+                    ESP_LOGE(TAG, "Send error");
+                    example_espnow_deinit(send_param);
+                    vTaskDelete(NULL);
+                }
+                break;
+            }
+            case EXAMPLE_ESPNOW_RECV_CB:
+            {
+                example_espnow_event_recv_cb_t *recv_cb = &evt.info.recv_cb;
+
+                ret = example_espnow_data_parse(recv_cb->data, recv_cb->data_len, &recv_state, &recv_seq, &recv_magic);
+                free(recv_cb->data);
+                if (ret == EXAMPLE_ESPNOW_DATA_BROADCAST) {
+                    ESP_LOGI(TAG, "Receive %dth broadcast data from: "MACSTR", len: %d", recv_seq, MAC2STR(recv_cb->mac_addr), recv_cb->data_len);
+
+                    /* If MAC address does not exist in peer list, add it to peer list. */
+                    if (esp_now_is_peer_exist(recv_cb->mac_addr) == false) {
+                        esp_now_peer_info_t *peer = malloc(sizeof(esp_now_peer_info_t));
+                        if (peer == NULL) {
+                            ESP_LOGE(TAG, "Malloc peer information fail");
+                            example_espnow_deinit(send_param);
+                            vTaskDelete(NULL);
+                        }
+                        memset(peer, 0, sizeof(esp_now_peer_info_t));
+                        peer->channel = CONFIG_ESPNOW_CHANNEL;
+                        peer->ifidx = ESPNOW_WIFI_IF;
+                        peer->encrypt = true;
+                        memcpy(peer->lmk, CONFIG_ESPNOW_LMK, ESP_NOW_KEY_LEN);
+                        memcpy(peer->peer_addr, recv_cb->mac_addr, ESP_NOW_ETH_ALEN);
+                        ESP_ERROR_CHECK( esp_now_add_peer(peer) );
+                        free(peer);
+                    }
+
+                    /* Indicates that the device has received broadcast ESPNOW data. */
+                    if (send_param->state == 0) {
+                        send_param->state = 1;
+                    }
+
+                    /* If receive broadcast ESPNOW data which indicates that the other device has received
+                     * broadcast ESPNOW data and the local magic number is bigger than that in the received
+                     * broadcast ESPNOW data, stop sending broadcast ESPNOW data and start sending unicast
+                     * ESPNOW data.
+                     */
+                    if (recv_state == 1) {
+                        /* The device which has the bigger magic number sends ESPNOW data, the other one
+                         * receives ESPNOW data.
+                         */
+                        if (send_param->unicast == false && send_param->magic >= recv_magic) {
+                           ESP_LOGI(TAG, "Start sending unicast data");
+                           ESP_LOGI(TAG, "send data to "MACSTR"", MAC2STR(recv_cb->mac_addr));
+
+                           /* Start sending unicast ESPNOW data. */
+                            memcpy(send_param->dest_mac, recv_cb->mac_addr, ESP_NOW_ETH_ALEN);
+                            example_espnow_data_prepare(send_param);
+                            if (esp_now_send(send_param->dest_mac, send_param->buffer, send_param->len) != ESP_OK) {
+                                ESP_LOGE(TAG, "Send error");
+                                example_espnow_deinit(send_param);
+                                vTaskDelete(NULL);
+                            }
+                            else {
+                                send_param->broadcast = false;
+                                send_param->unicast = true;
+                            }
+                        }
+                    }
+                }
+                else if (ret == EXAMPLE_ESPNOW_DATA_UNICAST) {
+                    ESP_LOGI(TAG, "Receive %dth unicast data from: "MACSTR", len: %d", recv_seq, MAC2STR(recv_cb->mac_addr), recv_cb->data_len);
+
+                    /* If receive unicast ESPNOW data, also stop sending broadcast ESPNOW data. */
+                    send_param->broadcast = false;
+                }
+                else {
+                    ESP_LOGI(TAG, "Receive error data from: "MACSTR"", MAC2STR(recv_cb->mac_addr));
+                }
+                break;
+            }
+            default:
+                ESP_LOGE(TAG, "Callback type error: %d", evt.id);
+                break;
+        }
+    }
+}
+
+static esp_err_t example_espnow_init(void)
+{
+    example_espnow_send_param_t *send_param;
+
+    example_espnow_queue = xQueueCreate(ESPNOW_QUEUE_SIZE, sizeof(example_espnow_event_t));
+    if (example_espnow_queue == NULL) {
+        ESP_LOGE(TAG, "Create mutex fail");
+        return ESP_FAIL;
+    }
+
+    /* Initialize ESPNOW and register sending and receiving callback function. */
+    ESP_ERROR_CHECK( esp_now_init() );
+    ESP_ERROR_CHECK( esp_now_register_send_cb(example_espnow_send_cb) );
+    ESP_ERROR_CHECK( esp_now_register_recv_cb(example_espnow_recv_cb) );
+
+    /* Set primary master key. */
+    ESP_ERROR_CHECK( esp_now_set_pmk((uint8_t *)CONFIG_ESPNOW_PMK) );
+
+    /* Add broadcast peer information to peer list. */
+    esp_now_peer_info_t *peer = malloc(sizeof(esp_now_peer_info_t));
+    if (peer == NULL) {
+        ESP_LOGE(TAG, "Malloc peer information fail");
+        vSemaphoreDelete(example_espnow_queue);
+        esp_now_deinit();
+        return ESP_FAIL;
+    }
+    memset(peer, 0, sizeof(esp_now_peer_info_t));
+    peer->channel = CONFIG_ESPNOW_CHANNEL;
+    peer->ifidx = ESPNOW_WIFI_IF;
+    peer->encrypt = false;
+    memcpy(peer->peer_addr, example_broadcast_mac, ESP_NOW_ETH_ALEN);
+    ESP_ERROR_CHECK( esp_now_add_peer(peer) );
+    free(peer);
+
+    /* Initialize sending parameters. */
+    send_param = malloc(sizeof(example_espnow_send_param_t));
+    memset(send_param, 0, sizeof(example_espnow_send_param_t));
+    if (send_param == NULL) {
+        ESP_LOGE(TAG, "Malloc send parameter fail");
+        vSemaphoreDelete(example_espnow_queue);
+        esp_now_deinit();
+        return ESP_FAIL;
+    }
+    send_param->unicast = false;
+    send_param->broadcast = true;
+    send_param->state = 0;
+    send_param->magic = esp_random();
+    send_param->count = CONFIG_ESPNOW_SEND_COUNT;
+    send_param->delay = CONFIG_ESPNOW_SEND_DELAY;
+    send_param->len = CONFIG_ESPNOW_SEND_LEN;
+    send_param->buffer = malloc(CONFIG_ESPNOW_SEND_LEN);
+    if (send_param->buffer == NULL) {
+        ESP_LOGE(TAG, "Malloc send buffer fail");
+        free(send_param);
+        vSemaphoreDelete(example_espnow_queue);
+        esp_now_deinit();
+        return ESP_FAIL;
+    }
+    memcpy(send_param->dest_mac, example_broadcast_mac, ESP_NOW_ETH_ALEN);
+    example_espnow_data_prepare(send_param);
+
+    xTaskCreate(example_espnow_task, "example_espnow_task", 2048, send_param, 4, NULL);
+
+    return ESP_OK;
+}
+
+static void example_espnow_deinit(example_espnow_send_param_t *send_param)
+{
+    free(send_param->buffer);
+    free(send_param);
+    vSemaphoreDelete(example_espnow_queue);
+    esp_now_deinit();
+}
+
+void app_main()
+{
+    // Initialize NVS
+    esp_err_t ret = nvs_flash_init();
+    if (ret == ESP_ERR_NVS_NO_FREE_PAGES) {
+        ESP_ERROR_CHECK( nvs_flash_erase() );
+        ret = nvs_flash_init();
+    }
+    ESP_ERROR_CHECK( ret );
+
+    example_wifi_init();
+    example_espnow_init();
+}
diff --git a/examples/wifi/iperf/Makefile b/examples/wifi/iperf/Makefile
new file mode 100755 (executable)
index 0000000..46d6578
--- /dev/null
@@ -0,0 +1,8 @@
+#
+# This is a project Makefile. It is assumed the directory this Makefile resides in is a
+# project subdirectory.
+#
+
+PROJECT_NAME := iperf
+
+include $(IDF_PATH)/make/project.mk
diff --git a/examples/wifi/iperf/README.md b/examples/wifi/iperf/README.md
new file mode 100644 (file)
index 0000000..c5fd968
--- /dev/null
@@ -0,0 +1,52 @@
+# Iperf Example
+
+This example implements the protocol used by the common performance measurement tool [iPerf](https://iperf.fr/). 
+Performance can be measured between two ESP32s running this example, or between a single ESP32 and a computer running the iPerf tool
+
+Demo steps to test station TCP Tx performance: 
+
+1. Build the iperf example with sdkconfig.defaults, which contains performance test specific configurations
+
+2. Run the demo as station mode and join the target AP
+   sta ssid password
+
+3. Run iperf as server on AP side
+   iperf -s -i 3
+
+4. Run iperf as client on ESP32 side
+   iperf -c 192.168.10.42 -i 3 -t 60
+
+The console output, which is printed by station TCP RX throughput test, looks like:
+
+>esp32> sta aptest
+>
+>I (5325) iperf: sta connecting to 'aptest'
+>
+>esp32> I (6017) event: ip: 192.168.10.248, mask: 255.255.255.0, gw: 192.168.10.1
+>
+>esp32> iperf -s -i 3 -t 1000
+>
+>I (14958) iperf: mode=tcp-server sip=192.168.10.248:5001, dip=0.0.0.0:5001, interval=3, time=1000
+>
+>Interval Bandwidth
+>
+>esp32> accept: 192.168.10.42,62958
+>
+>0-   3 sec       8.43 Mbits/sec
+>
+>3-   6 sec       36.16 Mbits/sec
+>
+>6-   9 sec       36.22 Mbits/sec
+>
+>9-  12 sec       36.44 Mbits/sec
+>
+>12-  15 sec       36.25 Mbits/sec
+>
+>15-  18 sec       24.36 Mbits/sec
+>
+>18-  21 sec       27.79 Mbits/sec
+
+
+Steps to test station/soft-AP TCP/UDP RX/TX throughput are similar as test steps in station TCP TX.
+
+See the README.md file in the upper level 'examples' directory for more information about examples.
diff --git a/examples/wifi/iperf/components/component.mk b/examples/wifi/iperf/components/component.mk
new file mode 100755 (executable)
index 0000000..da115f7
--- /dev/null
@@ -0,0 +1,11 @@
+#
+# Component Makefile
+#
+# This Makefile should, at the very least, just include $(SDK_PATH)/Makefile. By default, 
+# this will take the sources in the src/ directory, compile them and link them into 
+# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable,
+# please read the SDK documents if you need to do this.
+#
+
+#include $(IDF_PATH)/make/component_common.mk
+COMPONENT_ADD_INCLUDEDIRS := .
diff --git a/examples/wifi/iperf/components/iperf.c b/examples/wifi/iperf/components/iperf.c
new file mode 100644 (file)
index 0000000..03927d6
--- /dev/null
@@ -0,0 +1,455 @@
+/* Iperf Example - iperf implementation
+
+   This example code is in the Public Domain (or CC0 licensed, at your option.)
+
+   Unless required by applicable law or agreed to in writing, this
+   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+   CONDITIONS OF ANY KIND, either express or implied.
+*/
+
+#include <sys/socket.h>
+#include <stdio.h>
+#include <string.h>
+#include "esp_log.h"
+
+#include "iperf.h"
+
+typedef struct {
+    iperf_cfg_t cfg;
+    bool     finish;
+    uint32_t total_len;
+    uint32_t buffer_len;
+    uint8_t  *buffer;
+    uint32_t socket;
+} iperf_ctrl_t;
+
+typedef struct {
+    int32_t id;
+    uint32_t sec;
+    uint32_t usec;
+} iperf_udp_pkt_t;
+
+static bool s_iperf_is_running = false;
+static iperf_ctrl_t s_iperf_ctrl;
+static const char *TAG = "iperf";
+
+inline static bool iperf_is_udp_client(void)
+{
+    return ((s_iperf_ctrl.cfg.flag & IPERF_FLAG_CLIENT) && (s_iperf_ctrl.cfg.flag & IPERF_FLAG_UDP));
+}
+
+inline static bool iperf_is_udp_server(void)
+{
+    return ((s_iperf_ctrl.cfg.flag & IPERF_FLAG_SERVER) && (s_iperf_ctrl.cfg.flag & IPERF_FLAG_UDP));
+}
+
+inline static bool iperf_is_tcp_client(void)
+{
+    return ((s_iperf_ctrl.cfg.flag & IPERF_FLAG_CLIENT) && (s_iperf_ctrl.cfg.flag & IPERF_FLAG_TCP));
+}
+
+inline static bool iperf_is_tcp_server(void)
+{
+    return ((s_iperf_ctrl.cfg.flag & IPERF_FLAG_SERVER) && (s_iperf_ctrl.cfg.flag & IPERF_FLAG_TCP));
+}
+
+int iperf_get_socket_error_code(int socket)
+{
+    uint32_t optlen = sizeof(int);
+    int result;
+    int err;
+
+    err = getsockopt(socket, SOL_SOCKET, SO_ERROR, &result, &optlen);
+    if (err == -1) {
+        ESP_LOGE(TAG, "getsockopt failed: ret=%d", err);
+        return -1;
+    }
+
+    return result;
+}
+
+int iperf_show_socket_error_reason(const char *str, int socket)
+{
+    int err = iperf_get_socket_error_code(socket);
+
+    if (err != 0) {
+        ESP_LOGW(TAG, "%s error, error code: %d, reason: %s", str, err, strerror(err));
+    }
+
+    return err;
+}
+
+void iperf_report_task(void* arg)
+{
+    TickType_t delay_interval = (s_iperf_ctrl.cfg.interval * 1000)/portTICK_RATE_MS;
+    uint32_t interval = s_iperf_ctrl.cfg.interval;
+    uint32_t time = s_iperf_ctrl.cfg.time;
+    uint32_t last_len = 0;
+    uint32_t cur = 0;
+
+    printf("\n%16s %s\n", "Interval", "Bandwidth");
+    while (!s_iperf_ctrl.finish) {
+        vTaskDelay(delay_interval);
+        printf("%4d-%4d sec       %.2f Mbits/sec\n", cur, cur+interval, (double)((s_iperf_ctrl.total_len - last_len)*8)/interval/1e6);
+        cur += interval;
+        last_len = s_iperf_ctrl.total_len;
+        if (cur == time) {
+            break;
+        }
+    }
+
+    if (cur != 0) {
+        printf("%4d-%4d sec       %.2f Mbits/sec\n", 0, time, (double)(s_iperf_ctrl.total_len*8)/cur/1e6);
+    }
+
+    s_iperf_ctrl.finish = true;
+    vTaskDelete(NULL);
+}
+
+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);
+    if (ret != pdPASS)  {
+        ESP_LOGE(TAG, "create task %s failed", IPERF_REPORT_TASK_NAME);
+        return ESP_FAIL;
+    }
+    return ESP_OK;
+}
+
+esp_err_t iperf_run_tcp_server(void)
+{
+    socklen_t addr_len = sizeof(struct sockaddr);
+    struct sockaddr_in remote_addr;
+    struct sockaddr_in addr;
+    int actual_recv = 0;
+    int want_recv = 0;
+    uint8_t *buffer;
+    int listen_socket;
+    struct timeval t;
+    int socket;
+    int opt;
+
+    listen_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    if (listen_socket < 0) {
+        iperf_show_socket_error_reason("tcp server create", listen_socket);
+        return ESP_FAIL;
+    }
+
+    setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
+
+    addr.sin_family = AF_INET;
+    addr.sin_port = htons(s_iperf_ctrl.cfg.sport);
+    addr.sin_addr.s_addr = s_iperf_ctrl.cfg.sip;
+    if (bind(listen_socket, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
+        iperf_show_socket_error_reason("tcp server bind", listen_socket);
+        close(listen_socket);
+        return ESP_FAIL;
+    }
+
+    if (listen(listen_socket, 5) < 0) {
+        iperf_show_socket_error_reason("tcp server listen", listen_socket);
+        close(listen_socket);
+        return ESP_FAIL;
+    }
+
+    iperf_start_report();
+    buffer = s_iperf_ctrl.buffer;
+    want_recv = s_iperf_ctrl.buffer_len;
+    while (!s_iperf_ctrl.finish) {
+
+        /*TODO need to change to non-block mode */
+        socket = accept(listen_socket, (struct sockaddr*)&remote_addr, &addr_len);
+        if (socket < 0) {
+            iperf_show_socket_error_reason("tcp server listen", listen_socket);
+            close(listen_socket);
+            return ESP_FAIL;
+        } else {
+            printf("accept: %s,%d\n", inet_ntoa(remote_addr.sin_addr), htons(remote_addr.sin_port));
+
+            t.tv_sec = IPERF_SOCKET_RX_TIMEOUT;
+            setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t));
+        }
+
+        while (!s_iperf_ctrl.finish) {
+            actual_recv = recv(socket, buffer, want_recv, 0);
+            if (actual_recv < 0) {
+                iperf_show_socket_error_reason("tcp server recv", listen_socket);
+                break;
+            } else {
+                s_iperf_ctrl.total_len += actual_recv;
+            }
+        }
+
+        close(socket);
+    }
+
+    s_iperf_ctrl.finish = true;
+    close(listen_socket);
+    return ESP_OK;
+}
+
+esp_err_t iperf_run_udp_server(void)
+{
+    socklen_t addr_len = sizeof(struct sockaddr_in);
+    struct sockaddr_in addr;
+    int actual_recv = 0;
+    struct timeval t;
+    int want_recv = 0;
+    uint8_t *buffer;
+    int socket;
+    int opt;
+
+    socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+    if (socket < 0) {
+        iperf_show_socket_error_reason("udp server create", socket);
+        return ESP_FAIL;
+    }
+
+    setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
+
+    addr.sin_family = AF_INET;
+    addr.sin_port = htons(s_iperf_ctrl.cfg.sport);
+    addr.sin_addr.s_addr = s_iperf_ctrl.cfg.sip;
+    if (bind(socket, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
+        iperf_show_socket_error_reason("udp server bind", socket);
+        return ESP_FAIL;
+    }
+
+
+    addr.sin_family = AF_INET;
+    addr.sin_port = htons(s_iperf_ctrl.cfg.sport);
+    addr.sin_addr.s_addr = s_iperf_ctrl.cfg.sip;
+
+    iperf_start_report();
+    buffer = s_iperf_ctrl.buffer;
+    want_recv = s_iperf_ctrl.buffer_len;
+    ESP_LOGI(TAG, "want recv=%d", want_recv);
+
+    t.tv_sec = IPERF_SOCKET_RX_TIMEOUT;
+    setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t));
+
+    while (!s_iperf_ctrl.finish) {
+        actual_recv = recvfrom(socket, buffer, want_recv, 0, (struct sockaddr *)&addr, &addr_len);
+        if (actual_recv < 0) {
+            iperf_show_socket_error_reason("udp server recv", socket);
+        } else {
+            s_iperf_ctrl.total_len += actual_recv;
+        }
+    }
+
+    s_iperf_ctrl.finish = true;
+    close(socket);
+    return ESP_OK;
+}
+
+esp_err_t iperf_run_udp_client(void)
+{
+    struct sockaddr_in addr;
+    iperf_udp_pkt_t *udp;
+    int actual_send = 0;
+    bool retry = false;
+    uint32_t delay = 1;
+    int want_send = 0;
+    uint8_t *buffer;
+    int socket;
+    int opt;
+    int err;
+    int id;
+
+    socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+    if (socket < 0) {
+        iperf_show_socket_error_reason("udp server create", socket);
+        return ESP_FAIL;
+    }
+
+    setsockopt(socket, 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(socket, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
+        iperf_show_socket_error_reason("udp server bind", socket);
+        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;
+
+    iperf_start_report();
+    buffer = s_iperf_ctrl.buffer;
+    udp = (iperf_udp_pkt_t *)buffer;
+    want_send = s_iperf_ctrl.buffer_len;
+    id = 0;
+
+    while (!s_iperf_ctrl.finish) {
+        if (false == retry) {
+            id ++;
+            udp->id = htonl(id);
+            delay = 1;
+        }
+
+        retry = false;
+        actual_send = sendto(socket, buffer, want_send, 0, (struct sockaddr *)&addr, sizeof(addr));
+
+        if (actual_send != want_send) {
+            err = iperf_get_socket_error_code(socket);
+            if (err == ENOMEM) {
+                vTaskDelay(delay);
+                if (delay < IPERF_MAX_DELAY) {
+                    delay <<= 1;
+                }
+                retry = true;
+                continue;
+            } else {
+                ESP_LOGE(TAG, "udp client send abort: err=%d", err);
+                break;
+            }
+        } else {
+            s_iperf_ctrl.total_len += actual_send;
+        }
+    }
+
+    s_iperf_ctrl.finish = true;
+    close(socket);
+    return ESP_OK;
+}
+
+
+esp_err_t iperf_run_tcp_client(void)
+{
+    struct sockaddr_in addr;
+    int actual_send = 0;
+    int want_send = 0;
+    uint8_t *buffer;
+    int socket;
+    int opt;
+
+    socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    if (socket < 0) {
+        iperf_show_socket_error_reason("tcp client create", socket);
+        return ESP_FAIL;
+    }
+
+    setsockopt(socket, 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(socket, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
+        iperf_show_socket_error_reason("tcp client bind", socket);
+        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(socket, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+        iperf_show_socket_error_reason("tcp client connect", socket);
+        return ESP_FAIL;
+    }
+
+    iperf_start_report();
+    buffer = s_iperf_ctrl.buffer;
+    want_send = s_iperf_ctrl.buffer_len;
+    while (!s_iperf_ctrl.finish) {
+        actual_send = send(socket, buffer, want_send, 0);
+        if (actual_send <= 0) {
+            vTaskDelay(1);
+        } else {
+            s_iperf_ctrl.total_len += actual_send;
+        }
+    }
+
+    close(socket);
+    return ESP_OK;
+}
+
+void iperf_task_traffic(void *arg)
+{
+    if (iperf_is_udp_client()) {
+        iperf_run_udp_client();
+    } else if (iperf_is_udp_server()) {
+        iperf_run_udp_server();
+    } else if (iperf_is_tcp_client()) {
+        iperf_run_tcp_client();
+    } else {
+        iperf_run_tcp_server();
+    }
+
+    if (s_iperf_ctrl.buffer) {
+        free(s_iperf_ctrl.buffer);
+        s_iperf_ctrl.buffer = 0;
+    }
+    ESP_LOGI(TAG, "iperf exit");
+    s_iperf_is_running = false;
+    vTaskDelete(NULL);
+}
+
+uint32_t iperf_get_buffer_len(void)
+{
+    if (iperf_is_udp_client()) {
+        return IPERF_UDP_TX_LEN;
+    } else if (iperf_is_udp_server()) {
+        return IPERF_UDP_RX_LEN;
+    } else if (iperf_is_tcp_client()) {
+        return IPERF_TCP_TX_LEN;
+    } else {
+        return IPERF_TCP_RX_LEN;
+    }
+    return 0;
+}
+
+esp_err_t iperf_start(iperf_cfg_t *cfg)
+{
+    int ret;
+
+    if (!cfg) {
+        return ESP_FAIL;
+    }
+
+    if (s_iperf_is_running) {
+        ESP_LOGW(TAG, "iperf is running");
+        return ESP_FAIL;
+    }
+
+    memset(&s_iperf_ctrl, 0, sizeof(s_iperf_ctrl));
+    memcpy(&s_iperf_ctrl.cfg, cfg, sizeof(*cfg));
+    s_iperf_is_running = true;
+    s_iperf_ctrl.finish = false;
+    s_iperf_ctrl.buffer_len = iperf_get_buffer_len();
+    s_iperf_ctrl.buffer = (uint8_t *)malloc(s_iperf_ctrl.buffer_len);
+    if (!s_iperf_ctrl.buffer) {
+        ESP_LOGE(TAG, "create buffer: out of memory");
+        return ESP_FAIL;
+    }
+
+    ret = xTaskCreate(iperf_task_traffic, IPERF_TRAFFIC_TASK_NAME, IPERF_TRAFFIC_TASK_STACK, NULL, IPERF_TRAFFIC_TASK_PRIORITY, NULL);
+    if (ret != pdPASS)  {
+        ESP_LOGE(TAG, "create task %s failed", IPERF_TRAFFIC_TASK_NAME);
+        free(s_iperf_ctrl.buffer);
+        s_iperf_ctrl.buffer = 0;
+        return ESP_FAIL;
+    }
+
+    return ESP_OK;
+}
+
+esp_err_t iperf_stop(void)
+{
+    if (s_iperf_is_running) {
+        s_iperf_ctrl.finish = true;
+    }
+
+    while (s_iperf_is_running) {
+        ESP_LOGI(TAG, "wait current iperf to stop ...");
+        vTaskDelay(300/portTICK_RATE_MS);
+    }
+
+    return ESP_OK;
+}
+
diff --git a/examples/wifi/iperf/components/iperf.h b/examples/wifi/iperf/components/iperf.h
new file mode 100644 (file)
index 0000000..1f5a4ac
--- /dev/null
@@ -0,0 +1,61 @@
+/* Iperf Example - iperf declaration
+
+   This example code is in the Public Domain (or CC0 licensed, at your option.)
+
+   Unless required by applicable law or agreed to in writing, this
+   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+   CONDITIONS OF ANY KIND, either express or implied.
+*/
+
+#ifndef __IPERF_H_
+#define __IPERF_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define IPERF_FLAG_CLIENT (1)
+#define IPERF_FLAG_SERVER (1<<1)
+#define IPERF_FLAG_TCP    (1<<2)
+#define IPERF_FLAG_UDP    (1<<3)
+
+#define IPERF_DEFAULT_PORT     5001
+#define IPERF_DEFAULT_INTERVAL 3
+#define IPERF_DEFAULT_TIME     30
+
+#define IPERF_TRAFFIC_TASK_NAME       "iperf_traffic"
+#define IPERF_TRAFFIC_TASK_PRIORITY   19
+#define IPERF_TRAFFIC_TASK_STACK      4096
+#define IPERF_REPORT_TASK_NAME        "iperf_report"
+#define IPERF_REPORT_TASK_PRIORITY    10
+#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     (64<<10)
+
+#define IPERF_MAX_DELAY      64
+
+#define IPERF_SOCKET_RX_TIMEOUT      10
+#define IPERF_SOCKET_ACCEPT_TIMEOUT  5
+
+typedef struct {
+    uint32_t flag;
+    uint32_t dip;
+    uint32_t sip;
+    uint16_t dport;
+    uint16_t sport;
+    uint32_t interval;
+    uint32_t time;
+} iperf_cfg_t;
+
+esp_err_t iperf_start(iperf_cfg_t *cfg);
+esp_err_t iperf_stop(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/examples/wifi/iperf/main/cmd_decl.h b/examples/wifi/iperf/main/cmd_decl.h
new file mode 100644 (file)
index 0000000..78707ea
--- /dev/null
@@ -0,0 +1,14 @@
+/* Iperf example — declarations of command registration functions.
+
+   This example code is in the Public Domain (or CC0 licensed, at your option.)
+
+   Unless required by applicable law or agreed to in writing, this
+   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+   CONDITIONS OF ANY KIND, either express or implied.
+*/
+#pragma once
+
+// Register WiFi functions
+void register_wifi(void);
+void initialise_wifi(void);
+
diff --git a/examples/wifi/iperf/main/cmd_wifi.c b/examples/wifi/iperf/main/cmd_wifi.c
new file mode 100644 (file)
index 0000000..e26900d
--- /dev/null
@@ -0,0 +1,381 @@
+/* Iperf Example - wifi commands
+
+   This example code is in the Public Domain (or CC0 licensed, at your option.)
+
+   Unless required by applicable law or agreed to in writing, this
+   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+   CONDITIONS OF ANY KIND, either express or implied.
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include "esp_log.h"
+#include "esp_console.h"
+#include "argtable3/argtable3.h"
+#include "cmd_decl.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/event_groups.h"
+#include "esp_wifi.h"
+#include "tcpip_adapter.h"
+#include "esp_event_loop.h"
+#include "iperf.h"
+
+typedef struct {
+    struct arg_str *ip;
+    struct arg_lit *server;
+    struct arg_lit *udp;
+    struct arg_int *port;
+    struct arg_int *interval;
+    struct arg_int *time;
+    struct arg_lit *abort;
+    struct arg_end *end;
+} wifi_iperf_t;
+static wifi_iperf_t iperf_args;
+
+typedef struct {
+    struct arg_str *ssid;
+    struct arg_str *password;
+    struct arg_end *end;
+} wifi_args_t;
+static wifi_args_t sta_args;
+static wifi_args_t ap_args;
+static bool reconnect = true;
+static const char *TAG="iperf";
+
+static EventGroupHandle_t wifi_event_group;
+const int CONNECTED_BIT = BIT0;
+const int DISCONNECTED_BIT = BIT1;
+
+static esp_err_t event_handler(void *ctx, system_event_t *event)
+{
+    switch(event->event_id) {
+    case SYSTEM_EVENT_STA_GOT_IP:
+        xEventGroupClearBits(wifi_event_group, DISCONNECTED_BIT);
+        xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
+        break;
+    case SYSTEM_EVENT_STA_DISCONNECTED:
+        if (reconnect) {
+            ESP_LOGI(TAG, "sta disconnect, reconnect...");
+            esp_wifi_connect();
+        } else {
+            ESP_LOGI(TAG, "sta disconnect");
+        }
+        xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
+        xEventGroupSetBits(wifi_event_group, DISCONNECTED_BIT);
+        break;
+    default:
+        break;
+    }
+    return ESP_OK;
+}
+
+void initialise_wifi(void)
+{
+    esp_log_level_set("wifi", ESP_LOG_WARN);
+    static bool initialized = false;
+
+    if (initialized) {
+        return;
+    }
+
+    tcpip_adapter_init();
+    wifi_event_group = xEventGroupCreate();
+    ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) );
+    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
+    ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
+    ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
+    ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_NULL) );
+    ESP_ERROR_CHECK( esp_wifi_start() );
+    initialized = true;
+}
+
+static bool wifi_cmd_sta_join(const char* ssid, const char* pass)
+{
+    int bits = xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, 0, 1, 0);
+
+    wifi_config_t wifi_config = { 0 };
+
+    strlcpy((char*) wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid));
+    if (pass) {
+        strncpy((char*) wifi_config.sta.password, pass, sizeof(wifi_config.sta.password));
+    }
+
+    if (bits & CONNECTED_BIT) {
+        reconnect = false;
+        xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
+        ESP_ERROR_CHECK( esp_wifi_disconnect() );
+        xEventGroupWaitBits(wifi_event_group, DISCONNECTED_BIT, 0, 1, portTICK_RATE_MS);
+    }
+
+    reconnect = true;
+    ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
+    ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
+    ESP_ERROR_CHECK( esp_wifi_connect() );
+
+    xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, 0, 1, 5000/portTICK_RATE_MS);
+    
+    return true;
+}
+
+static int wifi_cmd_sta(int argc, char** argv)
+{
+    int nerrors = arg_parse(argc, argv, (void**) &sta_args);
+
+    if (nerrors != 0) {
+        arg_print_errors(stderr, sta_args.end, argv[0]);
+        return 1;
+    }
+
+    ESP_LOGI(TAG, "sta connecting to '%s'", sta_args.ssid->sval[0]);
+    wifi_cmd_sta_join(sta_args.ssid->sval[0], sta_args.password->sval[0]);
+    return 0;
+}
+
+
+static bool wifi_cmd_ap_set(const char* ssid, const char* pass)
+{
+    wifi_config_t wifi_config = {
+        .ap = {
+            .ssid = "",
+            .ssid_len = 0,
+            .max_connection = 4,
+            .password = "",
+            .authmode = WIFI_AUTH_WPA_WPA2_PSK
+        },
+    };
+
+    reconnect = false; 
+    strncpy((char*) wifi_config.ap.ssid, ssid, sizeof(wifi_config.ap.ssid));
+    if (pass) {
+        if (strlen(pass) != 0 && strlen(pass) < 8) {
+            reconnect = true;
+            ESP_LOGE(TAG, "password less than 8");
+            return false;
+        }
+        strncpy((char*) wifi_config.ap.password, pass, sizeof(wifi_config.ap.password));
+    }
+
+    if (strlen(pass) == 0) {
+        wifi_config.ap.authmode = WIFI_AUTH_OPEN;
+    }
+
+    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
+    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config));
+    return true;
+}
+
+static int wifi_cmd_ap(int argc, char** argv)
+{
+    int nerrors = arg_parse(argc, argv, (void**) &ap_args);
+
+    if (nerrors != 0) {
+        arg_print_errors(stderr, ap_args.end, argv[0]);
+        return 1;
+    }
+
+    wifi_cmd_ap_set(ap_args.ssid->sval[0], ap_args.password->sval[0]);
+    ESP_LOGI(TAG, "AP mode, %s %s", ap_args.ssid->sval[0], ap_args.password->sval[0]);
+    return 0;
+}
+
+static int wifi_cmd_query(int argc, char** argv)
+{
+    wifi_config_t cfg;
+    wifi_mode_t mode;
+
+    esp_wifi_get_mode(&mode);
+    if (WIFI_MODE_AP == mode) {
+        esp_wifi_get_config(WIFI_IF_AP, &cfg);
+        ESP_LOGI(TAG, "AP mode, %s %s", cfg.ap.ssid, cfg.ap.password);
+    } else if (WIFI_MODE_STA == mode) {
+        int bits = xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, 0, 1, 0);
+        if (bits & CONNECTED_BIT) {
+            esp_wifi_get_config(WIFI_IF_STA, &cfg);
+            ESP_LOGI(TAG, "sta mode, connected %s", cfg.ap.ssid);
+        } else {
+            ESP_LOGI(TAG, "sta mode, disconnected");
+        }
+    } else {
+        ESP_LOGI(TAG, "NULL mode");
+        return 0;
+    }
+
+    return 0;
+}
+
+static uint32_t wifi_get_local_ip(void)
+{
+    int bits = xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, 0, 1, 0);
+    tcpip_adapter_if_t ifx = TCPIP_ADAPTER_IF_AP;
+    tcpip_adapter_ip_info_t ip_info;
+    wifi_mode_t mode;
+
+    esp_wifi_get_mode(&mode);
+    if (WIFI_MODE_STA == mode) {
+        bits = xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, 0, 1, 0);
+        if (bits & CONNECTED_BIT) {
+            ifx = TCPIP_ADAPTER_IF_STA;
+        } else {
+            ESP_LOGE(TAG, "sta has no IP");
+            return 0;
+        }
+     }
+
+     tcpip_adapter_get_ip_info(ifx, &ip_info);
+     return ip_info.ip.addr;
+}
+
+static int wifi_cmd_iperf(int argc, char** argv)
+{
+    int nerrors = arg_parse(argc, argv, (void**) &iperf_args);
+    iperf_cfg_t cfg;
+
+    if (nerrors != 0) {
+        arg_print_errors(stderr, iperf_args.end, argv[0]);
+        return 0;
+    }
+
+    memset(&cfg, 0, sizeof(cfg));
+
+    if ( iperf_args.abort->count != 0) {
+        iperf_stop();
+        return 0;
+    }
+
+    if ( ((iperf_args.ip->count == 0) && (iperf_args.server->count == 0)) ||
+         ((iperf_args.ip->count != 0) && (iperf_args.server->count != 0)) ) {
+        ESP_LOGE(TAG, "should specific client/server mode");
+        return 0;
+    }
+
+    if (iperf_args.ip->count == 0) {
+        cfg.flag |= IPERF_FLAG_SERVER;
+    } else {
+        cfg.dip = ipaddr_addr(iperf_args.ip->sval[0]);
+        cfg.flag |= IPERF_FLAG_CLIENT;
+    }
+
+    cfg.sip = wifi_get_local_ip();
+    if (cfg.sip == 0) {
+        return 0;
+    }
+
+    if (iperf_args.udp->count == 0) {
+        cfg.flag |= IPERF_FLAG_TCP;
+    } else {
+        cfg.flag |= IPERF_FLAG_UDP;
+    }
+
+    if (iperf_args.port->count == 0) {
+        cfg.sport = IPERF_DEFAULT_PORT;
+        cfg.dport = IPERF_DEFAULT_PORT;
+    } else {
+        if (cfg.flag & IPERF_FLAG_SERVER) {
+            cfg.sport = iperf_args.port->ival[0];
+            cfg.dport = IPERF_DEFAULT_PORT;
+        } else {
+            cfg.sport = IPERF_DEFAULT_PORT;
+            cfg.dport = iperf_args.port->ival[0];
+        }
+    }
+
+    if (iperf_args.interval->count == 0) {
+        cfg.interval = IPERF_DEFAULT_INTERVAL;
+    } else {
+        cfg.interval = iperf_args.interval->ival[0];
+        if (cfg.interval <= 0) {
+            cfg.interval = IPERF_DEFAULT_INTERVAL;
+        }
+    }
+
+    if (iperf_args.time->count == 0) {
+        cfg.time = IPERF_DEFAULT_TIME;
+    } else {
+        cfg.time = iperf_args.time->ival[0];
+        if (cfg.time <= cfg.interval) {
+            cfg.time = cfg.interval;
+        }
+    }
+
+    ESP_LOGI(TAG, "mode=%s-%s sip=%d.%d.%d.%d:%d, dip=%d.%d.%d.%d:%d, interval=%d, time=%d",
+            cfg.flag&IPERF_FLAG_TCP?"tcp":"udp",
+            cfg.flag&IPERF_FLAG_SERVER?"server":"client",
+            cfg.sip&0xFF, (cfg.sip>>8)&0xFF, (cfg.sip>>16)&0xFF, (cfg.sip>>24)&0xFF, cfg.sport,
+            cfg.dip&0xFF, (cfg.dip>>8)&0xFF, (cfg.dip>>16)&0xFF, (cfg.dip>>24)&0xFF, cfg.dport,
+            cfg.interval, cfg.time);
+
+    iperf_start(&cfg);
+    
+    return 0;
+}
+
+static int restart(int argc, char** argv)
+{
+    ESP_LOGI(TAG, "Restarting");
+    esp_restart();
+}
+void register_wifi()
+{
+    sta_args.ssid = arg_str1(NULL, NULL, "<ssid>", "SSID of AP");
+    sta_args.password = arg_str0(NULL, NULL, "<pass>", "password of AP");
+    sta_args.end = arg_end(2);
+
+    const esp_console_cmd_t sta_cmd = {
+        .command = "sta",
+        .help = "WiFi is station mode, join specified soft-AP",
+        .hint = NULL,
+        .func = &wifi_cmd_sta,
+        .argtable = &sta_args
+    };
+
+    ESP_ERROR_CHECK( esp_console_cmd_register(&sta_cmd) );
+
+    ap_args.ssid = arg_str1(NULL, NULL, "<ssid>", "SSID of AP");
+    ap_args.password = arg_str0(NULL, NULL, "<pass>", "password of AP");
+    ap_args.end = arg_end(2);
+
+    const esp_console_cmd_t ap_cmd = {
+        .command = "ap",
+        .help = "AP mode, configure ssid and password",
+        .hint = NULL,
+        .func = &wifi_cmd_ap,
+        .argtable = &ap_args
+    };
+
+    ESP_ERROR_CHECK( esp_console_cmd_register(&ap_cmd) );
+
+    const esp_console_cmd_t query_cmd = {
+        .command = "query",
+        .help = "query WiFi info",
+        .hint = NULL,
+        .func = &wifi_cmd_query,
+    };
+    ESP_ERROR_CHECK( esp_console_cmd_register(&query_cmd) );
+
+    const esp_console_cmd_t restart_cmd = {
+        .command = "restart",
+        .help = "Restart the program",
+        .hint = NULL,
+        .func = &restart,
+    };
+    ESP_ERROR_CHECK( esp_console_cmd_register(&restart_cmd) );
+
+    iperf_args.ip = arg_str0("c", "client", "<ip>", "run in client mode, connecting to <host>");
+    iperf_args.server = arg_lit0("s", "server", "run in server mode");
+    iperf_args.udp = arg_lit0("u", "udp", "use UDP rather than TCP");
+    iperf_args.port = arg_int0("p", "port", "<port>", "server port to listen on/connect to");
+    iperf_args.interval = arg_int0("i", "interval", "<interval>", "seconds between periodic bandwidth reports");
+    iperf_args.time = arg_int0("t", "time", "<time>", "time in seconds to transmit for (default 10 secs)");
+    iperf_args.abort = arg_lit0("a", "abort", "abort running iperf");
+    iperf_args.end = arg_end(1);
+    const esp_console_cmd_t iperf_cmd = {
+        .command = "iperf",
+        .help = "iperf command",
+        .hint = NULL,
+        .func = &wifi_cmd_iperf,
+        .argtable = &iperf_args
+    };
+
+    ESP_ERROR_CHECK( esp_console_cmd_register(&iperf_cmd) );
+}
diff --git a/examples/wifi/iperf/main/component.mk b/examples/wifi/iperf/main/component.mk
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/examples/wifi/iperf/main/main.c b/examples/wifi/iperf/main/main.c
new file mode 100644 (file)
index 0000000..66af234
--- /dev/null
@@ -0,0 +1,136 @@
+/* Iperf Example
+
+   This example code is in the Public Domain (or CC0 licensed, at your option.)
+
+   Unless required by applicable law or agreed to in writing, this
+   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+   CONDITIONS OF ANY KIND, either express or implied.
+*/
+
+#include <errno.h>
+#include <string.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "esp_wifi.h"
+#include "esp_log.h"
+#include "esp_err.h"
+
+#include "esp_console.h"
+#include "esp_vfs_dev.h"
+#include "driver/uart.h"
+#include "linenoise/linenoise.h"
+#include "argtable3/argtable3.h"
+#include "cmd_decl.h"
+
+
+#define WIFI_CONNECTED_BIT BIT0
+
+static void initialize_console()
+{
+    /* Disable buffering on stdin and stdout */
+    setvbuf(stdin, NULL, _IONBF, 0);
+    setvbuf(stdout, NULL, _IONBF, 0);
+
+    /* Minicom, screen, idf_monitor send CR when ENTER key is pressed */
+    esp_vfs_dev_uart_set_rx_line_endings(ESP_LINE_ENDINGS_CR);
+    /* Move the caret to the beginning of the next line on '\n' */
+    esp_vfs_dev_uart_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF);
+
+    /* Install UART driver for interrupt-driven reads and writes */
+    ESP_ERROR_CHECK( uart_driver_install(CONFIG_CONSOLE_UART_NUM,
+            256, 0, 0, NULL, 0) );
+
+    /* Tell VFS to use UART driver */
+    esp_vfs_dev_uart_use_driver(CONFIG_CONSOLE_UART_NUM);
+
+    /* Initialize the console */
+    esp_console_config_t console_config = {
+            .max_cmdline_args = 32,
+            .max_cmdline_length = 256,
+#if CONFIG_LOG_COLORS
+            .hint_color = atoi(LOG_COLOR_CYAN)
+#endif
+    };
+    ESP_ERROR_CHECK( esp_console_init(&console_config) );
+
+    /* Configure linenoise line completion library */
+    /* Enable multiline editing. If not set, long commands will scroll within
+     * single line.
+     */
+    linenoiseSetMultiLine(1);
+
+    /* Tell linenoise where to get command completions and hints */
+    linenoiseSetCompletionCallback(&esp_console_get_completion);
+    linenoiseSetHintsCallback((linenoiseHintsCallback*) &esp_console_get_hint);
+
+    /* Set command history size */
+    linenoiseHistorySetMaxLen(100);
+}
+
+void app_main(void)
+{
+    initialise_wifi();
+    initialize_console();
+
+    /* Register commands */
+    esp_console_register_help_command();
+    register_wifi();
+
+    /* Prompt to be printed before each line.
+     * This can be customized, made dynamic, etc.
+     */
+    const char* prompt = LOG_COLOR_I "esp32> " LOG_RESET_COLOR;
+
+    printf("\n ==================================================\n");
+    printf(" |       Steps to test WiFi throughput            |\n");
+    printf(" |                                                |\n");
+    printf(" |  1. Print 'help' to gain overview of commands  |\n");
+    printf(" |  2. Configure device to station or soft-AP     |\n");
+    printf(" |  3. Setup WiFi connection                      |\n");
+    printf(" |  4. Run iperf to test UDP/TCP RX/TX throughput |\n");
+    printf(" |                                                |\n");
+    printf(" =================================================\n\n");
+
+    /* Figure out if the terminal supports escape sequences */
+    int probe_status = linenoiseProbe();
+    if (probe_status) { /* zero indicates success */
+        printf("\n"
+               "Your terminal application does not support escape sequences.\n"
+               "Line editing and history features are disabled.\n"
+               "On Windows, try using Putty instead.\n");
+        linenoiseSetDumbMode(1);
+#if CONFIG_LOG_COLORS
+        /* Since the terminal doesn't support escape sequences,
+         * don't use color codes in the prompt.
+         */
+        prompt = "esp32> ";
+#endif //CONFIG_LOG_COLORS
+    }
+
+    /* Main loop */
+    while(true) {
+        /* Get a line using linenoise.
+         * The line is returned when ENTER is pressed.
+         */
+        char* line = linenoise(prompt);
+        if (line == NULL) { /* Ignore empty lines */
+            continue;
+        }
+        /* Add the command to the history */
+        linenoiseHistoryAdd(line);
+
+        /* Try to run the command */
+        int ret;
+        esp_err_t err = esp_console_run(line, &ret);
+        if (err == ESP_ERR_NOT_FOUND) {
+            printf("Unrecognized command\n");
+        } else if (err == ESP_OK && ret != ESP_OK) {
+            printf("Command returned non-zero error code: 0x%x\n", ret);
+        } else if (err != ESP_OK) {
+            printf("Internal error: 0x%x\n", err);
+        }
+        /* linenoise allocates line buffer on the heap, so need to free it */
+        linenoiseFree(line);
+    }
+}
+
diff --git a/examples/wifi/iperf/sdkconfig.defaults b/examples/wifi/iperf/sdkconfig.defaults
new file mode 100644 (file)
index 0000000..f62698f
--- /dev/null
@@ -0,0 +1,23 @@
+CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y
+CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240
+CONFIG_MEMMAP_SMP=y
+
+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_TX_BA_WIN=16
+CONFIG_ESP32_WIFI_RX_BA_WIN=16
+
+CONFIG_FREERTOS_UNICORE=
+CONFIG_FREERTOS_HZ=1000
+
+CONFIG_INT_WDT=
+CONFIG_TASK_WDT=
+
+CONFIG_TCP_SND_BUF_DEFAULT=65535
+CONFIG_TCP_WND_DEFAULT=65535
+CONFIG_TCP_RECVMBOX_SIZE=64
+CONFIG_UDP_RECVMBOX_SIZE=64
+CONFIG_TCPIP_RECVMBOX_SIZE=64
+CONFIG_LWIP_ETHARP_TRUST_IP_MAC=
+
index 98bc601eddb8c2cb71fcd33633f2c7c0092d0044..dae26fb9a5e9756bec1e8f7a939d904927ce19c8 100644 (file)
@@ -68,7 +68,7 @@ static void wifi_power_save(void)
     wifi_config_t wifi_config = {
        .sta = {
            .ssid = DEFAULT_SSID,
-           .password = DEFAULT_PWD
+           .password = DEFAULT_PWD,
        },
     };
     ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
index 751668d4b0db6baf62121b90cf7001af900041e9..d05777e300987210fd8886fcde332e36513a00ea 100644 (file)
@@ -147,7 +147,11 @@ endef
 # component-specific feature, please don't! What you want is a
 # Makefile.projbuild for your component (see docs/build-system.rst for
 # more.)
-component_project_vars.mk:
+#
+# Note: The :: target here is not a mistake. This target should always be
+# executed, as dependencies are checked by the parent project-level make target.
+# See https://www.gnu.org/software/make/manual/make.html#index-_003a_003a-rules-_0028double_002dcolon_0029
+component_project_vars.mk::
        $(details) "Building component project variables list $(abspath $@)"
        @echo '# Automatically generated build file. Do not edit.' > $@
        @echo 'COMPONENT_INCLUDES += $(call MakeVariablePath,$(addprefix $(COMPONENT_PATH)/,$(COMPONENT_ADD_INCLUDEDIRS)))' >> $@
index e7a3c731606504f3ff74ff4a770b804a75c5d0b4..d2e75ddf9101c51f34ee39780e7c348fb3993a60 100755 (executable)
@@ -134,13 +134,13 @@ function run_tests()
 
     print_status "Can still clean build if all text files are CRLFs"
     make clean || failure "Unexpected failure to make clean"
-    find . -exec unix2dos {} \; # CRLFify template dir
+    find . -path .git -prune -exec unix2dos {} \; # CRLFify template dir
     # make a copy of esp-idf and CRLFify it
     CRLF_ESPIDF=${TESTDIR}/esp-idf-crlf
     mkdir -p ${CRLF_ESPIDF}
     cp -r ${IDF_PATH}/* ${CRLF_ESPIDF}
     # don't CRLFify executable files, as Linux will fail to execute them
-    find ${CRLF_ESPIDF} -type f ! -perm 755 -exec unix2dos {} \;
+    find ${CRLF_ESPIDF} -name .git -prune -name build -prune -type f ! -perm 755 -exec unix2dos {} \;
     make IDF_PATH=${CRLF_ESPIDF} || failure "Failed to build with CRLFs in source"
     # do the same checks we do for the clean build
     assert_built ${APP_BINS} ${BOOTLOADER_BINS} partitions_singleapp.bin
@@ -165,6 +165,8 @@ function run_tests()
     take_build_snapshot
     touch sdkconfig
     make
+    # check the component_project_vars.mk file was rebuilt
+    assert_rebuilt esp32/component_project_vars.mk
     # pick one each of .c, .cpp, .S that #includes sdkconfig.h
     # and therefore should rebuild
     assert_rebuilt newlib/syscall_table.o
index c421365f2e29ae3b06c4eef7802c8bd4b52edc38..6675294b7974dbb255aed3649057ff5e8e92b122 100644 (file)
@@ -173,9 +173,12 @@ gconf-objs := gconf.o zconf.tab.o
 
 hostprogs-y := conf nconf mconf kxgettext qconf gconf
 
-clean-files    := qconf.moc .tmp_qtcheck .tmp_gtkcheck *.d
+all-objs := $(conf-objs) $(mconf-objs) $(lxdialog)
+all-deps := $(all-objs:.o=.d)
+
+clean-files    := qconf.moc .tmp_qtcheck .tmp_gtkcheck
 clean-files    += zconf.tab.c zconf.lex.c zconf.hash.c gconf.glade.h
-clean-files += $(conf-objs) $(mconf-objs) conf mconf $(lxdialog)
+clean-files += $(all-objs) $(all-deps) conf mconf
 
 # Check that we have the required ncurses stuff installed for lxdialog (menuconfig)
 PHONY += dochecklxdialog
@@ -307,8 +310,8 @@ zconf.hash.c: zconf.gperf
 zconf.tab.c: zconf.y
        bison -t -l -p zconf -o zconf.tab.c zconf.y
 
-clean: 
+clean:
        rm -f $(clean-files)
 
--include $(wildcard *.d)
--include $(wildcard lxdialog/*.d)
+-include $(all-deps)
+
index 79df5ed4f9ea2b3f2665ecfc2823a7bf1980f46b..9e98145983fdf92856234443eb109628447d34d2 100755 (executable)
@@ -49,7 +49,7 @@ ccflags()
 
 # Temp file, try to clean up after us
 tmp=.lxdialog.tmp
-trap "rm -f $tmp" 0 1 2 3 15
+trap "rm -f $tmp ${tmp%.tmp}.d" 0 1 2 3 15
 
 # Check if we can link to ncurses
 check() {
index 4a99208d8aa6cf93246b0680e329765f3136a75b..656e34403ba81bacf472d6b2642fdc774527a158 100644 (file)
@@ -438,6 +438,7 @@ static int UnityCheckArraysForNull(UNITY_INTERNAL_PTR expected, UNITY_INTERNAL_P
         UnityTestResultsFailBegin(lineNumber);
         UnityPrint(UnityStrNullPointerForExpected);
         UnityAddMsgIfSpecified(msg);
+        UNITY_OUTPUT_CHAR('\n');
         UNITY_FAIL_AND_BAIL;
     }
 
@@ -447,6 +448,7 @@ static int UnityCheckArraysForNull(UNITY_INTERNAL_PTR expected, UNITY_INTERNAL_P
         UnityTestResultsFailBegin(lineNumber);
         UnityPrint(UnityStrNullPointerForActual);
         UnityAddMsgIfSpecified(msg);
+        UNITY_OUTPUT_CHAR('\n');
         UNITY_FAIL_AND_BAIL;
     }
 
@@ -474,6 +476,7 @@ void UnityAssertBits(const _U_SINT mask,
         UnityPrint(UnityStrWas);
         UnityPrintMask((_U_UINT)mask, (_U_UINT)actual);
         UnityAddMsgIfSpecified(msg);
+        UNITY_OUTPUT_CHAR('\n');
         UNITY_FAIL_AND_BAIL;
     }
 }
@@ -495,6 +498,7 @@ void UnityAssertEqualNumber(const _U_SINT expected,
         UnityPrint(UnityStrWas);
         UnityPrintNumberByStyle(actual, style);
         UnityAddMsgIfSpecified(msg);
+        UNITY_OUTPUT_CHAR('\n');
         UNITY_FAIL_AND_BAIL;
     }
 }
@@ -504,6 +508,7 @@ void UnityAssertEqualNumber(const _U_SINT expected,
     UnityTestResultsFailBegin(lineNumber); \
     UnityPrint(UnityStrPointless);         \
     UnityAddMsgIfSpecified(msg);           \
+    UNITY_OUTPUT_CHAR('\n');               \
     UNITY_FAIL_AND_BAIL; }
 
 /*-----------------------------------------------*/
@@ -548,6 +553,7 @@ void UnityAssertEqualIntArray(UNITY_INTERNAL_PTR expected,
                     UnityPrint(UnityStrWas);
                     UnityPrintNumberByStyle(*(UNITY_PTR_ATTRIBUTE const _US8*)ptr_act, style);
                     UnityAddMsgIfSpecified(msg);
+                    UNITY_OUTPUT_CHAR('\n');
                     UNITY_FAIL_AND_BAIL;
                 }
                 ptr_exp = (UNITY_INTERNAL_PTR)((_UP)ptr_exp + 1);
@@ -569,6 +575,7 @@ void UnityAssertEqualIntArray(UNITY_INTERNAL_PTR expected,
                     UnityPrint(UnityStrWas);
                     UnityPrintNumberByStyle(*(UNITY_PTR_ATTRIBUTE const _US16*)ptr_act, style);
                     UnityAddMsgIfSpecified(msg);
+                    UNITY_OUTPUT_CHAR('\n');
                     UNITY_FAIL_AND_BAIL;
                 }
                 ptr_exp = (UNITY_INTERNAL_PTR)((_UP)ptr_exp + 2);
@@ -591,6 +598,7 @@ void UnityAssertEqualIntArray(UNITY_INTERNAL_PTR expected,
                     UnityPrint(UnityStrWas);
                     UnityPrintNumberByStyle(*(UNITY_PTR_ATTRIBUTE const _US64*)ptr_act, style);
                     UnityAddMsgIfSpecified(msg);
+                    UNITY_OUTPUT_CHAR('\n');
                     UNITY_FAIL_AND_BAIL;
                 }
                 ptr_exp = (UNITY_INTERNAL_PTR)((_UP)ptr_exp + 8);
@@ -611,6 +619,7 @@ void UnityAssertEqualIntArray(UNITY_INTERNAL_PTR expected,
                     UnityPrint(UnityStrWas);
                     UnityPrintNumberByStyle(*(UNITY_PTR_ATTRIBUTE const _US32*)ptr_act, style);
                     UnityAddMsgIfSpecified(msg);
+                    UNITY_OUTPUT_CHAR('\n');
                     UNITY_FAIL_AND_BAIL;
                 }
                 ptr_exp = (UNITY_INTERNAL_PTR)((_UP)ptr_exp + 4);
@@ -667,6 +676,7 @@ void UnityAssertEqualFloatArray(UNITY_PTR_ATTRIBUTE const _UF* expected,
             UnityPrint(UnityStrDelta);
 #endif
             UnityAddMsgIfSpecified(msg);
+            UNITY_OUTPUT_CHAR('\n');
             UNITY_FAIL_AND_BAIL;
         }
         ptr_expected++;
@@ -708,6 +718,7 @@ void UnityAssertFloatsWithin(const _UF delta,
         UnityPrint(UnityStrDelta);
 #endif
         UnityAddMsgIfSpecified(msg);
+        UNITY_OUTPUT_CHAR('\n');
         UNITY_FAIL_AND_BAIL;
     }
 }
@@ -775,6 +786,7 @@ void UnityAssertFloatSpecial(const _UF actual,
         UnityPrint(trait_names[trait_index]);
 #endif
         UnityAddMsgIfSpecified(msg);
+        UNITY_OUTPUT_CHAR('\n');
         UNITY_FAIL_AND_BAIL;
     }
 }
@@ -828,6 +840,7 @@ void UnityAssertEqualDoubleArray(UNITY_PTR_ATTRIBUTE const _UD* expected,
             UnityPrint(UnityStrDelta);
 #endif
             UnityAddMsgIfSpecified(msg);
+            UNITY_OUTPUT_CHAR('\n');
             UNITY_FAIL_AND_BAIL;
         }
         ptr_expected++;
@@ -869,6 +882,7 @@ void UnityAssertDoublesWithin(const _UD delta,
         UnityPrint(UnityStrDelta);
 #endif
         UnityAddMsgIfSpecified(msg);
+        UNITY_OUTPUT_CHAR('\n');
         UNITY_FAIL_AND_BAIL;
     }
 }
@@ -937,6 +951,7 @@ void UnityAssertDoubleSpecial(const _UD actual,
         UnityPrint(trait_names[trait_index]);
 #endif
         UnityAddMsgIfSpecified(msg);
+        UNITY_OUTPUT_CHAR('\n');
         UNITY_FAIL_AND_BAIL;
     }
 }
@@ -979,6 +994,7 @@ void UnityAssertNumbersWithin( const _U_UINT delta,
         UnityPrint(UnityStrWas);
         UnityPrintNumberByStyle(actual, style);
         UnityAddMsgIfSpecified(msg);
+        UNITY_OUTPUT_CHAR('\n');
         UNITY_FAIL_AND_BAIL;
     }
 }
@@ -1018,6 +1034,7 @@ void UnityAssertEqualString(const char* expected,
       UnityTestResultsFailBegin(lineNumber);
       UnityPrintExpectedAndActualStrings(expected, actual);
       UnityAddMsgIfSpecified(msg);
+      UNITY_OUTPUT_CHAR('\n');
       UNITY_FAIL_AND_BAIL;
     }
 }
@@ -1058,6 +1075,7 @@ void UnityAssertEqualStringLen(const char* expected,
       UnityTestResultsFailBegin(lineNumber);
       UnityPrintExpectedAndActualStringsLen(expected, actual, length);
       UnityAddMsgIfSpecified(msg);
+      UNITY_OUTPUT_CHAR('\n');
       UNITY_FAIL_AND_BAIL;
     }
 }
@@ -1115,6 +1133,7 @@ void UnityAssertEqualStringArray( const char** expected,
             }
             UnityPrintExpectedAndActualStrings((const char*)(expected[j]), (const char*)(actual[j]));
             UnityAddMsgIfSpecified(msg);
+            UNITY_OUTPUT_CHAR('\n');
             UNITY_FAIL_AND_BAIL;
         }
     } while (++j < num_elements);
@@ -1165,6 +1184,7 @@ void UnityAssertEqualMemory( UNITY_INTERNAL_PTR expected,
                 UnityPrint(UnityStrWas);
                 UnityPrintNumberByStyle(*ptr_act, UNITY_DISPLAY_STYLE_HEX8);
                 UnityAddMsgIfSpecified(msg);
+                UNITY_OUTPUT_CHAR('\n');
                 UNITY_FAIL_AND_BAIL;
             }
             ptr_exp = (UNITY_INTERNAL_PTR)((_UP)ptr_exp + 1);
@@ -1208,7 +1228,7 @@ void UnityFail(const char* msg, const UNITY_LINE_TYPE line)
         }
         UnityPrint(msg);
     }
-
+    UNITY_OUTPUT_CHAR('\n');
     UNITY_FAIL_AND_BAIL;
 }
 
@@ -1225,6 +1245,7 @@ void UnityIgnore(const char* msg, const UNITY_LINE_TYPE line)
       UNITY_OUTPUT_CHAR(' ');
       UnityPrint(msg);
     }
+    UNITY_OUTPUT_CHAR('\n');
     UNITY_IGNORE_AND_BAIL;
 }
 
index ef985f2711985b4bea6dc2578c79d6975c870920..8e73fd525738b8e32d7b43025131231e8c1c2934 100644 (file)
@@ -10,6 +10,7 @@
 #include "esp_log.h"
 #include "soc/cpu.h"
 #include "esp_heap_caps.h"
+#include "test_utils.h"
 
 #define unity_printf ets_printf
 
@@ -37,6 +38,7 @@ const size_t CRITICAL_LEAK_THRESHOLD = 4096;
 void setUp(void)
 {
     printf("%s", ""); /* sneakily lazy-allocate the reent structure for this test task */
+    get_test_data_partition();  /* allocate persistent partition table structures */
 
     before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
     before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
index 165977b0a5e2ef044d2bef8a9d386c48868d560a..e3f6cf9531410defe72a71fe384e49b79a7f1613 100644 (file)
@@ -37,7 +37,7 @@ pacman --noconfirm -S --needed gettext-devel gcc git make ncurses-devel flex bis
 
 # Workaround for errors when running "git submodule" commands
 # See https://github.com/Alexpux/MSYS2-packages/issues/735
-rm /mingw32/bin/envsubst.exe
+rm -f /mingw32/bin/envsubst.exe
 
 python -m pip install --upgrade pip
 
@@ -49,6 +49,7 @@ echo "Downloading precompiled toolchain ${TOOLCHAIN_ZIP}..."
 cd ~
 curl -LO --retry 10 http://dl.espressif.com/dl/${TOOLCHAIN_ZIP}
 cd /opt
+rm -rf /opt/xtensa-esp32-elf  # for upgrades
 unzip ~/${TOOLCHAIN_ZIP}
 rm ~/${TOOLCHAIN_ZIP}
 
@@ -58,8 +59,7 @@ cat > /etc/profile.d/esp32_toolchain.sh << EOF
 export PATH="\$PATH:/opt/xtensa-esp32-elf/bin"
 EOF
 
-# clean up pacman packages to save some disk space
-pacman --noconfirm -R unzip
+# clean up pacman package cache to save some disk space
 pacman --noconfirm -Scc
 
 cat << EOF