diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 703e011c35..7cb63be8c3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -247,6 +247,8 @@ deploy_docs: expire_in: 6 mos script: + # remove artifacts from last stage (UT logs) + - rm -rf $LOG_PATH # add gitlab ssh key - mkdir -p ~/.ssh - chmod 700 ~/.ssh @@ -268,6 +270,8 @@ deploy_docs: # can only be triggered - triggers script: + # remove artifacts from last stage (UT logs) + - rm -rf $LOG_PATH # must be night build triggers, otherwise exit without test - test $NIGHT_BUILD = "Yes" || exit 0 # add gitlab ssh key @@ -345,7 +349,6 @@ IT_Function_TCPIP_02: tags: - ESP32_IDF - SSC_T1_1 - - SSC_T2_1 before_script: - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Function_TCPIP_02.yml @@ -372,10 +375,73 @@ IT_Function_TCPIP_05: tags: - ESP32_IDF - SSC_T1_1 - - SSC_T2_1 before_script: - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Function_TCPIP_05.yml +IT_Stress_WIFI_01: + <<: *test_template_night + tags: + - ESP32_IDF + - SSC_T5_1 + before_script: + - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Stress_WIFI_01.yml + +IT_Stress_TCPIP_01: + <<: *test_template_night + tags: + - ESP32_IDF + - SSC_T1_1 + before_script: + - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Stress_TCPIP_01.yml + +IT_Stress_TCPIP_02: + <<: *test_template_night + tags: + - ESP32_IDF + - SSC_T2_1 + before_script: + - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Stress_TCPIP_02.yml + +IT_Stress_TCPIP_03: + <<: *test_template_night + tags: + - ESP32_IDF + - SSC_T1_1 + before_script: + - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Stress_TCPIP_03.yml + +IT_Stress_TCPIP_04: + <<: *test_template_night + tags: + - ESP32_IDF + - SSC_T2_1 + before_script: + - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Stress_TCPIP_04.yml + +IT_Stable_TCPIP_01: + <<: *test_template_night + tags: + - ESP32_IDF + - SSC_T5_1 + before_script: + - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Stable_TCPIP_01.yml + +IT_Stable_TCPIP_02: + <<: *test_template_night + tags: + - ESP32_IDF + - SSC_T1_1 + before_script: + - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Stable_TCPIP_02.yml + +IT_Stable_TCPIP_03: + <<: *test_template_night + tags: + - ESP32_IDF + - SSC_T5_1 + before_script: + - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Stable_TCPIP_03.yml + IT_Function_TCPIP_06: <<: *test_template_night tags: @@ -388,7 +454,7 @@ IT_Function_WIFI_03: <<: *test_template tags: - ESP32_IDF - - SSC_T3_PhyMode + - SSC_T1_APC before_script: - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Function_WIFI_03.yml @@ -396,7 +462,7 @@ IT_Function_WIFI_04: <<: *test_template tags: - ESP32_IDF - - SSC_T1_APC + - SSC_T3_PhyMode before_script: - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Function_WIFI_04.yml @@ -422,7 +488,6 @@ IT_Function_TCPIP_07: - ESP32_IDF - SSC_T1_1 - SSC_T1_2 - - SSC_T2_1 before_script: - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Function_TCPIP_07.yml @@ -431,6 +496,7 @@ IT_Function_TCPIP_08: tags: - ESP32_IDF - SSC_T1_1 + - SSC_T2_1 before_script: - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Function_TCPIP_08.yml @@ -439,6 +505,7 @@ IT_Function_TCPIP_09: tags: - ESP32_IDF - SSC_T1_1 + - SSC_T1_2 before_script: - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Function_TCPIP_09.yml @@ -448,7 +515,6 @@ IT_Function_TCPIP_10: - ESP32_IDF - SSC_T1_1 - SSC_T1_2 - - SSC_T2_1 before_script: - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Function_TCPIP_10.yml @@ -457,7 +523,6 @@ IT_Function_TCPIP_11: tags: - ESP32_IDF - SSC_T1_1 - - SSC_T1_2 before_script: - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Function_TCPIP_11.yml @@ -468,3 +533,4 @@ IT_Function_TCPIP_12: - SSC_T1_1 before_script: - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Function_TCPIP_12.yml + diff --git a/components/bootloader/Kconfig.projbuild b/components/bootloader/Kconfig.projbuild index 50165d0e58..270369b2c6 100644 --- a/components/bootloader/Kconfig.projbuild +++ b/components/bootloader/Kconfig.projbuild @@ -1,7 +1,7 @@ menu "Bootloader config" choice LOG_BOOTLOADER_LEVEL bool "Bootloader log verbosity" - default LOG_BOOTLOADER_LEVEL_WARN + default LOG_BOOTLOADER_LEVEL_INFO help Specify how much output to see in bootloader logs. @@ -32,23 +32,24 @@ endmenu -menu "Secure boot configuration" +menu "Security features" -choice SECURE_BOOTLOADER - bool "Secure bootloader" - default SECURE_BOOTLOADER_DISABLED +config SECURE_BOOT_ENABLED + bool "Enable secure boot in bootloader" + default N help - Build a bootloader with the secure boot flag enabled. + Build a bootloader which enables secure boot on first boot. - Secure bootloader can be one-time-flash (chip will only ever - boot that particular bootloader), or a digest key can be used - to allow the secure bootloader to be re-flashed with - modifications. Secure boot also permanently disables JTAG. + Once enabled, secure boot will not boot a modified bootloader. The bootloader will only load a partition table or boot an app if the data has a verified digital signature. + + When enabling secure boot, JTAG and ROM BASIC Interpreter are permanently disabled by default. See docs/security/secure-boot.rst for details. -config SECURE_BOOTLOADER_DISABLED - bool "Disabled" +choice SECURE_BOOTLOADER_MODE + bool "Secure bootloader mode" + depends on SECURE_BOOT_ENABLED + default SECURE_BOOTLOADER_ONE_TIME_FLASH config SECURE_BOOTLOADER_ONE_TIME_FLASH bool "One-time flash" @@ -62,7 +63,7 @@ config SECURE_BOOTLOADER_REFLASHABLE help Generate a reusable secure bootloader key, derived (via SHA-256) from the secure boot signing key. - This allows the secure bootloader to be re-flashed by anyone with access to the secure boot signing key. + This allows the secure bootloader to be re-flashed by anyone with access to the secure boot signing key. This option is less secure than one-time flash, because a leak of the digest key from one device allows reflashing of any device that uses it. @@ -70,10 +71,10 @@ endchoice config SECURE_BOOT_SIGNING_KEY string "Secure boot signing key" - depends on SECURE_BOOTLOADER_ENABLED - default secure_boot_signing_key.pem + depends on SECURE_BOOT_ENABLED + default secure_boot_signing_key.pem help - Path to the key file used to sign partition tables and app images for secure boot. + Path to the key file used to sign partition tables and app images for secure boot. Once secure boot is enabled, bootloader will only boot if partition table and app image are signed. Key file is an ECDSA private key (NIST256p curve) in PEM format. @@ -84,35 +85,106 @@ config SECURE_BOOT_SIGNING_KEY See docs/security/secure-boot.rst for details. -config SECURE_BOOT_DISABLE_JTAG - bool "First boot: Permanently disable JTAG" - depends on SECURE_BOOTLOADER_ENABLED - default Y +config SECURE_BOOT_INSECURE + bool "Allow potentially insecure options" + depends on SECURE_BOOT_ENABLED + default N + help + You can disable some of the default protections offered by secure boot, in order to enable testing or a custom combination of security features. + + Only enable these options if you are very sure. + + Refer to docs/security/secure-boot.rst and docs/security/flash-encryption.rst for details. + +config FLASH_ENCRYPTION_ENABLED + bool "Enable flash encryption on boot" + default N + help + If this option is set, flash contents will be encrypted by the bootloader on first boot. + + Note: After first boot, the system will be permanently encrypted. + See docs/securityflash-encryption.rst for details. + +config FLASH_ENCRYPTION_INSECURE + bool "Allow potentially insecure options" + depends on FLASH_ENCRYPTION_ENABLED + default N + help + You can disable some of the default protections offered by flash encryption, in order to enable testing or a custom combination of security features. + + Only enable these options if you are very sure. + + Refer to docs/security/secure-boot.rst and docs/security/flash-encryption.rst for details. + +menu "Potentially insecure options" + visible if FLASH_ENCRYPTION_INSECURE || SECURE_BOOT_INSECURE + +# NOTE: Options in this menu NEED to have SECURE_BOOT_INSECURE +# and/or FLASH_ENCRYPTION_INSECURE in "depends on", as the menu +# itself doesn't enable/disable its children (if it's not set, +# it's possible for the insecure menu to be disabled but the insecure option +# to remain on which is very bad.) + +config SECURE_BOOT_ALLOW_ROM_BASIC + bool "Leave ROM BASIC Interpreter available on reset" + depends on SECURE_BOOT_INSECURE + default N help - Bootloader permanently disable JTAG (across entire chip) when enabling secure boot. This happens on first boot of the bootloader. + If not set (default), bootloader permanently disables ROM BASIC (on UART console) as a fallback if the bootloader image becomes invalid. This happens on first boot. - It is recommended this option remains set for production environments. + Only set this option in testing environments. -config SECURE_BOOT_DISABLE_ROM_BASIC - bool "First boot: Permanently disable ROM BASIC fallback" - depends on SECURE_BOOTLOADER_ENABLED - default Y +config SECURE_BOOT_ALLOW_JTAG + bool "Allow JTAG Debugging" + depends on SECURE_BOOT_INSECURE || FLASH_ENCRYPTION_INSECURE + default N help - Bootloader permanently disables ROM BASIC (on UART console) as a fallback if the bootloader image becomes invalid. This happens on first boot. + If not set (default), the bootloader will permanently disable JTAG (across entire chip) on first boot when either secure boot or flash encryption is enabled. - It is recommended this option remains set in production environments. + Setting this option leaves JTAG on for debugging, which negates all protections of flash encryption and some of the protections of secure boot. + + Only set this option in testing environments. + + +config FLASH_ENCRYPTION_UART_BOOTLOADER_ALLOW_ENCRYPT + bool "Leave UART bootloader encryption enabled" + depends on FLASH_ENCRYPTION_INSECURE + default N + help + If not set (default), the bootloader will permanently disable UART bootloader encryption access on first boot. If set, the UART bootloader will still be able to access hardware encryption. + + It is recommended to only set this option in testing environments. + +config FLASH_ENCRYPTION_UART_BOOTLOADER_ALLOW_DECRYPT + bool "Leave UART bootloader decryption enabled" + depends on FLASH_ENCRYPTION_INSECURE + default N + help + If not set (default), the bootloader will permanently disable UART bootloader decryption access on first boot. If set, the UART bootloader will still be able to access hardware decryption. + + Only set this option in testing environments. Setting this option allows complete bypass of flash encryption. + +config FLASH_ENCRYPTION_UART_BOOTLOADER_ALLOW_CACHE + bool "Leave UART bootloader flash cache enabled" + depends on FLASH_ENCRYPTION_INSECURE + default N + help + If not set (default), the bootloader will permanently disable UART bootloader flash cache access on first boot. If set, the UART bootloader will still be able to access the flash cache. + + Only set this option in testing environments. config SECURE_BOOT_TEST_MODE - bool "Test mode: don't actually enable secure boot" - depends on SECURE_BOOTLOADER_ENABLED + bool "Secure boot test mode: don't permanently set any efuses" + depends on SECURE_BOOT_INSECURE default N help If this option is set, all permanent secure boot changes (via Efuse) are disabled. - This option is for testing purposes only - it effectively completely disables secure boot protection. + Log output will state changes which would be applied, but they will not be. -config SECURE_BOOTLOADER_ENABLED - bool - default SECURE_BOOTLOADER_ONE_TIME_FLASH || SECURE_BOOTLOADER_REFLASHABLE + This option is for testing purposes only - it completely disables secure boot protection. + + +endmenu # potentially insecure endmenu \ No newline at end of file diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index a49f3d8686..3f83803ad5 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -18,6 +18,9 @@ BOOTLOADER_BIN=$(BOOTLOADER_BUILD_DIR)/bootloader.bin SECURE_BOOT_SIGNING_KEY=$(abspath $(call dequote,$(CONFIG_SECURE_BOOT_SIGNING_KEY))) export SECURE_BOOT_SIGNING_KEY # used by bootloader_support component +# Has a matching value in bootloader_support esp_flash_partitions.h +BOOTLOADER_OFFSET := 0x1000 + # Custom recursive make for bootloader sub-project BOOTLOADER_MAKE=+$(MAKE) -C $(BOOTLOADER_COMPONENT_PATH)/src \ V=$(V) BUILD_DIR_BASE=$(BOOTLOADER_BUILD_DIR) TEST_COMPONENTS= @@ -29,55 +32,36 @@ $(BOOTLOADER_BIN): $(SDKCONFIG_MAKEFILE) clean: bootloader-clean -ifdef CONFIG_SECURE_BOOTLOADER_DISABLED +ifndef CONFIG_SECURE_BOOT_ENABLED # If secure boot disabled, bootloader flashing is integrated # with 'make flash' and no warnings are printed. bootloader: $(BOOTLOADER_BIN) @echo $(SEPARATOR) @echo "Bootloader built. Default flash command is:" - @echo "$(ESPTOOLPY_WRITE_FLASH) 0x1000 $^" + @echo "$(ESPTOOLPY_WRITE_FLASH) $(BOOTLOADER_OFFSET) $^" -ESPTOOL_ALL_FLASH_ARGS += 0x1000 $(BOOTLOADER_BIN) +ESPTOOL_ALL_FLASH_ARGS += $(BOOTLOADER_OFFSET) $(BOOTLOADER_BIN) bootloader-flash: $(BOOTLOADER_BIN) $(ESPTOOLPY_WRITE_FLASH) 0x1000 $^ else ifdef CONFIG_SECURE_BOOTLOADER_ONE_TIME_FLASH -#### TEMPORARILY DISABLE THIS OPTION -ifneq ("$(IDF_INSECURE_SECURE_BOOT)","1") -bootloader: - @echo "Secure boot features are not yet mature, so the current secure bootloader will not properly secure the device" - @echo "If you flash this bootloader, you will be left with an non-updateable bootloader that is missing features." - @echo "If you really want to do this, set the environment variable IDF_INSECURE_SECURE_BOOT=1 and rerun make." - exit 1 -else - # One time flashing requires user to run esptool.py command themselves, # and warning is printed about inability to reflash. bootloader: $(BOOTLOADER_BIN) @echo $(SEPARATOR) @echo "Bootloader built. One-time flash command is:" - @echo "$(ESPTOOLPY_WRITE_FLASH) 0x1000 $(BOOTLOADER_BIN)" + @echo "$(ESPTOOLPY_WRITE_FLASH) $(BOOTLOADER_OFFSET) $(BOOTLOADER_BIN)" @echo $(SEPARATOR) @echo "* IMPORTANT: After first boot, BOOTLOADER CANNOT BE RE-FLASHED on same device" -endif # IDF_INSECURE_SECURE_BOOT else ifdef CONFIG_SECURE_BOOTLOADER_REFLASHABLE # Reflashable secure bootloader # generates a digest binary (bootloader + digest) -#### TEMPORARILY DISABLE THIS OPTION -ifneq ("$(IDF_INSECURE_SECURE_BOOT)","1") -bootloader: - @echo "Secure boot features are not yet mature, so the current secure bootloader will not properly secure the device." - @echo "If using this feature, expect to reflash the bootloader at least one more time." - @echo "If you really want to do this, set the environment variable IDF_INSECURE_SECURE_BOOT=1 and rerun make." - exit 1 -else - BOOTLOADER_DIGEST_BIN := $(BOOTLOADER_BUILD_DIR)/bootloader-reflash-digest.bin SECURE_BOOTLOADER_KEY := $(BOOTLOADER_BUILD_DIR)/secure-bootloader-key.bin @@ -88,7 +72,7 @@ bootloader: $(BOOTLOADER_DIGEST_BIN) @echo $(SEPARATOR) @echo "Bootloader built and secure digest generated. First time flash command is:" @echo "$(ESPEFUSEPY) burn_key secure_boot $(SECURE_BOOTLOADER_KEY)" - @echo "$(ESPTOOLPY_WRITE_FLASH) 0x1000 $(BOOTLOADER_BIN)" + @echo "$(ESPTOOLPY_WRITE_FLASH) $(BOOTLOADER_OFFSET) $(BOOTLOADER_BIN)" @echo $(SEPARATOR) @echo "To reflash the bootloader after initial flash:" @echo "$(ESPTOOLPY_WRITE_FLASH) 0x0 $(BOOTLOADER_DIGEST_BIN)" @@ -100,14 +84,16 @@ $(BOOTLOADER_DIGEST_BIN): $(BOOTLOADER_BIN) $(SECURE_BOOTLOADER_KEY) @echo "DIGEST $(notdir $@)" $(Q) $(ESPSECUREPY) digest_secure_bootloader -k $(SECURE_BOOTLOADER_KEY) -o $@ $< -endif # IDF_INSECURE_SECURE_BOOT else bootloader: @echo "Invalid bootloader target: bad sdkconfig?" @exit 1 endif +ifndef CONFIG_SECURE_BOOT_ENABLED +# don't build bootloader by default is secure boot is enabled all_binaries: $(BOOTLOADER_BIN) +endif bootloader-clean: $(BOOTLOADER_MAKE) app-clean diff --git a/components/bootloader/src/main/bootloader_config.h b/components/bootloader/src/main/bootloader_config.h index 4559d5f816..d097083d2a 100644 --- a/components/bootloader/src/main/bootloader_config.h +++ b/components/bootloader/src/main/bootloader_config.h @@ -34,20 +34,6 @@ extern "C" #define RTC_DATA_LOW 0x50000000 #define RTC_DATA_HIGH 0x50002000 -#define PART_TYPE_APP 0x00 -#define PART_SUBTYPE_FACTORY 0x00 -#define PART_SUBTYPE_OTA_FLAG 0x10 -#define PART_SUBTYPE_OTA_MASK 0x0f -#define PART_SUBTYPE_TEST 0x20 - -#define PART_TYPE_DATA 0x01 -#define PART_SUBTYPE_DATA_OTA 0x00 -#define PART_SUBTYPE_DATA_RF 0x01 -#define PART_SUBTYPE_DATA_WIFI 0x02 - -#define PART_TYPE_END 0xff -#define PART_SUBTYPE_END 0xff - #define SPI_ERROR_LOG "spi flash error" typedef struct { diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index c3ddf0e10a..44de10e87d 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -39,6 +39,7 @@ #include "sdkconfig.h" #include "esp_image_format.h" #include "esp_secure_boot.h" +#include "esp_flash_encrypt.h" #include "bootloader_flash.h" #include "bootloader_config.h" @@ -121,7 +122,7 @@ bool load_partition_table(bootloader_state_t* bs) ESP_LOGI(TAG, "Partition Table:"); ESP_LOGI(TAG, "## Label Usage Type ST Offset Length"); -#ifdef CONFIG_SECURE_BOOTLOADER_ENABLED +#ifdef CONFIG_SECURE_BOOT_ENABLED if(esp_secure_boot_enabled()) { ESP_LOGI(TAG, "Verifying partition table signature..."); esp_err_t err = esp_secure_boot_verify_signature(ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_DATA_LEN); @@ -230,7 +231,9 @@ void bootloader_main() { uart_console_configure(); ESP_LOGI(TAG, "Espressif ESP32 2nd stage bootloader v. %s", BOOT_VERSION); - +#if defined(CONFIG_SECURE_BOOT_ENABLED) || defined(CONFIG_FLASH_ENCRYPTION_ENABLED) + esp_err_t err; +#endif esp_image_header_t fhdr; bootloader_state_t bs; SpiFlashOpResult spiRet1,spiRet2; @@ -245,7 +248,7 @@ void bootloader_main() REG_CLR_BIT( TIMG_WDTCONFIG0_REG(0), TIMG_WDT_FLASHBOOT_MOD_EN ); SPIUnlock(); - if(esp_image_load_header(0x1000, &fhdr) != ESP_OK) { + if(esp_image_load_header(0x1000, true, &fhdr) != ESP_OK) { ESP_LOGE(TAG, "failed to load bootloader header!"); return; } @@ -324,12 +327,11 @@ void bootloader_main() return; } - ESP_LOGI(TAG, "Loading app partition at offset %08x", load_part_pos); - -#ifdef CONFIG_SECURE_BOOTLOADER_ENABLED +#ifdef CONFIG_SECURE_BOOT_ENABLED /* Generate secure digest from this bootloader to protect future modifications */ - esp_err_t err = esp_secure_boot_permanently_enable(); + ESP_LOGI(TAG, "Checking secure boot..."); + err = esp_secure_boot_permanently_enable(); if (err != ESP_OK) { ESP_LOGE(TAG, "Bootloader digest generation failed (%d). SECURE BOOT IS NOT ENABLED.", err); /* Allow booting to continue, as the failure is probably @@ -338,15 +340,28 @@ void bootloader_main() } #endif - if(fhdr.encrypt_flag == 0x01) { - /* encrypt flash */ - if (false == flash_encrypt(&bs)) { - ESP_LOGE(TAG, "flash encrypt failed"); - return; - } +#ifdef CONFIG_FLASH_ENCRYPTION_ENABLED + /* encrypt flash */ + ESP_LOGI(TAG, "Checking flash encryption..."); + bool flash_encryption_enabled = esp_flash_encryption_enabled(); + err = esp_flash_encrypt_check_and_update(); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Flash encryption check failed (%d).", err); + return; } + if (!flash_encryption_enabled && esp_flash_encryption_enabled()) { + /* Flash encryption was just enabled for the first time, + so issue a system reset to ensure flash encryption + cache resets properly */ + ESP_LOGI(TAG, "Resetting with flash encryption enabled..."); + REG_WRITE(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_SYS_RST); + return; + } +#endif + // copy loaded segments to RAM, set up caches for mapped segments, and start application + ESP_LOGI(TAG, "Loading app partition at offset %08x", load_part_pos); unpack_load_app(&load_part_pos); } @@ -358,13 +373,13 @@ static void unpack_load_app(const esp_partition_pos_t* partition) uint32_t image_length; /* TODO: verify the app image as part of OTA boot decision, so can have fallbacks */ - err = esp_image_basic_verify(partition->offset, &image_length); + err = esp_image_basic_verify(partition->offset, true, &image_length); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to verify app image @ 0x%x (%d)", partition->offset, err); return; } -#ifdef CONFIG_SECURE_BOOTLOADER_ENABLED +#ifdef CONFIG_SECURE_BOOT_ENABLED if (esp_secure_boot_enabled()) { ESP_LOGI(TAG, "Verifying app signature @ 0x%x (length 0x%x)", partition->offset, image_length); err = esp_secure_boot_verify_signature(partition->offset, image_length); @@ -376,7 +391,7 @@ static void unpack_load_app(const esp_partition_pos_t* partition) } #endif - if (esp_image_load_header(partition->offset, &image_header) != ESP_OK) { + if (esp_image_load_header(partition->offset, true, &image_header) != ESP_OK) { ESP_LOGE(TAG, "Failed to load app image header @ 0x%x", partition->offset); return; } @@ -402,8 +417,8 @@ static void unpack_load_app(const esp_partition_pos_t* partition) esp_image_segment_header_t segment_header; uint32_t data_offs; if(esp_image_load_segment_header(segment, partition->offset, - &image_header, &segment_header, - &data_offs) != ESP_OK) { + &image_header, true, + &segment_header, &data_offs) != ESP_OK) { ESP_LOGE(TAG, "failed to load segment header #%d", segment); return; } @@ -622,6 +637,7 @@ void print_flash_info(const esp_image_header_t* phdr) #endif } +#if CONFIG_CONSOLE_UART_CUSTOM static uint32_t get_apb_freq(void) { // Get the value of APB clock from RTC memory. @@ -639,6 +655,7 @@ static uint32_t get_apb_freq(void) return APB_CLK_FREQ_ROM; } } +#endif static void uart_console_configure(void) { diff --git a/components/bootloader/src/main/component.mk b/components/bootloader/src/main/component.mk index 01b07b9498..e98545fc47 100644 --- a/components/bootloader/src/main/component.mk +++ b/components/bootloader/src/main/component.mk @@ -5,5 +5,8 @@ # we pull in bootloader-specific linker arguments. # -COMPONENT_ADD_LDFLAGS := -L $(COMPONENT_PATH) -lmain -T esp32.bootloader.ld -T $(IDF_PATH)/components/esp32/ld/esp32.rom.ld +LINKER_SCRIPTS := esp32.bootloader.ld $(IDF_PATH)/components/esp32/ld/esp32.rom.ld +COMPONENT_ADD_LDFLAGS := -L $(COMPONENT_PATH) -lmain $(addprefix -T ,$(LINKER_SCRIPTS)) + +COMPONENT_ADD_LINKER_DEPS := $(LINKER_SCRIPTS) diff --git a/components/bootloader/src/main/flash_encrypt.c b/components/bootloader/src/main/flash_encrypt.c deleted file mode 100644 index 2b1479097d..0000000000 --- a/components/bootloader/src/main/flash_encrypt.c +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -#include "esp_types.h" -#include "esp_attr.h" -#include "esp_log.h" -#include "esp_err.h" - -#include "rom/cache.h" -#include "rom/ets_sys.h" -#include "rom/spi_flash.h" - -#include "soc/dport_reg.h" -#include "soc/io_mux_reg.h" -#include "soc/efuse_reg.h" -#include "soc/rtc_cntl_reg.h" - -#include "sdkconfig.h" - -#include "bootloader_config.h" -#include "esp_image_format.h" - -static const char* TAG = "flash_encrypt"; - -/** - * @function : bitcount - * @description: calculate bit 1 in flash_crypt_cnt - * if it's even number, need encrypt flash data, and burn efuse - * - * @inputs: n flash_crypt_cnt - * @return: number of 1 in flash_crypt_cnt - * - */ -int bitcount(int n){ - int count = 0; - while (n > 0) { - count += n & 1; - n >>= 1; - } - return count; - -} -/** - * @function : flash_encrypt_write - * @description: write encrypted data in flash - * - * @inputs: pos address in flash - * len size of data need encrypt - * @return: return true, if the write flash success - * - */ -bool flash_encrypt_write(uint32_t pos, uint32_t len) -{ - SpiFlashOpResult spiRet; - uint32_t buf[1024]; - int i = 0; - Cache_Read_Disable(0); - for (i = 0;i<((len-1)/0x1000 + 1);i++) { - spiRet = SPIRead(pos, buf, SPI_SEC_SIZE); - if (spiRet != SPI_FLASH_RESULT_OK) { - Cache_Read_Enable(0); - ESP_LOGE(TAG, SPI_ERROR_LOG); - return false; - } - spiRet = SPIEraseSector(pos/SPI_SEC_SIZE); - if (spiRet != SPI_FLASH_RESULT_OK) { - Cache_Read_Enable(0); - ESP_LOGE(TAG, SPI_ERROR_LOG); - return false; - } - spiRet = SPI_Encrypt_Write(pos, buf, SPI_SEC_SIZE); - if (spiRet != SPI_FLASH_RESULT_OK) { - Cache_Read_Enable(0); - ESP_LOGE(TAG, SPI_ERROR_LOG); - return false; - } - pos += SPI_SEC_SIZE; - } - Cache_Read_Enable(0); - return true; -} - - -/** - * @function : flash_encrypt - * @description: encrypt 2nd boot ,partition table ,factory bin ��test bin (if use)��ota bin - * ��OTA info sector. - * - * @inputs: bs bootloader state structure used to save the data - * - * @return: return true, if the encrypt flash success - * - */ -bool flash_encrypt(bootloader_state_t *bs) -{ - esp_err_t err; - uint32_t image_len = 0; - uint32_t flash_crypt_cnt = REG_GET_FIELD(EFUSE_BLK0_RDATA0_REG, EFUSE_FLASH_CRYPT_CNT); - uint8_t count = bitcount(flash_crypt_cnt); - ESP_LOGD(TAG, "flash encrypt cnt %x, bitcount %d", flash_crypt_cnt, count); - - if ((count % 2) == 0) { - /* encrypt iv and abstract */ - if (false == flash_encrypt_write(0, SPI_SEC_SIZE)) { - ESP_LOGE(TAG, "encrypt iv and abstract error"); - return false; - } - - /* encrypt bootloader image */ - err = esp_image_basic_verify(0x1000, &image_len); - if(err == ESP_OK && image_len != 0) { - if (false == flash_encrypt_write(0x1000, image_len)) { - ESP_LOGE(TAG, "encrypt 2nd boot error"); - return false; - } - } else { - ESP_LOGE(TAG, "2nd boot len error"); - return false; - } - - /* encrypt partition table */ - if (false == flash_encrypt_write(ESP_PARTITION_TABLE_ADDR, SPI_SEC_SIZE)) { - ESP_LOGE(TAG, "encrypt partition table error"); - return false; - } - - /* encrypt write factory bin */ - if(bs->factory.offset != 0 && bs->factory.size != 0) { - ESP_LOGD(TAG, "have factory bin"); - if (false == flash_encrypt_write(bs->factory.offset, bs->factory.size)) { - ESP_LOGE(TAG, "encrypt factory bin error"); - return false; - } - } - - /* encrypt write test bin */ - if(bs->test.offset != 0 && bs->test.size != 0) { - ESP_LOGD(TAG, "have test bin"); - if (false == flash_encrypt_write(bs->test.offset, bs->test.size)) { - ESP_LOGE(TAG, "encrypt test bin error"); - return false; - } - } - - /* encrypt write ota bin */ - for (int i = 0; i < 16; i++) { - if(bs->ota[i].offset != 0 && bs->ota[i].size != 0) { - ESP_LOGD(TAG, "have ota[%d] bin",i); - if (false == flash_encrypt_write(bs->ota[i].offset, bs->ota[i].size)) { - ESP_LOGE(TAG, "encrypt ota bin error"); - return false; - } - } - } - - /* encrypt write ota info bin */ - if (false == flash_encrypt_write(bs->ota_info.offset, 2*SPI_SEC_SIZE)) { - ESP_LOGE(TAG, "encrypt ota info error"); - return false; - } - - REG_SET_FIELD(EFUSE_BLK0_WDATA0_REG, EFUSE_FLASH_CRYPT_CNT, 0x04); - REG_WRITE(EFUSE_CONF_REG, 0x5A5A); /* efuse_pgm_op_ena, force no rd/wr disable */ - REG_WRITE(EFUSE_CMD_REG, 0x02); /* efuse_pgm_cmd */ - while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_pagm_cmd=0 */ - ESP_LOGW(TAG, "burn flash_crypt_cnt"); - REG_WRITE(EFUSE_CONF_REG, 0x5AA5); /* efuse_read_op_ena, release force */ - REG_WRITE(EFUSE_CMD_REG, 0x01); /* efuse_read_cmd */ - while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_read_cmd=0 */ - return true; - } else { - ESP_LOGI(TAG, "flash already encrypted."); - return true; - } -} diff --git a/components/bootloader_support/Makefile.projbuild b/components/bootloader_support/Makefile.projbuild new file mode 100644 index 0000000000..ee62930c60 --- /dev/null +++ b/components/bootloader_support/Makefile.projbuild @@ -0,0 +1,7 @@ +$(SECURE_BOOT_SIGNING_KEY): + @echo "Need to generate secure boot signing key." + @echo "One way is to run this command:" + @echo "$(ESPSECUREPY) generate_signing_key $@" + @echo "Keep key file safe after generating." + @echo "(See secure boot documentation for risks & alternatives.)" + @exit 1 diff --git a/components/bootloader_support/component.mk b/components/bootloader_support/component.mk index a7e5b849dc..1435dbb76b 100755 --- a/components/bootloader_support/component.mk +++ b/components/bootloader_support/component.mk @@ -12,19 +12,11 @@ COMPONENT_SRCDIRS := src # # Secure boot signing key support # -ifdef CONFIG_SECURE_BOOTLOADER_ENABLED +ifdef CONFIG_SECURE_BOOT_ENABLED # this path is created relative to the component build directory SECURE_BOOT_VERIFICATION_KEY := $(abspath signature_verification_key.bin) -$(SECURE_BOOT_SIGNING_KEY): - @echo "Need to generate secure boot signing key." - @echo "One way is to run this command:" - @echo "$(ESPSECUREPY) generate_signing_key $@" - @echo "Keep key file safe after generating." - @echo "(See secure boot documentation for risks & alternatives.)" - @exit 1 - $(SECURE_BOOT_VERIFICATION_KEY): $(SECURE_BOOT_SIGNING_KEY) $(ESPSECUREPY) extract_public_key --keyfile $< $@ diff --git a/components/bootloader_support/include/esp_efuse.h b/components/bootloader_support/include/esp_efuse.h new file mode 100644 index 0000000000..41588396c4 --- /dev/null +++ b/components/bootloader_support/include/esp_efuse.h @@ -0,0 +1,56 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef _ESP_EFUSE_H +#define _ESP_EFUSE_H + +#include "soc/efuse_reg.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* @brief Permanently update values written to the efuse write registers + * + * After updating EFUSE_BLKx_WDATAx_REG registers with new values to + * write, call this function to permanently write them to efuse. + * + * @note Setting bits in efuse is permanent, they cannot be unset. + * + * @note Due to this restriction you don't need to copy values to + * Efuse write registers from the matching read registers, bits which + * are set in the read register but unset in the matching write + * register will be unchanged when new values are burned. + * + * @note This function is not threadsafe, if calling code updates + * efuse values from multiple tasks then this is caller's + * responsibility to serialise. + * + * After burning new efuses, the read registers are updated to match + * the new efuse values. + */ +void esp_efuse_burn_new_values(void); + +/* @brief Reset efuse write registers + * + * Efuse write registers are written to zero, to negate + * any changes that have been staged here. + */ +void esp_efuse_reset(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __ESP_EFUSE_H */ + diff --git a/components/bootloader_support/include/esp_flash_encrypt.h b/components/bootloader_support/include/esp_flash_encrypt.h new file mode 100644 index 0000000000..015dea030a --- /dev/null +++ b/components/bootloader_support/include/esp_flash_encrypt.h @@ -0,0 +1,91 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef __ESP32_FLASH_ENCRYPT_H +#define __ESP32_FLASH_ENCRYPT_H + +#include +#include +#include "esp_spi_flash.h" +#include "soc/efuse_reg.h" + +/* Support functions for flash encryption features. + + Can be compiled as part of app or bootloader code. +*/ + +/** @brief Is flash encryption currently enabled in hardware? + * + * Flash encryption is enabled if the FLASH_CRYPT_CNT efuse has an odd number of bits set. + * + * @return true if flash encryption is enabled. + */ +static inline bool esp_flash_encryption_enabled(void) { + uint32_t flash_crypt_cnt = REG_GET_FIELD(EFUSE_BLK0_RDATA0_REG, EFUSE_RD_FLASH_CRYPT_CNT); + return __builtin_parity(flash_crypt_cnt) == 1; +} + +/* @brief Update on-device flash encryption + * + * Intended to be called as part of the bootloader process if flash + * encryption is enabled in device menuconfig. + * + * If FLASH_CRYPT_CNT efuse parity is 1 (ie odd number of bits set), + * then return ESP_OK immediately (indicating flash encryption is enabled + * and functional). + * + * If FLASH_CRYPT_CNT efuse parity is 0 (ie even number of bits set), + * assume the flash has just been written with plaintext that needs encrypting. + * + * The following regions of flash are encrypted in place: + * + * - The bootloader image, if a valid plaintext image is found.[*] + * - The partition table, if a valid plaintext table is found. + * - Any app partition that contains a valid plaintext app image. + * - Any other partitions with the "encrypt" flag set. [**] + * + * After the re-encryption process completes, a '1' bit is added to the + * FLASH_CRYPT_CNT value (setting the parity to 1) and the EFUSE is re-burned. + * + * [*] If reflashing bootloader with secure boot enabled, pre-encrypt + * the bootloader before writing it to flash or secure boot will fail. + * + * [**] For this reason, if serial re-flashing a previous flashed + * device with secure boot enabled and using FLASH_CRYPT_CNT to + * trigger re-encryption, you must simultaneously re-flash plaintext + * content to all partitions with the "encrypt" flag set or this + * data will be corrupted (encrypted twice). + * + * @note The post-condition of this function is that all + * partitions that should be encrypted are encrypted. + * + * @note Take care not to power off the device while this function + * is running, or the partition currently being encrypted will be lost. + * + * @return ESP_OK if all operations succeeded, ESP_ERR_INVALID_STATE + * if a fatal error occured during encryption of all partitions. + */ +esp_err_t esp_flash_encrypt_check_and_update(void); + + +/** @brief Encrypt-in-place a block of flash sectors + * + * @param src_addr Source offset in flash. Should be multiple of 4096 bytes. + * @param data_length Length of data to encrypt in bytes. Will be rounded up to next multiple of 4096 bytes. + * + * @return ESP_OK if all operations succeeded, ESP_ERR_FLASH_OP_FAIL + * if SPI flash fails, ESP_ERR_FLASH_OP_TIMEOUT if flash times out. + */ +esp_err_t esp_flash_encrypt_region(uint32_t src_addr, size_t data_length); + +#endif diff --git a/components/bootloader_support/include/esp_flash_partitions.h b/components/bootloader_support/include/esp_flash_partitions.h new file mode 100644 index 0000000000..63ee822126 --- /dev/null +++ b/components/bootloader_support/include/esp_flash_partitions.h @@ -0,0 +1,39 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef __ESP_FLASH_PARTITIONS_H +#define __ESP_FLASH_PARTITIONS_H + +#include "esp_err.h" +#include "esp_flash_data_types.h" +#include + +/* Pre-partition table fixed flash offsets */ +#define ESP_BOOTLOADER_DIGEST_OFFSET 0x0 +#define ESP_BOOTLOADER_OFFSET 0x1000 /* Offset of bootloader image. Has matching value in bootloader KConfig.projbuild file. */ +#define ESP_PARTITION_TABLE_OFFSET 0x8000 /* Offset of partition table. Has matching value in partition_table Kconfig.projbuild file. */ + +#define ESP_PARTITION_TABLE_MAX_LEN 0xC00 /* Maximum length of partition table data */ +#define ESP_PARTITION_TABLE_MAX_ENTRIES (ESP_PARTITION_TABLE_MAX_LEN / sizeof(esp_partition_info_t)) /* Maximum length of partition table data, including terminating entry */ + +/* @brief Verify the partition table (does not include verifying secure boot cryptographic signature) + * + * @param partition_table Pointer to at least ESP_PARTITION_TABLE_MAX_ENTRIES of potential partition table data. (ESP_PARTITION_TABLE_MAX_LEN bytes.) + * @param log_errors Log errors if the partition table is invalid. + * @param num_partitions If result is ESP_OK, num_partitions is updated with total number of partitions (not including terminating entry). + * + * @return ESP_OK on success, ESP_ERR_INVALID_STATE if partition table is not valid. + */ +esp_err_t esp_partition_table_basic_verify(const esp_partition_info_t *partition_table, bool log_errors, int *num_partitions); + +#endif diff --git a/components/bootloader_support/include/esp_image_format.h b/components/bootloader_support/include/esp_image_format.h index a32a50a4a5..e0759c3c0e 100644 --- a/components/bootloader_support/include/esp_image_format.h +++ b/components/bootloader_support/include/esp_image_format.h @@ -77,33 +77,40 @@ typedef struct { /** * @brief Read an ESP image header from flash. * + * If encryption is enabled, data will be transparently decrypted. + * * @param src_addr Address in flash to load image header. Must be 4 byte aligned. + * @param log_errors Log error output if image header appears invalid. * @param[out] image_header Pointer to an esp_image_header_t struture to be filled with data. If the function fails, contents are undefined. * * @return ESP_OK if image header was loaded, ESP_ERR_IMAGE_FLASH_FAIL * if a SPI flash error occurs, ESP_ERR_IMAGE_INVALID if the image header * appears invalid. */ -esp_err_t esp_image_load_header(uint32_t src_addr, esp_image_header_t *image_header); +esp_err_t esp_image_load_header(uint32_t src_addr, bool log_errors, esp_image_header_t *image_header); /** * @brief Read the segment header and data offset of a segment in the image. * + * If encryption is enabled, data will be transparently decrypted. + * * @param index Index of the segment to load information for. * @param src_addr Base address in flash of the image. * @param[in] image_header Pointer to the flash image header, already loaded by @ref esp_image_load_header(). + * @param log_errors Log errors reading the segment header. * @param[out] segment_header Pointer to a segment header structure to be filled with data. If the function fails, contents are undefined. * @param[out] segment_data_offset Pointer to the data offset of the segment. * * @return ESP_OK if segment_header & segment_data_offset were loaded successfully, ESP_ERR_IMAGE_FLASH_FAIL if a SPI flash error occurs, ESP_ERR_IMAGE_INVALID if the image header appears invalid, ESP_ERR_INVALID_ARG if the index is invalid. */ -esp_err_t esp_image_load_segment_header(uint8_t index, uint32_t src_addr, const esp_image_header_t *image_header, esp_image_segment_header_t *segment_header, uint32_t *segment_data_offset); +esp_err_t esp_image_load_segment_header(uint8_t index, uint32_t src_addr, const esp_image_header_t *image_header, bool log_errors, esp_image_segment_header_t *segment_header, uint32_t *segment_data_offset); /** - * @brief Return length of an image in flash. Non-cryptographically validates image integrity in the process. + * @brief Non-cryptographically validate app image integrity. On success, length of image is provided to caller. * - * If the image has a secure boot signature appended, the signature is not checked and this length is not included in the result. + * If the image has a secure boot signature appended, the signature is not checked and this length is not included in the + * output value. * * Image validation checks: * - Magic byte @@ -111,13 +118,17 @@ esp_err_t esp_image_load_segment_header(uint8_t index, uint32_t src_addr, const * - Total image no longer than 16MB * - 8 bit image checksum is valid * + * If flash encryption is enabled, the image will be tranpsarently decrypted. + * * @param src_addr Offset of the start of the image in flash. Must be 4 byte aligned. + * @param allow_decrypt If true and flash encryption is enabled, the image will be transparently decrypted. + * @param log_errors Log errors verifying the image. * @param[out] length Length of the image, set to a value if the image is valid. Can be null. * * @return ESP_OK if image is valid, ESP_FAIL or ESP_ERR_IMAGE_INVALID on errors. * */ -esp_err_t esp_image_basic_verify(uint32_t src_addr, uint32_t *length); +esp_err_t esp_image_basic_verify(uint32_t src_addr, bool log_errors, uint32_t *length); typedef struct { diff --git a/components/bootloader_support/include/esp_secure_boot.h b/components/bootloader_support/include/esp_secure_boot.h index f9fc57708d..8e33a8b460 100644 --- a/components/bootloader_support/include/esp_secure_boot.h +++ b/components/bootloader_support/include/esp_secure_boot.h @@ -67,9 +67,25 @@ esp_err_t esp_secure_boot_permanently_enable(void); * @param src_addr Starting offset of the data in flash. * @param length Length of data in bytes. Signature is appended -after- length bytes. * + * If flash encryption is enabled, the image will be transparently decrypted while being verified. + * * @return ESP_OK if signature is valid, ESP_ERR_INVALID_STATE if * signature fails, ESP_FAIL for other failures (ie can't read flash). */ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length); +/** @brief Secure boot verification block, on-flash data format. */ +typedef struct { + uint32_t version; + uint8_t signature[64]; +} esp_secure_boot_sig_block_t; + +#define FLASH_OFFS_SECURE_BOOT_IV_DIGEST 0 + +/** @brief Secure boot IV+digest header */ +typedef struct { + uint8_t iv[128]; + uint8_t digest[64]; +} esp_secure_boot_iv_digest_t; + #endif diff --git a/components/bootloader_support/include_priv/bootloader_flash.h b/components/bootloader_support/include_priv/bootloader_flash.h index 769c47b904..763136e03c 100644 --- a/components/bootloader_support/include_priv/bootloader_flash.h +++ b/components/bootloader_support/include_priv/bootloader_flash.h @@ -18,6 +18,9 @@ #include #include #include +#include "esp_spi_flash.h" + +#define FLASH_SECTOR_SIZE 0x1000 /* Provide a Flash API for bootloader_support code, that can be used from bootloader or app code. @@ -56,14 +59,45 @@ void bootloader_munmap(const void *mapping); /** * @brief Read data from Flash. * - * @note Both src and dest have to be 4-byte aligned. + * + * @note All of src, dest and size have to be 4-byte aligned. * * @param src source address of the data in Flash. * @param dest pointer to the destination buffer * @param size length of data + * @param allow_decrypt If true and flash encryption is enabled, data on flash + * will be decrypted transparently as part of the read. + * + * @return ESP_OK on success, ESP_ERR_FLASH_OP_FAIL on SPI failure, + * ESP_ERR_FLASH_OP_TIMEOUT on SPI timeout. + */ +esp_err_t bootloader_flash_read(size_t src_addr, void *dest, size_t size, bool allow_decrypt); + + +/** + * @brief Write data to Flash. + * + * @note All of dest_addr, src and size have to be 4-byte aligned. If write_encrypted is set, dest_addr and size must be 32-byte aligned. + * + * Note: In bootloader, when write_encrypted == true, the src buffer is encrypted in place. + * + * @param dest_addr Destination address to write in Flash. + * @param src Pointer to the data to write to flash + * @param size Length of data in bytes. + * @param write_encrypted If true, data will be written encrypted on flash. + * + * @return ESP_OK on success, ESP_ERR_FLASH_OP_FAIL on SPI failure, + * ESP_ERR_FLASH_OP_TIMEOUT on SPI timeout. + */ +esp_err_t bootloader_flash_write(size_t dest_addr, void *src, size_t size, bool write_encrypted); + +/** + * @brief Erase the Flash sector. + * + * @param sector Sector number, the count starts at sector 0, 4KB per sector. * * @return esp_err_t */ -esp_err_t bootloader_flash_read(size_t src_addr, void *dest, size_t size); +esp_err_t bootloader_flash_erase_sector(size_t sector); #endif diff --git a/components/bootloader_support/include_priv/bootloader_random.h b/components/bootloader_support/include_priv/bootloader_random.h new file mode 100644 index 0000000000..86d42e31d9 --- /dev/null +++ b/components/bootloader_support/include_priv/bootloader_random.h @@ -0,0 +1,25 @@ +// Copyright 2010-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include + +/** + * @brief Fill buffer with 'length' random bytes + * + * @param buffer Pointer to buffer + * @param length This many bytes of random data will be copied to buffer + */ +void bootloader_fill_random(void *buffer, size_t length); diff --git a/components/bootloader_support/src/bootloader_flash.c b/components/bootloader_support/src/bootloader_flash.c index 3fd107dea8..405da732b3 100644 --- a/components/bootloader_support/src/bootloader_flash.c +++ b/components/bootloader_support/src/bootloader_flash.c @@ -46,38 +46,68 @@ void bootloader_munmap(const void *mapping) map = 0; } -esp_err_t bootloader_flash_read(size_t src, void *dest, size_t size) +esp_err_t bootloader_flash_read(size_t src, void *dest, size_t size, bool allow_decrypt) { return spi_flash_read(src, dest, size); } +esp_err_t bootloader_flash_write(size_t dest_addr, void *src, size_t size, bool write_encrypted) +{ + if (write_encrypted) { + return spi_flash_write_encrypted(dest_addr, src, size); + } else { + return spi_flash_write(dest_addr, src, size); + } +} + +esp_err_t bootloader_flash_erase_sector(size_t sector) +{ + return spi_flash_erase_sector(sector); +} + #else /* Bootloader version, uses ROM functions only */ +#include #include #include static const char *TAG = "bootloader_flash"; +/* Use first 50 blocks in MMU for bootloader_mmap, + 50th block for bootloader_flash_read +*/ +#define MMU_BLOCK0_VADDR 0x3f400000 +#define MMU_BLOCK50_VADDR 0x3f720000 +#define MMU_FLASH_MASK 0xffff0000 +#define MMU_BLOCK_SIZE 0x00010000 + static bool mapped; +static uint32_t current_read_mapping = UINT32_MAX; + const void *bootloader_mmap(uint32_t src_addr, uint32_t size) { if (mapped) { ESP_LOGE(TAG, "tried to bootloader_mmap twice"); return NULL; /* can't map twice */ } + if (size > 0x320000) { + /* Allow mapping up to 50 of the 51 available MMU blocks (last one used for reads) */ + ESP_LOGE(TAG, "bootloader_mmap excess size %x", size); + return NULL; + } - uint32_t src_addr_aligned = src_addr & 0xffff0000; - uint32_t count = (size + (src_addr - src_addr_aligned) + 0xffff) / 0x10000; + uint32_t src_addr_aligned = src_addr & MMU_FLASH_MASK; + uint32_t count = (size + (src_addr - src_addr_aligned) + 0xffff) / MMU_BLOCK_SIZE; Cache_Read_Disable(0); Cache_Flush(0); ESP_LOGD(TAG, "mmu set paddr=%08x count=%d", src_addr_aligned, count ); - cache_flash_mmu_set( 0, 0, 0x3f400000, src_addr_aligned, 64, count ); + cache_flash_mmu_set( 0, 0, MMU_BLOCK0_VADDR, src_addr_aligned, 64, count ); Cache_Read_Enable( 0 ); mapped = true; - return (void *)(0x3f400000 + (src_addr - src_addr_aligned)); + return (void *)(MMU_BLOCK0_VADDR + (src_addr - src_addr_aligned)); } void bootloader_munmap(const void *mapping) @@ -88,25 +118,12 @@ void bootloader_munmap(const void *mapping) Cache_Flush(0); mmu_init(0); mapped = false; + current_read_mapping = UINT32_MAX; } } -esp_err_t bootloader_flash_read(size_t src_addr, void *dest, size_t size) +static esp_err_t spi_to_esp_err(SpiFlashOpResult r) { - if(src_addr & 3) { - ESP_LOGE(TAG, "bootloader_flash_read src_addr 0x%x not 4-byte aligned", src_addr); - return ESP_FAIL; - } - if((intptr_t)dest & 3) { - ESP_LOGE(TAG, "bootloader_flash_read dest 0x%x not 4-byte aligned", (intptr_t)dest); - return ESP_FAIL; - } - - Cache_Read_Disable(0); - Cache_Flush(0); - SpiFlashOpResult r = SPIRead(src_addr, dest, size); - Cache_Read_Enable(0); - switch(r) { case SPI_FLASH_RESULT_OK: return ESP_OK; @@ -119,4 +136,101 @@ esp_err_t bootloader_flash_read(size_t src_addr, void *dest, size_t size) } } +static esp_err_t bootloader_flash_read_no_decrypt(size_t src_addr, void *dest, size_t size) +{ + Cache_Read_Disable(0); + Cache_Flush(0); + SpiFlashOpResult r = SPIRead(src_addr, dest, size); + Cache_Read_Enable(0); + + return spi_to_esp_err(r); +} + +static esp_err_t bootloader_flash_read_allow_decrypt(size_t src_addr, void *dest, size_t size) +{ + uint32_t *dest_words = (uint32_t *)dest; + + /* Use the 51st MMU mapping to read from flash in 64KB blocks. + (MMU will transparently decrypt if encryption is enabled.) + */ + for (int word = 0; word < size / 4; word++) { + uint32_t word_src = src_addr + word * 4; /* Read this offset from flash */ + uint32_t map_at = word_src & MMU_FLASH_MASK; /* Map this 64KB block from flash */ + uint32_t *map_ptr; + if (map_at != current_read_mapping) { + /* Move the 64KB mmu mapping window to fit map_at */ + Cache_Read_Disable(0); + Cache_Flush(0); + ESP_LOGD(TAG, "mmu set block paddr=0x%08x (was 0x%08x)", map_at, current_read_mapping); + int e = cache_flash_mmu_set(0, 0, MMU_BLOCK50_VADDR, map_at, 64, 1); + if (e != 0) { + ESP_LOGE(TAG, "cache_flash_mmu_set failed: %d\n", e); + Cache_Read_Enable(0); + return ESP_FAIL; + } + current_read_mapping = map_at; + Cache_Read_Enable(0); + } + map_ptr = (uint32_t *)(MMU_BLOCK50_VADDR + (word_src - map_at)); + dest_words[word] = *map_ptr; + } + return ESP_OK; +} + +esp_err_t bootloader_flash_read(size_t src_addr, void *dest, size_t size, bool allow_decrypt) +{ + if (src_addr & 3) { + ESP_LOGE(TAG, "bootloader_flash_read src_addr 0x%x not 4-byte aligned", src_addr); + return ESP_FAIL; + } + if (size & 3) { + ESP_LOGE(TAG, "bootloader_flash_read size 0x%x not 4-byte aligned", size); + return ESP_FAIL; + } + if ((intptr_t)dest & 3) { + ESP_LOGE(TAG, "bootloader_flash_read dest 0x%x not 4-byte aligned", (intptr_t)dest); + return ESP_FAIL; + } + + if (allow_decrypt) { + return bootloader_flash_read_allow_decrypt(src_addr, dest, size); + } else { + return bootloader_flash_read_no_decrypt(src_addr, dest, size); + } +} + +esp_err_t bootloader_flash_write(size_t dest_addr, void *src, size_t size, bool write_encrypted) +{ + esp_err_t err; + size_t alignment = write_encrypted ? 32 : 4; + if ((dest_addr % alignment) != 0) { + ESP_LOGE(TAG, "bootloader_flash_write dest_addr 0x%x not %d-byte aligned", dest_addr, alignment); + return ESP_FAIL; + } + if ((size % alignment) != 0) { + ESP_LOGE(TAG, "bootloader_flash_write size 0x%x not %d-byte aligned", size, alignment); + return ESP_FAIL; + } + if (((intptr_t)src % 4) != 0) { + ESP_LOGE(TAG, "bootloader_flash_write src 0x%x not 4 byte aligned", (intptr_t)src); + return ESP_FAIL; + } + + err = spi_to_esp_err(SPIUnlock()); + if (err != ESP_OK) { + return err; + } + + if (write_encrypted) { + return spi_to_esp_err(SPI_Encrypt_Write(dest_addr, src, size)); + } else { + return spi_to_esp_err(SPIWrite(dest_addr, src, size)); + } +} + +esp_err_t bootloader_flash_erase_sector(size_t sector) +{ + return spi_to_esp_err(SPIEraseSector(sector)); +} + #endif diff --git a/components/bootloader_support/src/bootloader_random.c b/components/bootloader_support/src/bootloader_random.c new file mode 100644 index 0000000000..fd780c4b7b --- /dev/null +++ b/components/bootloader_support/src/bootloader_random.c @@ -0,0 +1,53 @@ +// Copyright 2010-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include "bootloader_random.h" +#include "soc/wdev_reg.h" + +#ifndef BOOTLOADER_BUILD +#include "esp_system.h" +#endif + +void bootloader_fill_random(void *buffer, size_t length) +{ + uint8_t *buffer_bytes = (uint8_t *)buffer; + uint32_t random; + + /* TODO: enable HW RNG clock + + Until this clock is enabled, this is not secure + */ + + for (int i = 0; i < length; i++) { + if (i == 0 || i % 4 == 0) { /* redundant check is for a compiler warning */ +#ifdef BOOTLOADER_BUILD + /* HW RNG generates 32 bits entropy per 16 APB cycles, + in bootloader CPU clock == APB clock. + + We are being conservative here and waiting at least + that long, as loop shift overhead, etc will add more + cycles. + */ + asm volatile("nop; nop; nop; nop;"); + asm volatile("nop; nop; nop; nop;"); + asm volatile("nop; nop; nop; nop;"); + asm volatile("nop; nop; nop; nop;"); + random = REG_READ(WDEV_RND_REG); +#else + random = esp_random(); +#endif + } + + buffer_bytes[i] = random >> ((i % 4) * 8); + } +} diff --git a/components/bootloader_support/src/efuse.c b/components/bootloader_support/src/efuse.c new file mode 100644 index 0000000000..e90ba1b7f6 --- /dev/null +++ b/components/bootloader_support/src/efuse.c @@ -0,0 +1,47 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include "esp_efuse.h" + +#define EFUSE_CONF_WRITE 0x5A5A /* efuse_pgm_op_ena, force no rd/wr disable */ +#define EFUSE_CONF_READ 0x5AA5 /* efuse_read_op_ena, release force */ + +#define EFUSE_CMD_PGM 0x02 +#define EFUSE_CMD_READ 0x01 + +void esp_efuse_burn_new_values(void) +{ + REG_WRITE(EFUSE_CONF_REG, EFUSE_CONF_WRITE); + REG_WRITE(EFUSE_CMD_REG, EFUSE_CMD_PGM); + while (REG_READ(EFUSE_CMD_REG) != 0) { + } + REG_WRITE(EFUSE_CONF_REG, EFUSE_CONF_READ); + REG_WRITE(EFUSE_CMD_REG, EFUSE_CMD_READ); + while (REG_READ(EFUSE_CMD_REG) != 0) { + } + esp_efuse_reset(); +} + +void esp_efuse_reset(void) +{ + REG_WRITE(EFUSE_CONF_REG, EFUSE_CONF_READ); + const uint32_t block_start[4] = { EFUSE_BLK0_WDATA0_REG, EFUSE_BLK1_WDATA0_REG, + EFUSE_BLK2_WDATA0_REG, EFUSE_BLK3_WDATA0_REG }; + const uint32_t block_end[4] = { EFUSE_BLK0_WDATA6_REG, EFUSE_BLK1_WDATA7_REG, + EFUSE_BLK2_WDATA7_REG, EFUSE_BLK3_WDATA7_REG }; + for (int i = 0; i < 4; i++) { + for (uint32_t r = block_start[i]; r <= block_end[i]; r+= 4) { + REG_WRITE(r, 0); + } + } +} diff --git a/components/bootloader_support/src/esp_image_format.c b/components/bootloader_support/src/esp_image_format.c index ca75f6ae45..8b156680c6 100644 --- a/components/bootloader_support/src/esp_image_format.c +++ b/components/bootloader_support/src/esp_image_format.c @@ -22,26 +22,30 @@ static const char *TAG = "esp_image"; #define SIXTEEN_MB 0x1000000 #define ESP_ROM_CHECKSUM_INITIAL 0xEF -esp_err_t esp_image_load_header(uint32_t src_addr, esp_image_header_t *image_header) +esp_err_t esp_image_load_header(uint32_t src_addr, bool log_errors, esp_image_header_t *image_header) { esp_err_t err; ESP_LOGD(TAG, "reading image header @ 0x%x", src_addr); - err = bootloader_flash_read(src_addr, image_header, sizeof(esp_image_header_t)); + err = bootloader_flash_read(src_addr, image_header, sizeof(esp_image_header_t), true); if (err == ESP_OK) { if (image_header->magic != ESP_IMAGE_HEADER_MAGIC) { - ESP_LOGE(TAG, "image at 0x%x has invalid magic byte", src_addr); + if (log_errors) { + ESP_LOGE(TAG, "image at 0x%x has invalid magic byte", src_addr); + } err = ESP_ERR_IMAGE_INVALID; } - if (image_header->spi_mode > ESP_IMAGE_SPI_MODE_SLOW_READ) { - ESP_LOGW(TAG, "image at 0x%x has invalid SPI mode %d", src_addr, image_header->spi_mode); - } - if (image_header->spi_speed > ESP_IMAGE_SPI_SPEED_80M) { - ESP_LOGW(TAG, "image at 0x%x has invalid SPI speed %d", src_addr, image_header->spi_speed); - } - if (image_header->spi_size > ESP_IMAGE_FLASH_SIZE_MAX) { - ESP_LOGW(TAG, "image at 0x%x has invalid SPI size %d", src_addr, image_header->spi_size); + if (log_errors) { + if (image_header->spi_mode > ESP_IMAGE_SPI_MODE_SLOW_READ) { + ESP_LOGW(TAG, "image at 0x%x has invalid SPI mode %d", src_addr, image_header->spi_mode); + } + if (image_header->spi_speed > ESP_IMAGE_SPI_SPEED_80M) { + ESP_LOGW(TAG, "image at 0x%x has invalid SPI speed %d", src_addr, image_header->spi_speed); + } + if (image_header->spi_size > ESP_IMAGE_FLASH_SIZE_MAX) { + ESP_LOGW(TAG, "image at 0x%x has invalid SPI size %d", src_addr, image_header->spi_size); + } } } @@ -51,23 +55,27 @@ esp_err_t esp_image_load_header(uint32_t src_addr, esp_image_header_t *image_hea return err; } -esp_err_t esp_image_load_segment_header(uint8_t index, uint32_t src_addr, const esp_image_header_t *image_header, esp_image_segment_header_t *segment_header, uint32_t *segment_data_offset) +esp_err_t esp_image_load_segment_header(uint8_t index, uint32_t src_addr, const esp_image_header_t *image_header, bool log_errors, esp_image_segment_header_t *segment_header, uint32_t *segment_data_offset) { esp_err_t err = ESP_OK; uint32_t next_addr = src_addr + sizeof(esp_image_header_t); if(index >= image_header->segment_count) { - ESP_LOGE(TAG, "index %d higher than segment count %d", index, image_header->segment_count); + if (log_errors) { + ESP_LOGE(TAG, "index %d higher than segment count %d", index, image_header->segment_count); + } return ESP_ERR_INVALID_ARG; } for(int i = 0; i <= index && err == ESP_OK; i++) { ESP_LOGV(TAG, "loading segment header %d at offset 0x%x", i, next_addr); - err = bootloader_flash_read(next_addr, segment_header, sizeof(esp_image_segment_header_t)); + err = bootloader_flash_read(next_addr, segment_header, sizeof(esp_image_segment_header_t), true); if (err == ESP_OK) { if ((segment_header->data_len & 3) != 0 || segment_header->data_len >= SIXTEEN_MB) { - ESP_LOGE(TAG, "invalid segment length 0x%x", segment_header->data_len); + if (log_errors) { + ESP_LOGE(TAG, "invalid segment length 0x%x", segment_header->data_len); + } err = ESP_ERR_IMAGE_INVALID; } next_addr += sizeof(esp_image_segment_header_t); @@ -85,15 +93,14 @@ esp_err_t esp_image_load_segment_header(uint8_t index, uint32_t src_addr, const return err; } -esp_err_t esp_image_basic_verify(uint32_t src_addr, uint32_t *p_length) +esp_err_t esp_image_basic_verify(uint32_t src_addr, bool log_errors, uint32_t *p_length) { esp_err_t err; - uint8_t buf[16]; + uint8_t buf[128]; uint8_t checksum = ESP_ROM_CHECKSUM_INITIAL; esp_image_header_t image_header; esp_image_segment_header_t segment_header = { 0 }; uint32_t segment_data_offs = 0; - const uint8_t *segment_data; uint32_t end_addr; uint32_t length; @@ -101,7 +108,7 @@ esp_err_t esp_image_basic_verify(uint32_t src_addr, uint32_t *p_length) *p_length = 0; } - err = esp_image_load_header(src_addr, &image_header); + err = esp_image_load_header(src_addr, log_errors, &image_header); if (err != ESP_OK) { return err; } @@ -110,34 +117,38 @@ esp_err_t esp_image_basic_verify(uint32_t src_addr, uint32_t *p_length) /* Checksum each segment's data */ for (int i = 0; i < image_header.segment_count; i++) { - err = esp_image_load_segment_header(i, src_addr, &image_header, + err = esp_image_load_segment_header(i, src_addr, &image_header, log_errors, &segment_header, &segment_data_offs); if (err != ESP_OK) { return err; } - segment_data = bootloader_mmap(segment_data_offs, segment_header.data_len); - if (segment_data == NULL) { - ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", segment_data_offs, segment_header.data_len); - return ESP_FAIL; + for (int i = 0; i < segment_header.data_len; i += sizeof(buf)) { + err = bootloader_flash_read(segment_data_offs + i, buf, sizeof(buf), true); + if (err != ESP_OK) { + return err; + } + for (int j = 0; j < sizeof(buf) && i + j < segment_header.data_len; j++) { + checksum ^= buf[j]; + } } - for(int i = 0; i < segment_header.data_len; i++) { - checksum ^= segment_data[i]; - } - bootloader_munmap(segment_data); } /* End of image, verify checksum */ end_addr = segment_data_offs + segment_header.data_len; if (end_addr < src_addr) { - ESP_LOGE(TAG, "image offset has wrapped"); + if (log_errors) { + ESP_LOGE(TAG, "image offset has wrapped"); + } return ESP_ERR_IMAGE_INVALID; } length = end_addr - src_addr; if (length >= SIXTEEN_MB) { - ESP_LOGE(TAG, "invalid total length 0x%x", length); + if (log_errors) { + ESP_LOGE(TAG, "invalid total length 0x%x", length); + } return ESP_ERR_IMAGE_INVALID; } @@ -147,10 +158,12 @@ esp_err_t esp_image_basic_verify(uint32_t src_addr, uint32_t *p_length) length = length - (length % 16); ESP_LOGV(TAG, "padded image length 0x%x", length); ESP_LOGD(TAG, "reading checksum block at 0x%x", src_addr + length - 16); - bootloader_flash_read(src_addr + length - 16, buf, 16); + bootloader_flash_read(src_addr + length - 16, buf, 16, true); if (checksum != buf[15]) { - ESP_LOGE(TAG, "checksum failed. Calculated 0x%x read 0x%x", - checksum, buf[15]); + if (log_errors) { + ESP_LOGE(TAG, "checksum failed. Calculated 0x%x read 0x%x", + checksum, buf[15]); + } return ESP_ERR_IMAGE_INVALID; } diff --git a/components/bootloader_support/src/flash_encrypt.c b/components/bootloader_support/src/flash_encrypt.c new file mode 100644 index 0000000000..f4fd7e783d --- /dev/null +++ b/components/bootloader_support/src/flash_encrypt.c @@ -0,0 +1,329 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "bootloader_flash.h" +#include "bootloader_random.h" +#include "esp_image_format.h" +#include "esp_flash_encrypt.h" +#include "esp_flash_partitions.h" +#include "esp_flash_data_types.h" +#include "esp_secure_boot.h" +#include "esp_efuse.h" +#include "esp_log.h" +#include "rom/secure_boot.h" + +#include "rom/cache.h" +#include "rom/spi_flash.h" /* TODO: Remove this */ + +static const char *TAG = "flash_encrypt"; + +/* Static functions for stages of flash encryption */ +static esp_err_t initialise_flash_encryption(void); +static esp_err_t encrypt_flash_contents(uint32_t flash_crypt_cnt, bool flash_crypt_wr_dis); +static esp_err_t encrypt_bootloader(); +static esp_err_t encrypt_and_load_partition_table(esp_partition_info_t *partition_table, int *num_partitions); +static esp_err_t encrypt_partition(int index, const esp_partition_info_t *partition); + +esp_err_t esp_flash_encrypt_check_and_update(void) +{ + uint32_t efuse_blk0 = REG_READ(EFUSE_BLK0_RDATA0_REG); + ESP_LOGV(TAG, "efuse_blk0 raw value %08x", efuse_blk0); + uint32_t flash_crypt_cnt = (efuse_blk0 & EFUSE_RD_FLASH_CRYPT_CNT_M) >> EFUSE_RD_FLASH_CRYPT_CNT_S; + bool flash_crypt_wr_dis = efuse_blk0 & EFUSE_WR_DIS_FLASH_CRYPT_CNT; + ESP_LOGV(TAG, "efuse FLASH_CRYPT_CNT 0x%x WR_DIS_FLASH_CRYPT_CNT 0x%x", flash_crypt_cnt, flash_crypt_wr_dis); + + if (__builtin_parity(flash_crypt_cnt) == 1) { + /* Flash is already encrypted */ + int left = (7 - __builtin_popcount(flash_crypt_cnt)) / 2; + if (flash_crypt_wr_dis) { + left = 0; /* can't update FLASH_CRYPT_CNT, no more flashes */ + } + ESP_LOGI(TAG, "flash encryption is enabled (%d plaintext flashes left)", left); + return ESP_OK; + } + else { + /* Flash is not encrypted, so encrypt it! */ + return encrypt_flash_contents(flash_crypt_cnt, flash_crypt_wr_dis); + } +} + +static esp_err_t initialise_flash_encryption(void) +{ + /* Before first flash encryption pass, need to initialise key & crypto config */ + + /* Generate key */ + uint32_t dis_reg = REG_READ(EFUSE_BLK0_RDATA0_REG); + bool efuse_key_read_protected = dis_reg & EFUSE_RD_DIS_BLK1; + bool efuse_key_write_protected = dis_reg & EFUSE_WR_DIS_BLK1; + if (efuse_key_read_protected == false + && efuse_key_write_protected == false + && REG_READ(EFUSE_BLK1_RDATA0_REG) == 0 + && REG_READ(EFUSE_BLK1_RDATA1_REG) == 0 + && REG_READ(EFUSE_BLK1_RDATA2_REG) == 0 + && REG_READ(EFUSE_BLK1_RDATA3_REG) == 0 + && REG_READ(EFUSE_BLK1_RDATA4_REG) == 0 + && REG_READ(EFUSE_BLK1_RDATA5_REG) == 0 + && REG_READ(EFUSE_BLK1_RDATA6_REG) == 0 + && REG_READ(EFUSE_BLK1_RDATA7_REG) == 0) { + + /* On-device key generation is temporarily disabled, until + * RNG operation during bootloader is qualified. + * See docs/security/flash-encryption.rst for details. */ + ESP_LOGE(TAG, "On-device key generation is not yet available."); + return ESP_ERR_NOT_SUPPORTED; + } else { + + if(!(efuse_key_read_protected && efuse_key_write_protected)) { + ESP_LOGE(TAG, "Flash encryption key has to be either unset or both read and write protected"); + return ESP_ERR_INVALID_STATE; + } + ESP_LOGW(TAG, "Using pre-loaded flash encryption key in EFUSE block 1"); + } + /* CRYPT_CONFIG determines which bits of the AES block key are XORed + with bits from the flash address, to provide the key tweak. + + CRYPT_CONFIG == 0 is effectively AES ECB mode (NOT SUPPORTED) + + For now this is hardcoded to XOR all 256 bits of the key. + + If you need to override it, you can pre-burn this efuse to the + desired value and then write-protect it, in which case this + operation does nothing. Please note this is not recommended! + */ + ESP_LOGI(TAG, "Setting CRYPT_CONFIG efuse to 0xF"); + REG_WRITE(EFUSE_BLK0_WDATA5_REG, EFUSE_FLASH_CRYPT_CONFIG_M); + esp_efuse_burn_new_values(); + + uint32_t new_wdata6 = 0; +#ifndef CONFIG_FLASH_ENCRYPTION_UART_BOOTLOADER_ALLOW_ENCRYPT + ESP_LOGI(TAG, "Disable UART bootloader encryption..."); + new_wdata6 |= EFUSE_DISABLE_DL_ENCRYPT; +#else + ESP_LOGW(TAG, "Not disabling UART bootloader encryption"); +#endif +#ifndef CONFIG_FLASH_ENCRYPTION_UART_BOOTLOADER_ALLOW_DECRYPT + ESP_LOGI(TAG, "Disable UART bootloader decryption..."); + new_wdata6 |= EFUSE_DISABLE_DL_DECRYPT; +#else + ESP_LOGW(TAG, "Not disabling UART bootloader decryption - SECURITY COMPROMISED"); +#endif +#ifndef CONFIG_FLASH_ENCRYPTION_UART_BOOTLOADER_ALLOW_CACHE + ESP_LOGI(TAG, "Disable UART bootloader MMU cache..."); + new_wdata6 |= EFUSE_DISABLE_DL_CACHE; +#else + ESP_LOGW(TAG, "Not disabling UART bootloader MMU cache - SECURITY COMPROMISED"); +#endif +#ifndef CONFIG_SECURE_BOOT_ALLOW_JTAG + ESP_LOGI(TAG, "Disable JTAG..."); + new_wdata6 |= EFUSE_RD_DISABLE_JTAG; +#else + ESP_LOGW(TAG, "Not disabling JTAG - SECURITY COMPROMISED"); +#endif + + if (new_wdata6 != 0) { + REG_WRITE(EFUSE_BLK0_WDATA6_REG, new_wdata6); + esp_efuse_burn_new_values(); + } + + return ESP_OK; +} + +/* Encrypt all flash data that should be encrypted */ +static esp_err_t encrypt_flash_contents(uint32_t flash_crypt_cnt, bool flash_crypt_wr_dis) +{ + esp_err_t err; + esp_partition_info_t partition_table[ESP_PARTITION_TABLE_MAX_ENTRIES]; + int num_partitions; + + /* If the last flash_crypt_cnt bit is burned or write-disabled, the + device can't re-encrypt itself. */ + if (flash_crypt_wr_dis || flash_crypt_cnt == 0xFF) { + ESP_LOGE(TAG, "Cannot re-encrypt data (FLASH_CRYPT_CNT 0x%02x write disabled %d", flash_crypt_cnt, flash_crypt_wr_dis); + return ESP_FAIL; + } + + if (flash_crypt_cnt == 0) { + /* Very first flash of encrypted data: generate keys, etc. */ + err = initialise_flash_encryption(); + if (err != ESP_OK) { + return err; + } + } + + err = encrypt_bootloader(); + if (err != ESP_OK) { + return err; + } + + err = encrypt_and_load_partition_table(partition_table, &num_partitions); + if (err != ESP_OK) { + return err; + } + + /* Now iterate the just-loaded partition table, looking for entries to encrypt + */ + + /* Go through each partition and encrypt if necessary */ + for (int i = 0; i < num_partitions; i++) { + err = encrypt_partition(i, &partition_table[i]); + if (err != ESP_OK) { + return err; + } + } + + ESP_LOGD(TAG, "All flash regions checked for encryption pass"); + + /* Set least significant 0-bit in flash_crypt_cnt */ + int ffs_inv = __builtin_ffs((~flash_crypt_cnt) & 0xFF); + /* ffs_inv shouldn't be zero, as zero implies flash_crypt_cnt == 0xFF */ + uint32_t new_flash_crypt_cnt = flash_crypt_cnt + (1 << (ffs_inv - 1)); + ESP_LOGD(TAG, "FLASH_CRYPT_CNT 0x%x -> 0x%x", flash_crypt_cnt, new_flash_crypt_cnt); + REG_SET_FIELD(EFUSE_BLK0_WDATA0_REG, EFUSE_FLASH_CRYPT_CNT, new_flash_crypt_cnt); + esp_efuse_burn_new_values(); + + ESP_LOGI(TAG, "Flash encryption completed"); + + return ESP_OK; +} + +static esp_err_t encrypt_bootloader() +{ + esp_err_t err; + uint32_t image_length; + /* Check for plaintext bootloader */ + if (esp_image_basic_verify(ESP_BOOTLOADER_OFFSET, false, &image_length) == ESP_OK) { + ESP_LOGD(TAG, "bootloader is plaintext. Encrypting..."); + err = esp_flash_encrypt_region(ESP_BOOTLOADER_OFFSET, image_length); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to encrypt bootloader in place: 0x%x", err); + return err; + } + + if (esp_secure_boot_enabled()) { + /* If secure boot is enabled and bootloader was plaintext, also + need to encrypt secure boot IV+digest. + */ + ESP_LOGD(TAG, "Encrypting secure bootloader IV & digest..."); + err = esp_flash_encrypt_region(FLASH_OFFS_SECURE_BOOT_IV_DIGEST, + FLASH_SECTOR_SIZE); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to encrypt bootloader IV & digest in place: 0x%x", err); + return err; + } + } + } + else { + ESP_LOGW(TAG, "no valid bootloader was found"); + } + + return ESP_OK; +} + +static esp_err_t encrypt_and_load_partition_table(esp_partition_info_t *partition_table, int *num_partitions) +{ + esp_err_t err; + /* Check for plaintext partition table */ + err = bootloader_flash_read(ESP_PARTITION_TABLE_OFFSET, partition_table, ESP_PARTITION_TABLE_MAX_LEN, false); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to read partition table data"); + return err; + } + if (esp_partition_table_basic_verify(partition_table, false, num_partitions) == ESP_OK) { + ESP_LOGD(TAG, "partition table is plaintext. Encrypting..."); + esp_err_t err = esp_flash_encrypt_region(ESP_PARTITION_TABLE_OFFSET, + FLASH_SECTOR_SIZE); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to encrypt partition table in place. %x", err); + return err; + } + } + else { + ESP_LOGE(TAG, "Failed to read partition table data - not plaintext?"); + return ESP_ERR_INVALID_STATE; + } + + /* Valid partition table loded */ + return ESP_OK; +} + + +static esp_err_t encrypt_partition(int index, const esp_partition_info_t *partition) +{ + esp_err_t err; + uint32_t image_len = partition->pos.size; + bool should_encrypt = (partition->flags & PART_FLAG_ENCRYPTED); + + if (partition->type == PART_TYPE_APP) { + /* check if the partition holds an unencrypted app */ + if (esp_image_basic_verify(partition->pos.offset, false, &image_len) == ESP_OK) { + if(image_len > partition->pos.size) { + ESP_LOGE(TAG, "partition entry %d has image longer than partition (%d vs %d)", index, image_len, partition->pos.size); + should_encrypt = false; + } else { + should_encrypt = true; + } + } else { + should_encrypt = false; + } + } + + if (!should_encrypt) { + return ESP_OK; + } + else { + /* should_encrypt */ + ESP_LOGI(TAG, "Encrypting partition %d at offset 0x%x...", index, partition->pos.offset); + + err = esp_flash_encrypt_region(partition->pos.offset, partition->pos.size); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to encrypt partition %d", index); + } + return err; + } +} + + +esp_err_t esp_flash_encrypt_region(uint32_t src_addr, size_t data_length) +{ + esp_err_t err; + uint32_t buf[FLASH_SECTOR_SIZE / sizeof(uint32_t)]; + + if (src_addr % FLASH_SECTOR_SIZE != 0) { + ESP_LOGE(TAG, "esp_flash_encrypt_region bad src_addr 0x%x",src_addr); + return ESP_FAIL; + } + + for (size_t i = 0; i < data_length; i += FLASH_SECTOR_SIZE) { + uint32_t sec_start = i + src_addr; + err = bootloader_flash_read(sec_start, buf, FLASH_SECTOR_SIZE, false); + if (err != ESP_OK) { + goto flash_failed; + } + err = bootloader_flash_erase_sector(sec_start / FLASH_SECTOR_SIZE); + if (err != ESP_OK) { + goto flash_failed; + } + err = bootloader_flash_write(sec_start, buf, FLASH_SECTOR_SIZE, true); + if (err != ESP_OK) { + goto flash_failed; + } + } + return ESP_OK; + + flash_failed: + ESP_LOGE(TAG, "flash operation failed: 0x%x", err); + return err; +} diff --git a/components/bootloader_support/src/flash_partitions.c b/components/bootloader_support/src/flash_partitions.c new file mode 100644 index 0000000000..ed427df1a6 --- /dev/null +++ b/components/bootloader_support/src/flash_partitions.c @@ -0,0 +1,49 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include "esp_flash_partitions.h" +#include "esp_log.h" + +static const char *TAG = "flash_parts"; + +esp_err_t esp_partition_table_basic_verify(const esp_partition_info_t *partition_table, bool log_errors, int *num_partitions) +{ + int num_parts; + *num_partitions = 0; + + for(num_parts = 0; num_parts < ESP_PARTITION_TABLE_MAX_ENTRIES; num_parts++) { + const esp_partition_info_t *part = &partition_table[num_parts]; + + if(part->magic == 0xFFFF + && part->type == PART_TYPE_END + && part->subtype == PART_SUBTYPE_END) { + /* TODO: check md5 */ + ESP_LOGD(TAG, "partition table verified, %d entries", num_parts); + *num_partitions = num_parts; + return ESP_OK; + } + + if(part->magic != ESP_PARTITION_MAGIC) { + if (log_errors) { + ESP_LOGE(TAG, "partition %d invalid magic number 0x%x", num_parts, part->magic); + } + return ESP_ERR_INVALID_STATE; + } + } + + if (log_errors) { + ESP_LOGE(TAG, "partition table has no terminating entry, not valid"); + } + return ESP_ERR_INVALID_STATE; +} + diff --git a/components/bootloader_support/src/secure_boot.c b/components/bootloader_support/src/secure_boot.c index d908d39c38..c5ae1f19ea 100644 --- a/components/bootloader_support/src/secure_boot.c +++ b/components/bootloader_support/src/secure_boot.c @@ -20,7 +20,6 @@ #include "rom/cache.h" #include "rom/ets_sys.h" -#include "rom/spi_flash.h" #include "rom/secure_boot.h" #include "soc/dport_reg.h" @@ -31,15 +30,14 @@ #include "sdkconfig.h" #include "bootloader_flash.h" +#include "bootloader_random.h" #include "esp_image_format.h" #include "esp_secure_boot.h" +#include "esp_flash_encrypt.h" +#include "esp_efuse.h" static const char* TAG = "secure_boot"; -#define HASH_BLOCK_SIZE 128 -#define IV_LEN HASH_BLOCK_SIZE -#define DIGEST_LEN 64 - /** * @function : secure_boot_generate * @description: generate boot digest (aka "abstract") & iv @@ -47,39 +45,26 @@ static const char* TAG = "secure_boot"; * @inputs: image_len - length of image to calculate digest for */ static bool secure_boot_generate(uint32_t image_len){ - SpiFlashOpResult spiRet; - /* buffer is uint32_t not uint8_t to meet ROM SPI API signature */ - uint32_t buf[IV_LEN / sizeof(uint32_t)]; - const void *image; + esp_err_t err; + esp_secure_boot_iv_digest_t digest; + const uint32_t *image; /* hardware secure boot engine only takes full blocks, so round up the image length. The additional data should all be 0xFF. */ - if (image_len % HASH_BLOCK_SIZE != 0) { - image_len = (image_len / HASH_BLOCK_SIZE + 1) * HASH_BLOCK_SIZE; + if (image_len % sizeof(digest.iv) != 0) { + image_len = (image_len / sizeof(digest.iv) + 1) * sizeof(digest.iv); } ets_secure_boot_start(); - ets_secure_boot_rd_iv(buf); + ets_secure_boot_rd_iv((uint32_t *)digest.iv); ets_secure_boot_hash(NULL); - Cache_Read_Disable(0); /* iv stored in sec 0 */ - spiRet = SPIEraseSector(0); - if (spiRet != SPI_FLASH_RESULT_OK) + err = bootloader_flash_erase_sector(0); + if (err != ESP_OK) { - ESP_LOGE(TAG, "SPI erase failed %d", spiRet); + ESP_LOGE(TAG, "SPI erase failed: 0x%x", err); return false; } - Cache_Read_Enable(0); - - /* write iv to flash, 0x0000, 128 bytes (1024 bits) */ - ESP_LOGD(TAG, "write iv to flash."); - spiRet = SPIWrite(0, buf, IV_LEN); - if (spiRet != SPI_FLASH_RESULT_OK) - { - ESP_LOGE(TAG, "SPI write failed %d", spiRet); - return false; - } - bzero(buf, sizeof(buf)); /* generate digest from image contents */ image = bootloader_mmap(0x1000, image_len); @@ -87,22 +72,22 @@ static bool secure_boot_generate(uint32_t image_len){ ESP_LOGE(TAG, "bootloader_mmap(0x1000, 0x%x) failed", image_len); return false; } - for (int i = 0; i < image_len; i+= HASH_BLOCK_SIZE) { - ets_secure_boot_hash(image + i/sizeof(void *)); + for (int i = 0; i < image_len; i+= sizeof(digest.iv)) { + ets_secure_boot_hash(&image[i/sizeof(uint32_t)]); } bootloader_munmap(image); ets_secure_boot_obtain(); - ets_secure_boot_rd_abstract(buf); + ets_secure_boot_rd_abstract((uint32_t *)digest.digest); ets_secure_boot_finish(); - ESP_LOGD(TAG, "write digest to flash."); - spiRet = SPIWrite(0x80, buf, DIGEST_LEN); - if (spiRet != SPI_FLASH_RESULT_OK) { - ESP_LOGE(TAG, "SPI write failed %d", spiRet); + ESP_LOGD(TAG, "write iv+digest to flash"); + err = bootloader_flash_write(FLASH_OFFS_SECURE_BOOT_IV_DIGEST, &digest, + sizeof(digest), esp_flash_encryption_enabled()); + if (err != ESP_OK) { + ESP_LOGE(TAG, "SPI write failed: 0x%x", err); return false; } - ESP_LOGD(TAG, "write digest to flash."); Cache_Read_Enable(0); return true; } @@ -111,14 +96,9 @@ static bool secure_boot_generate(uint32_t image_len){ static inline void burn_efuses() { #ifdef CONFIG_SECURE_BOOT_TEST_MODE - ESP_LOGE(TAG, "SECURE BOOT TEST MODE. Not really burning any efuses!"); + ESP_LOGE(TAG, "SECURE BOOT TEST MODE. Not really burning any efuses! NOT SECURE"); #else - REG_WRITE(EFUSE_CONF_REG, 0x5A5A); /* efuse_pgm_op_ena, force no rd/wr disable */ - REG_WRITE(EFUSE_CMD_REG, 0x02); /* efuse_pgm_cmd */ - while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_pagm_cmd=0 */ - REG_WRITE(EFUSE_CONF_REG, 0x5AA5); /* efuse_read_op_ena, release force */ - REG_WRITE(EFUSE_CMD_REG, 0x01); /* efuse_read_cmd */ - while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_read_cmd=0 */ + esp_efuse_burn_new_values(); #endif } @@ -131,7 +111,7 @@ esp_err_t esp_secure_boot_permanently_enable(void) { return ESP_OK; } - err = esp_image_basic_verify(0x1000, &image_len); + err = esp_image_basic_verify(0x1000, true, &image_len); if (err != ESP_OK) { ESP_LOGE(TAG, "bootloader image appears invalid! error %d", err); return err; @@ -150,25 +130,12 @@ esp_err_t esp_secure_boot_permanently_enable(void) { && REG_READ(EFUSE_BLK2_RDATA5_REG) == 0 && REG_READ(EFUSE_BLK2_RDATA6_REG) == 0 && REG_READ(EFUSE_BLK2_RDATA7_REG) == 0) { - ESP_LOGI(TAG, "Generating new secure boot key..."); - /* reuse the secure boot IV generation function to generate - the key, as this generator uses the hardware RNG. */ - uint32_t buf[32]; - ets_secure_boot_start(); - ets_secure_boot_rd_iv(buf); - ets_secure_boot_finish(); - for (int i = 0; i < 8; i++) { - ESP_LOGV(TAG, "EFUSE_BLK2_WDATA%d_REG = 0x%08x", i, buf[i]); - REG_WRITE(EFUSE_BLK2_WDATA0_REG + 4*i, buf[i]); - } - bzero(buf, sizeof(buf)); - burn_efuses(); - ESP_LOGI(TAG, "Read & write protecting new key..."); - REG_WRITE(EFUSE_BLK0_WDATA0_REG, EFUSE_WR_DIS_BLK2 | EFUSE_RD_DIS_BLK2); - burn_efuses(); - efuse_key_read_protected = true; - efuse_key_write_protected = true; + /* On-device key generation is temporarily disabled, until + * RNG operation during bootloader is qualified. + * See docs/security/secure-boot.rst for details. */ + ESP_LOGE(TAG, "On-device key generation is not yet available."); + return ESP_ERR_NOT_SUPPORTED; } else { ESP_LOGW(TAG, "Using pre-loaded secure boot key in EFUSE block 2"); } @@ -180,6 +147,7 @@ esp_err_t esp_secure_boot_permanently_enable(void) { } ESP_LOGI(TAG, "Digest generation complete."); +#ifndef CONFIG_SECURE_BOOT_TEST_MODE if (!efuse_key_read_protected) { ESP_LOGE(TAG, "Pre-loaded key is not read protected. Refusing to blow secure boot efuse."); return ESP_ERR_INVALID_STATE; @@ -188,21 +156,26 @@ esp_err_t esp_secure_boot_permanently_enable(void) { ESP_LOGE(TAG, "Pre-loaded key is not write protected. Refusing to blow secure boot efuse."); return ESP_ERR_INVALID_STATE; } +#endif ESP_LOGI(TAG, "blowing secure boot efuse..."); ESP_LOGD(TAG, "before updating, EFUSE_BLK0_RDATA6 %x", REG_READ(EFUSE_BLK0_RDATA6_REG)); uint32_t new_wdata6 = EFUSE_RD_ABS_DONE_0; - #ifdef CONFIG_SECURE_BOOT_DISABLE_JTAG - ESP_LOGI(TAG, "disabling JTAG..."); +#ifndef CONFIG_SECURE_BOOT_ALLOW_JTAG + ESP_LOGI(TAG, "Disable JTAG..."); new_wdata6 |= EFUSE_RD_DISABLE_JTAG; - #endif +#else + ESP_LOGW(TAG, "Not disabling JTAG - SECURITY COMPROMISED"); +#endif - #ifdef CONFIG_SECURE_BOOT_DISABLE_UART_BOOTLOADER - ESP_LOGI(TAG, "disabling UART bootloader..."); - new_wdata6 |= EFUSE_RD_CONSOLE_DEBUG_DISABLE_S; - #endif +#ifndef CONFIG_SECURE_BOOT_ALLOW_ROM_BASIC + ESP_LOGI(TAG, "Disable ROM BASIC interpreter fallback..."); + new_wdata6 |= EFUSE_RD_CONSOLE_DEBUG_DISABLE; +#else + ESP_LOGW(TAG, "Not disabling ROM BASIC fallback - SECURITY COMPROMISED"); +#endif REG_WRITE(EFUSE_BLK0_WDATA6_REG, new_wdata6); burn_efuses(); diff --git a/components/bootloader_support/src/secure_boot_signatures.c b/components/bootloader_support/src/secure_boot_signatures.c index 076dfb335c..3ca2f2e919 100644 --- a/components/bootloader_support/src/secure_boot_signatures.c +++ b/components/bootloader_support/src/secure_boot_signatures.c @@ -27,11 +27,6 @@ typedef SHA_CTX sha_context; #include "hwcrypto/sha.h" #endif -typedef struct { - uint32_t version; - uint8_t signature[64]; -} signature_block_t; - static const char* TAG = "secure_boot"; extern const uint8_t signature_verification_key_start[] asm("_binary_signature_verification_key_bin_start"); @@ -47,7 +42,7 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length) uint8_t digest[32]; ptrdiff_t keylen; const uint8_t *data; - const signature_block_t *sigblock; + const esp_secure_boot_sig_block_t *sigblock; bool is_valid; #ifdef BOOTLOADER_BUILD const uint8_t *digest_data; @@ -56,13 +51,13 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length) ESP_LOGD(TAG, "verifying signature src_addr 0x%x length 0x%x", src_addr, length); - data = bootloader_mmap(src_addr, length + sizeof(signature_block_t)); + data = bootloader_mmap(src_addr, length + sizeof(esp_secure_boot_sig_block_t)); if(data == NULL) { - ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", src_addr, length+sizeof(signature_block_t)); + ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", src_addr, length+sizeof(esp_secure_boot_sig_block_t)); return ESP_FAIL; } - sigblock = (const signature_block_t *)(data + length); + sigblock = (const esp_secure_boot_sig_block_t *)(data + length); if (sigblock->version != 0) { ESP_LOGE(TAG, "src 0x%x has invalid signature version field 0x%08x", src_addr, sigblock->version); diff --git a/components/bt/component.mk b/components/bt/component.mk index 6d1bac3c8b..a51478af93 100644 --- a/components/bt/component.mk +++ b/components/bt/component.mk @@ -33,8 +33,10 @@ COMPONENT_ADD_INCLUDEDIRS := bluedroid/bta/include \ LIBS := btdm_app COMPONENT_ADD_LDFLAGS := -lbt -L $(COMPONENT_PATH)/lib \ - $(addprefix -l,$(LIBS)) \ - $(LINKER_SCRIPTS) + $(addprefix -l,$(LIBS)) + +# re-link program if BT binary libs change +COMPONENT_ADD_LINKER_DEPS := $(patsubst %,$(COMPONENT_PATH)/lib/lib%.a,$(LIBS)) COMPONENT_SRCDIRS := bluedroid/bta/dm \ bluedroid/bta/gatt \ @@ -68,7 +70,4 @@ COMPONENT_SRCDIRS := bluedroid/bta/dm \ bluedroid \ . -ALL_LIB_FILES := $(patsubst %,$(COMPONENT_PATH)/lib/lib%.a,$(LIBS)) -$(COMPONENT_LIBRARY): $(ALL_LIB_FILES) - COMPONENT_SUBMODULES += lib diff --git a/components/driver/gpio.c b/components/driver/gpio.c index f8694597a8..1f916921bc 100644 --- a/components/driver/gpio.c +++ b/components/driver/gpio.c @@ -14,6 +14,7 @@ #include #include "esp_err.h" #include "esp_intr.h" +#include "esp_intr_alloc.h" #include "freertos/FreeRTOS.h" #include "freertos/xtensa_api.h" #include "driver/gpio.h" @@ -21,11 +22,12 @@ #include "soc/soc.h" #include "esp_log.h" -static const char* GPIO_TAG = "GPIO"; -#define GPIO_CHECK(a, str, ret_val) if (!(a)) { \ - ESP_LOGE(GPIO_TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \ - return (ret_val); \ - } +static const char* GPIO_TAG = "gpio"; +#define GPIO_CHECK(a, str, ret_val) \ + if (!(a)) { \ + ESP_LOGE(GPIO_TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str); \ + return (ret_val); \ + } const uint32_t GPIO_PIN_MUX_REG[GPIO_PIN_COUNT] = { GPIO_PIN_REG_0, @@ -320,14 +322,10 @@ esp_err_t gpio_config(gpio_config_t *pGPIOConfig) return ESP_OK; } -esp_err_t gpio_isr_register(uint32_t gpio_intr_num, void (*fn)(void*), void * arg) +esp_err_t gpio_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags, gpio_isr_handle_t *handle) { GPIO_CHECK(fn, "GPIO ISR null", ESP_ERR_INVALID_ARG); - ESP_INTR_DISABLE(gpio_intr_num); - intr_matrix_set(xPortGetCoreID(), ETS_GPIO_INTR_SOURCE, gpio_intr_num); - xt_set_interrupt_handler(gpio_intr_num, fn, arg); - ESP_INTR_ENABLE(gpio_intr_num); - return ESP_OK; + return esp_intr_alloc(ETS_GPIO_INTR_SOURCE, intr_alloc_flags, fn, arg, handle); } /*only level interrupt can be used for wake-up function*/ diff --git a/components/driver/include/driver/gpio.h b/components/driver/include/driver/gpio.h index de7525bd5f..fba013fe8b 100644 --- a/components/driver/include/driver/gpio.h +++ b/components/driver/include/driver/gpio.h @@ -23,6 +23,7 @@ #include "soc/gpio_sig_map.h" #include "rom/gpio.h" #include "esp_attr.h" +#include "esp_intr_alloc.h" #ifdef __cplusplus extern "C" { @@ -203,6 +204,9 @@ typedef enum { GPIO_FLOATING, /*!< Pad floating */ } gpio_pull_mode_t; + + +typedef intr_handle_t gpio_isr_handle_t; typedef void (*gpio_event_callback)(gpio_num_t gpio_intr_num); /** @@ -343,19 +347,18 @@ esp_err_t gpio_wakeup_disable(gpio_num_t gpio_num); * Users should know that which CPU is running and then pick a INUM that is not used by system. * We can find the information of INUM and interrupt level in soc.h. * - * @param gpio_intr_num GPIO interrupt number,check the info in soc.h, and please see the core-isa.h for more details * @param fn Interrupt handler function. - * - * @note - * Note that the handler function MUST be defined with attribution of "IRAM_ATTR". - * + * @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred) + * ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info. * @param arg Parameter for handler function + * @param handle Pointer to return handle. If non-NULL, a handle for the interrupt will + * be returned here. * * @return * - ESP_OK Success ; * - ESP_ERR_INVALID_ARG GPIO error */ -esp_err_t gpio_isr_register(uint32_t gpio_intr_num, void (*fn)(void*), void * arg); +esp_err_t gpio_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags, gpio_isr_handle_t *handle); @@ -415,7 +418,7 @@ esp_err_t gpio_pulldown_dis(gpio_num_t gpio_num); */ /** - *----------EXAMPLE TO CONIFGURE GPIO AS OUTPUT ------------ * + *----------EXAMPLE TO CONFIGURE GPIO AS OUTPUT ------------ * * @code{c} * gpio_config_t io_conf; * io_conf.intr_type = GPIO_INTR_DISABLE; //disable interrupt @@ -428,7 +431,7 @@ esp_err_t gpio_pulldown_dis(gpio_num_t gpio_num); **/ /** - *----------EXAMPLE TO CONIFGURE GPIO AS OUTPUT ------------ * + *----------EXAMPLE TO CONFIGURE GPIO AS OUTPUT ------------ * * @code{c} * io_conf.intr_type = GPIO_INTR_POSEDGE; //set posedge interrupt * io_conf.mode = GPIO_MODE_INPUT; //set as input @@ -441,8 +444,7 @@ esp_err_t gpio_pulldown_dis(gpio_num_t gpio_num); /** *----------EXAMPLE TO SET ISR HANDLER ---------------------- * @code{c} - * //the first parameter is INUM, you can pick one form interrupt level 1/2 which is not used by the system. - * gpio_isr_register(18,gpio_intr_test,NULL); //hook the isr handler for GPIO interrupt + * gpio_isr_register(gpio_intr_test,NULL, 0); //hook the isr handler for GPIO interrupt * @endcode * @note * 1. user should arrange the INUMs that used, better not to use a same INUM for different interrupt. diff --git a/components/driver/include/driver/ledc.h b/components/driver/include/driver/ledc.h index e07787b2b1..fb97c6c011 100644 --- a/components/driver/include/driver/ledc.h +++ b/components/driver/include/driver/ledc.h @@ -21,6 +21,7 @@ #include "soc/ledc_struct.h" #include "driver/gpio.h" #include "driver/periph_ctrl.h" +#include "esp_intr_alloc.h" #ifdef __cplusplus extern "C" { @@ -100,6 +101,7 @@ typedef struct { uint32_t freq_hz; /*!< LEDC timer frequency(Hz)*/ } ledc_timer_config_t; +typedef intr_handle_t ledc_isr_handle_t; /** * @brief LEDC channel configuration @@ -257,20 +259,20 @@ esp_err_t ledc_set_fade(ledc_mode_t speed_mode, uint32_t channel, uint32_t duty, /** * @brief register LEDC interrupt handler, the handler is an ISR. * The handler will be attached to the same CPU core that this function is running on. - * @note - * Users should know that which CPU is running and then pick a INUM that is not used by system. - * We can find the information of INUM and interrupt level in soc.h. - * @param ledc_intr_num LEDC interrupt number, check the info in soc.h, and please see the core-isa.h for more details + * * @param fn Interrupt handler function. - * @note - * Note that the handler function MUST be defined with attribution of "IRAM_ATTR". + * @param arg User-supplied argument passed to the handler function. + * @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred) + * ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info. * @param arg Parameter for handler function + * @param handle Pointer to return handle. If non-NULL, a handle for the interrupt will + * be returned here. * * @return * - ESP_OK Success * - ESP_ERR_INVALID_ARG Function pointer error. */ -esp_err_t ledc_isr_register(uint32_t ledc_intr_num, void (*fn)(void*), void * arg); +esp_err_t ledc_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags, ledc_isr_handle_t *handle); /** * @brief configure LEDC settings @@ -398,13 +400,8 @@ esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, uint32_t channel, uint * ----------------EXAMPLE OF LEDC INTERRUPT ------------------ * @code{c} * //we have fade_end interrupt and counter overflow interrupt. we just give an example of fade_end interrupt here. - * ledc_isr_register(18, ledc_isr_handler, NULL); //hook the isr handler for LEDC interrupt + * ledc_isr_register(ledc_isr_handler, NULL, 0); //hook the isr handler for LEDC interrupt * @endcode - * @note - * 1. the first parameter is INUM, you can pick one form interrupt level 1/2 which is not used by the system. - * 2. user should arrange the INUMs that used, better not to use a same INUM for different interrupt source. - * 3. do not pick the INUM that already occupied by the system. - * 4. refer to soc.h to check which INUMs that can be used. * * ----------------EXAMPLE OF INTERRUPT HANDLER --------------- * @code{c} diff --git a/components/driver/include/driver/pcnt.h b/components/driver/include/driver/pcnt.h index d2620cf15e..f5a10581c0 100644 --- a/components/driver/include/driver/pcnt.h +++ b/components/driver/include/driver/pcnt.h @@ -12,6 +12,7 @@ #include "soc/pcnt_struct.h" #include "soc/gpio_sig_map.h" #include "driver/gpio.h" +#include "esp_intr_alloc.h" #ifdef __cplusplus extern "C" { @@ -76,6 +77,8 @@ typedef struct { pcnt_channel_t channel; /*!< the PCNT channel */ } pcnt_config_t; +typedef intr_handle_t pcnt_isr_handle_t; + /** * @brief Configure Pulse Counter unit * @@ -213,21 +216,19 @@ esp_err_t pcnt_get_event_value(pcnt_unit_t unit, pcnt_evt_type_t evt_type, int16 /** * @brief Register PCNT interrupt handler, the handler is an ISR. * The handler will be attached to the same CPU core that this function is running on. - * @note - * Users should know that which CPU is running and then pick a INUM that is not used by system. - * We can find the information of INUM and interrupt level in soc.h. * - * @param pcnt_intr_num PCNT interrupt number, check the info in soc.h, and please see the core-isa.h for more details * @param fn Interrupt handler function. - * @note - * Note that the handler function MUST be defined with attribution of "IRAM_ATTR". * @param arg Parameter for handler function + * @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred) + * ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info. + * @param handle Pointer to return handle. If non-NULL, a handle for the interrupt will + * be returned here. * * @return * - ESP_OK Success * - ESP_ERR_INVALID_ARG Function pointer error. */ -esp_err_t pcnt_isr_register(uint32_t pcnt_intr_num, void (*fn)(void*), void * arg); +esp_err_t pcnt_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags, pcnt_isr_handle_t *handle); /** * @brief Configure PCNT pulse signal input pin and control input pin diff --git a/components/driver/include/driver/rmt.h b/components/driver/include/driver/rmt.h index 50a5c743dd..24df1ac8ed 100644 --- a/components/driver/include/driver/rmt.h +++ b/components/driver/include/driver/rmt.h @@ -117,6 +117,8 @@ typedef struct { }; } rmt_config_t; +typedef intr_handle_t rmt_isr_handle_t; + /** * @brief Set RMT clock divider, channel clock is divided from source clock. * @@ -566,27 +568,32 @@ esp_err_t rmt_config(rmt_config_t* rmt_param); * @brief register RMT interrupt handler, the handler is an ISR. * * The handler will be attached to the same CPU core that this function is running on. - * Users should know that which CPU is running and then pick a INUM that is not used by system. - * We can find the information of INUM and interrupt level in soc.h. - * @note - * If you already called rmt_driver_install to use system RMT driver, + * @note If you already called rmt_driver_install to use system RMT driver, * please do not register ISR handler again. * - * @param rmt_intr_num RMT interrupt number, check the info in soc.h, and please see the core-isa.h for more details - * * @param fn Interrupt handler function. - * - * @note - * the handler function MUST be defined with attribution of "IRAM_ATTR". - * * @param arg Parameter for handler function + * @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred) + * ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info. + * @param handle If non-zero, a handle to later clean up the ISR gets stored here. * * @return * - ESP_OK Success * - ESP_ERR_INVALID_ARG Function pointer error. * - ESP_FAIL System driver installed, can not register ISR handler for RMT */ -esp_err_t rmt_isr_register(uint8_t rmt_intr_num, void (* fn)(void* ), void * arg); +esp_err_t rmt_isr_register(void (* fn)(void* ), void * arg, int intr_alloc_flags, rmt_isr_handle_t *handle); + +/** + * @brief Deregister previously registered RMT interrupt handler + * + * @param handle Handle obtained from rmt_isr_register + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Handle invalid + */ +esp_err_t rmt_isr_deregister(rmt_isr_handle_t handle); /** * @brief Fill memory data of channel with given RMT items. @@ -727,7 +734,7 @@ esp_err_t rmt_get_ringbuf_handler(rmt_channel_t channel, RingbufHandle_t* buf_ha * rmt_config(&rmt_tx); * * //install system RMT driver, disable rx ringbuffer for transmitter. - * rmt_driver_install(rmt_tx.channel, 0, RMT_INTR_NUM); + * rmt_driver_install(rmt_tx.channel, 0, 0); * } * * @endcode @@ -747,25 +754,20 @@ esp_err_t rmt_get_ringbuf_handler(rmt_channel_t channel, RingbufHandle_t* buf_ha * rmt_config(&rmt_rx); * * //install system RMT driver. - * rmt_driver_install(rmt_rx.channel, 1000, RMT_INTR_NUM); + * rmt_driver_install(rmt_rx.channel, 1000, 0); * } * * ----------------EXAMPLE OF RMT INTERRUPT ------------------ * @code{c} * - * rmt_isr_register(RMT_INTR_NUM, rmt_isr, NULL); //hook the ISR handler for RMT interrupt + * rmt_isr_register(rmt_isr, NULL, 0); //hook the ISR handler for RMT interrupt * @endcode * @note * 0. If you have called rmt_driver_install, you don't need to set ISR handler any more. - * 1. the first parameter is INUM, you can pick one form interrupt level 1/2 which is not used by the system. - * 2. user should arrange the INUMs that used, better not to use a same INUM for different interrupt source. - * 3. do not pick the INUM that already occupied by the system. - * 4. refer to soc.h to check which INUMs that can be used. * * ----------------EXAMPLE OF INTERRUPT HANDLER --------------- * @code{c} * #include "esp_attr.h" - * //we should add 'IRAM_ATTR' attribution when we declare the isr function * void IRAM_ATTR rmt_isr_handler(void* arg) * { * //read RMT interrupt status. diff --git a/components/driver/include/driver/timer.h b/components/driver/include/driver/timer.h index c0ad7116e4..134fd504fb 100644 --- a/components/driver/include/driver/timer.h +++ b/components/driver/include/driver/timer.h @@ -19,6 +19,7 @@ #include "soc/soc.h" #include "soc/timer_group_reg.h" #include "soc/timer_group_struct.h" +#include "esp_intr_alloc.h" #ifdef __cplusplus extern "C" { @@ -94,12 +95,19 @@ typedef enum { typedef struct { bool alarm_en; /*!< Timer alarm enable */ bool counter_en; /*!< Counter enable */ - timer_count_dir_t counter_dir; /*!< Counter direction */ timer_intr_mode_t intr_type; /*!< Interrupt mode */ + timer_count_dir_t counter_dir; /*!< Counter direction */ bool auto_reload; /*!< Timer auto-reload */ uint16_t divider; /*!< Counter clock divider*/ } timer_config_t; + +/** + * @brief Interrupt handle, used in order to free the isr after use. + * Aliases to an int handle for now. + */ +typedef intr_handle_t timer_isr_handle_t; + /** * @brief Read the counter value of hardware timer. * @@ -245,21 +253,20 @@ esp_err_t timer_set_alarm(timer_group_t group_num, timer_idx_t timer_num, timer_ /** * @brief register Timer interrupt handler, the handler is an ISR. * The handler will be attached to the same CPU core that this function is running on. - * @note - * Users should know that which CPU is running and then pick a INUM that is not used by system. - * We can find the information of INUM and interrupt level in soc.h. * * @param group_num Timer group number * @param timer_num Timer index of timer group - * @param timer_intr_num TIMER interrupt number, check the info in soc.h, and please see the core-isa.h for more details - * @param intr_type Timer interrupt type * @param fn Interrupt handler function. * @note - * Code inside the handler function can only call functions in IRAM, so cannot call other timer APIs. - * Use direct register access to access timers from inside the ISR. + * In case the this is called with the INIRAM flag, code inside the handler function can + * only call functions in IRAM, so it cannot call other timer APIs. + * Use direct register access to access timers from inside the ISR in this case. * * @param arg Parameter for handler function - * + * @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred) + * ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info. + * @param handle Pointer to return handle. If non-NULL, a handle for the interrupt will + * be returned here. * @return * - ESP_OK Success * - ESP_ERR_INVALID_ARG Function pointer error. @@ -268,7 +275,7 @@ esp_err_t timer_set_alarm(timer_group_t group_num, timer_idx_t timer_num, timer_ * - ESP_OK Success * - ESP_ERR_INVALID_ARG Parameter error */ -esp_err_t timer_isr_register(timer_group_t group_num, timer_idx_t timer_num, int timer_intr_num, timer_intr_mode_t intr_type, void (*fn)(void*), void * arg); +esp_err_t timer_isr_register(timer_group_t group_num, timer_idx_t timer_num, void (*fn)(void*), void * arg, int intr_alloc_flags, timer_isr_handle_t *handle); /** @brief Initializes and configure the timer. * diff --git a/components/driver/include/driver/touch_pad.h b/components/driver/include/driver/touch_pad.h index b8dc6e7534..4f78b2351b 100644 --- a/components/driver/include/driver/touch_pad.h +++ b/components/driver/include/driver/touch_pad.h @@ -19,6 +19,7 @@ extern "C" { #endif #include "esp_intr.h" #include "esp_err.h" +#include "esp_intr_alloc.h" #define TOUCH_PAD_SLEEP_CYCLE_CONFIG (0x1000)//The Time is 150Khz,the Max value is 0xffff #define TOUCH_PAD_MEASURE_CYCLE_CONFIG (0xffff)//The Time is 8Mhz,the Max value is 0xffff typedef enum { @@ -34,6 +35,9 @@ typedef enum { TOUCH_PAD_NUM9, /*!< Touch pad channel 0 is GPIO32*/ TOUCH_PAD_MAX, } touch_pad_t; + +typedef intr_handle_t touch_isr_handle_t; + /** * @brief Initialize touch module. * @@ -79,44 +83,40 @@ esp_err_t touch_pad_read(touch_pad_t touch_num, uint16_t * touch_value); /** * @brief register TouchPad interrupt handler, the handler is an ISR. * The handler will be attached to the same CPU core that this function is running on. - * @note - * Users should know that which CPU is running and then pick a INUM that is not used by system. - * We can find the information of INUM and interrupt level in soc.h. * - * @param touch_intr_num Touch interrupt number,check the info in soc.h, and please see the core-isa.h for more details * @param fn Interrupt handler function. - * - * @note - * Note that the handler function MUST be defined with attribution of "IRAM_ATTR". - * * @param arg Parameter for handler function + * @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred) + * ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info. + * @param handle Pointer to return handle. If non-NULL, a handle for the interrupt will + * be returned here. * * @return * - ESP_OK Success ; * - ESP_ERR_INVALID_ARG GPIO error */ -esp_err_t touch_pad_isr_handler_register(uint32_t touch_intr_num, void(*fn)(void*), void *arg); +esp_err_t touch_pad_isr_handler_register(void(*fn)(void *), void *arg, int intr_alloc_flags, touch_isr_handle_t *handle); /** * *************** ATTENTION ********************/ /** *@attention -*Touch button is through the body's capacitive characteristics, -*there is a charge discharge circuit inside the. When the hands touch, -*the charge and discharge time will be slow. -*Because of the different hardware, each pad needs to be calibrated at the factory. -*We use touch_pad_read to determine factory parament. -*/ + *Touch button is through the body's capacitive characteristics, + *there is a charge discharge circuit inside the. When the hands touch, + *the charge and discharge time will be slow. + *Because of the different hardware, each pad needs to be calibrated at the factory. + *We use touch_pad_read to determine factory parameters. + */ /** - *----------EXAMPLE TO CONIFGURE GPIO AS OUTPUT ------------ * + *----------EXAMPLE TO CONFIGURE GPIO AS OUTPUT ------------ * * @code{c} * touch_pad_init(); * void taskA(void* arg) * { * for(;;){ * vtaskDelay(20/portTICK_PERIOD_MS); - * ets_printf("tocuch pad value %u\n",touch_pad_read(0));//Take the touched status and untouched status value + * ets_printf("touch pad value %u\n",touch_pad_read(0));//Take the touched status and untouched status value * } * } * @endcode @@ -124,22 +124,17 @@ esp_err_t touch_pad_isr_handler_register(uint32_t touch_intr_num, void(*fn)(void /** *----------EXAMPLE TO SET ISR HANDLER ---------------------- * @code{c} - * //the first parameter is INUM, you can pick one form interrupt level 1/2 which is not used by the system. - * touch_pad_isr_handler_register(19,rtc_intr,NULL); //hook the isr handler for TouchPad interrupt + * touch_pad_isr_handler_register(rtc_intr,NULL, 0, NULL) //hook the isr handler for TouchPad interrupt * @endcode - * @note - * 1. user should arrange the INUMs that used, better not to use a same INUM for different interrupt. - * 2. do not pick the INUM that already occupied by the system. - * 3. refer to soc.h to check which INUMs that can be used. */ /** *----------EXAMPLE TO USE TOUCH_PAD------------ * * @code{c} * touch_pad_init();//only init one time * touch_pad_config(0,300);//set the intr threshold,use touch_pad_read to determine this threshold - * touch_pad_isr_handler_register(19,rtc_intr,NULL) + * touch_pad_isr_handler_register(rtc_intr,NULL, 0, NULL) * #include "esp_attr.h" - * void IRAM_ATTR rtc_intr(void * arg) + * void rtc_intr(void * arg) * { * uint32_t pad_intr = READ_PERI_REG(SARADC_SAR_TOUCH_CTRL2_REG) & 0x3ff; * uint8_t i = 0; diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index 1ddfcad9fa..951ada929a 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -366,21 +366,31 @@ esp_err_t uart_enable_tx_intr(uart_port_t uart_num, int enable, int thresh); * @brief register UART interrupt handler(ISR). * * @note UART ISR handler will be attached to the same CPU core that this function is running on. - * Users should know that which CPU is running and then pick a INUM that is not used by system. - * We can find the information of INUM and interrupt level in soc.h. - * - * @attention The ISR handler function MUST be defined with attribution of "IRAM_ATTR" for now. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param uart_intr_num UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details * @param fn Interrupt handler function. * @param arg parameter for handler function + * @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred) + * ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info. * * @return * - ESP_OK Success * - ESP_FAIL Parameter error */ -esp_err_t uart_isr_register(uart_port_t uart_num, uint8_t uart_intr_num, void (*fn)(void*), void * arg); +esp_err_t uart_isr_register(uart_port_t uart_num, void (*fn)(void*), void * arg, int intr_alloc_flags); + + +/** + * @brief Free UART interrupt handler registered by uart_isr_register. Must be called on the same core as + * uart_isr_register was called. + * + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error + */ +esp_err_t uart_isr_free(uart_port_t uart_num); /** * @brief Set UART pin number @@ -461,14 +471,15 @@ esp_err_t uart_intr_config(uart_port_t uart_num, const uart_intr_config_t *intr_ * @param tx_buffer_size UART TX ring buffer size. * If set to zero, driver will not use TX buffer, TX function will block task until all data have been sent out.. * @param queue_size UART event queue size/depth. - * @param uart_intr_num UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details * @param uart_queue UART event queue handle, if set NULL, driver will not use an event queue. + * @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred) + * ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info. * * @return * - ESP_OK Success * - ESP_FAIL Parameter error */ -esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, int uart_intr_num, void* uart_queue); +esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, void* uart_queue, int intr_alloc_flags); /** * @brief Uninstall UART driver. @@ -733,7 +744,7 @@ esp_err_t uart_flush(uart_port_t uart_num); * //Set UART log level * esp_log_level_set(TAG, ESP_LOG_INFO); * //Install UART driver, and get the queue. - * uart_driver_install(uart_num, 1024 * 2, 1024*4, 10, 17, &uart0_queue); + * uart_driver_install(uart_num, 1024 * 2, 1024*4, 10, &uart0_queue, 0); * //Create a task to handler UART event from ISR * xTaskCreate(uart_task, "uTask", 1024, (void*)uart_num, 10, NULL); * } diff --git a/components/driver/ledc.c b/components/driver/ledc.c index 893c78a6be..77ca975969 100644 --- a/components/driver/ledc.c +++ b/components/driver/ledc.c @@ -13,6 +13,7 @@ // limitations under the License. #include #include "esp_intr.h" +#include "esp_intr_alloc.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include "freertos/xtensa_api.h" @@ -20,12 +21,13 @@ #include "driver/ledc.h" #include "esp_log.h" -static const char* LEDC_TAG = "LEDC"; +static const char* LEDC_TAG = "ledc"; static portMUX_TYPE ledc_spinlock = portMUX_INITIALIZER_UNLOCKED; -#define LEDC_CHECK(a, str, ret_val) if (!(a)) { \ - ESP_LOGE(LEDC_TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \ - return (ret_val); \ - } +#define LEDC_CHECK(a, str, ret_val) \ + if (!(a)) { \ + ESP_LOGE(LEDC_TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str); \ + return (ret_val); \ + } esp_err_t ledc_timer_set(ledc_mode_t speed_mode, ledc_timer_t timer_sel, uint32_t div_num, uint32_t bit_num, ledc_clk_src_t clk_src) { @@ -113,16 +115,14 @@ static esp_err_t ledc_enable_intr_type(ledc_mode_t speed_mode, uint32_t channel, return ESP_OK; } -esp_err_t ledc_isr_register(uint32_t ledc_intr_num, void (*fn)(void*), void * arg) +esp_err_t ledc_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags, ledc_isr_handle_t *handle) { + esp_err_t ret; LEDC_CHECK(fn, "ledc isr null", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); - ESP_INTR_DISABLE(ledc_intr_num); - intr_matrix_set(xPortGetCoreID(), ETS_LEDC_INTR_SOURCE, ledc_intr_num); - xt_set_interrupt_handler(ledc_intr_num, fn, arg); - ESP_INTR_ENABLE(ledc_intr_num); + ret=esp_intr_alloc(ETS_LEDC_INTR_SOURCE, intr_alloc_flags, fn, arg, handle); portEXIT_CRITICAL(&ledc_spinlock); - return ESP_OK; + return ret; } esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf) diff --git a/components/driver/pcnt.c b/components/driver/pcnt.c index e65d733c01..ac40898897 100644 --- a/components/driver/pcnt.c +++ b/components/driver/pcnt.c @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. #include "esp_log.h" +#include "esp_intr_alloc.h" #include "driver/pcnt.h" #include "driver/periph_ctrl.h" @@ -23,12 +24,14 @@ #define PCNT_COUNT_MODE_ERR_STR "PCNT COUNTER MODE ERROR" #define PCNT_CTRL_MODE_ERR_STR "PCNT CTRL MODE ERROR" #define PCNT_EVT_TYPE_ERR_STR "PCNT value type error" -#define PCNT_CHECK(a,str,ret_val) if(!(a)) { \ - ESP_LOGE(PCNT_TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \ - return (ret_val); \ - } -static const char* PCNT_TAG = "PCNT"; +static const char* PCNT_TAG = "pcnt"; +#define PCNT_CHECK(a, str, ret_val) \ + if (!(a)) { \ + ESP_LOGE(PCNT_TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str); \ + return (ret_val); \ + } + static portMUX_TYPE pcnt_spinlock = portMUX_INITIALIZER_UNLOCKED; #define PCNT_ENTER_CRITICAL(mux) portENTER_CRITICAL(mux) @@ -266,13 +269,9 @@ esp_err_t pcnt_filter_disable(pcnt_unit_t unit) return ESP_OK; } -esp_err_t pcnt_isr_register(uint32_t pcnt_intr_num, void (*fun)(void*), void * arg) +esp_err_t pcnt_isr_register(void (*fun)(void*), void * arg, int intr_alloc_flags, pcnt_isr_handle_t *handle) { PCNT_CHECK(fun != NULL, PCNT_ADDRESS_ERR_STR, ESP_ERR_INVALID_ARG); - ESP_INTR_DISABLE(pcnt_intr_num); - intr_matrix_set(xPortGetCoreID(), ETS_PCNT_INTR_SOURCE, pcnt_intr_num); - xt_set_interrupt_handler(pcnt_intr_num, fun, arg); - ESP_INTR_ENABLE(pcnt_intr_num); - return ESP_OK; + return esp_intr_alloc(ETS_PCNT_INTR_SOURCE, intr_alloc_flags, fun, arg, handle); } diff --git a/components/driver/rmt.c b/components/driver/rmt.c index f23fa257d0..e29f190024 100644 --- a/components/driver/rmt.c +++ b/components/driver/rmt.c @@ -21,6 +21,7 @@ #include "esp_intr.h" #include "esp_log.h" #include "esp_err.h" +#include "esp_intr_alloc.h" #include "soc/gpio_sig_map.h" #include "soc/rmt_struct.h" #include "driver/periph_ctrl.h" @@ -43,13 +44,16 @@ #define RMT_DRIVER_ERROR_STR "RMT DRIVER ERR" #define RMT_DRIVER_LENGTH_ERROR_STR "RMT PARAM LEN ERROR" -static const char* RMT_TAG = "RMT"; +static const char* RMT_TAG = "rmt"; static bool s_rmt_driver_installed = false; +static rmt_isr_handle_t s_rmt_driver_intr_handle; + +#define RMT_CHECK(a, str, ret_val) \ + if (!(a)) { \ + ESP_LOGE(RMT_TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str); \ + return (ret_val); \ + } -#define RMT_CHECK(a, str, ret) if (!(a)) { \ - ESP_LOGE(RMT_TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \ - return (ret); \ - } static portMUX_TYPE rmt_spinlock = portMUX_INITIALIZER_UNLOCKED; typedef struct { @@ -472,17 +476,21 @@ esp_err_t rmt_fill_tx_items(rmt_channel_t channel, rmt_item32_t* item, uint16_t return ESP_OK; } -esp_err_t rmt_isr_register(uint8_t rmt_intr_num, void (*fn)(void*), void * arg) +esp_err_t rmt_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags, rmt_isr_handle_t *handle) { + esp_err_t ret; RMT_CHECK((fn != NULL), RMT_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG); RMT_CHECK(s_rmt_driver_installed == false, "RMT DRIVER INSTALLED, CAN NOT REG ISR HANDLER", ESP_FAIL); portENTER_CRITICAL(&rmt_spinlock); - ESP_INTR_DISABLE(rmt_intr_num); - intr_matrix_set(xPortGetCoreID(), ETS_RMT_INTR_SOURCE, rmt_intr_num); - xt_set_interrupt_handler(rmt_intr_num, fn, arg); - ESP_INTR_ENABLE(rmt_intr_num); + ret=esp_intr_alloc(ETS_RMT_INTR_SOURCE, intr_alloc_flags, fn, arg, handle); portEXIT_CRITICAL(&rmt_spinlock); - return ESP_OK; + return ret; +} + + +esp_err_t rmt_isr_deregister(rmt_isr_handle_t handle) +{ + return esp_intr_free(handle); } static int IRAM_ATTR rmt_get_mem_len(rmt_channel_t channel) @@ -616,10 +624,10 @@ esp_err_t rmt_driver_uninstall(rmt_channel_t channel) free(p_rmt_obj[channel]); p_rmt_obj[channel] = NULL; s_rmt_driver_installed = false; - return ESP_OK; + return rmt_isr_deregister(s_rmt_driver_intr_handle); } -esp_err_t rmt_driver_install(rmt_channel_t channel, size_t rx_buf_size, int rmt_intr_num) +esp_err_t rmt_driver_install(rmt_channel_t channel, size_t rx_buf_size, int intr_alloc_flags) { RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); if(p_rmt_obj[channel] != NULL) { @@ -627,7 +635,6 @@ esp_err_t rmt_driver_install(rmt_channel_t channel, size_t rx_buf_size, int rmt_ return ESP_FAIL; } - ESP_INTR_DISABLE(rmt_intr_num); p_rmt_obj[channel] = (rmt_obj_t*) malloc(sizeof(rmt_obj_t)); if(p_rmt_obj[channel] == NULL) { @@ -652,11 +659,10 @@ esp_err_t rmt_driver_install(rmt_channel_t channel, size_t rx_buf_size, int rmt_ rmt_set_err_intr_en(channel, 1); } if(s_rmt_driver_installed == false) { - rmt_isr_register(rmt_intr_num, rmt_driver_isr_default, NULL); + rmt_isr_register(rmt_driver_isr_default, NULL, intr_alloc_flags, &s_rmt_driver_intr_handle); s_rmt_driver_installed = true; } rmt_set_tx_intr_en(channel, 1); - ESP_INTR_ENABLE(rmt_intr_num); return ESP_OK; } diff --git a/components/driver/rtc_module.c b/components/driver/rtc_module.c index 5da738b452..ecb7e434d1 100644 --- a/components/driver/rtc_module.c +++ b/components/driver/rtc_module.c @@ -264,15 +264,10 @@ esp_err_t rtc_gpio_pulldown_dis(gpio_num_t gpio_num) /*--------------------------------------------------------------- Touch Pad ---------------------------------------------------------------*/ -esp_err_t touch_pad_isr_handler_register(uint32_t touch_intr_num, void(*fn)(void *), void *arg) +esp_err_t touch_pad_isr_handler_register(void(*fn)(void *), void *arg, int intr_alloc_flags, touch_isr_handle_t *handle) { RTC_MODULE_CHECK(fn, "Touch_Pad ISR null", ESP_ERR_INVALID_ARG); - ESP_INTR_DISABLE(touch_intr_num); - intr_matrix_set(xPortGetCoreID(), ETS_RTC_CORE_INTR_SOURCE, touch_intr_num); - xt_set_interrupt_handler(touch_intr_num, fn, arg); - ESP_INTR_ENABLE(touch_intr_num); - - return ESP_OK; + return esp_intr_alloc(ETS_RTC_CORE_INTR_SOURCE, intr_alloc_flags, fn, arg, handle); } static esp_err_t touch_pad_get_io_num(touch_pad_t touch_num, gpio_num_t *gpio_num) @@ -383,21 +378,6 @@ static esp_err_t touch_start(touch_pad_t touch_num) return ESP_OK; } -static esp_err_t touch_stop(touch_pad_t touch_num) -{ - RTC_MODULE_CHECK(touch_num < TOUCH_PAD_MAX, "Touch_Pad Num Err", ESP_ERR_INVALID_ARG); - portENTER_CRITICAL(&rtc_spinlock); - - //Disable Digital rtc control :work mode and out mode - CLEAR_PERI_REG_MASK(SENS_SAR_TOUCH_ENABLE_REG, (1 << (touch_num + SENS_TOUCH_PAD_WORKEN_S)) | \ - (1 << (touch_num + SENS_TOUCH_PAD_OUTEN2_S)) | \ - (1 << (touch_num + SENS_TOUCH_PAD_OUTEN1_S))); - - portEXIT_CRITICAL(&rtc_spinlock); - - return ESP_OK; -} - esp_err_t touch_pad_config(touch_pad_t touch_num, uint16_t threshold) { RTC_MODULE_CHECK(touch_num < TOUCH_PAD_MAX, "Touch_Pad Num Err", ESP_ERR_INVALID_ARG); @@ -533,7 +513,6 @@ esp_err_t adc1_config_width(adc_bits_width_t width_bit) int adc1_get_voltage(adc1_channel_t channel) { uint16_t adc_value; - uint8_t atten = 0; RTC_MODULE_CHECK(channel < ADC1_CHANNEL_MAX, "ADC Channel Err", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&rtc_spinlock); @@ -613,23 +592,6 @@ static esp_err_t dac_out_enable(dac_channel_t channel) return ESP_OK; } -static esp_err_t dac_out_disable(dac_channel_t channel) -{ - if (channel == DAC_CHANNEL_1) { - portENTER_CRITICAL(&rtc_spinlock); - CLEAR_PERI_REG_MASK(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_XPD_DAC | RTC_IO_PDAC1_DAC_XPD_FORCE); - portEXIT_CRITICAL(&rtc_spinlock); - } else if (channel == DAC_CHANNEL_2) { - portENTER_CRITICAL(&rtc_spinlock); - CLEAR_PERI_REG_MASK(RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_XPD_DAC | RTC_IO_PDAC2_DAC_XPD_FORCE); - portEXIT_CRITICAL(&rtc_spinlock); - } else { - return ESP_ERR_INVALID_ARG; - } - - return ESP_OK; -} - esp_err_t dac_out_voltage(dac_channel_t channel, uint8_t dac_value) { RTC_MODULE_CHECK(channel < DAC_CHANNEL_MAX, "DAC Channel Err", ESP_ERR_INVALID_ARG); diff --git a/components/driver/timer.c b/components/driver/timer.c index b305a41468..a8dacf0a04 100644 --- a/components/driver/timer.c +++ b/components/driver/timer.c @@ -15,16 +15,19 @@ #include "esp_log.h" #include "esp_err.h" #include "esp_intr.h" +#include "esp_intr_alloc.h" #include "freertos/FreeRTOS.h" #include "freertos/xtensa_api.h" #include "driver/timer.h" #include "driver/periph_ctrl.h" -static const char* TIMER_TAG = "TIMER_GROUP"; -#define TIMER_CHECK(a, str, ret_val) if (!(a)) { \ - ESP_LOGE(TIMER_TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \ - return (ret_val); \ - } +static const char* TIMER_TAG = "timer_group"; +#define TIMER_CHECK(a, str, ret_val) \ + if (!(a)) { \ + ESP_LOGE(TIMER_TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str); \ + return (ret_val); \ + } + #define TIMER_GROUP_NUM_ERROR "TIMER GROUP NUM ERROR" #define TIMER_NUM_ERROR "HW TIMER NUM ERROR" #define TIMER_PARAM_ADDR_ERROR "HW TIMER PARAM ADDR ERROR" @@ -167,36 +170,38 @@ esp_err_t timer_set_alarm(timer_group_t group_num, timer_idx_t timer_num, timer_ return ESP_OK; } -esp_err_t timer_isr_register(timer_group_t group_num, timer_idx_t timer_num, int timer_intr_num, - timer_intr_mode_t intr_type, void (*fn)(void*), void * arg) +esp_err_t timer_isr_register(timer_group_t group_num, timer_idx_t timer_num, + void (*fn)(void*), void * arg, int intr_alloc_flags, timer_isr_handle_t *handle) { TIMER_CHECK(group_num < TIMER_GROUP_MAX, TIMER_GROUP_NUM_ERROR, ESP_ERR_INVALID_ARG); TIMER_CHECK(timer_num < TIMER_MAX, TIMER_NUM_ERROR, ESP_ERR_INVALID_ARG); TIMER_CHECK(fn != NULL, TIMER_PARAM_ADDR_ERROR, ESP_ERR_INVALID_ARG); - ESP_INTR_DISABLE(timer_intr_num); int intr_source = 0; + uint32_t status_reg = 0; + int mask = 0; switch(group_num) { case TIMER_GROUP_0: default: - if(intr_type == TIMER_INTR_LEVEL) { + if((intr_alloc_flags & ESP_INTR_FLAG_EDGE) == 0) { intr_source = ETS_TG0_T0_LEVEL_INTR_SOURCE + timer_num; } else { intr_source = ETS_TG0_T0_EDGE_INTR_SOURCE + timer_num; } + status_reg = TIMG_INT_ST_TIMERS_REG(0); + mask = 1<conf1.txfifo_empty_thrhd = thresh & UART_TXFIFO_EMPTY_THRHD_V; UART[uart_num]->int_ena.txfifo_empty = enable & 0x1; UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); - ESP_INTR_ENABLE(p_uart_obj[uart_num]->intr_num); return ESP_OK; } -esp_err_t uart_isr_register(uart_port_t uart_num, uint8_t uart_intr_num, void (*fn)(void*), void * arg) +esp_err_t uart_isr_register(uart_port_t uart_num, void (*fn)(void*), void * arg, int intr_alloc_flags) { + int ret; UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); - ESP_INTR_DISABLE(uart_intr_num); switch(uart_num) { case UART_NUM_1: - intr_matrix_set(xPortGetCoreID(), ETS_UART1_INTR_SOURCE, uart_intr_num); + ret=esp_intr_alloc(ETS_UART1_INTR_SOURCE, intr_alloc_flags, fn, arg, &p_uart_obj[uart_num]->intr_handle); break; case UART_NUM_2: - intr_matrix_set(xPortGetCoreID(), ETS_UART2_INTR_SOURCE, uart_intr_num); + ret=esp_intr_alloc(ETS_UART2_INTR_SOURCE, intr_alloc_flags, fn, arg, &p_uart_obj[uart_num]->intr_handle); break; case UART_NUM_0: default: - intr_matrix_set(xPortGetCoreID(), ETS_UART0_INTR_SOURCE, uart_intr_num); + ret=esp_intr_alloc(ETS_UART0_INTR_SOURCE, intr_alloc_flags, fn, arg, &p_uart_obj[uart_num]->intr_handle); break; } - xt_set_interrupt_handler(uart_intr_num, fn, arg); - ESP_INTR_ENABLE(uart_intr_num); UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); - return ESP_OK; + return ret; +} + + +esp_err_t uart_isr_free(uart_port_t uart_num) +{ + esp_err_t ret; + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); + if (p_uart_obj[uart_num]->intr_handle==NULL) return ESP_ERR_INVALID_ARG; + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + ret=esp_intr_free(p_uart_obj[uart_num]->intr_handle); + p_uart_obj[uart_num]->intr_handle=NULL; + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + return ret; } //internal signal can be output to multiple GPIO pads @@ -859,7 +873,7 @@ esp_err_t uart_flush(uart_port_t uart_num) //rx sem protect the ring buffer read related functions xSemaphoreTake(p_uart->rx_mux, (portTickType)portMAX_DELAY); - ESP_INTR_DISABLE(p_uart->intr_num); + esp_intr_disable(p_uart->intr_handle); while(true) { if(p_uart->rx_head_ptr) { vRingbufferReturnItem(p_uart->rx_ring_buf, p_uart->rx_head_ptr); @@ -876,12 +890,12 @@ esp_err_t uart_flush(uart_port_t uart_num) p_uart->rx_ptr = NULL; p_uart->rx_cur_remain = 0; p_uart->rx_head_ptr = NULL; - ESP_INTR_ENABLE(p_uart->intr_num); + esp_intr_enable(p_uart->intr_handle); xSemaphoreGive(p_uart->rx_mux); if(p_uart->tx_buf_size > 0) { xSemaphoreTake(p_uart->tx_mux, (portTickType)portMAX_DELAY); - ESP_INTR_DISABLE(p_uart->intr_num); + esp_intr_disable(p_uart->intr_handle); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); UART[uart_num]->int_ena.txfifo_empty = 0; UART[uart_num]->int_clr.txfifo_empty = 1; @@ -901,19 +915,18 @@ esp_err_t uart_flush(uart_port_t uart_num) p_uart->tx_ptr = NULL; p_uart->tx_waiting_brk = 0; p_uart->tx_waiting_fifo = false; - ESP_INTR_ENABLE(p_uart->intr_num); + esp_intr_enable(p_uart->intr_handle); xSemaphoreGive(p_uart->tx_mux); } uart_reset_fifo(uart_num); return ESP_OK; } -esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, int uart_intr_num, void* uart_queue) +esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, void* uart_queue, int intr_alloc_flags) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); UART_CHECK((rx_buffer_size > 0), "uart rx buffer length error", ESP_FAIL); if(p_uart_obj[uart_num] == NULL) { - ESP_INTR_DISABLE(uart_intr_num); p_uart_obj[uart_num] = (uart_obj_t*) malloc(sizeof(uart_obj_t)); if(p_uart_obj[uart_num] == NULL) { ESP_LOGE(UART_TAG, "UART driver malloc error"); @@ -926,7 +939,6 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b p_uart_obj[uart_num]->tx_brk_sem = xSemaphoreCreateBinary(); p_uart_obj[uart_num]->tx_mux = xSemaphoreCreateMutex(); p_uart_obj[uart_num]->rx_mux = xSemaphoreCreateMutex(); - p_uart_obj[uart_num]->intr_num = uart_intr_num; p_uart_obj[uart_num]->queue_size = queue_size; p_uart_obj[uart_num]->tx_ptr = NULL; p_uart_obj[uart_num]->tx_head = NULL; @@ -959,7 +971,7 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b ESP_LOGE(UART_TAG, "UART driver already installed"); return ESP_FAIL; } - uart_isr_register(uart_num, uart_intr_num, uart_rx_intr_handler_default, p_uart_obj[uart_num]); + uart_isr_register(uart_num, uart_rx_intr_handler_default, p_uart_obj[uart_num], intr_alloc_flags); uart_intr_config_t uart_intr = { .intr_enable_mask = UART_RXFIFO_FULL_INT_ENA_M | UART_RXFIFO_TOUT_INT_ENA_M @@ -972,7 +984,6 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b .txfifo_empty_intr_thresh = UART_EMPTY_THRESH_DEFAULT }; uart_intr_config(uart_num, &uart_intr); - ESP_INTR_ENABLE(uart_intr_num); return ESP_OK; } @@ -984,10 +995,9 @@ esp_err_t uart_driver_delete(uart_port_t uart_num) ESP_LOGI(UART_TAG, "ALREADY NULL"); return ESP_OK; } - ESP_INTR_DISABLE(p_uart_obj[uart_num]->intr_num); + esp_intr_free(p_uart_obj[uart_num]->intr_handle); uart_disable_rx_intr(uart_num); uart_disable_tx_intr(uart_num); - uart_isr_register(uart_num, p_uart_obj[uart_num]->intr_num, NULL, NULL); if(p_uart_obj[uart_num]->tx_fifo_sem) { vSemaphoreDelete(p_uart_obj[uart_num]->tx_fifo_sem); diff --git a/components/esp32/component.mk b/components/esp32/component.mk index 858b5471b9..4ceb13e837 100644 --- a/components/esp32/component.mk +++ b/components/esp32/component.mk @@ -6,31 +6,27 @@ COMPONENT_SRCDIRS := . hwcrypto LIBS := core net80211 phy rtc pp wpa smartconfig coexist wps wpa2 -LINKER_SCRIPTS += -T esp32_out.ld -T esp32.common.ld -T esp32.rom.ld -T esp32.peripherals.ld +LINKER_SCRIPTS += esp32.common.ld esp32.rom.ld esp32.peripherals.ld ifeq ("$(CONFIG_NEWLIB_NANO_FORMAT)","y") -LINKER_SCRIPTS += -T esp32.rom.nanofmt.ld +LINKER_SCRIPTS += esp32.rom.nanofmt.ld endif COMPONENT_ADD_LDFLAGS := -lesp32 \ - $(COMPONENT_PATH)/libhal.a \ - -L$(COMPONENT_PATH)/lib \ - $(addprefix -l,$(LIBS)) \ - -L $(COMPONENT_PATH)/ld \ - $(LINKER_SCRIPTS) + $(COMPONENT_PATH)/libhal.a \ + -L$(COMPONENT_PATH)/lib \ + $(addprefix -l,$(LIBS)) \ + -L $(COMPONENT_PATH)/ld \ + -T esp32_out.ld \ + $(addprefix -T ,$(LINKER_SCRIPTS)) ALL_LIB_FILES := $(patsubst %,$(COMPONENT_PATH)/lib/lib%.a,$(LIBS)) COMPONENT_SUBMODULES += lib -# this is a hack to make sure the app is re-linked if the binary -# libraries change or are updated. If they change, the main esp32 -# library will be rebuild by AR andthis will trigger a re-linking of -# the entire app. -# -# It would be better for components to be able to expose any of these -# non-standard dependencies via get_variable, but this will do for now. -$(COMPONENT_LIBRARY): $(ALL_LIB_FILES) +# final linking of project ELF depends on all binary libraries, and +# all linker scripts (except esp32_out.ld, as this is code generated here.) +COMPONENT_ADD_LINKER_DEPS := $(ALL_LIB_FILES) $(addprefix ld/,$(LINKER_SCRIPTS)) # Preprocess esp32.ld linker script into esp32_out.ld # diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 8802b7dcd7..c7667fec61 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -173,12 +173,6 @@ void start_cpu0_default(void) uart_div_modify(CONFIG_CONSOLE_UART_NUM, (APB_CLK_FREQ << 4) / CONFIG_CONSOLE_UART_BAUDRATE); #if CONFIG_BROWNOUT_DET esp_brownout_init(); -#endif -#if CONFIG_INT_WDT - esp_int_wdt_init(); -#endif -#if CONFIG_TASK_WDT - esp_task_wdt_init(); #endif esp_setup_time_syscalls(); esp_vfs_dev_uart_register(); @@ -194,6 +188,12 @@ void start_cpu0_default(void) _GLOBAL_REENT->_stderr = (FILE*) &__sf_fake_stderr; #endif do_global_ctors(); +#if CONFIG_INT_WDT + esp_int_wdt_init(); +#endif +#if CONFIG_TASK_WDT + esp_task_wdt_init(); +#endif #if !CONFIG_FREERTOS_UNICORE esp_crosscore_int_init(); #endif @@ -228,8 +228,11 @@ void start_cpu1_default(void) while (port_xSchedulerRunning[0] == 0) { ; } + //Take care putting stuff here: if asked, FreeRTOS will happily tell you the scheduler + //has started, but it isn't active *on this CPU* yet. esp_crosscore_int_init(); - ESP_LOGI(TAG, "Starting scheduler on APP CPU."); + + ESP_EARLY_LOGI(TAG, "Starting scheduler on APP CPU."); xPortStartScheduler(); } #endif //!CONFIG_FREERTOS_UNICORE diff --git a/components/esp32/crosscore_int.c b/components/esp32/crosscore_int.c index 60f972a2a2..f7ea4f6a74 100644 --- a/components/esp32/crosscore_int.c +++ b/components/esp32/crosscore_int.c @@ -17,6 +17,7 @@ #include "esp_attr.h" #include "esp_err.h" #include "esp_intr.h" +#include "esp_intr_alloc.h" #include "rom/ets_sys.h" #include "rom/uart.h" @@ -72,14 +73,11 @@ void esp_crosscore_int_init() { portENTER_CRITICAL(&reasonSpinlock); reason[xPortGetCoreID()]=0; portEXIT_CRITICAL(&reasonSpinlock); - ESP_INTR_DISABLE(ETS_FROM_CPU_INUM); if (xPortGetCoreID()==0) { - intr_matrix_set(xPortGetCoreID(), ETS_FROM_CPU_INTR0_SOURCE, ETS_FROM_CPU_INUM); + esp_intr_alloc(ETS_FROM_CPU_INTR0_SOURCE, ESP_INTR_FLAG_IRAM, esp_crosscore_isr, (void*)&reason[xPortGetCoreID()], NULL); } else { - intr_matrix_set(xPortGetCoreID(), ETS_FROM_CPU_INTR1_SOURCE, ETS_FROM_CPU_INUM); + esp_intr_alloc(ETS_FROM_CPU_INTR1_SOURCE, ESP_INTR_FLAG_IRAM, esp_crosscore_isr, (void*)&reason[xPortGetCoreID()], NULL); } - xt_set_interrupt_handler(ETS_FROM_CPU_INUM, esp_crosscore_isr, (void*)&reason[xPortGetCoreID()]); - ESP_INTR_ENABLE(ETS_FROM_CPU_INUM); } void esp_crosscore_int_send_yield(int coreId) { diff --git a/components/esp32/include/esp_flash_data_types.h b/components/esp32/include/esp_flash_data_types.h index 783f2c59bb..cb6dfc3a1f 100644 --- a/components/esp32/include/esp_flash_data_types.h +++ b/components/esp32/include/esp_flash_data_types.h @@ -47,9 +47,24 @@ typedef struct { uint8_t subtype; esp_partition_pos_t pos; uint8_t label[16]; - uint8_t reserved[4]; + uint32_t flags; } esp_partition_info_t; +#define PART_TYPE_APP 0x00 +#define PART_SUBTYPE_FACTORY 0x00 +#define PART_SUBTYPE_OTA_FLAG 0x10 +#define PART_SUBTYPE_OTA_MASK 0x0f +#define PART_SUBTYPE_TEST 0x20 + +#define PART_TYPE_DATA 0x01 +#define PART_SUBTYPE_DATA_OTA 0x00 +#define PART_SUBTYPE_DATA_RF 0x01 +#define PART_SUBTYPE_DATA_WIFI 0x02 + +#define PART_TYPE_END 0xff +#define PART_SUBTYPE_END 0xff + +#define PART_FLAG_ENCRYPTED (1<<0) #ifdef __cplusplus } diff --git a/components/esp32/include/esp_heap_alloc_caps.h b/components/esp32/include/esp_heap_alloc_caps.h index d371ca5ed8..cb880d6a42 100644 --- a/components/esp32/include/esp_heap_alloc_caps.h +++ b/components/esp32/include/esp_heap_alloc_caps.h @@ -14,18 +14,18 @@ #ifndef HEAP_ALLOC_CAPS_H #define HEAP_ALLOC_CAPS_H -#define MALLOC_CAP_EXEC (1<<0) //Memory must be able to run executable code -#define MALLOC_CAP_32BIT (1<<1) //Memory must allow for aligned 32-bit data accesses -#define MALLOC_CAP_8BIT (1<<2) //Memory must allow for 8/16/...-bit data accesses -#define MALLOC_CAP_DMA (1<<3) //Memory must be able to accessed by DMA -#define MALLOC_CAP_PID2 (1<<4) //Memory must be mapped to PID2 memory space -#define MALLOC_CAP_PID3 (1<<5) //Memory must be mapped to PID3 memory space -#define MALLOC_CAP_PID4 (1<<6) //Memory must be mapped to PID4 memory space -#define MALLOC_CAP_PID5 (1<<7) //Memory must be mapped to PID5 memory space -#define MALLOC_CAP_PID6 (1<<8) //Memory must be mapped to PID6 memory space -#define MALLOC_CAP_PID7 (1<<9) //Memory must be mapped to PID7 memory space -#define MALLOC_CAP_SPISRAM (1<<10) //Memory must be in SPI SRAM -#define MALLOC_CAP_INVALID (1<<31) //Memory can't be used / list end marker +#define MALLOC_CAP_EXEC (1<<0) //Memory must be able to run executable code +#define MALLOC_CAP_32BIT (1<<1) //Memory must allow for aligned 32-bit data accesses +#define MALLOC_CAP_8BIT (1<<2) //Memory must allow for 8/16/...-bit data accesses +#define MALLOC_CAP_DMA (1<<3) //Memory must be able to accessed by DMA +#define MALLOC_CAP_PID2 (1<<4) //Memory must be mapped to PID2 memory space +#define MALLOC_CAP_PID3 (1<<5) //Memory must be mapped to PID3 memory space +#define MALLOC_CAP_PID4 (1<<6) //Memory must be mapped to PID4 memory space +#define MALLOC_CAP_PID5 (1<<7) //Memory must be mapped to PID5 memory space +#define MALLOC_CAP_PID6 (1<<8) //Memory must be mapped to PID6 memory space +#define MALLOC_CAP_PID7 (1<<9) //Memory must be mapped to PID7 memory space +#define MALLOC_CAP_SPISRAM (1<<10) //Memory must be in SPI SRAM +#define MALLOC_CAP_INVALID (1<<31) //Memory can't be used / list end marker void heap_alloc_caps_init(); diff --git a/components/esp32/include/esp_intr_alloc.h b/components/esp32/include/esp_intr_alloc.h new file mode 100644 index 0000000000..c1f91dd2e3 --- /dev/null +++ b/components/esp32/include/esp_intr_alloc.h @@ -0,0 +1,267 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef __ESP_INTR_ALLOC_H__ +#define __ESP_INTR_ALLOC_H__ + +#include +#include +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/** @addtogroup Intr_Alloc + * @{ + */ + + +/** @brief Interrupt allocation flags + * + * These flags can be used to specify which interrupt qualities the + * code calling esp_intr_alloc* needs. + * + */ + +//Keep the LEVELx values as they are here; they match up with (1<3 + * is requested, because these types of interrupts aren't C-callable. + * @param arg Optional argument for passed to the interrupt handler + * @param ret_handle Pointer to an intr_handle_t to store a handle that can later be + * used to request details or free the interrupt. Can be NULL if no handle + * is required. + * + * @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid. + * ESP_ERR_NOT_FOUND No free interrupt found with the specified flags + * ESP_OK otherwise + */ +esp_err_t esp_intr_alloc(int source, int flags, intr_handler_t handler, void *arg, intr_handle_t *ret_handle); + + +/** + * @brief Allocate an interrupt with the given parameters. + * + * + * This essentially does the same as esp_intr_alloc, but allows specifying a register and mask + * combo. For shared interrupts, the handler is only called if a read from the specified + * register, ANDed with the mask, returns non-zero. By passing an interrupt status register + * address and a fitting mask, this can be used to accelerate interrupt handling in the case + * a shared interrupt is triggered; by checking the interrupt statuses first, the code can + * decide which ISRs can be skipped + * + * @param source The interrupt source. One of the ETS_*_INTR_SOURCE interrupt mux + * sources, as defined in soc/soc.h, or one of the internal + * ETS_INTERNAL_*_INTR_SOURCE sources as defined in this header. + * @param flags An ORred mask of the ESP_INTR_FLAG_* defines. These restrict the + * choice of interrupts that this routine can choose from. If this value + * is 0, it will default to allocating a non-shared interrupt of level + * 1, 2 or 3. If this is ESP_INTR_FLAG_SHARED, it will allocate a shared + * interrupt of level 1. Setting ESP_INTR_FLAG_INTRDISABLED will return + * from this function with the interrupt disabled. + * @param intrstatusreg The address of an interrupt status register + * @param intrstatusmask A mask. If a read of address intrstatusreg has any of the bits + * that are 1 in the mask set, the ISR will be called. If not, it will be + * skipped. + * @param handler The interrupt handler. Must be NULL when an interrupt of level >3 + * is requested, because these types of interrupts aren't C-callable. + * @param arg Optional argument for passed to the interrupt handler + * @param ret_handle Pointer to an intr_handle_t to store a handle that can later be + * used to request details or free the interrupt. Can be NULL if no handle + * is required. + * + * @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid. + * ESP_ERR_NOT_FOUND No free interrupt found with the specified flags + * ESP_OK otherwise + */ +esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusreg, uint32_t intrstatusmask, intr_handler_t handler, void *arg, intr_handle_t *ret_handle); + + +/** + * @brief Disable and free an interrupt. + * + * Use an interrupt handle to disable the interrupt and release the resources + * associated with it. + * + * @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus + * + * @return ESP_ERR_INVALID_ARG if handle is invalid, or esp_intr_free runs on another core than + * where the interrupt is allocated on. + * ESP_OK otherwise + */ +esp_err_t esp_intr_free(intr_handle_t handle); + + +/** + * @brief Get CPU number an interrupt is tied to + * + * @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus + * + * @return The core number where the interrupt is allocated + */ +int esp_intr_get_cpu(intr_handle_t handle); + +/** + * @brief Get the allocated interrupt for a certain handle + * + * @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus + * + * @return The interrupt number + */ +int esp_intr_get_intno(intr_handle_t handle); + + +/** + * @brief Disable the interrupt associated with the handle + * + * @note For local interrupts (ESP_INTERNAL_* sources), this function has to be called on the + * CPU the interrupt is allocated on. Other interrupts have no such restriction. + * + * @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus + * + * @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid. + * ESP_OK otherwise + */ +esp_err_t esp_intr_disable(intr_handle_t handle); + +/** + * @brief Ensable the interrupt associated with the handle + * + * @note For local interrupts (ESP_INTERNAL_* sources), this function has to be called on the + * CPU the interrupt is allocated on. Other interrupts have no such restriction. + * + * @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus + * + * @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid. + * ESP_OK otherwise + */ +esp_err_t esp_intr_enable(intr_handle_t handle); + + +/** + * @brief Disable interrupts that aren't specifically marked as running from IRAM + */ +void esp_intr_noniram_disable(); + + +/** + * @brief Re-enable interrupts disabled by esp_intr_noniram_disable + */ +void esp_intr_noniram_enable(); + +/**@}*/ + + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/components/esp32/include/rom/spi_flash.h b/components/esp32/include/rom/spi_flash.h index 1e4dddeeb9..b1e4a7c937 100644 --- a/components/esp32/include/rom/spi_flash.h +++ b/components/esp32/include/rom/spi_flash.h @@ -490,16 +490,20 @@ SpiFlashOpResult SPI_Prepare_Encrypt_Data(uint32_t flash_addr, uint32_t *data); void SPI_Write_Encrypt_Disable(void); /** - * @brief Encrpto writing data to flash, you should Erase it yourself if need. - * Please do not call this function in SDK. + * @brief Write data to flash with transparent encryption. + * @note Sectors to be written should already be erased. * - * @param uint32_t flash_addr : Address to write, should be 32 bytes aligned. + * @note Please do not call this function in SDK. * - * @param uint32_t *data : The pointer to data which is to write. + * @param uint32_t flash_addr : Address to write, should be 32 byte aligned. + * + * @param uint32_t *data : The pointer to data to write. Note, this pointer must + * be 32 bit aligned and the content of the data will be + * modified by the encryption function. * * @param uint32_t len : Length to write, should be 32 bytes aligned. * - * @return SPI_FLASH_RESULT_OK : Encrypto write OK. + * @return SPI_FLASH_RESULT_OK : Data written successfully. * SPI_FLASH_RESULT_ERR : Encrypto write error. * SPI_FLASH_RESULT_TIMEOUT : Encrypto write timeout. */ diff --git a/components/esp32/include/soc/soc.h b/components/esp32/include/soc/soc.h index 3991152f21..b93bae7298 100755 --- a/components/esp32/include/soc/soc.h +++ b/components/esp32/include/soc/soc.h @@ -79,8 +79,8 @@ //set bits of register controlled by mask #define REG_SET_BITS(_r, _b, _m) (*(volatile uint32_t*)(_r) = (*(volatile uint32_t*)(_r) & ~(_m)) | ((_b) & (_m))) -//get field from register, used when _f is not left shifted by _f##_S -#define REG_GET_FIELD(_r, _f) ((REG_READ(_r) >> (_f##_S)) & (_f)) +//get field from register, uses field _S & _V to determine mask +#define REG_GET_FIELD(_r, _f) ((REG_READ(_r) >> (_f##_S)) & (_f##_V)) //set field to register, used when _f is not left shifted by _f##_S #define REG_SET_FIELD(_r, _f, _v) (REG_WRITE((_r),((REG_READ(_r) & ~((_f) << (_f##_S)))|(((_v) & (_f))<<(_f##_S))))) @@ -264,14 +264,14 @@ * Intr num Level Type PRO CPU usage APP CPU uasge * 0 1 extern level WMAC Reserved * 1 1 extern level BT/BLE Host VHCI Reserved - * 2 1 extern level FROM_CPU FROM_CPU - * 3 1 extern level TG0_WDT Reserved + * 2 1 extern level + * 3 1 extern level * 4 1 extern level WBB * 5 1 extern level BT Controller * 6 1 timer FreeRTOS Tick(L1) FreeRTOS Tick(L1) * 7 1 software Reserved Reserved * 8 1 extern level BLE Controller - * 9 1 extern level EMAC + * 9 1 extern level * 10 1 extern edge Internal Timer * 11 3 profiling * 12 1 extern level @@ -300,10 +300,7 @@ //CPU0 Interrupt number reserved, not touch this. #define ETS_WMAC_INUM 0 #define ETS_BT_HOST_INUM 1 -#define ETS_FROM_CPU_INUM 2 -#define ETS_T0_WDT_INUM 3 #define ETS_WBB_INUM 4 -#define ETS_EMAC_INUM 9 #define ETS_TG0_T1_INUM 10 /**< use edge interrupt*/ #define ETS_FRC1_INUM 22 #define ETS_T1_WDT_INUM 24 diff --git a/components/esp32/include/soc/wdev_reg.h b/components/esp32/include/soc/wdev_reg.h index 7189c9ec39..f3217cb0d6 100644 --- a/components/esp32/include/soc/wdev_reg.h +++ b/components/esp32/include/soc/wdev_reg.h @@ -14,5 +14,7 @@ #pragma once +#include "soc.h" + /* Hardware random number generator register */ #define WDEV_RND_REG 0x60035144 diff --git a/components/esp32/intr_alloc.c b/components/esp32/intr_alloc.c new file mode 100644 index 0000000000..57a6a97dea --- /dev/null +++ b/components/esp32/intr_alloc.c @@ -0,0 +1,739 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + + +#include "sdkconfig.h" +#include +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include +#include "esp_err.h" +#include "esp_log.h" +#include "esp_intr.h" +#include "esp_attr.h" +#include "esp_intr_alloc.h" +#include +#include + +static const char* TAG = "intr_alloc"; + + +#define ETS_INTERNAL_TIMER0_INTR_NO 6 +#define ETS_INTERNAL_TIMER1_INTR_NO 15 +#define ETS_INTERNAL_TIMER2_INTR_NO 16 +#define ETS_INTERNAL_SW0_INTR_NO 7 +#define ETS_INTERNAL_SW1_INTR_NO 29 +#define ETS_INTERNAL_PROFILING_INTR_NO 11 + + +/* +Define this to debug the choices made when allocating the interrupt. This leads to much debugging +output within a critical region, which can lead to weird effects like e.g. the interrupt watchdog +being triggered, that is why it is separate from the normal LOG* scheme. +*/ +//define DEBUG_INT_ALLOC_DECISIONS +#ifdef DEBUG_INT_ALLOC_DECISIONS +# define ALCHLOG(...) ESP_EARLY_LOGD(TAG, __VA_ARGS__) +#else +# define ALCHLOG(...) do {} while (0) +#endif + + +typedef enum { + INTDESC_NORMAL=0, + INTDESC_RESVD, + INTDESC_SPECIAL //for xtensa timers / software ints +} int_desc_flag_t; + +typedef enum { + INTTP_LEVEL=0, + INTTP_EDGE, + INTTP_NA +} int_type_t; + +typedef struct { + int level; + int_type_t type; + int_desc_flag_t cpuflags[2]; +} int_desc_t; + + +//We should mark the interrupt for the timer used by FreeRTOS as reserved. The specific timer +//is selectable using menuconfig; we use these cpp bits to convert that into something we can use in +//the table below. +#if CONFIG_FREERTOS_CORETIMER_0 +#define INT6RES INTDESC_RESVD +#else +#define INT6RES INTDESC_SPECIAL +#endif + +#if CONFIG_FREERTOS_CORETIMER_1 +#define INT15RES INTDESC_RESVD +#else +#define INT15RES INTDESC_SPECIAL +#endif + +#if CONFIG_FREERTOS_CORETIMER_2 +#define INT16RES INTDESC_RESVD +#else +#define INT16RES INTDESC_SPECIAL +#endif + +//This is basically a software-readable version of the interrupt usage table in include/soc/soc.h +const static int_desc_t int_desc[32]={ + { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //0 + { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //1 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //2 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //3 + { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_NORMAL} }, //4 + { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_NORMAL} }, //5 + { 1, INTTP_NA, {INT6RES, INT6RES } }, //6 + { 1, INTTP_NA, {INTDESC_SPECIAL,INTDESC_SPECIAL}}, //7 + { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //8 + { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //9 //FRC1 + { 1, INTTP_EDGE , {INTDESC_RESVD, INTDESC_RESVD } }, //10 //FRC2 + { 3, INTTP_NA, {INTDESC_SPECIAL,INTDESC_SPECIAL}}, //11 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //12 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //13 + { 7, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //14, NMI + { 3, INTTP_NA, {INT15RES, INT15RES } }, //15 + { 5, INTTP_NA, {INT16RES, INT16RES } }, //16 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //17 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //18 + { 2, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //19 + { 2, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //20 + { 2, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //21 + { 3, INTTP_EDGE, {INTDESC_RESVD, INTDESC_NORMAL} }, //22 + { 3, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //23 + { 4, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_NORMAL} }, //24 + { 4, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //25 + { 5, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //26 + { 3, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //27 + { 4, INTTP_EDGE, {INTDESC_NORMAL, INTDESC_NORMAL} }, //28 + { 3, INTTP_NA, {INTDESC_SPECIAL,INTDESC_SPECIAL}}, //29 + { 4, INTTP_EDGE, {INTDESC_RESVD, INTDESC_RESVD } }, //30 + { 5, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //31 +}; + +typedef struct shared_vector_desc_t shared_vector_desc_t; +typedef struct vector_desc_t vector_desc_t; + +struct shared_vector_desc_t { + int disabled: 1; + int source: 8; + volatile uint32_t *statusreg; + uint32_t statusmask; + intr_handler_t isr; + void *arg; + shared_vector_desc_t *next; +}; + + +#define VECDESC_FL_RESERVED (1<<0) +#define VECDESC_FL_INIRAM (1<<1) +#define VECDESC_FL_SHARED (1<<2) +#define VECDESC_FL_NONSHARED (1<<3) + +//Pack using bitfields for better memory use +struct vector_desc_t { + int flags: 16; //OR of VECDESC_FLAG_* defines + unsigned int cpu: 1; + unsigned int intno: 5; + int source: 8; //Interrupt mux flags, used when not shared + shared_vector_desc_t *shared_vec_info; //used when VECDESC_FL_SHARED + vector_desc_t *next; +}; + +struct intr_handle_data_t { + vector_desc_t *vector_desc; + shared_vector_desc_t *shared_vector_desc; +}; + + +//Linked list of vector descriptions, sorted by cpu.intno value +static vector_desc_t *vector_desc_head; + +//This bitmask has an 1 if the int should be disabled when the flash is disabled. +static uint32_t non_iram_int_mask[portNUM_PROCESSORS]; +//This bitmask has 1 in it if the int was disabled using esp_intr_noniram_disable. +static uint32_t non_iram_int_disabled[portNUM_PROCESSORS]; +static bool non_iram_int_disabled_flag[portNUM_PROCESSORS]; + + +static portMUX_TYPE spinlock = portMUX_INITIALIZER_UNLOCKED; + +//Inserts an item into vector_desc list so that the list is sorted +//with an incrementing cpu.intno value. +static void insert_vector_desc(vector_desc_t *to_insert) +{ + vector_desc_t *vd=vector_desc_head; + vector_desc_t *prev=NULL; + while(vd!=NULL) { + if (vd->cpu > to_insert->cpu) break; + if (vd->cpu == to_insert->cpu && vd->intno >= to_insert->intno) break; + prev=vd; + vd=vd->next; + } + if (vd==NULL && prev==NULL) { + //First item + vector_desc_head=to_insert; + vector_desc_head->next=NULL; + } else { + prev->next=to_insert; + to_insert->next=vd; + } +} + +//Returns a vector_desc entry for an intno/cpu, or NULL if none exists. +static vector_desc_t *find_desc_for_int(int intno, int cpu) +{ + vector_desc_t *vd=vector_desc_head; + while(vd!=NULL) { + if (vd->cpu==cpu && vd->intno==intno) break; + vd=vd->next; + } + return vd; +} + +//Returns a vector_desc entry for an intno/cpu. +//Either returns a preexisting one or allocates a new one and inserts +//it into the list. Returns NULL on malloc fail. +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)); + if (newvd==NULL) return NULL; + memset(newvd, 0, sizeof(vector_desc_t)); + newvd->intno=intno; + newvd->cpu=cpu; + insert_vector_desc(newvd); + return newvd; + } else { + return vd; + } +} + +esp_err_t esp_intr_mark_shared(int intno, int cpu, bool is_int_ram) +{ + if (intno>31) return ESP_ERR_INVALID_ARG; + if (cpu>=portNUM_PROCESSORS) return ESP_ERR_INVALID_ARG; + + portENTER_CRITICAL(&spinlock); + vector_desc_t *vd=get_desc_for_int(intno, cpu); + if (vd==NULL) { + portEXIT_CRITICAL(&spinlock); + return ESP_ERR_NO_MEM; + } + vd->flags=VECDESC_FL_SHARED; + if (is_int_ram) vd->flags|=VECDESC_FL_INIRAM; + portEXIT_CRITICAL(&spinlock); + + return ESP_OK; +} + +esp_err_t esp_intr_reserve(int intno, int cpu) +{ + if (intno>31) return ESP_ERR_INVALID_ARG; + if (cpu>=portNUM_PROCESSORS) return ESP_ERR_INVALID_ARG; + + portENTER_CRITICAL(&spinlock); + vector_desc_t *vd=get_desc_for_int(intno, cpu); + if (vd==NULL) { + portEXIT_CRITICAL(&spinlock); + return ESP_ERR_NO_MEM; + } + vd->flags=VECDESC_FL_RESERVED; + portEXIT_CRITICAL(&spinlock); + + return ESP_OK; +} + +//Interrupt handler table and unhandled uinterrupt routine. Duplicated +//from xtensa_intr.c... it's supposed to be private, but we need to look +//into it in order to see if someone allocated an int using +//xt_set_interrupt_handler. +typedef struct xt_handler_table_entry { + void * handler; + void * arg; +} xt_handler_table_entry; +extern xt_handler_table_entry _xt_interrupt_table[XCHAL_NUM_INTERRUPTS*portNUM_PROCESSORS]; +extern void xt_unhandled_interrupt(void * arg); + +//Returns true if handler for interrupt is not the default unhandled interrupt handler +static bool int_has_handler(int intr, int cpu) +{ + return (_xt_interrupt_table[intr*portNUM_PROCESSORS+cpu].handler != xt_unhandled_interrupt); +} + + +//Locate a free interrupt compatible with the flags given. +//The 'force' argument can be -1, or 0-31 to force checking a certain interrupt. +//When a CPU is forced, the INTDESC_SPECIAL marked interrupts are also accepted. +static int get_free_int(int flags, int cpu, int force) +{ + int x; + int best=-1; + int bestLevel=9; + int bestSharedCt=INT_MAX; + //Default vector desc, for vectors not in the linked list + vector_desc_t empty_vect_desc; + memset(&empty_vect_desc, 0, sizeof(vector_desc_t)); + //Level defaults to any low/med interrupt + if (!(flags&ESP_INTR_FLAG_LEVELMASK)) flags|=ESP_INTR_FLAG_LOWMED; + + ALCHLOG(TAG, "get_free_int: start looking. Current cpu: %d", cpu); + //Iterate over the 32 possible interrupts + for (x=0; x<32; x++) { + //Grab the vector_desc for this vector. + vector_desc_t *vd=find_desc_for_int(x, cpu); + if (vd==NULL) vd=&empty_vect_desc; + //See if we have a forced interrupt; if so, bail out if this is not it. + if (force!=-1 && force!=x) { + ALCHLOG(TAG, "Ignoring int %d: forced to %d", x, force); + continue; + } + ALCHLOG(TAG, "Int %d reserved %d level %d %s hasIsr %d", + x, int_desc[x].cpuflags[cpu]==INTDESC_RESVD, int_desc[x].level, + int_desc[x].type==INTTP_LEVEL?"LEVEL":"EDGE", int_has_handler(x, cpu)); + //Check if interrupt is not reserved by design + if (int_desc[x].cpuflags[cpu]==INTDESC_RESVD) { + ALCHLOG(TAG, "....Unusable: reserved"); + continue; + } + if (int_desc[x].cpuflags[cpu]==INTDESC_SPECIAL && force==-1) { + ALCHLOG(TAG, "....Unusable: special-purpose int"); + continue; + } + //Check if the interrupt level is acceptable + if (!(flags&(1<flags&VECDESC_FL_SHARED)) { + ALCHLOG(TAG, "....Unusable: already allocated"); + continue; + } + //Ints can't be both shared and non-shared. + assert(!((vd->flags&VECDESC_FL_SHARED)&&(vd->flags&VECDESC_FL_NONSHARED))); + //check if interrupt is reserved at runtime + if (vd->flags&VECDESC_FL_RESERVED) { + ALCHLOG(TAG, "....Unusable: reserved at runtime."); + continue; + } + //check if interrupt already is in use by a non-shared interrupt + if (vd->flags&VECDESC_FL_NONSHARED) { + ALCHLOG(TAG, "....Unusable: already in (non-shared) use."); + continue; + } + if (flags&ESP_INTR_FLAG_SHARED) { + //We're allocating a shared int. + bool in_iram_flag=((flags&ESP_INTR_FLAG_IRAM)!=0); + bool desc_in_iram_flag=((vd->flags&VECDESC_FL_INIRAM)!=0); + //Bail out if int is shared, but iram property doesn't match what we want. + if ((vd->flags&VECDESC_FL_SHARED) && (desc_in_iram_flag!=in_iram_flag)) { + ALCHLOG(TAG, "....Unusable: shared but iram prop doesn't match"); + continue; + } + //See if int already is used as a shared interrupt. + if (vd->flags&VECDESC_FL_SHARED) { + //We can use this already-marked-as-shared interrupt. Count the already attached isrs in order to see + //how useful it is. + int no=0; + shared_vector_desc_t *svdesc=vd->shared_vec_info; + while (svdesc!=NULL) { + no++; + svdesc=svdesc->next; + } + if (noint_desc[x].level) { + //Seems like this shared vector is both okay and has the least amount of ISRs already attached to it. + best=x; + bestSharedCt=no; + bestLevel=int_desc[x].level; + ALCHLOG(TAG, "...int %d more usable as a shared int: has %d existing vectors", x, no); + } else { + ALCHLOG(TAG, "...worse than int %d", best); + } + } else { + if (best==-1) { + //We haven't found a feasible shared interrupt yet. This one is still free and usable, even if + //not marked as shared. + //Remember it in case we don't find any other shared interrupt that qualifies. + if (bestLevel>int_desc[x].level) { + best=x; + bestLevel=int_desc[x].level; + ALCHLOG(TAG, "...int %d usable as a new shared int", x); + } + } else { + ALCHLOG(TAG, "...already have a shared int"); + } + } + } else { + //We need an unshared IRQ; can't use shared ones; bail out if this is shared. + if (vd->flags&VECDESC_FL_SHARED) { + ALCHLOG(TAG, "...Unusable: int is shared, we need non-shared."); + continue; + } + //Seems this interrupt is feasible. Select it and break out of the loop; no need to search further. + if (bestLevel>int_desc[x].level) { + best=x; + bestLevel=int_desc[x].level; + } else { + ALCHLOG(TAG, "...worse than int %d", best); + } + } + } + ALCHLOG(TAG, "get_free_int: using int %d", best); + + //Okay, by now we have looked at all potential interrupts and hopefully have selected the best one in best. + return best; +} + + +//Common shared isr handler. Chain-call all ISRs. +static void IRAM_ATTR shared_intr_isr(void *arg) +{ + vector_desc_t *vd=(vector_desc_t*)arg; + shared_vector_desc_t *sh_vec=vd->shared_vec_info; + portENTER_CRITICAL(&spinlock); + while(sh_vec) { + if (!sh_vec->disabled) { + if ((sh_vec->statusreg == NULL) || (*sh_vec->statusreg & sh_vec->statusmask)) { + sh_vec->isr(sh_vec->arg); + } + } + sh_vec=sh_vec->next; + } + portEXIT_CRITICAL(&spinlock); +} + + +//We use ESP_EARLY_LOG* here because this can be called before the scheduler is running. +esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusreg, uint32_t intrstatusmask, intr_handler_t handler, + void *arg, intr_handle_t *ret_handle) +{ + intr_handle_data_t *ret=NULL; + int force=-1; + ESP_EARLY_LOGV(TAG, "esp_intr_alloc_intrstatus (cpu %d): checking args", xPortGetCoreID()); + //Shared interrupts should be level-triggered. + if ((flags&ESP_INTR_FLAG_SHARED) && (flags&ESP_INTR_FLAG_EDGE)) return ESP_ERR_INVALID_ARG; + //You can't set an handler / arg for a non-C-callable interrupt. + if ((flags&ESP_INTR_FLAG_HIGH) && (handler)) return ESP_ERR_INVALID_ARG; + //Shared ints should have handler and non-processor-local source + if ((flags&ESP_INTR_FLAG_SHARED) && (!handler || source<0)) return ESP_ERR_INVALID_ARG; + //Statusreg should have a mask + if (intrstatusreg && !intrstatusmask) return ESP_ERR_INVALID_ARG; + + //Default to prio 1 for shared interrupts. Default to prio 1, 2 or 3 for non-shared interrupts. + if ((flags&ESP_INTR_FLAG_LEVELMASK)==0) { + if (flags&ESP_INTR_FLAG_SHARED) { + flags|=ESP_INTR_FLAG_LEVEL1; + } else { + flags|=ESP_INTR_FLAG_LOWMED; + } + } + ESP_EARLY_LOGV(TAG, "esp_intr_alloc_intrstatus (cpu %d): Args okay. Resulting flags 0x%X", xPortGetCoreID(), flags); + + //Check 'special' interrupt sources. These are tied to one specific interrupt, so we + //have to force get_free_int to only look at that. + if (source==ETS_INTERNAL_TIMER0_INTR_SOURCE) force=ETS_INTERNAL_TIMER0_INTR_NO; + if (source==ETS_INTERNAL_TIMER1_INTR_SOURCE) force=ETS_INTERNAL_TIMER1_INTR_NO; + if (source==ETS_INTERNAL_TIMER2_INTR_SOURCE) force=ETS_INTERNAL_TIMER2_INTR_NO; + if (source==ETS_INTERNAL_SW0_INTR_SOURCE) force=ETS_INTERNAL_SW0_INTR_NO; + if (source==ETS_INTERNAL_SW1_INTR_SOURCE) force=ETS_INTERNAL_SW1_INTR_NO; + 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)); + if (ret==NULL) return ESP_ERR_NO_MEM; + + portENTER_CRITICAL(&spinlock); + int cpu=xPortGetCoreID(); + //See if we can find an interrupt that matches the flags. + int intr=get_free_int(flags, cpu, force); + if (intr==-1) { + //None found. Bail out. + portEXIT_CRITICAL(&spinlock); + free(ret); + return ESP_ERR_NOT_FOUND; + } + //Get an int vector desc for int. + vector_desc_t *vd=get_desc_for_int(intr, cpu); + if (vd==NULL) { + portEXIT_CRITICAL(&spinlock); + free(ret); + return ESP_ERR_NO_MEM; + } + + //Allocate that int! + if (flags&ESP_INTR_FLAG_SHARED) { + //Populate vector entry and add to linked list. + shared_vector_desc_t *sh_vec=malloc(sizeof(shared_vector_desc_t)); + if (sh_vec==NULL) { + portEXIT_CRITICAL(&spinlock); + free(ret); + return ESP_ERR_NO_MEM; + } + memset(sh_vec, 0, sizeof(shared_vector_desc_t)); + sh_vec->statusreg=(uint32_t*)intrstatusreg; + sh_vec->statusmask=intrstatusmask; + sh_vec->isr=handler; + sh_vec->arg=arg; + sh_vec->next=vd->shared_vec_info; + sh_vec->source=source; + sh_vec->disabled=0; + vd->shared_vec_info=sh_vec; + vd->flags|=VECDESC_FL_SHARED; + //(Re-)set shared isr handler to new value. + xt_set_interrupt_handler(intr, shared_intr_isr, vd); + } else { + //Mark as unusable for other interrupt sources. This is ours now! + vd->flags=VECDESC_FL_NONSHARED; + if (handler) { + xt_set_interrupt_handler(intr, handler, arg); + } + if (flags&ESP_INTR_FLAG_EDGE) xthal_set_intclear(1 << intr); + vd->source=source; + } + if (flags&ESP_INTR_FLAG_IRAM) { + vd->flags|=VECDESC_FL_INIRAM; + non_iram_int_mask[cpu]&=~(1<flags&=~VECDESC_FL_INIRAM; + non_iram_int_mask[cpu]|=(1<=0) { + intr_matrix_set(cpu, source, intr); + } + + //Fill return handle data. + ret->vector_desc=vd; + ret->shared_vector_desc=vd->shared_vec_info; + + //Enable int at CPU-level; + ESP_INTR_ENABLE(intr); + + //If interrupt has to be started disabled, do that now; ints won't be enabled for real until the end + //of the critical section. + if (flags&ESP_INTR_FLAG_INTRDISABLED) { + esp_intr_disable(ret); + } + + portEXIT_CRITICAL(&spinlock); + + //Fill return handle if needed, otherwise free handle. + if (ret_handle!=NULL) { + *ret_handle=ret; + } else { + free(ret); + } + + ESP_EARLY_LOGD(TAG, "Connected src %d to int %d (cpu %d)", source, intr, cpu); + return ESP_OK; +} + +esp_err_t esp_intr_alloc(int source, int flags, intr_handler_t handler, void *arg, intr_handle_t *ret_handle) +{ + /* + As an optimization, we can create a table with the possible interrupt status registers and masks for every single + source there is. We can then add code here to look up an applicable value and pass that to the + esp_intr_alloc_intrstatus function. + */ + return esp_intr_alloc_intrstatus(source, flags, 0, 0, handler, arg, ret_handle); +} + + +esp_err_t esp_intr_free(intr_handle_t handle) +{ + bool free_shared_vector=false; + if (!handle) return ESP_ERR_INVALID_ARG; + //This routine should be called from the interrupt the task is scheduled on. + if (handle->vector_desc->cpu!=xPortGetCoreID()) return ESP_ERR_INVALID_ARG; + + portENTER_CRITICAL(&spinlock); + esp_intr_disable(handle); + if (handle->vector_desc->flags&VECDESC_FL_SHARED) { + //Find and kill the shared int + shared_vector_desc_t *svd=handle->vector_desc->shared_vec_info; + shared_vector_desc_t *prevsvd=NULL; + assert(svd); //should be something in there for a shared int + while (svd!=NULL) { + if (svd==handle->shared_vector_desc) { + //Found it. Now kill it. + if (prevsvd) { + prevsvd->next=svd->next; + } else { + handle->vector_desc->shared_vec_info=svd->next; + } + free(svd); + break; + } + prevsvd=svd; + svd=svd->next; + } + //If nothing left, disable interrupt. + if (handle->vector_desc->shared_vec_info==NULL) free_shared_vector=true; + ESP_LOGV(TAG, "esp_intr_free: Deleting shared int: %s. Shared int is %s", svd?"not found or last one":"deleted", free_shared_vector?"empty now.":"still in use"); + } + + if ((handle->vector_desc->flags&VECDESC_FL_NONSHARED) || free_shared_vector) { + ESP_LOGV(TAG, "esp_intr_free: Disabling int, killing handler"); + //Reset to normal handler + xt_set_interrupt_handler(handle->vector_desc->intno, xt_unhandled_interrupt, (void*)((int)handle->vector_desc->intno)); + //Theoretically, we could free the vector_desc... not sure if that's worth the few bytes of memory + //we save.(We can also not use the same exit path for empty shared ints anymore if we delete + //the desc.) For now, just mark it as free. + handle->vector_desc->flags&=!(VECDESC_FL_NONSHARED|VECDESC_FL_RESERVED); + //Also kill non_iram mask bit. + non_iram_int_mask[handle->vector_desc->cpu]&=~(1<<(handle->vector_desc->intno)); + } + portEXIT_CRITICAL(&spinlock); + free(handle); + return ESP_OK; +} + +int esp_intr_get_intno(intr_handle_t handle) +{ + return handle->vector_desc->intno; +} + +int esp_intr_get_cpu(intr_handle_t handle) +{ + return handle->vector_desc->cpu; +} + +/* + Interrupt disabling strategy: + If the source is >=0 (meaning a muxed interrupt), we disable it by muxing the interrupt to a non-connected + interrupt. If the source is <0 (meaning an internal, per-cpu interrupt), we disable it using ESP_INTR_DISABLE. + This allows us to, for the muxed CPUs, disable an int from the other core. It also allows disabling shared + interrupts. + */ + +//Muxing an interrupt source to interrupt 6, 7, 11, 15, 16 or 29 cause the interrupt to effectively be disabled. +#define INT_MUX_DISABLED_INTNO 6 + +esp_err_t esp_intr_enable(intr_handle_t handle) +{ + if (!handle) return ESP_ERR_INVALID_ARG; + portENTER_CRITICAL(&spinlock); + int source; + if (handle->shared_vector_desc) { + handle->shared_vector_desc->disabled=0; + source=handle->shared_vector_desc->source; + } else { + source=handle->vector_desc->source; + } + if (source >= 0) { + //Disabled using int matrix; re-connect to enable + intr_matrix_set(handle->vector_desc->cpu, source, handle->vector_desc->intno); + } else { + //Re-enable using cpu int ena reg + if (handle->vector_desc->cpu!=xPortGetCoreID()) return ESP_ERR_INVALID_ARG; //Can only enable these ints on this cpu + ESP_INTR_ENABLE(handle->vector_desc->intno); + } + portEXIT_CRITICAL(&spinlock); + return ESP_OK; +} + +esp_err_t esp_intr_disable(intr_handle_t handle) +{ + if (!handle) return ESP_ERR_INVALID_ARG; + portENTER_CRITICAL(&spinlock); + int source; + if (handle->shared_vector_desc) { + handle->shared_vector_desc->disabled=1; + source=handle->shared_vector_desc->source; + } else { + source=handle->vector_desc->source; + } + if (source >= 0) { + //Disable using int matrix + intr_matrix_set(handle->vector_desc->cpu, source, INT_MUX_DISABLED_INTNO); + } else { + //Disable using per-cpu regs + if (handle->vector_desc->cpu!=xPortGetCoreID()) { + portEXIT_CRITICAL(&spinlock); + return ESP_ERR_INVALID_ARG; //Can only enable these ints on this cpu + } + ESP_INTR_DISABLE(handle->vector_desc->intno); + } + portEXIT_CRITICAL(&spinlock); + return ESP_OK; +} + + +void esp_intr_noniram_disable() +{ + int oldint; + int cpu=xPortGetCoreID(); + int intmask=~non_iram_int_mask[cpu]; + assert(non_iram_int_disabled_flag[cpu]==false); + non_iram_int_disabled_flag[cpu]=true; + asm volatile ( + "movi %0,0\n" + "xsr %0,INTENABLE\n" //disable all ints first + "rsync\n" + "and a3,%0,%1\n" //mask ints that need disabling + "wsr a3,INTENABLE\n" //write back + "rsync\n" + :"=r"(oldint):"r"(intmask):"a3"); + //Save which ints we did disable + non_iram_int_disabled[cpu]=oldint&non_iram_int_mask[cpu]; +} + +void esp_intr_noniram_enable() +{ + int cpu=xPortGetCoreID(); + int intmask=non_iram_int_disabled[cpu]; + assert(non_iram_int_disabled_flag[cpu]==true); + non_iram_int_disabled_flag[cpu]=false; + asm volatile ( + "movi a3,0\n" + "xsr a3,INTENABLE\n" + "rsync\n" + "or a3,a3,%0\n" + "wsr a3,INTENABLE\n" + "rsync\n" + ::"r"(intmask):"a3"); +} + +//These functions are provided in ROM, but the ROM-based functions use non-multicore-capable +//virtualized interrupt levels. Thus, we disable them in the ld file and provide working +//equivalents here. + + +void IRAM_ATTR ets_isr_unmask(unsigned int mask) { + xt_ints_on(mask); +} + +void IRAM_ATTR ets_isr_mask(unsigned int mask) { + xt_ints_off(mask); +} + + + + diff --git a/components/esp32/ld/esp32.rom.ld b/components/esp32/ld/esp32.rom.ld index b557271fa7..6241ff840e 100644 --- a/components/esp32/ld/esp32.rom.ld +++ b/components/esp32/ld/esp32.rom.ld @@ -169,12 +169,6 @@ PROVIDE ( ets_get_xtal_scale = 0x4000856c ); PROVIDE ( ets_install_putc1 = 0x40007d18 ); PROVIDE ( ets_install_putc2 = 0x40007d38 ); PROVIDE ( ets_install_uart_printf = 0x40007d28 ); -PROVIDE ( ets_intr_count = 0x3ffe03fc ); -PROVIDE ( ets_intr_lock = 0x400067b0 ); -PROVIDE ( ets_intr_unlock = 0x400067c4 ); -PROVIDE ( ets_isr_attach = 0x400067ec ); -PROVIDE ( ets_isr_mask = 0x400067fc ); -PROVIDE ( ets_isr_unmask = 0x40006808 ); PROVIDE ( ets_post = 0x4000673c ); PROVIDE ( ets_printf = 0x40007d54 ); PROVIDE ( ets_readySet_ = 0x3ffe01f0 ); @@ -1730,6 +1724,13 @@ PROVIDE ( Xthal_intlevel = 0x3ff9c2b4 ); PROVIDE ( xthal_memcpy = 0x4000c0bc ); PROVIDE ( xthal_set_ccompare = 0x4000c058 ); PROVIDE ( xthal_set_intclear = 0x4000c1ec ); +PROVIDE ( _xtos_set_intlevel = 0x4000bfdc ); +/* +These functions are xtos-related (or call xtos-related functions) and do not play well +with multicore FreeRTOS. Where needed, we provide alternatives that are multicore +compatible. +*/ +/* PROVIDE ( _xtos_alloca_handler = 0x40000010 ); PROVIDE ( _xtos_cause3_handler = 0x40000dd8 ); PROVIDE ( _xtos_c_handler_table = 0x3ffe0548 ); @@ -1748,13 +1749,19 @@ PROVIDE ( _xtos_return_from_exc = 0x4000c034 ); PROVIDE ( _xtos_set_exception_handler = 0x4000074c ); PROVIDE ( _xtos_set_interrupt_handler = 0x4000bf78 ); PROVIDE ( _xtos_set_interrupt_handler_arg = 0x4000bf34 ); -PROVIDE ( _xtos_set_intlevel = 0x4000bfdc ); PROVIDE ( _xtos_set_min_intlevel = 0x4000bff8 ); PROVIDE ( _xtos_set_vpri = 0x40000934 ); PROVIDE ( _xtos_syscall_handler = 0x40000790 ); PROVIDE ( _xtos_unhandled_exception = 0x4000c024 ); PROVIDE ( _xtos_unhandled_interrupt = 0x4000c01c ); PROVIDE ( _xtos_vpri_enabled = 0x3ffe0654 ); +PROVIDE ( ets_intr_count = 0x3ffe03fc ); +PROVIDE ( ets_intr_lock = 0x400067b0 ); +PROVIDE ( ets_intr_unlock = 0x400067c4 ); +PROVIDE ( ets_isr_attach = 0x400067ec ); +PROVIDE ( ets_isr_mask = 0x400067fc ); +PROVIDE ( ets_isr_unmask = 0x40006808 ); +*/ /* Following are static data, but can be used, not generated by script <<<<< btdm data */ PROVIDE ( ld_acl_env = 0x3ffb8258 ); PROVIDE ( ld_active_ch_map = 0x3ffb8334 ); diff --git a/components/esp32/task_wdt.c b/components/esp32/task_wdt.c index 860556b8c5..8585260e2f 100644 --- a/components/esp32/task_wdt.c +++ b/components/esp32/task_wdt.c @@ -27,6 +27,7 @@ #include #include "esp_err.h" #include "esp_intr.h" +#include "esp_intr_alloc.h" #include "esp_attr.h" #include "esp_freertos_hooks.h" #include "soc/timer_group_struct.h" @@ -51,7 +52,7 @@ static wdt_task_t *wdt_task_list=NULL; static portMUX_TYPE taskwdt_spinlock = portMUX_INITIALIZER_UNLOCKED; -static void IRAM_ATTR task_wdt_isr(void *arg) { +static void task_wdt_isr(void *arg) { wdt_task_t *wdttask; const char *cpu; //Feed the watchdog so we do not reset @@ -71,21 +72,21 @@ static void IRAM_ATTR task_wdt_isr(void *arg) { return; } //Watchdog got triggered because at least one task did not report in. - ets_printf(DRAM_STR("Task watchdog got triggered. The following tasks did not feed the watchdog in time:\n")); + 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) { 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(DRAM_STR(" - %s (%s)\n"), pcTaskGetTaskName(wdttask->task_handle), cpu); + ets_printf(" - %s (%s)\n", pcTaskGetTaskName(wdttask->task_handle), cpu); } } ets_printf(DRAM_STR("Tasks currently running:\n")); for (int x=0; x +#include +#include "rom/ets_sys.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" +#include "freertos/xtensa_api.h" +#include "unity.h" +#include "soc/uart_reg.h" +#include "soc/dport_reg.h" +#include "soc/io_mux_reg.h" +#include "esp_intr_alloc.h" +#include "driver/timer.h" + + +#define TIMER_DIVIDER 16 /*!< Hardware timer clock divider */ +#define TIMER_SCALE (TIMER_BASE_CLK / TIMER_DIVIDER) /*!< used to calculate counter value */ +#define TIMER_INTERVAL0_SEC (3.4179) /*!< test interval for timer 0 */ +#define TIMER_INTERVAL1_SEC (5.78) /*!< test interval for timer 1 */ + + +static void my_timer_init(int timer_group, int timer_idx, int ival) +{ + timer_config_t config; + config.alarm_en = 1; + config.auto_reload = 1; + config.counter_dir = TIMER_COUNT_UP; + config.divider = TIMER_DIVIDER; + config.intr_type = TIMER_INTR_LEVEL; + config.counter_en = TIMER_PAUSE; + /*Configure timer*/ + timer_init(timer_group, timer_idx, &config); + /*Stop timer counter*/ + timer_pause(timer_group, timer_idx); + /*Load counter value */ + timer_set_counter_value(timer_group, timer_idx, 0x00000000ULL); + /*Set alarm value*/ + timer_set_alarm_value(timer_group, timer_idx, ival); + /*Enable timer interrupt*/ + timer_enable_intr(timer_group, timer_idx); +} + +static volatile int count[4]={0,0,0,0}; + + +static void timer_isr(void *arg) +{ + int timer_idx = (int)arg; + count[timer_idx]++; + if (timer_idx==0) { + TIMERG0.int_clr_timers.t0 = 1; + TIMERG0.hw_timer[0].update=1; + TIMERG0.hw_timer[0].config.alarm_en = 1; + } + if (timer_idx==1) { + TIMERG0.int_clr_timers.t1 = 1; + TIMERG0.hw_timer[1].update=1; + TIMERG0.hw_timer[1].config.alarm_en = 1; + } + if (timer_idx==2) { + TIMERG1.int_clr_timers.t0 = 1; + TIMERG1.hw_timer[0].update=1; + TIMERG1.hw_timer[0].config.alarm_en = 1; + } + if (timer_idx==3) { + TIMERG1.int_clr_timers.t1 = 1; + TIMERG1.hw_timer[1].update=1; + TIMERG1.hw_timer[1].config.alarm_en = 1; + } +// ets_printf("int %d\n", timer_idx); +} + + +static void timer_test(int flags) { + int x; + timer_isr_handle_t inth[4]; + my_timer_init(TIMER_GROUP_0, TIMER_0, 110000); + my_timer_init(TIMER_GROUP_0, TIMER_1, 120000); + my_timer_init(TIMER_GROUP_1, TIMER_0, 130000); + my_timer_init(TIMER_GROUP_1, TIMER_1, 140000); + timer_isr_register(TIMER_GROUP_0, TIMER_0, timer_isr, (void*)0, flags|ESP_INTR_FLAG_INTRDISABLED, &inth[0]); + timer_isr_register(TIMER_GROUP_0, TIMER_1, timer_isr, (void*)1, flags, &inth[1]); + timer_isr_register(TIMER_GROUP_1, TIMER_0, timer_isr, (void*)2, flags, &inth[2]); + timer_isr_register(TIMER_GROUP_1, TIMER_1, timer_isr, (void*)3, flags, &inth[3]); + timer_start(TIMER_GROUP_0, TIMER_0); + timer_start(TIMER_GROUP_0, TIMER_1); + timer_start(TIMER_GROUP_1, TIMER_0); + timer_start(TIMER_GROUP_1, TIMER_1); + + for (x=0; x<4; x++) count[x]=0; + printf("Interrupts allocated: %d (dis) %d %d %d\n", + esp_intr_get_intno(inth[0]), esp_intr_get_intno(inth[1]), + esp_intr_get_intno(inth[2]), esp_intr_get_intno(inth[3])); + printf("Timer values on start: %d %d %d %d\n", count[0], count[1], count[2], count[3]); + vTaskDelay(1000 / portTICK_RATE_MS); + printf("Timer values after 1 sec: %d %d %d %d\n", count[0], count[1], count[2], count[3]); + TEST_ASSERT(count[0]==0); + TEST_ASSERT(count[1]!=0); + TEST_ASSERT(count[2]!=0); + TEST_ASSERT(count[3]!=0); + + printf("Disabling timers 1 and 2...\n"); + esp_intr_enable(inth[0]); + esp_intr_disable(inth[1]); + esp_intr_disable(inth[2]); + for (x=0; x<4; x++) count[x]=0; + vTaskDelay(1000 / portTICK_RATE_MS); + printf("Timer values after 1 sec: %d %d %d %d\n", count[0], count[1], count[2], count[3]); + TEST_ASSERT(count[0]!=0); + TEST_ASSERT(count[1]==0); + TEST_ASSERT(count[2]==0); + TEST_ASSERT(count[3]!=0); + printf("Disabling other half...\n"); + esp_intr_enable(inth[1]); + esp_intr_enable(inth[2]); + esp_intr_disable(inth[0]); + esp_intr_disable(inth[3]); + for (x=0; x<4; x++) count[x]=0; + vTaskDelay(1000 / portTICK_RATE_MS); + printf("Timer values after 1 sec: %d %d %d %d\n", count[0], count[1], count[2], count[3]); + TEST_ASSERT(count[0]==0); + TEST_ASSERT(count[1]!=0); + TEST_ASSERT(count[2]!=0); + TEST_ASSERT(count[3]==0); + printf("Done.\n"); + esp_intr_free(inth[0]); + esp_intr_free(inth[1]); + esp_intr_free(inth[2]); + esp_intr_free(inth[3]); +} + +static volatile int int_timer_ctr; + + +void int_timer_handler(void *arg) { + xthal_set_ccompare(1, xthal_get_ccount()+8000000); + int_timer_ctr++; +} + +void local_timer_test() +{ + intr_handle_t ih; + esp_err_t r; + r=esp_intr_alloc(ETS_INTERNAL_TIMER1_INTR_SOURCE, 0, int_timer_handler, NULL, &ih); + TEST_ASSERT(r==ESP_OK); + printf("Int timer 1 intno %d\n", esp_intr_get_intno(ih)); + xthal_set_ccompare(1, xthal_get_ccount()+8000000); + int_timer_ctr=0; + vTaskDelay(1000 / portTICK_RATE_MS); + printf("Timer val after 1 sec: %d\n", int_timer_ctr); + TEST_ASSERT(int_timer_ctr!=0); + printf("Disabling int\n"); + esp_intr_disable(ih); + int_timer_ctr=0; + vTaskDelay(1000 / portTICK_RATE_MS); + printf("Timer val after 1 sec: %d\n", int_timer_ctr); + TEST_ASSERT(int_timer_ctr==0); + printf("Re-enabling\n"); + esp_intr_enable(ih); + vTaskDelay(1000 / portTICK_RATE_MS); + printf("Timer val after 1 sec: %d\n", int_timer_ctr); + TEST_ASSERT(int_timer_ctr!=0); + + printf("Free int, re-alloc disabled\n"); + r=esp_intr_free(ih); + TEST_ASSERT(r==ESP_OK); + r=esp_intr_alloc(ETS_INTERNAL_TIMER1_INTR_SOURCE, ESP_INTR_FLAG_INTRDISABLED, int_timer_handler, NULL, &ih); + TEST_ASSERT(r==ESP_OK); + int_timer_ctr=0; + vTaskDelay(1000 / portTICK_RATE_MS); + printf("Timer val after 1 sec: %d\n", int_timer_ctr); + TEST_ASSERT(int_timer_ctr==0); + printf("Re-enabling\n"); + esp_intr_enable(ih); + vTaskDelay(1000 / portTICK_RATE_MS); + printf("Timer val after 1 sec: %d\n", int_timer_ctr); + TEST_ASSERT(int_timer_ctr!=0); + r=esp_intr_free(ih); + TEST_ASSERT(r==ESP_OK); + printf("Done.\n"); +} + + +TEST_CASE("Intr_alloc test, CPU-local int source", "[esp32]") +{ + local_timer_test(); +} + +TEST_CASE("Intr_alloc test, private ints", "[esp32]") +{ + timer_test(0); +} + +TEST_CASE("Intr_alloc test, shared ints", "[esp32]") +{ + timer_test(ESP_INTR_FLAG_SHARED); +} diff --git a/components/esptool_py/Makefile.projbuild b/components/esptool_py/Makefile.projbuild index acbada7244..54221f1795 100644 --- a/components/esptool_py/Makefile.projbuild +++ b/components/esptool_py/Makefile.projbuild @@ -28,13 +28,13 @@ ESPTOOLPY_WRITE_FLASH=$(ESPTOOLPY_SERIAL) write_flash $(if $(CONFIG_ESPTOOLPY_CO ESPTOOL_ALL_FLASH_ARGS += $(CONFIG_APP_OFFSET) $(APP_BIN) -ifdef CONFIG_SECURE_BOOTLOADER_ENABLED +ifdef CONFIG_SECURE_BOOT_ENABLED ifndef IS_BOOTLOADER_BUILD # for secure boot, add a signing step to get from unsiged app to signed app APP_BIN_UNSIGNED := $(APP_BIN:.bin=-unsigned.bin) -$(APP_BIN): $(APP_BIN_UNSIGNED) - $(ESPSECUREPY) sign_data --keyfile $(SECURE_BOOT_SIGNING_KEY) -o $@ $^ # signed in-place +$(APP_BIN): $(APP_BIN_UNSIGNED) $(SECURE_BOOT_SIGNING_KEY) + $(ESPSECUREPY) sign_data --keyfile $(SECURE_BOOT_SIGNING_KEY) -o $@ $< endif endif # non-secure boot (or bootloader), both these files are the same @@ -45,7 +45,7 @@ $(APP_BIN_UNSIGNED): $(APP_ELF) $(ESPTOOLPY_SRC) flash: all_binaries $(ESPTOOLPY_SRC) @echo "Flashing binaries to serial port $(ESPPORT) (app at offset $(CONFIG_APP_OFFSET))..." -ifdef CONFIG_SECURE_BOOTLOADER_ENABLED +ifdef CONFIG_SECURE_BOOT_ENABLED @echo "(Secure boot enabled, so bootloader not flashed automatically. See 'make bootloader' output)" endif $(ESPTOOLPY_WRITE_FLASH) $(ESPTOOL_ALL_FLASH_ARGS) diff --git a/components/esptool_py/esptool b/components/esptool_py/esptool index 5c6962e894..adc914b91a 160000 --- a/components/esptool_py/esptool +++ b/components/esptool_py/esptool @@ -1 +1 @@ -Subproject commit 5c6962e894e0a118c9a4b5760876433493449260 +Subproject commit adc914b91ac6d2cfd1ace56307b4374eb9439e14 diff --git a/components/ethernet/emac_main.c b/components/ethernet/emac_main.c index cbbab5149e..3dc61d809c 100644 --- a/components/ethernet/emac_main.c +++ b/components/ethernet/emac_main.c @@ -35,6 +35,7 @@ #include "esp_err.h" #include "esp_log.h" #include "esp_eth.h" +#include "esp_intr_alloc.h" #include "emac_common.h" #include "emac_desc.h" @@ -305,19 +306,16 @@ static void IRAM_ATTR emac_process_intr(void *arg) } } +//ToDo: this should only be called once because this allocates the interrupt as well. static void emac_enable_intr() { //init emac intr - REG_SET_FIELD(DPORT_PRO_EMAC_INT_MAP_REG, DPORT_PRO_EMAC_INT_MAP, ETS_EMAC_INUM); - xt_set_interrupt_handler(ETS_EMAC_INUM, emac_process_intr, NULL); - xt_ints_on(1 << ETS_EMAC_INUM); - + esp_intr_alloc(ETS_ETH_MAC_INTR_SOURCE, 0, emac_process_intr, NULL, NULL); REG_WRITE(EMAC_DMAINTERRUPT_EN_REG, EMAC_INTR_ENABLE_BIT); } static void emac_disable_intr() { - xt_ints_off(1 << ETS_EMAC_INUM); REG_WRITE(EMAC_DMAINTERRUPT_EN_REG, 0); } diff --git a/components/freertos/test/test_tls_deletecb.c b/components/freertos/test/test_tls_deletecb.c index 5277b761ab..8628f42ec9 100644 --- a/components/freertos/test/test_tls_deletecb.c +++ b/components/freertos/test/test_tls_deletecb.c @@ -16,7 +16,7 @@ static void tskdelcb(int no, void *arg) { - printf("Delete callback: %d = %p!\n", no, arg); + ets_printf("Delete callback: %d = %p!\n", no, arg); } diff --git a/components/freertos/xtensa_intr.c b/components/freertos/xtensa_intr.c index 2f5dc3542e..4767032277 100644 --- a/components/freertos/xtensa_intr.c +++ b/components/freertos/xtensa_intr.c @@ -39,7 +39,6 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #if XCHAL_HAVE_EXCEPTIONS /* Handler table is in xtensa_intr_asm.S */ -// Todo: Make multicore - JD extern xt_exc_handler _xt_exception_table[XCHAL_EXCCAUSE_NUM*portNUM_PROCESSORS]; diff --git a/components/freertos/xtensa_intr_asm.S b/components/freertos/xtensa_intr_asm.S index 8c7ae63fdb..f2d236108d 100644 --- a/components/freertos/xtensa_intr_asm.S +++ b/components/freertos/xtensa_intr_asm.S @@ -40,6 +40,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------- */ + +#if XT_USE_SWPRI +/* Warning - this is not multicore-compatible. */ .data .global _xt_intdata .align 8 @@ -53,7 +56,7 @@ _xt_intdata: _xt_intenable: .word 0 /* Virtual INTENABLE */ _xt_vpri_mask: .word 0xFFFFFFFF /* Virtual priority mask */ - +#endif /* ------------------------------------------------------------------------------- @@ -124,7 +127,8 @@ _xt_exception_table: unsigned int xt_ints_on ( unsigned int mask ) Enables a set of interrupts. Does not simply set INTENABLE directly, but - computes it as a function of the current virtual priority. + computes it as a function of the current virtual priority if XT_USE_SWPRI is + enabled. Can be called from interrupt handlers. ------------------------------------------------------------------------------- */ @@ -137,7 +141,9 @@ _xt_exception_table: xt_ints_on: ENTRY0 + #if XCHAL_HAVE_INTERRUPTS +#if XT_USE_SWPRI movi a3, 0 movi a4, _xt_intdata xsr a3, INTENABLE /* Disables all interrupts */ @@ -149,6 +155,15 @@ xt_ints_on: and a5, a5, a6 /* a5 = _xt_intenable & _xt_vpri_mask */ wsr a5, INTENABLE /* Reenable interrupts */ mov a2, a3 /* Previous mask */ +#else + movi a3, 0 + xsr a3, INTENABLE /* Disables all interrupts */ + rsync + or a2, a3, a2 /* set bits in mask */ + wsr a2, INTENABLE /* Re-enable ints */ + rsync + mov a2, a3 /* return prev mask */ +#endif #else movi a2, 0 /* Return zero */ #endif @@ -162,7 +177,8 @@ xt_ints_on: unsigned int xt_ints_off ( unsigned int mask ) Disables a set of interrupts. Does not simply set INTENABLE directly, - but computes it as a function of the current virtual priority. + but computes it as a function of the current virtual priority if XT_USE_SWPRI is + enabled. Can be called from interrupt handlers. ------------------------------------------------------------------------------- */ @@ -176,6 +192,7 @@ xt_ints_off: ENTRY0 #if XCHAL_HAVE_INTERRUPTS +#if XT_USE_SWPRI movi a3, 0 movi a4, _xt_intdata xsr a3, INTENABLE /* Disables all interrupts */ @@ -188,6 +205,16 @@ xt_ints_off: and a5, a5, a6 /* a5 = _xt_intenable & _xt_vpri_mask */ wsr a5, INTENABLE /* Reenable interrupts */ mov a2, a3 /* Previous mask */ +#else + movi a4, 0 + xsr a4, INTENABLE /* Disables all interrupts */ + rsync + or a3, a4, a2 /* set bits in mask */ + xor a3, a3, a2 /* invert bits in mask set in mask, essentially clearing them */ + wsr a3, INTENABLE /* Re-enable ints */ + rsync + mov a2, a4 /* return prev mask */ +#endif #else movi a2, 0 /* return zero */ #endif diff --git a/components/idf_test/integration_test/CIConfigs/IT_Function_SYS_01.yml b/components/idf_test/integration_test/CIConfigs/IT_Function_SYS_01.yml index 51a6899029..2d332d38a0 100644 --- a/components/idf_test/integration_test/CIConfigs/IT_Function_SYS_01.yml +++ b/components/idf_test/integration_test/CIConfigs/IT_Function_SYS_01.yml @@ -2,4 +2,6 @@ Config: {execute count: 1, execute order: in order} DUT: [SSC1] Filter: - Add: - ID: [SYS_MISC_0101, SYS_MISC_0201] + ID: + - SYS_MISC_0101 # test reboot function + - SYS_MISC_0201 # get heap size test diff --git a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_01.yml b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_01.yml index e86fac28ae..d9bad02c3e 100644 --- a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_01.yml +++ b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_01.yml @@ -2,9 +2,34 @@ Config: {execute count: 1, execute order: in order} DUT: [SSC2, SSC1] Filter: - Add: - ID: [^TCPIP_DHCP_0302, TCPIP_DHCP_0302, TCPIP_DHCP_0301, TCPIP_TCP_0403, TCPIP_TCP_0402, - TCPIP_TCP_0401, TCPIP_TCP_0407, TCPIP_TCP_0406, ^TCPIP_TCP_0411, TCPIP_TCP_0404, - TCPIP_TCP_0408, TCPIP_TCP_0110, ^TCPIP_TCP_0111, TCPIP_TCP_0115, TCPIP_IP_0101, - TCPIP_IP_0102, ^TCPIP_IGMP_0102, ^TCPIP_IGMP_0101, ^TCPIP_IGMP_0104, TCPIP_IGMP_0104, - TCPIP_IGMP_0103, TCPIP_IGMP_0102, TCPIP_IGMP_0101, TCPIP_UDP_0108, TCPIP_UDP_0106, - TCPIP_UDP_0107, TCPIP_UDP_0105, TCPIP_UDP_0101, TCPIP_IGMP_0204, TCPIP_IGMP_0201] + ID: + - TCPIP_DHCP_0101 # dhcp client function test + - TCPIP_DHCP_0103 # dhcp status query + - TCPIP_DHCP_0201 # server dhcp lease test + - TCPIP_DHCP_0202 # dhcp server ip pool + - TCPIP_DHCP_0203 # dhcp server ip pool empty + - TCPIP_DHCP_0204 # dhcp server timeout test + - TCPIP_DHCP_0205 # disconnect STA if config dhcp server + - TCPIP_DHCP_0206 # dhcp server assign same IP to same MAC when it's not released + - TCPIP_DHCP_0207 # dhcp server prefer assign released IP to new client + - TCPIP_DHCP_0208 # dhcp server reconfig and new client able to get first IP in pool + - TCPIP_DHCP_0209 # dhcp server reconfig, old client and new client able to get IP + - TCPIP_DHCP_0210 # dhcp server reconfig, old client able to get IP (discover with requested IP) + - TCPIP_DHCP_0211 # dhcp server reconfig, old client able to renew IP (direct send request) + - TCPIP_DHCP_0212 # dhcp server function test + - TCPIP_DHCP_0301 # sta dhcp static ip interaction + - TCPIP_DHCP_0302 # ap dhcp static ip interaction + - TCPIP_IGMP_0101 # station IGMP join group address check + - TCPIP_IGMP_0102 # station IGMP leave group address check + - TCPIP_IGMP_0103 # softAP IGMP join group address check + - TCPIP_IGMP_0104 # softAP IGMP leave group address check + - TCPIP_IGMP_0201 # station IGMP recv packets + - TCPIP_IGMP_0202 # station send multicast packets + - TCPIP_IGMP_0203 # softAP IGMP recv packets + - TCPIP_IGMP_0204 # softAP send multicast packets + - TCPIP_IP_0101 # sta set and query static ip test + - TCPIP_IP_0102 # ap set and query static ip test + - TCPIP_TCP_0101 # STA mode, connect test. use different ip, port + - TCPIP_TCP_0102 # STA mode, server listen test. use different kinds of port + - TCPIP_TCP_0103 # STA mode, send/recv basic test + - TCPIP_TCP_0104 # STA mode, shutdown basic test diff --git a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_02.yml b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_02.yml index a746cdd913..a1a927a082 100644 --- a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_02.yml +++ b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_02.yml @@ -1,10 +1,35 @@ Config: {execute count: 1, execute order: in order} -DUT: [SSC2, SSC1] +DUT: [SSC1] Filter: - Add: - ID: [TCPIP_IGMP_0202, TCPIP_IGMP_0203, ^TCPIP_TCP_0403, ^TCPIP_TCP_0408, TCPIP_UDP_0201, - ^TCPIP_DHCP_0301, ^TCPIP_TCP_0101, ^TCPIP_TCP_0103, ^TCPIP_TCP_0105, ^TCPIP_TCP_0104, - ^TCPIP_TCP_0107, ^TCPIP_TCP_0106, ^TCPIP_DHCP_0210, ^TCPIP_DHCP_0211, ^TCPIP_DHCP_0212, - ^TCPIP_TCP_0404, TCPIP_TCP_0212, TCPIP_TCP_0210, ^TCPIP_TCP_0406, ^TCPIP_TCP_0407, - ^TCPIP_TCP_0401, ^TCPIP_TCP_0210, ^TCPIP_TCP_0212, TCPIP_DHCP_0211, TCPIP_DHCP_0210, - TCPIP_DHCP_0212, TCPIP_DHCP_0101, TCPIP_DHCP_0103, TCPIP_DHCP_0206, TCPIP_DHCP_0207] + ID: + - TCPIP_TCP_0105 # STA mode, close for different types of TCP sockets test + - TCPIP_TCP_0106 # STA mode, create max TCP sockets test + - TCPIP_TCP_0107 # STA mode, accept max TCP client by server test + - TCPIP_TCP_0110 # AP mode, connect test. use different ip, port + - TCPIP_TCP_0111 # AP mode, server listen test. use different kinds of port + - TCPIP_TCP_0112 # AP mode, send/recv basic test + - TCPIP_TCP_0113 # AP mode, shutdown basic test + - TCPIP_TCP_0114 # AP mode, close for different types of TCP sockets test + - TCPIP_TCP_0115 # AP mode, create max TCP sockets test + - TCPIP_TCP_0116 # AP mode, accept max TCP client by server test + - TCPIP_TCP_0201 # STA mode, connect test. use socket in state that can't connect + - TCPIP_TCP_0202 # STA mode, server listen test. use socket in state that can't listen + - TCPIP_TCP_0203 # send test. use socket in state that can't send + - TCPIP_TCP_0204 # STA mode, recv buffer test + - TCPIP_TCP_0206 # STA mode, get active socket info test + - TCPIP_TCP_0207 # AP mode, connect test. use socket in state that can't connect + - TCPIP_TCP_0208 # AP mode, server listen test. use socket in state that can't listen + - TCPIP_TCP_0210 # AP mode, recv buffer test + - TCPIP_TCP_0212 # AP mode, get active socket info test + - TCPIP_TCP_0401 # do TCP send after WIFI disconnected + - TCPIP_TCP_0402 # close TCP socket after WIFI disconnected + - TCPIP_TCP_0403 # do TCP send after mode changed + - TCPIP_TCP_0404 # close TCP socket after mode changed + - TCPIP_TCP_0406 # close TCP socket after PC NIC disabled + - TCPIP_TCP_0407 # do TCP send after IP changed + - TCPIP_TCP_0408 # close TCP socket after IP changed + - TCPIP_TCP_0411 # do TCP send after socket changed + - TCPIP_TCP_0412 # close TCP send after socket changed + - TCPIP_UDP_0101 # STA mode, udp bind test. use different ip, port + - TCPIP_UDP_0105 # STA mode, close UDP sockets test diff --git a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_03.yml b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_03.yml index f5f0abe5db..dcbd419acc 100644 --- a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_03.yml +++ b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_03.yml @@ -2,9 +2,34 @@ Config: {execute count: 1, execute order: in order} DUT: [SSC2, SSC1] Filter: - Add: - ID: [^TCPIP_IP_0102, ^TCPIP_UDP_0105, ^TCPIP_UDP_0107, ^TCPIP_UDP_0106, ^TCPIP_UDP_0101, - TCPIP_TCP_0202, ^TCPIP_UDP_0108, ^TCPIP_IGMP_0201, ^TCPIP_IGMP_0203, ^TCPIP_IGMP_0202, - ^TCPIP_IGMP_0103, TCPIP_UDP_0114, TCPIP_UDP_0113, TCPIP_UDP_0112, TCPIP_UDP_0202, - TCPIP_DHCP_0205, TCPIP_DHCP_0202, TCPIP_DHCP_0203, ^TCPIP_TCP_0102, TCPIP_TCP_0106, - TCPIP_TCP_0107, TCPIP_TCP_0104, TCPIP_TCP_0105, TCPIP_TCP_0102, TCPIP_TCP_0103, - TCPIP_TCP_0101, ^TCPIP_TCP_0116, ^TCPIP_TCP_0114, ^TCPIP_TCP_0115, ^TCPIP_TCP_0112] + ID: + - TCPIP_UDP_0106 # STA mode, create max udp socket test + - TCPIP_UDP_0107 # STA mode, get active socket info test + - TCPIP_UDP_0108 # AP mode, udp bind test. use different ip, port + - TCPIP_UDP_0112 # AP mode, close UDP sockets test + - TCPIP_UDP_0113 # AP mode, create max udp socket test + - TCPIP_UDP_0114 # AP mode, get active socket info test + - TCPIP_UDP_0201 # STA mode, recv buffer test + - TCPIP_UDP_0202 # AP mode, recv buffer test + - ^TCPIP_DHCP_0101 # dhcp client function test + - ^TCPIP_DHCP_0103 # dhcp status query + - ^TCPIP_DHCP_0201 # server dhcp lease test + - ^TCPIP_DHCP_0202 # dhcp server ip pool + - ^TCPIP_DHCP_0203 # dhcp server ip pool empty + - ^TCPIP_DHCP_0204 # dhcp server timeout test + - ^TCPIP_DHCP_0205 # disconnect STA if config dhcp server + - ^TCPIP_DHCP_0206 # dhcp server assign same IP to same MAC when it's not released + - ^TCPIP_DHCP_0207 # dhcp server prefer assign released IP to new client + - ^TCPIP_DHCP_0208 # dhcp server reconfig and new client able to get first IP in pool + - ^TCPIP_DHCP_0209 # dhcp server reconfig, old client and new client able to get IP + - ^TCPIP_DHCP_0210 # dhcp server reconfig, old client able to get IP (discover with requested IP) + - ^TCPIP_DHCP_0211 # dhcp server reconfig, old client able to renew IP (direct send request) + - ^TCPIP_DHCP_0212 # dhcp server function test + - ^TCPIP_DHCP_0301 # sta dhcp static ip interaction + - ^TCPIP_DHCP_0302 # ap dhcp static ip interaction + - ^TCPIP_IGMP_0101 # station IGMP join group address check + - ^TCPIP_IGMP_0102 # station IGMP leave group address check + - ^TCPIP_IGMP_0103 # softAP IGMP join group address check + - ^TCPIP_IGMP_0104 # softAP IGMP leave group address check + - ^TCPIP_IGMP_0201 # station IGMP recv packets + - ^TCPIP_IGMP_0202 # station send multicast packets diff --git a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_04.yml b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_04.yml index b59e8c60cd..17c74c6f70 100644 --- a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_04.yml +++ b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_04.yml @@ -2,9 +2,34 @@ Config: {execute count: 1, execute order: in order} DUT: [SSC2, SSC1] Filter: - Add: - ID: [^TCPIP_TCP_0113, ^TCPIP_TCP_0110, TCPIP_DHCP_0209, ^TCPIP_DHCP_0209, ^TCPIP_DHCP_0207, - ^TCPIP_DHCP_0206, ^TCPIP_DHCP_0205, ^TCPIP_DHCP_0204, ^TCPIP_DHCP_0203, ^TCPIP_DHCP_0202, - ^TCPIP_DHCP_0201, TCPIP_TCP_0204, TCPIP_TCP_0207, TCPIP_TCP_0206, TCPIP_TCP_0201, - ^TCPIP_DHCP_0101, TCPIP_TCP_0203, ^TCPIP_DHCP_0103, ^TCPIP_DHCP_0208, TCPIP_TCP_0208, - ^TCPIP_TCP_0202, ^TCPIP_TCP_0203, TCPIP_DHCP_0204, ^TCPIP_TCP_0201, ^TCPIP_TCP_0206, - ^TCPIP_TCP_0207, ^TCPIP_TCP_0204, TCPIP_DHCP_0201, ^TCPIP_TCP_0208, TCPIP_DHCP_0208] + ID: + - ^TCPIP_IGMP_0203 # softAP IGMP recv packets + - ^TCPIP_IGMP_0204 # softAP send multicast packets + - ^TCPIP_IP_0101 # sta set and query static ip test + - ^TCPIP_IP_0102 # ap set and query static ip test + - ^TCPIP_TCP_0101 # STA mode, connect test. use different ip, port + - ^TCPIP_TCP_0102 # STA mode, server listen test. use different kinds of port + - ^TCPIP_TCP_0103 # STA mode, send/recv basic test + - ^TCPIP_TCP_0104 # STA mode, shutdown basic test + - ^TCPIP_TCP_0105 # STA mode, close for different types of TCP sockets test + - ^TCPIP_TCP_0106 # STA mode, create max TCP sockets test + - ^TCPIP_TCP_0107 # STA mode, accept max TCP client by server test + - ^TCPIP_TCP_0110 # AP mode, connect test. use different ip, port + - ^TCPIP_TCP_0111 # AP mode, server listen test. use different kinds of port + - ^TCPIP_TCP_0112 # AP mode, send/recv basic test + - ^TCPIP_TCP_0113 # AP mode, shutdown basic test + - ^TCPIP_TCP_0114 # AP mode, close for different types of TCP sockets test + - ^TCPIP_TCP_0115 # AP mode, create max TCP sockets test + - ^TCPIP_TCP_0116 # AP mode, accept max TCP client by server test + - ^TCPIP_TCP_0201 # STA mode, connect test. use socket in state that can't connect + - ^TCPIP_TCP_0202 # STA mode, server listen test. use socket in state that can't listen + - ^TCPIP_TCP_0203 # send test. use socket in state that can't send + - ^TCPIP_TCP_0204 # STA mode, recv buffer test + - ^TCPIP_TCP_0206 # STA mode, get active socket info test + - ^TCPIP_TCP_0207 # AP mode, connect test. use socket in state that can't connect + - ^TCPIP_TCP_0208 # AP mode, server listen test. use socket in state that can't listen + - ^TCPIP_TCP_0210 # AP mode, recv buffer test + - ^TCPIP_TCP_0212 # AP mode, get active socket info test + - ^TCPIP_TCP_0401 # do TCP send after WIFI disconnected + - ^TCPIP_TCP_0402 # close TCP socket after WIFI disconnected + - ^TCPIP_TCP_0403 # do TCP send after mode changed diff --git a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_05.yml b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_05.yml index 627d67c408..0b07056588 100644 --- a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_05.yml +++ b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_05.yml @@ -1,8 +1,21 @@ Config: {execute count: 1, execute order: in order} -DUT: [SSC2, SSC1] +DUT: [SSC1] Filter: - Add: - ID: [^TCPIP_IGMP_0204, ^TCPIP_TCP_0412, TCPIP_TCP_0411, TCPIP_TCP_0412, ^TCPIP_UDP_0112, - ^TCPIP_UDP_0113, ^TCPIP_UDP_0114, ^TCPIP_UDP_0202, ^TCPIP_UDP_0201, ^TCPIP_IP_0101, - ^TCPIP_TCP_0402, TCPIP_TCP_0114, TCPIP_TCP_0116, TCPIP_TCP_0111, TCPIP_TCP_0113, - TCPIP_TCP_0112] + ID: + - ^TCPIP_TCP_0404 # close TCP socket after mode changed + - ^TCPIP_TCP_0406 # close TCP socket after PC NIC disabled + - ^TCPIP_TCP_0407 # do TCP send after IP changed + - ^TCPIP_TCP_0408 # close TCP socket after IP changed + - ^TCPIP_TCP_0411 # do TCP send after socket changed + - ^TCPIP_TCP_0412 # close TCP send after socket changed + - ^TCPIP_UDP_0101 # STA mode, udp bind test. use different ip, port + - ^TCPIP_UDP_0105 # STA mode, close UDP sockets test + - ^TCPIP_UDP_0106 # STA mode, create max udp socket test + - ^TCPIP_UDP_0107 # STA mode, get active socket info test + - ^TCPIP_UDP_0108 # AP mode, udp bind test. use different ip, port + - ^TCPIP_UDP_0112 # AP mode, close UDP sockets test + - ^TCPIP_UDP_0113 # AP mode, create max udp socket test + - ^TCPIP_UDP_0114 # AP mode, get active socket info test + - ^TCPIP_UDP_0201 # STA mode, recv buffer test + - ^TCPIP_UDP_0202 # AP mode, recv buffer test diff --git a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_06.yml b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_06.yml index 7cf6279995..bdfeda4bb4 100644 --- a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_06.yml +++ b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_06.yml @@ -2,4 +2,6 @@ Config: {execute count: 1, execute order: in order} DUT: [SSC1] Filter: - Add: - ID: [TCPIP_TCP_0405, ^TCPIP_TCP_0405] + ID: + - TCPIP_TCP_0405 # do TCP send after PC NIC disabled + - ^TCPIP_TCP_0405 # do TCP send after PC NIC disabled diff --git a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_07.yml b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_07.yml index 839ac972f4..862077d54a 100644 --- a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_07.yml +++ b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_07.yml @@ -1,10 +1,35 @@ Config: {execute count: 1, execute order: in order} -DUT: [SSC2, SSC1] +DUT: [SSC1] Filter: - Add: - ID: [TCPIP_UDP_0303, TCPIP_UDP_0303, TCPIP_UDP_0303, TCPIP_UDP_0303, TCPIP_UDP_0303, - TCPIP_ICMP_0101, TCPIP_ICMP_0101, TCPIP_ICMP_0101, TCPIP_ICMP_0101, TCPIP_ICMP_0101, - TCPIP_DNS_0102, TCPIP_DNS_0102, TCPIP_DNS_0102, TCPIP_DNS_0102, TCPIP_DNS_0102, - TCPIP_DNS_0101, TCPIP_DNS_0101, TCPIP_DNS_0101, TCPIP_DNS_0101, TCPIP_DNS_0101, - ^TCPIP_ICMP_0101, ^TCPIP_ICMP_0101, ^TCPIP_ICMP_0101, ^TCPIP_ICMP_0101, ^TCPIP_ICMP_0101, - TCPIP_UDP_0109, TCPIP_UDP_0109, TCPIP_UDP_0109, TCPIP_UDP_0109, TCPIP_UDP_0109] + ID: + - TCPIP_DNS_0101 # get host by name test + - TCPIP_DNS_0101 # get host by name test + - TCPIP_DNS_0101 # get host by name test + - TCPIP_DNS_0101 # get host by name test + - TCPIP_DNS_0101 # get host by name test + - TCPIP_DNS_0102 # TCP connect to iot.espressif.com + - TCPIP_DNS_0102 # TCP connect to iot.espressif.com + - TCPIP_DNS_0102 # TCP connect to iot.espressif.com + - TCPIP_DNS_0102 # TCP connect to iot.espressif.com + - TCPIP_DNS_0102 # TCP connect to iot.espressif.com + - TCPIP_DNS_0103 # UDP send to iot.expressif.com + - TCPIP_DNS_0103 # UDP send to iot.expressif.com + - TCPIP_DNS_0103 # UDP send to iot.expressif.com + - TCPIP_DNS_0103 # UDP send to iot.expressif.com + - TCPIP_DNS_0103 # UDP send to iot.expressif.com + - TCPIP_ICMP_0101 # ping function test + - TCPIP_ICMP_0101 # ping function test + - TCPIP_ICMP_0101 # ping function test + - TCPIP_ICMP_0101 # ping function test + - TCPIP_ICMP_0101 # ping function test + - TCPIP_UDP_0102 # STA mode, sendto test. use different ip, port + - TCPIP_UDP_0102 # STA mode, sendto test. use different ip, port + - TCPIP_UDP_0102 # STA mode, sendto test. use different ip, port + - TCPIP_UDP_0102 # STA mode, sendto test. use different ip, port + - TCPIP_UDP_0102 # STA mode, sendto test. use different ip, port + - TCPIP_UDP_0103 # STA mode, sendto test with different length + - TCPIP_UDP_0103 # STA mode, sendto test with different length + - TCPIP_UDP_0103 # STA mode, sendto test with different length + - TCPIP_UDP_0103 # STA mode, sendto test with different length + - TCPIP_UDP_0103 # STA mode, sendto test with different length diff --git a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_08.yml b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_08.yml index b318b09377..af6612e35b 100644 --- a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_08.yml +++ b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_08.yml @@ -1,10 +1,35 @@ Config: {execute count: 1, execute order: in order} -DUT: [SSC1] +DUT: [SSC2, SSC1] Filter: - Add: - ID: [TCPIP_UDP_0104, TCPIP_UDP_0104, TCPIP_UDP_0104, TCPIP_UDP_0104, TCPIP_UDP_0104, - TCPIP_UDP_0102, TCPIP_UDP_0102, TCPIP_UDP_0102, TCPIP_UDP_0102, TCPIP_UDP_0102, - TCPIP_UDP_0103, TCPIP_UDP_0103, TCPIP_UDP_0103, TCPIP_UDP_0103, TCPIP_UDP_0103, - ^TCPIP_UDP_0307, ^TCPIP_UDP_0307, ^TCPIP_UDP_0307, ^TCPIP_UDP_0307, ^TCPIP_UDP_0307, - ^TCPIP_UDP_0306, ^TCPIP_UDP_0306, ^TCPIP_UDP_0306, ^TCPIP_UDP_0306, ^TCPIP_UDP_0306, - ^TCPIP_UDP_0305, ^TCPIP_UDP_0305, ^TCPIP_UDP_0305, ^TCPIP_UDP_0305, ^TCPIP_UDP_0305] + ID: + - TCPIP_UDP_0104 # STA mode, recvfrom basic test + - TCPIP_UDP_0104 # STA mode, recvfrom basic test + - TCPIP_UDP_0104 # STA mode, recvfrom basic test + - TCPIP_UDP_0104 # STA mode, recvfrom basic test + - TCPIP_UDP_0104 # STA mode, recvfrom basic test + - TCPIP_UDP_0109 # AP mode, sendto test. use different ip, port + - TCPIP_UDP_0109 # AP mode, sendto test. use different ip, port + - TCPIP_UDP_0109 # AP mode, sendto test. use different ip, port + - TCPIP_UDP_0109 # AP mode, sendto test. use different ip, port + - TCPIP_UDP_0109 # AP mode, sendto test. use different ip, port + - TCPIP_UDP_0110 # AP mode, sendto test with different length + - TCPIP_UDP_0110 # AP mode, sendto test with different length + - TCPIP_UDP_0110 # AP mode, sendto test with different length + - TCPIP_UDP_0110 # AP mode, sendto test with different length + - TCPIP_UDP_0110 # AP mode, sendto test with different length + - TCPIP_UDP_0111 # AP mode, recvfrom basic test + - TCPIP_UDP_0111 # AP mode, recvfrom basic test + - TCPIP_UDP_0111 # AP mode, recvfrom basic test + - TCPIP_UDP_0111 # AP mode, recvfrom basic test + - TCPIP_UDP_0111 # AP mode, recvfrom basic test + - TCPIP_UDP_0301 # do UDP send after WIFI disconnected + - TCPIP_UDP_0301 # do UDP send after WIFI disconnected + - TCPIP_UDP_0301 # do UDP send after WIFI disconnected + - TCPIP_UDP_0301 # do UDP send after WIFI disconnected + - TCPIP_UDP_0301 # do UDP send after WIFI disconnected + - TCPIP_UDP_0302 # close UDP socket after WIFI disconnected + - TCPIP_UDP_0302 # close UDP socket after WIFI disconnected + - TCPIP_UDP_0302 # close UDP socket after WIFI disconnected + - TCPIP_UDP_0302 # close UDP socket after WIFI disconnected + - TCPIP_UDP_0302 # close UDP socket after WIFI disconnected diff --git a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_09.yml b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_09.yml index 50b50a3eb6..1a78479f39 100644 --- a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_09.yml +++ b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_09.yml @@ -2,9 +2,34 @@ Config: {execute count: 1, execute order: in order} DUT: [SSC1] Filter: - Add: - ID: [^TCPIP_UDP_0304, ^TCPIP_UDP_0304, ^TCPIP_UDP_0304, ^TCPIP_UDP_0304, ^TCPIP_UDP_0304, - ^TCPIP_UDP_0302, ^TCPIP_UDP_0302, ^TCPIP_UDP_0302, ^TCPIP_UDP_0302, ^TCPIP_UDP_0302, - ^TCPIP_UDP_0301, ^TCPIP_UDP_0301, ^TCPIP_UDP_0301, ^TCPIP_UDP_0301, ^TCPIP_UDP_0301, - ^TCPIP_UDP_0104, ^TCPIP_UDP_0104, ^TCPIP_UDP_0104, ^TCPIP_UDP_0104, ^TCPIP_UDP_0104, - ^TCPIP_UDP_0103, ^TCPIP_UDP_0103, ^TCPIP_UDP_0103, ^TCPIP_UDP_0103, ^TCPIP_UDP_0103, - ^TCPIP_UDP_0102, ^TCPIP_UDP_0102, ^TCPIP_UDP_0102, ^TCPIP_UDP_0102, ^TCPIP_UDP_0102] + ID: + - TCPIP_UDP_0303 # do UDP send after mode changed + - TCPIP_UDP_0303 # do UDP send after mode changed + - TCPIP_UDP_0303 # do UDP send after mode changed + - TCPIP_UDP_0303 # do UDP send after mode changed + - TCPIP_UDP_0303 # do UDP send after mode changed + - TCPIP_UDP_0304 # close UDP socket after mode changed + - TCPIP_UDP_0304 # close UDP socket after mode changed + - TCPIP_UDP_0304 # close UDP socket after mode changed + - TCPIP_UDP_0304 # close UDP socket after mode changed + - TCPIP_UDP_0304 # close UDP socket after mode changed + - TCPIP_UDP_0305 # close UDP socket after PC NIC disabled + - TCPIP_UDP_0305 # close UDP socket after PC NIC disabled + - TCPIP_UDP_0305 # close UDP socket after PC NIC disabled + - TCPIP_UDP_0305 # close UDP socket after PC NIC disabled + - TCPIP_UDP_0305 # close UDP socket after PC NIC disabled + - TCPIP_UDP_0306 # do UDP send after IP changed + - TCPIP_UDP_0306 # do UDP send after IP changed + - TCPIP_UDP_0306 # do UDP send after IP changed + - TCPIP_UDP_0306 # do UDP send after IP changed + - TCPIP_UDP_0306 # do UDP send after IP changed + - TCPIP_UDP_0307 # close UDP socket after IP changed + - TCPIP_UDP_0307 # close UDP socket after IP changed + - TCPIP_UDP_0307 # close UDP socket after IP changed + - TCPIP_UDP_0307 # close UDP socket after IP changed + - TCPIP_UDP_0307 # close UDP socket after IP changed + - ^TCPIP_DNS_0101 # get host by name test + - ^TCPIP_DNS_0101 # get host by name test + - ^TCPIP_DNS_0101 # get host by name test + - ^TCPIP_DNS_0101 # get host by name test + - ^TCPIP_DNS_0101 # get host by name test diff --git a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_10.yml b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_10.yml index 44b7bd1893..1b7ca56756 100644 --- a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_10.yml +++ b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_10.yml @@ -1,10 +1,35 @@ Config: {execute count: 1, execute order: in order} -DUT: [SSC2, SSC1] +DUT: [SSC1] Filter: - Add: - ID: [TCPIP_UDP_0111, TCPIP_UDP_0111, TCPIP_UDP_0111, TCPIP_UDP_0111, TCPIP_UDP_0111, - TCPIP_UDP_0110, TCPIP_UDP_0110, TCPIP_UDP_0110, TCPIP_UDP_0110, TCPIP_UDP_0110, - ^TCPIP_DNS_0101, ^TCPIP_DNS_0101, ^TCPIP_DNS_0101, ^TCPIP_DNS_0101, ^TCPIP_DNS_0101, - ^TCPIP_DNS_0103, ^TCPIP_DNS_0103, ^TCPIP_DNS_0103, ^TCPIP_DNS_0103, ^TCPIP_DNS_0103, - ^TCPIP_DNS_0102, ^TCPIP_DNS_0102, ^TCPIP_DNS_0102, ^TCPIP_DNS_0102, ^TCPIP_DNS_0102, - TCPIP_UDP_0304, TCPIP_UDP_0304, TCPIP_UDP_0304, TCPIP_UDP_0304, TCPIP_UDP_0304] + ID: + - ^TCPIP_DNS_0102 # TCP connect to iot.espressif.com + - ^TCPIP_DNS_0102 # TCP connect to iot.espressif.com + - ^TCPIP_DNS_0102 # TCP connect to iot.espressif.com + - ^TCPIP_DNS_0102 # TCP connect to iot.espressif.com + - ^TCPIP_DNS_0102 # TCP connect to iot.espressif.com + - ^TCPIP_DNS_0103 # UDP send to iot.expressif.com + - ^TCPIP_DNS_0103 # UDP send to iot.expressif.com + - ^TCPIP_DNS_0103 # UDP send to iot.expressif.com + - ^TCPIP_DNS_0103 # UDP send to iot.expressif.com + - ^TCPIP_DNS_0103 # UDP send to iot.expressif.com + - ^TCPIP_ICMP_0101 # ping function test + - ^TCPIP_ICMP_0101 # ping function test + - ^TCPIP_ICMP_0101 # ping function test + - ^TCPIP_ICMP_0101 # ping function test + - ^TCPIP_ICMP_0101 # ping function test + - ^TCPIP_UDP_0102 # STA mode, sendto test. use different ip, port + - ^TCPIP_UDP_0102 # STA mode, sendto test. use different ip, port + - ^TCPIP_UDP_0102 # STA mode, sendto test. use different ip, port + - ^TCPIP_UDP_0102 # STA mode, sendto test. use different ip, port + - ^TCPIP_UDP_0102 # STA mode, sendto test. use different ip, port + - ^TCPIP_UDP_0103 # STA mode, sendto test with different length + - ^TCPIP_UDP_0103 # STA mode, sendto test with different length + - ^TCPIP_UDP_0103 # STA mode, sendto test with different length + - ^TCPIP_UDP_0103 # STA mode, sendto test with different length + - ^TCPIP_UDP_0103 # STA mode, sendto test with different length + - ^TCPIP_UDP_0104 # STA mode, recvfrom basic test + - ^TCPIP_UDP_0104 # STA mode, recvfrom basic test + - ^TCPIP_UDP_0104 # STA mode, recvfrom basic test + - ^TCPIP_UDP_0104 # STA mode, recvfrom basic test + - ^TCPIP_UDP_0104 # STA mode, recvfrom basic test diff --git a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_11.yml b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_11.yml index be615d0878..ed03676da8 100644 --- a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_11.yml +++ b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_11.yml @@ -2,9 +2,34 @@ Config: {execute count: 1, execute order: in order} DUT: [SSC1] Filter: - Add: - ID: [TCPIP_UDP_0305, TCPIP_UDP_0305, TCPIP_UDP_0305, TCPIP_UDP_0305, TCPIP_UDP_0305, - TCPIP_UDP_0306, TCPIP_UDP_0306, TCPIP_UDP_0306, TCPIP_UDP_0306, TCPIP_UDP_0306, - TCPIP_UDP_0307, TCPIP_UDP_0307, TCPIP_UDP_0307, TCPIP_UDP_0307, TCPIP_UDP_0307, - TCPIP_UDP_0301, TCPIP_UDP_0301, TCPIP_UDP_0301, TCPIP_UDP_0301, TCPIP_UDP_0301, - TCPIP_UDP_0302, TCPIP_UDP_0302, TCPIP_UDP_0302, TCPIP_UDP_0302, TCPIP_UDP_0302, - TCPIP_DNS_0103, TCPIP_DNS_0103, TCPIP_DNS_0103, TCPIP_DNS_0103, TCPIP_DNS_0103] + ID: + - ^TCPIP_UDP_0110 # AP mode, sendto test with different length + - ^TCPIP_UDP_0110 # AP mode, sendto test with different length + - ^TCPIP_UDP_0110 # AP mode, sendto test with different length + - ^TCPIP_UDP_0110 # AP mode, sendto test with different length + - ^TCPIP_UDP_0110 # AP mode, sendto test with different length + - ^TCPIP_UDP_0301 # do UDP send after WIFI disconnected + - ^TCPIP_UDP_0301 # do UDP send after WIFI disconnected + - ^TCPIP_UDP_0301 # do UDP send after WIFI disconnected + - ^TCPIP_UDP_0301 # do UDP send after WIFI disconnected + - ^TCPIP_UDP_0301 # do UDP send after WIFI disconnected + - ^TCPIP_UDP_0302 # close UDP socket after WIFI disconnected + - ^TCPIP_UDP_0302 # close UDP socket after WIFI disconnected + - ^TCPIP_UDP_0302 # close UDP socket after WIFI disconnected + - ^TCPIP_UDP_0302 # close UDP socket after WIFI disconnected + - ^TCPIP_UDP_0302 # close UDP socket after WIFI disconnected + - ^TCPIP_UDP_0303 # do UDP send after mode changed + - ^TCPIP_UDP_0303 # do UDP send after mode changed + - ^TCPIP_UDP_0303 # do UDP send after mode changed + - ^TCPIP_UDP_0303 # do UDP send after mode changed + - ^TCPIP_UDP_0303 # do UDP send after mode changed + - ^TCPIP_UDP_0304 # close UDP socket after mode changed + - ^TCPIP_UDP_0304 # close UDP socket after mode changed + - ^TCPIP_UDP_0304 # close UDP socket after mode changed + - ^TCPIP_UDP_0304 # close UDP socket after mode changed + - ^TCPIP_UDP_0304 # close UDP socket after mode changed + - ^TCPIP_UDP_0305 # close UDP socket after PC NIC disabled + - ^TCPIP_UDP_0305 # close UDP socket after PC NIC disabled + - ^TCPIP_UDP_0305 # close UDP socket after PC NIC disabled + - ^TCPIP_UDP_0305 # close UDP socket after PC NIC disabled + - ^TCPIP_UDP_0305 # close UDP socket after PC NIC disabled diff --git a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_12.yml b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_12.yml index 73b0187eed..a0f7e389df 100644 --- a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_12.yml +++ b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_12.yml @@ -2,5 +2,14 @@ Config: {execute count: 1, execute order: in order} DUT: [SSC1] Filter: - Add: - ID: [^TCPIP_UDP_0303, ^TCPIP_UDP_0303, ^TCPIP_UDP_0303, ^TCPIP_UDP_0303, ^TCPIP_UDP_0303, - ^TCPIP_UDP_0110, ^TCPIP_UDP_0110, ^TCPIP_UDP_0110, ^TCPIP_UDP_0110, ^TCPIP_UDP_0110] + ID: + - ^TCPIP_UDP_0306 # do UDP send after IP changed + - ^TCPIP_UDP_0306 # do UDP send after IP changed + - ^TCPIP_UDP_0306 # do UDP send after IP changed + - ^TCPIP_UDP_0306 # do UDP send after IP changed + - ^TCPIP_UDP_0306 # do UDP send after IP changed + - ^TCPIP_UDP_0307 # close UDP socket after IP changed + - ^TCPIP_UDP_0307 # close UDP socket after IP changed + - ^TCPIP_UDP_0307 # close UDP socket after IP changed + - ^TCPIP_UDP_0307 # close UDP socket after IP changed + - ^TCPIP_UDP_0307 # close UDP socket after IP changed diff --git a/components/idf_test/integration_test/CIConfigs/IT_Function_WIFI_01.yml b/components/idf_test/integration_test/CIConfigs/IT_Function_WIFI_01.yml index 9f8424f6d2..c59f161924 100644 --- a/components/idf_test/integration_test/CIConfigs/IT_Function_WIFI_01.yml +++ b/components/idf_test/integration_test/CIConfigs/IT_Function_WIFI_01.yml @@ -2,9 +2,34 @@ Config: {execute count: 1, execute order: in order} DUT: [SSC2, SSC1] Filter: - Add: - ID: [^WIFI_CONN_0601, ^WIFI_ADDR_0101, WIFI_SCAN_0103, WIFI_SCAN_0102, WIFI_SCAN_0101, - WIFI_SCAN_0105, WIFI_SCAN_0104, ^WIFI_CONN_0103, WIFI_CONN_0201, WIFI_CONN_0904, - ^WIFI_SCAN_0102, ^WIFI_SCAN_0103, ^WIFI_SCAN_0104, WIFI_CONN_0401, WIFI_ADDR_0101, - WIFI_ADDR_0102, WIFI_CONN_0301, WIFI_SCAN_0301, WIFI_SCAN_0303, ^WIFI_CONN_0801, - WIFI_SCAN_0304, ^WIFI_CONN_0301, WIFI_CONN_0501, WIFI_CONN_0502, ^WIFI_CONN_0401, - WIFI_MODE_0101, WIFI_MODE_0103, WIFI_MODE_0102, ^WIFI_CONN_0904, ^WIFI_CONN_0901] + ID: + - WIFI_ADDR_0101 # set mac, query mac + - WIFI_ADDR_0102 # set mac and do scan/JAP/SAP + - WIFI_CONN_0101 # station SAP+JAP test, different encryption + - WIFI_CONN_0102 # station SAP+JAP test, different channel + - WIFI_CONN_0103 # station SAP+JAP test, ssid hidden + - WIFI_CONN_0104 # station SAP test, max allowed sta + - WIFI_CONN_0201 # JAP query test + - WIFI_CONN_0301 # AP config query test + - WIFI_CONN_0401 # auto reconnect test + - WIFI_CONN_0501 # reconnect policy test + - WIFI_CONN_0502 # will not do reconnect after manually disconnected + - WIFI_CONN_0601 # list stations connected to soft ap test + - WIFI_CONN_0801 # test auth change event + - WIFI_CONN_0901 # test wifi disconnect reason REASON_ASSOC_LEAVE, REASON_4WAY_HANDSHAKE_TIMEOUT, REASON_NO_AP_FOUND + - WIFI_CONN_0904 # test wifi disconnect reason REASON_ASSOC_TOOMANY, REASON_HANDSHAKE_TIMEOUT, REASON_ASSOC_EXPIRE + - WIFI_MODE_0101 # mode switch test (sta mode) + - WIFI_MODE_0102 # mode switch test (AP mode) + - WIFI_MODE_0103 # mode switch test (STA+AP mode) + - WIFI_SCAN_0101 # scan with scan config ssid + - WIFI_SCAN_0102 # scan with scan config bssid + - WIFI_SCAN_0103 # scan with scan config channel + - WIFI_SCAN_0104 # scan with scan config show hidden + - WIFI_SCAN_0105 # scan with several configs + - WIFI_SCAN_0301 # reject scan request before scan finished + - WIFI_SCAN_0302 # scan in congest channel + - WIFI_SCAN_0303 # scan during JAP + - WIFI_SCAN_0304 # scan during ext STA join SoftAP + - ^WIFI_ADDR_0101 # set mac, query mac + - ^WIFI_ADDR_0102 # set mac and do scan/JAP/SAP + - ^WIFI_CONN_0101 # station SAP+JAP test, different encryption diff --git a/components/idf_test/integration_test/CIConfigs/IT_Function_WIFI_02.yml b/components/idf_test/integration_test/CIConfigs/IT_Function_WIFI_02.yml index 74e6ca612d..406950f790 100644 --- a/components/idf_test/integration_test/CIConfigs/IT_Function_WIFI_02.yml +++ b/components/idf_test/integration_test/CIConfigs/IT_Function_WIFI_02.yml @@ -2,6 +2,19 @@ Config: {execute count: 1, execute order: in order} DUT: [SSC2, SSC1] Filter: - Add: - ID: [WIFI_SCAN_0302, WIFI_CONN_0601, ^WIFI_CONN_0201, ^WIFI_ADDR_0102, WIFI_CONN_0901, - WIFI_CONN_0801, ^WIFI_CONN_0104, WIFI_CONN_0104, WIFI_CONN_0101, WIFI_CONN_0102, - WIFI_CONN_0103, ^WIFI_SCAN_0101, ^WIFI_CONN_0101, ^WIFI_CONN_0502, ^WIFI_CONN_0501] + ID: + - ^WIFI_CONN_0103 # station SAP+JAP test, ssid hidden + - ^WIFI_CONN_0104 # station SAP test, max allowed sta + - ^WIFI_CONN_0201 # JAP query test + - ^WIFI_CONN_0301 # AP config query test + - ^WIFI_CONN_0401 # auto reconnect test + - ^WIFI_CONN_0501 # reconnect policy test + - ^WIFI_CONN_0502 # will not do reconnect after manually disconnected + - ^WIFI_CONN_0601 # list stations connected to soft ap test + - ^WIFI_CONN_0801 # test auth change event + - ^WIFI_CONN_0901 # test wifi disconnect reason REASON_ASSOC_LEAVE, REASON_4WAY_HANDSHAKE_TIMEOUT, REASON_NO_AP_FOUND + - ^WIFI_CONN_0904 # test wifi disconnect reason REASON_ASSOC_TOOMANY, REASON_HANDSHAKE_TIMEOUT, REASON_ASSOC_EXPIRE + - ^WIFI_SCAN_0101 # scan with scan config ssid + - ^WIFI_SCAN_0102 # scan with scan config bssid + - ^WIFI_SCAN_0103 # scan with scan config channel + - ^WIFI_SCAN_0104 # scan with scan config show hidden diff --git a/components/idf_test/integration_test/CIConfigs/IT_Function_WIFI_03.yml b/components/idf_test/integration_test/CIConfigs/IT_Function_WIFI_03.yml index 0f0b4bbaad..e5de3ea97c 100644 --- a/components/idf_test/integration_test/CIConfigs/IT_Function_WIFI_03.yml +++ b/components/idf_test/integration_test/CIConfigs/IT_Function_WIFI_03.yml @@ -1,6 +1,7 @@ Config: {execute count: 1, execute order: in order} -DUT: [SSC3, SSC2, SSC1] +DUT: [SSC1] Filter: - Add: - ID: [WIFI_PHY_0502, WIFI_PHY_0503, WIFI_PHY_0501, WIFI_PHY_0506, WIFI_PHY_0505, - WIFI_PHY_0504] + ID: + - WIFI_CONN_0902 # test wifi disconnect reason REASON_BEACON_TIMEOUT + - ^WIFI_CONN_0902 # test wifi disconnect reason REASON_BEACON_TIMEOUT diff --git a/components/idf_test/integration_test/CIConfigs/IT_Function_WIFI_04.yml b/components/idf_test/integration_test/CIConfigs/IT_Function_WIFI_04.yml index 50f8707c72..fb4950f7da 100644 --- a/components/idf_test/integration_test/CIConfigs/IT_Function_WIFI_04.yml +++ b/components/idf_test/integration_test/CIConfigs/IT_Function_WIFI_04.yml @@ -1,5 +1,11 @@ Config: {execute count: 1, execute order: in order} -DUT: [SSC1] +DUT: [SSC3, SSC2, SSC1] Filter: - Add: - ID: [^WIFI_CONN_0902, WIFI_CONN_0902] + ID: + - WIFI_PHY_0501 # SoftAP STA in channel1 20M, STA changed to channel2 20M + - WIFI_PHY_0502 # SoftAP STA in channel1 20M, STA changed to channel2 40M + - WIFI_PHY_0503 # SoftAP STA in channel1, SoftAP 20M, STA 40M, STA changed to channel2 20M + - WIFI_PHY_0504 # SoftAP STA in channel1, SoftAP 20M, STA 40M, STA changed to channel2 40M + - WIFI_PHY_0505 # SoftAP STA in channel1, SoftAP 40M, STA 40M, STA changed to channel2 20M + - WIFI_PHY_0506 # SoftAP STA in channel1, SoftAP 40M, STA 40M, STA changed to channel2 40M diff --git a/components/idf_test/integration_test/CIConfigs/IT_Function_WIFI_05.yml b/components/idf_test/integration_test/CIConfigs/IT_Function_WIFI_05.yml index 3694a698b7..298e49a5bf 100644 --- a/components/idf_test/integration_test/CIConfigs/IT_Function_WIFI_05.yml +++ b/components/idf_test/integration_test/CIConfigs/IT_Function_WIFI_05.yml @@ -2,4 +2,6 @@ Config: {execute count: 1, execute order: in order} DUT: [SSC1] Filter: - Add: - ID: [^WIFI_CONN_0903, WIFI_CONN_0903] + ID: + - WIFI_CONN_0903 # test wifi disconnect reason REASON_AUTH_EXPIRE + - ^WIFI_CONN_0903 # test wifi disconnect reason REASON_AUTH_EXPIRE diff --git a/components/idf_test/integration_test/CIConfigs/IT_Function_WIFI_06.yml b/components/idf_test/integration_test/CIConfigs/IT_Function_WIFI_06.yml index dd42815a91..b655bbc6c4 100644 --- a/components/idf_test/integration_test/CIConfigs/IT_Function_WIFI_06.yml +++ b/components/idf_test/integration_test/CIConfigs/IT_Function_WIFI_06.yml @@ -2,5 +2,13 @@ Config: {execute count: 1, execute order: in order} DUT: [SSC2, SSC1] Filter: - Add: - ID: [WIFI_SCAN_0201, WIFI_PHY_0403, WIFI_PHY_0402, WIFI_PHY_0401, WIFI_PHY_0407, - WIFI_PHY_0406, WIFI_PHY_0405, WIFI_PHY_0404, WIFI_PHY_0408] + ID: + - WIFI_PHY_0401 # SoftAP ext AP in defferent channel, both bandwidth 20M, STA connect to AP then Softap get connected + - WIFI_PHY_0402 # SoftAP ext AP in defferent channel, both bandwidth 20M, Softap get connected than STA connect to AP + - WIFI_PHY_0403 # SoftAP ext AP in defferent channel, SoftAP 20M, ext AP 40M, STA connect to AP then Softap get connected + - WIFI_PHY_0404 # SoftAP ext AP in defferent channel, SoftAP 20M, ext AP 40M, Softap get connected than STA connect to AP + - WIFI_PHY_0405 # SoftAP ext AP in defferent channel, both bandwidth 40M, STA connect to AP then Softap get connected + - WIFI_PHY_0406 # SoftAP ext AP in defferent channel, both bandwidth 40M, Softap get connected than STA connect to AP + - WIFI_PHY_0407 # SoftAP ext AP in defferent channel, SoftAP 40M, ext AP 20M, STA connect to AP then Softap get connected + - WIFI_PHY_0408 # SoftAP ext AP in defferent channel, SoftAP 40M, ext AP 20M, Softap get connected than STA connect to AP + - WIFI_SCAN_0201 # STA in differnt PHY mode to scan AP in different PHY mode diff --git a/components/idf_test/integration_test/CIConfigs/IT_Stable_TCPIP_01.yml b/components/idf_test/integration_test/CIConfigs/IT_Stable_TCPIP_01.yml new file mode 100644 index 0000000000..5e9fea164f --- /dev/null +++ b/components/idf_test/integration_test/CIConfigs/IT_Stable_TCPIP_01.yml @@ -0,0 +1,6 @@ +Config: {execute count: 1, execute order: in order} +DUT: [SSC3, SSC2, SSC1, SSC5, SSC4] +Filter: +- Add: + ID: + - TCPIP_TCP_5101 # 1 AP 4 STA TCP stable test diff --git a/components/idf_test/integration_test/CIConfigs/IT_Stable_TCPIP_02.yml b/components/idf_test/integration_test/CIConfigs/IT_Stable_TCPIP_02.yml new file mode 100644 index 0000000000..4f70706052 --- /dev/null +++ b/components/idf_test/integration_test/CIConfigs/IT_Stable_TCPIP_02.yml @@ -0,0 +1,6 @@ +Config: {execute count: 1, execute order: in order} +DUT: [SSC1] +Filter: +- Add: + ID: + - TCPIP_TCP_5102 # send random length segment to target diff --git a/components/idf_test/integration_test/CIConfigs/IT_Stable_TCPIP_03.yml b/components/idf_test/integration_test/CIConfigs/IT_Stable_TCPIP_03.yml new file mode 100644 index 0000000000..14cc0695ab --- /dev/null +++ b/components/idf_test/integration_test/CIConfigs/IT_Stable_TCPIP_03.yml @@ -0,0 +1,6 @@ +Config: {execute count: 1, execute order: in order} +DUT: [SSC3, SSC2, SSC1, SSC5, SSC4] +Filter: +- Add: + ID: + - TCPIP_TCP_5103 # TCP SoftSTA send/recv stress test diff --git a/components/idf_test/integration_test/CIConfigs/IT_Stress_TCPIP_01.yml b/components/idf_test/integration_test/CIConfigs/IT_Stress_TCPIP_01.yml new file mode 100644 index 0000000000..6ec2644dca --- /dev/null +++ b/components/idf_test/integration_test/CIConfigs/IT_Stress_TCPIP_01.yml @@ -0,0 +1,6 @@ +Config: {execute count: 1, execute order: in order} +DUT: [SSC1] +Filter: +- Add: + ID: + - TCPIP_TCP_5001 # test possible TCP connect/disconnect method diff --git a/components/idf_test/integration_test/CIConfigs/IT_Stress_TCPIP_02.yml b/components/idf_test/integration_test/CIConfigs/IT_Stress_TCPIP_02.yml new file mode 100644 index 0000000000..a6315ddeeb --- /dev/null +++ b/components/idf_test/integration_test/CIConfigs/IT_Stress_TCPIP_02.yml @@ -0,0 +1,6 @@ +Config: {execute count: 1, execute order: in order} +DUT: [SSC2, SSC1] +Filter: +- Add: + ID: + - TCPIP_TCP_5201 # TCP send/recv stress test diff --git a/components/idf_test/integration_test/CIConfigs/IT_Stress_TCPIP_03.yml b/components/idf_test/integration_test/CIConfigs/IT_Stress_TCPIP_03.yml new file mode 100644 index 0000000000..6fd1ba9128 --- /dev/null +++ b/components/idf_test/integration_test/CIConfigs/IT_Stress_TCPIP_03.yml @@ -0,0 +1,6 @@ +Config: {execute count: 1, execute order: in order} +DUT: [SSC1] +Filter: +- Add: + ID: + - TCPIP_TCP_5202 # TCP send/recv data validation diff --git a/components/idf_test/integration_test/CIConfigs/IT_Stress_TCPIP_04.yml b/components/idf_test/integration_test/CIConfigs/IT_Stress_TCPIP_04.yml new file mode 100644 index 0000000000..0bc473a8b7 --- /dev/null +++ b/components/idf_test/integration_test/CIConfigs/IT_Stress_TCPIP_04.yml @@ -0,0 +1,6 @@ +Config: {execute count: 1, execute order: in order} +DUT: [SSC2, SSC1] +Filter: +- Add: + ID: + - TCPIP_UDP_5001 # UDP send/recv stress test diff --git a/components/idf_test/integration_test/CIConfigs/IT_Stress_WIFI_01.yml b/components/idf_test/integration_test/CIConfigs/IT_Stress_WIFI_01.yml new file mode 100644 index 0000000000..4ab82dded9 --- /dev/null +++ b/components/idf_test/integration_test/CIConfigs/IT_Stress_WIFI_01.yml @@ -0,0 +1,6 @@ +Config: {execute count: 1, execute order: in order} +DUT: [SSC3, SSC2, SSC1, SSC5, SSC4] +Filter: +- Add: + ID: + - WIFI_CONN_5101 # max sta connect to SotfAP and disconnect diff --git a/components/idf_test/integration_test/TestCaseAll.yml b/components/idf_test/integration_test/TestCaseAll.yml index 9e4823a297..fcdbd9340c 100644 --- a/components/idf_test/integration_test/TestCaseAll.yml +++ b/components/idf_test/integration_test/TestCaseAll.yml @@ -4307,6 +4307,287 @@ test cases: test point 1: abnormal/special use test point 2: TCP handling abnormal event version: v1 (2016-8-15) +- CI ready: 'Yes' + ID: TCPIP_TCP_5001 + SDK: '8266_NonOS + + 8266_RTOS + + ESP32_IDF' + Test App: SSC + auto test: 'Yes' + category: Stress + cmd set: + - TCPStress/TCPConnection + - - max_connection = 5 + - [dummy] + - - execute_time = 300 + - [''] + - - connect_method = ["C_01", "C_02", "C_05", "C_07"] + - [''] + - - disconnect_method = ["D_01", "D_03", "D_05", "D_06"] + - [''] + - - pc_ip = "pc_ip" + - [''] + - - target_ip = "target_ip" + - [''] + comment: '' + execution time: 5.0 + expected result: 1. succeed + initial condition: STAM2 + initial condition description (auto): sta mode, join AP, DHCP on + level: Integration + module: TCPIP + steps: '1. random choose connect method to do connect, random choose a method to + close + + Loop executing step 1' + sub module: TCP + summary: test possible TCP connect/disconnect method + test environment: SSC_T1_1 + test environment description (auto): 'PC has 2 wired NIC connected to AP. + + PC has 1 WiFi NIC. + + 1 SSC target connect with PC by UART.' + test point 1: function + stress + test point 2: TCP connect and disconnect test + version: v2 (2016-11-15) +- CI ready: 'Yes' + ID: TCPIP_TCP_5101 + SDK: '8266_NonOS + + 8266_RTOS + + ESP32_IDF' + Test App: SSC + auto test: 'Yes' + category: Stable + cmd set: + - TCPStress/TCPAPNSTA + - - send_len = 1460 + - [dummy] + - - test_time = 720 + - [''] + - - server_port = "" + - [''] + - - server_echo = True + - [''] + - - sta_number = 4 + - [''] + - - send_delay = 50 + - [''] + - - ap_ip = "" + - [''] + comment: '' + execution time: 12.0 + expected result: '1. succeed + + 2. succeed + + 3. succeed + + 4. all TCP connection not disconnected' + initial condition: None + initial condition description (auto): none + level: Integration + module: TCPIP + steps: '1. all sta connect to softap + + 2. create tcp server on softap + + 3. all sta connect to softap tcp server + + 4. do bi-direction send on all tcp connections' + sub module: TCP + summary: 1 AP 4 STA TCP stable test + test environment: SSC_T5_1 + test environment description (auto): 5 SSC target connect with PC by UART. + test point 1: stable + test point 2: TCP stable test + version: v2 (2016-11-15) +- CI ready: 'Yes' + ID: TCPIP_TCP_5102 + SDK: '8266_NonOS + + 8266_RTOS + + ESP32_IDF' + Test App: SSC + auto test: 'Yes' + category: Stable + cmd set: + - TCPStress/TCPRandomSend + - - delay_config = [0, 0.01, 0.1, 0.5, 1] + - [dummy] + - - send_count = 1000 + - [''] + - - test_time = 300 + - [''] + comment: '' + execution time: 12.0 + expected result: '1. succeed + + 2. succeed' + initial condition: STAM2 + initial condition description (auto): sta mode, join AP, DHCP on + level: Integration + module: TCPIP + steps: '1. create TCP connection + + 2. PC send random length data to target' + sub module: TCP + summary: send random length segment to target + test environment: SSC_T1_1 + test environment description (auto): 'PC has 2 wired NIC connected to AP. + + PC has 1 WiFi NIC. + + 1 SSC target connect with PC by UART.' + test point 1: stable + test point 2: TCP stable test + version: v2 (2016-11-15) +- CI ready: 'Yes' + ID: TCPIP_TCP_5103 + SDK: '8266_NonOS + + 8266_RTOS + + ESP32_IDF' + Test App: SSC + auto test: 'Yes' + category: Stable + cmd set: + - TCPStress/TCPSoftAPSTASendRecv + - - send_len = 1460 + - [dummy] + - - test_time = 720 + - [''] + - - server_port = "" + - [''] + - - server_port_2 = "" + - [''] + - - server_echo = True + - [''] + - - sta_number = 3 + - [''] + - - send_delay = 50 + - [''] + - - ap_ip = "" + - [''] + comment: '' + execution time: 12.0 + expected result: '1. succeed + + 2. verify reciveid data on target and PC succeed' + initial condition: None + initial condition description (auto): none + level: Integration + module: TCPIP + steps: '1. create TCP connection + + 2. send specified pattern on both direction' + sub module: TCP + summary: TCP SoftSTA send/recv stress test + test environment: SSC_T5_1 + test environment description (auto): 5 SSC target connect with PC by UART. + test point 1: stable + test point 2: TCP stable test + version: v2 (2016-11-15) +- CI ready: 'Yes' + ID: TCPIP_TCP_5201 + SDK: '8266_NonOS + + 8266_RTOS + + ESP32_IDF' + Test App: SSC + auto test: 'Yes' + category: Stress + cmd set: + - TCPStress/TCPSendRecv + - - send_len = 1460 + - [dummy] + - - test_time = 300 + - [''] + - - duplex = True + - [''] + - - conn_num = 5 + - [''] + - - send_delay = 20 + - [''] + comment: '' + execution time: 12.0 + expected result: '1. succeed + + 2. succeed + + 3. all TCP connection not disconnected' + initial condition: T2_1 + initial condition description (auto): target 1 as SoftAP, target 2 as STA + level: Integration + module: TCPIP + steps: '1. sta connect to softap + + 2. create multiple tcp connection + + 3. do send/recv on all tcp connections' + sub module: TCP + summary: TCP send/recv stress test + test environment: SSC_T2_1 + test environment description (auto): 'PC has 1 wired NIC connected to AP. + + PC has 1 WiFi NIC. + + 2 SSC target connect with PC by UART.' + test point 1: stress + test point 2: TCP stress test + version: v2 (2016-11-15) +- CI ready: 'Yes' + ID: TCPIP_TCP_5202 + SDK: '8266_NonOS + + 8266_RTOS + + ESP32_IDF' + Test App: SSC + auto test: 'Yes' + category: Stress + cmd set: + - TCPStress/TCPDataValidation + - - test_time = 1440 + - [dummy] + - - tx_enable = True + - [''] + - - rx_enable = True + - [''] + - - conn_num = 1 + - [''] + - - send_len = 1024 + - [''] + comment: '' + execution time: 24.0 + expected result: '1. succeed + + 2. verify reciveid data on target and PC succeed' + initial condition: STAM2 + initial condition description (auto): sta mode, join AP, DHCP on + level: Integration + module: TCPIP + steps: '1. create TCP connection + + 2. send specified pattern on both direction' + sub module: TCP + summary: TCP send/recv data validation + test environment: SSC_T1_1 + test environment description (auto): 'PC has 2 wired NIC connected to AP. + + PC has 1 WiFi NIC. + + 1 SSC target connect with PC by UART.' + test point 1: stress + test point 2: TCP stress test + version: v2 (2016-11-15) - CI ready: 'Yes' ID: TCPIP_UDP_0101 SDK: '8266_NonOS @@ -5653,6 +5934,55 @@ test cases: test point 1: abnormal/special use test point 2: UDP handling abnormal event version: v1 (2016-8-15) +- CI ready: 'Yes' + ID: TCPIP_UDP_5001 + SDK: '8266_NonOS + + 8266_RTOS + + ESP32_IDF' + Test App: SSC + auto test: 'Yes' + category: Stress + cmd set: + - UDPStress/UDPSendRecv + - - send_len = 1460 + - [dummy] + - - test_time = 300 + - [''] + - - duplex = True + - [''] + - - conn_num = 5 + - [''] + - - send_delay = 20 + - [''] + comment: '' + execution time: 12.0 + expected result: '1. succeed + + 2. succeed + + 3. succeed' + initial condition: T2_1 + initial condition description (auto): target 1 as SoftAP, target 2 as STA + level: Integration + module: TCPIP + steps: '1. sta connect to softap + + 2. create multiple udp + + 3. do send/recv on all udp' + sub module: UDP + summary: UDP send/recv stress test + test environment: SSC_T2_1 + test environment description (auto): 'PC has 1 wired NIC connected to AP. + + PC has 1 WiFi NIC. + + 2 SSC target connect with PC by UART.' + test point 1: stress + test point 2: UDP stress test + version: v2 (2016-11-15) - CI ready: 'Yes' ID: WIFI_ADDR_0101 SDK: '8266_NonOS @@ -6666,6 +6996,53 @@ test cases: test point 1: basic function test point 2: wifi disconnect reason test version: v1 (2016-8-15) +- CI ready: 'Yes' + ID: WIFI_CONN_5101 + SDK: '8266_NonOS + + 8266_RTOS + + ESP32_IDF' + Test App: SSC + auto test: 'Yes' + category: Stress + cmd set: + - WiFiStress/SoftAPNSTA + - - sta_num = 4 + - [dummy] + - - max_sta = 4 + - [''] + - - test_time = 300 + - [''] + - - delay1 = [0, 1] + - [''] + - - delay2 = [0, 1] + - [''] + - - change_mac = True + - [''] + - - channel = 1 + - [''] + comment: '' + execution time: 5.0 + expected result: '1. succeed + + 2. JAP succeed' + initial condition: None + initial condition description (auto): none + level: Integration + module: WIFI MAC + steps: '1. 1 target set to softap mode and rest set to sta mode + + 2. all sta random join and disconnect from softap + + Loop step 2' + sub module: WIFI Connect + summary: max sta connect to SotfAP and disconnect + test environment: SSC_T5_1 + test environment description (auto): 5 SSC target connect with PC by UART. + test point 1: stress + test point 2: SoftAP WIFI connect/disconnect stress test + version: v2 (2016-11-15) - CI ready: 'Yes' ID: WIFI_MODE_0101 SDK: '8266_NonOS diff --git a/components/idf_test/integration_test/TestCaseScript/TCPStress/TCPAPNSTA.py b/components/idf_test/integration_test/TestCaseScript/TCPStress/TCPAPNSTA.py new file mode 100755 index 0000000000..c8c50fc7a2 --- /dev/null +++ b/components/idf_test/integration_test/TestCaseScript/TCPStress/TCPAPNSTA.py @@ -0,0 +1,223 @@ +from TCAction import TCActionBase +from NativeLog import NativeLog +import time +import random +import string + + +TEST_COUNT_ONE_ROUND = 500 + + +class TestCase(TCActionBase.CommonTCActionBase): + + def __init__(self, test_case, test_env, timeout=30, log_path=TCActionBase.LOG_PATH): + TCActionBase.CommonTCActionBase.__init__(self, test_case, test_env, timeout=timeout, log_path=log_path) + self.send_len = 1460 + self.server_echo = True + self.sta_number = 4 + self.test_time = 12 * 60 + self.send_delay = 50 + # load param from excel + cmd_set = test_case["cmd set"] + for i in range(1, len(cmd_set)): + if cmd_set[i][0] != "dummy": + cmd_string = "self." + cmd_set[i][0] + exec cmd_string + self.result_cntx = TCActionBase.ResultCheckContext(self, test_env, self.tc_name) + pass + + def cleanup(self): + checker_stings = [] + test_action_strings = [] + for i in range(self.sta_number + 1): + checker_stings.append("R SSC%s C +RECVPRINT:1" % (i+1)) + test_action_strings.append("SSC SSC%s soc -R -o 1" % (i+1)) + fail_string = "Fail, Fail to turn on recv print" + self.load_and_exe_one_step(checker_stings, test_action_strings, fail_string) + pass + + def execute(self): + TCActionBase.TCActionBase.execute(self) + self.result_cntx.start() + + try: + # configurable params + send_len = self.send_len + server_echo = self.server_echo + sta_number = self.sta_number + test_time = self.test_time * 60 + send_delay = self.send_delay + ap_ip = self.get_parameter("target_ap_ip") + # configurable params + except StandardError, e: + NativeLog.add_trace_critical("Error configuration for TCPTransparent script, error is %s" % e) + raise StandardError("Error configuration") + + # step0 reboot + checker_stings = [] + test_action_string = [] + + for i in range(sta_number+1): + checker_stings.append("P SSC%d C !!!ready!!!" % (i+1)) + test_action_string.append("SSCC SSC%d reboot" % (i+1)) + + fail_string = "Fail, Fail to reboot" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # switch off recv print + checker_stings = [] + test_action_strings = [] + for i in range(self.sta_number + 1): + checker_stings.append("R SSC%s C +RECVPRINT:0" % (i+1)) + test_action_strings.append("SSC SSC%s soc -R -o 0" % (i+1)) + fail_string = "Fail, Fail to turn off recv print" + self.load_and_exe_one_step(checker_stings, test_action_strings, fail_string) + + # step1 set ap on SSC1, create server + checker_stings = ["R SSC1 C +MODE:OK"] + test_action_string = ["SSCC SSC1 op -S -o 2"] + fail_string = "Fail, Fail set mode" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + ssid = "".join([random.choice(string.lowercase) for m in range(10)]) + password = "".join([random.choice(string.lowercase) for m in range(10)]) + tcp_port = random.randint(10000, 20000) + + checker_stings = ["R SSC1 C +SAP:OK"] + test_action_string = ["SSCC SSC1 ap -S -s %s -p %s -t 3 -m 8" % (ssid, password)] + fail_string = "Fail, Fail set ap" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + checker_stings = ["R SSC1 A :BIND:(\d+),OK"] + test_action_string = ["SSCC SSC1 soc -B -t TCP -p %s" % tcp_port] + fail_string = "Fail, Fail create server" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + checker_stings = ["R SSC1 RE LISTEN:(\d+),OK"] + test_action_string = ["SSCC SSC1 soc -L -s "] + fail_string = "Fail, Fail create server" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # step 2, 8 SSC target(SSC2 - SSC9) join SSC1 soft AP + checker_stings = [] + test_action_string = [] + for i in range(sta_number): + checker_stings.append("P SSC%d C +MODE:OK" % (i+2)) + test_action_string.append("SSCC SSC%d op -S -o 1" % (i+2)) + fail_string = "Fail, Fail set mode" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + checker_stings = [] + test_action_string = [] + for i in range(sta_number): + checker_stings.append("P SSC%d C +JAP:CONNECTED,%s" % (i+2, ssid)) + test_action_string.append("SSCC SSC%d ap -C -s %s -p %s" % (i+2, ssid, password)) + fail_string = "Fail, Fail to connect to server" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # step 3, create client on SSC2 - SSC9 + checker_stings = [] + test_action_string = [] + for i in range(sta_number): + checker_stings.append("P SSC%d A :BIND:(\d+),OK" % (i+2, i+2)) + test_action_string.append("SSCC SSC%d soc -B -t TCP" % (i+2)) + fail_string = "Fail, Fail to connect to server" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + for i in range(sta_number): + checker_stings = ["P SSC%d RE CONNECT:(\d+),OK" % (i+2), + "P SSC1 A :ACCEPT:(\d+),.+" % (i+2)] + test_action_string = ["SSCC SSC%d soc -C -s -i %s -p %s" % + (i+2, i+2, ap_ip, tcp_port)] + fail_string = "Fail, Fail to connect to server" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + start_time = time.time() + # step 4, do send/recv + while time.time() - start_time < test_time: + checker_stings = [] + test_action_string = [] + for i in range(sta_number): + checker_stings.append("P SSC%d RE \+SEND:\d+,OK NC CLOSED" % (i+2)) + test_action_string.append("SSC SSC%d soc -S -s -l %d -n %d -j %d" % + (i+2, i+2, send_len, TEST_COUNT_ONE_ROUND, send_delay)) + if server_echo is True: + test_action_string.append("SSC SSC1 soc -S -s -l %d -n %d -j %d" % + (i+2, send_len, TEST_COUNT_ONE_ROUND, send_delay)) + checker_stings.append("P SSC1 RE \"\+SEND:%%%%s,OK\"%%%%() NC CLOSED)" % + (i+2)) + + fail_string = "Fail, Failed to send/recv data" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string, + check_freq=1, check_time=300) is False: + break + + NativeLog.add_prompt_trace("time escape: %s" % (time.time() - start_time)) + + if (time.time() - start_time) >= test_time: + self.result_cntx.set_result("Succeed") + else: + self.result_cntx.set_result("Failed") + # TODO: create a function to create TCP connections. reuse not copy paste code + checker_stings = [] + test_action_string = [] + for i in range(sta_number + 1): + checker_stings.append("P SSC%d C CLOSEALL" % (i + 1)) + test_action_string.append("SSCC SSC%d soc -T" % (i + 1)) + fail_string = "Fail, Fail to close socket" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + server_port = random.randint(20000, 30000) + checker_stings = ["R SSC1 A :BIND:(\d+),OK"] + test_action_string = ["SSCC SSC1 soc -B -t TCP -p %s" % server_port] + fail_string = "Fail, Fail to bind socket" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + checker_stings = ["R SSC1 RE LISTEN:(\d+),OK"] + test_action_string = ["SSCC SSC1 soc -L -s "] + fail_string = "Fail, Fail to listen" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + checker_stings = [] + test_action_string = [] + for i in range(sta_number): + checker_stings.append("P SSC%d A :BIND:(\d+),OK" % (i + 2, i + 2)) + test_action_string.append("SSCC SSC%d soc -B -t TCP" % (i + 2)) + fail_string = "Fail, Fail to connect to server" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + for i in range(sta_number): + checker_stings = ["P SSC%d RE CONNECT:(\d+),OK" % (i + 2), + "P SSC1 A :ACCEPT:(\d+),.+" % (i + 2)] + test_action_string = ["SSCC SSC%d soc -C -s -i %s -p %s" % + (i + 2, i + 2, ap_ip, server_port)] + fail_string = "Fail, Fail to connect to server" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # finally, execute done + + def result_check(self, port_name, data): + TCActionBase.CommonTCActionBase.result_check(self, port_name, data) + self.result_cntx.append_data(port_name, data) + + +def main(): + pass + +if __name__ == '__main__': + main() + diff --git a/components/idf_test/integration_test/TestCaseScript/TCPStress/TCPConnection.py b/components/idf_test/integration_test/TestCaseScript/TCPStress/TCPConnection.py new file mode 100755 index 0000000000..7bedcaf8a1 --- /dev/null +++ b/components/idf_test/integration_test/TestCaseScript/TCPStress/TCPConnection.py @@ -0,0 +1,335 @@ +import random +import re +import socket +import threading +import time + +import TCPConnectionUtility +from NativeLog import NativeLog +from TCAction import PerformanceTCBase + +DELAY_RANGE = [10, 3000] +CONNECTION_STRUCTURE = ("Connection handler", "PC socket", "Target socket id", + "Target port", "PC port", "PC state", "Target state") + +# max fail count for one connection during test +MAX_FAIL_COUNT = 10 + + +class CheckerBase(threading.Thread): + + CHECK_ITEM = ("CONDITION", "NOTIFIER", "ID", "DATA") + SLEEP_TIME = 0.1 # sleep 100ms between each check action + + def __init__(self): + threading.Thread.__init__(self) + self.setDaemon(True) + self.exit_event = threading.Event() + self.sync_lock = threading.Lock() + self.check_item_list = [] + self.check_item_id = 0 + + def run(self): + while self.exit_event.isSet() is False: + self.process() + pass + + def process(self): + pass + + def add_check_item(self, condition, notifier): + with self.sync_lock: + check_item_id = self.check_item_id + self.check_item_id += 1 + self.check_item_list.append(dict(zip(self.CHECK_ITEM, (condition, notifier, check_item_id, str())))) + return check_item_id + + def remove_check_item(self, check_item_id): + ret = None + with self.sync_lock: + check_items = filter(lambda x: x["ID"] == check_item_id, self.check_item_list) + if len(check_items) > 0: + self.check_item_list.remove(check_items[0]) + ret = check_items[0]["DATA"] + return ret + + def exit(self): + self.exit_event.set() + pass + + +# check on serial port +class SerialPortChecker(CheckerBase): + def __init__(self, serial_reader): + CheckerBase.__init__(self) + self.serial_reader = serial_reader + pass + + # check condition for serial is compiled regular expression pattern + @staticmethod + def do_check(check_item, data): + match = check_item["CONDITION"].search(data) + if match is not None: + pos = data.find(match.group()) + len(match.group()) + # notify user + check_item["NOTIFIER"]("serial", match) + else: + pos = -1 + return pos + + def process(self): + # do check + with self.sync_lock: + # read data + new_data = self.serial_reader() + # NativeLog.add_trace_info("[debug][read data] %s" % new_data) + # do check each item + for check_item in self.check_item_list: + # NativeLog.add_trace_info("[debug][read data][ID][%s]" % check_item["ID"]) + check_item["DATA"] += new_data + self.do_check(check_item, check_item["DATA"]) + time.sleep(self.SLEEP_TIME) + + +# handle PC TCP server accept and notify user +class TCPServerChecker(CheckerBase): + def __init__(self, server_sock): + CheckerBase.__init__(self) + self.server_sock = server_sock + server_sock.settimeout(self.SLEEP_TIME) + self.accepted_socket_list = [] + + # check condition for tcp accepted sock is tcp source port + @staticmethod + def do_check(check_item, data): + for sock_addr_pair in data: + addr = sock_addr_pair[1] + if addr[1] == check_item["CONDITION"]: + # same port, so this is the socket that matched, notify and remove it from list + check_item["NOTIFIER"]("tcp", sock_addr_pair[0]) + data.remove(sock_addr_pair) + + def process(self): + # do accept + try: + client_sock, addr = self.server_sock.accept() + self.accepted_socket_list.append((client_sock, addr)) + except socket.error: + pass + # do check + with self.sync_lock: + check_item_list = self.check_item_list + for check_item in check_item_list: + self.do_check(check_item, self.accepted_socket_list) + pass + + +# this thread handles one tcp connection. +class ConnectionHandler(threading.Thread): + CHECK_FREQ = CheckerBase.SLEEP_TIME/2 + + def __init__(self, utility, serial_checker, tcp_checker, connect_method, disconnect_method, test_case): + threading.Thread.__init__(self) + self.setDaemon(True) + self.utility = utility + self.connect_method = connect_method + self.disconnect_method = disconnect_method + self.exit_event = threading.Event() + # following members are used in communication with checker threads + self.serial_checker = serial_checker + self.tcp_checker = tcp_checker + self.serial_notify_event = threading.Event() + self.tcp_notify_event = threading.Event() + self.serial_result = None + self.tcp_result = None + self.serial_check_item_id = None + self.tcp_check_item_id = None + self.data_cache = None + self.fail_count = 0 + self.test_case = test_case + pass + + def log_error(self): + self.fail_count += 1 + if self.fail_count > MAX_FAIL_COUNT: + self.test_case.error_detected() + + def new_connection_structure(self): + connection = dict.fromkeys(CONNECTION_STRUCTURE, None) + connection["Connection handler"] = self + return connection + + def run(self): + while self.exit_event.isSet() is False: + connection = self.new_connection_structure() + # do connect + connect_method_choice = random.choice(self.connect_method) + if self.utility.execute_tcp_method(connect_method_choice, connection) is False: + self.log_error() + # check if established + if self.utility.is_established_state(connection) is True: + time.sleep(float(random.randint(DELAY_RANGE[0], DELAY_RANGE[1]))/1000) + # do disconnect if established + disconnect_method_choice = random.choice(self.disconnect_method) + if self.utility.execute_tcp_method(disconnect_method_choice, connection) is False: + self.log_error() + # make sure target socket closed + self.utility.close_connection(connection) + time.sleep(float(random.randint(DELAY_RANGE[0], DELAY_RANGE[1]))/1000) + pass + + # serial_condition: re string + # tcp_condition: target local port + def add_checkers(self, serial_condition=None, tcp_condition=None): + # cleanup + self.serial_result = None + self.tcp_result = None + self.serial_notify_event.clear() + self.tcp_notify_event.clear() + # serial_checker + if serial_condition is not None: + pattern = re.compile(serial_condition) + self.serial_check_item_id = self.serial_checker.add_check_item(pattern, self.notifier) + else: + # set event so that serial check always pass + self.serial_notify_event.set() + if tcp_condition is not None: + self.tcp_check_item_id = self.tcp_checker.add_check_item(tcp_condition, self.notifier) + else: + # set event so that tcp check always pass + self.tcp_notify_event.set() + # NativeLog.add_trace_info("[Debug] add check item %s, connection is %s" % (self.serial_check_item_id, self)) + pass + + def get_checker_results(self, timeout=5): + time1 = time.time() + while time.time() - time1 < timeout: + # if one type of checker is not set, its event will be set in add_checkers + if self.serial_notify_event.isSet() is True and self.tcp_notify_event.isSet() is True: + break + time.sleep(self.CHECK_FREQ) + # do cleanup + # NativeLog.add_trace_info("[Debug] remove check item %s, connection is %s" % (self.serial_check_item_id, self)) + self.data_cache = self.serial_checker.remove_check_item(self.serial_check_item_id) + self.tcp_checker.remove_check_item(self.tcp_check_item_id) + # self.serial_check_item_id = None + # self.tcp_check_item_id = None + return self.serial_result, self.tcp_result + + def notifier(self, typ, result): + if typ == "serial": + self.serial_notify_event.set() + self.serial_result = result + elif typ == "tcp": + self.tcp_notify_event.set() + self.tcp_result = result + + def exit(self): + self.exit_event.set() + pass + + +class TestCase(PerformanceTCBase.PerformanceTCBase): + def __init__(self, test_case, test_env, timeout=120, log_path=None): + PerformanceTCBase.PerformanceTCBase.__init__(self, test_case, test_env, + timeout=timeout, log_path=log_path) + self.max_connection = 5 + self.execute_time = 120 # execute time default 120 minutes + self.pc_ip = "pc_ip" + self.target_ip = "target_ip" + self.connect_method = ["C_01"] + self.disconnect_method = ["D_05"] + + cmd_set = test_case["cmd set"] + # load param from excel + for i in range(1, len(cmd_set)): + if cmd_set[i][0] != "dummy": + cmd_string = "self." + cmd_set[i][0] + exec cmd_string + + self.error_event = threading.Event() + self.serial_lock = threading.Lock() + pass + + def serial_reader(self): + return self.serial_read_data("SSC1") + + def send_ssc_command(self, data): + with self.serial_lock: + time.sleep(0.05) + self.serial_write_line("SSC1", data) + + def error_detected(self): + self.error_event.set() + + def process(self): + # parameters + max_connection = self.max_connection + execute_time = self.execute_time * 60 + pc_ip = self.get_parameter(self.pc_ip) + target_ip = self.get_parameter(self.target_ip) + connect_method = self.connect_method + disconnect_method = self.disconnect_method + server_port = random.randint(30000, 50000) + + # step 1, create TCP server on target and PC + # create TCP server on target + self.serial_write_line("SSC1", "soc -B -t TCP -p %s" % server_port) + match = self.check_regular_expression("SSC1", re.compile("BIND:(\d+),OK")) + if match is None: + NativeLog.add_prompt_trace("Failed to create TCP server on target") + return + target_sock_id = match.group(1) + + self.serial_write_line("SSC1", "soc -L -s %s" % target_sock_id) + if self.check_response("SSC1", "+LISTEN:%s,OK" % target_sock_id) is False: + NativeLog.add_prompt_trace("Failed to create TCP server on target") + return + + # create TCP server on PC + try: + server_sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) + server_sock.bind((pc_ip, server_port)) + server_sock.listen(5) + except StandardError: + NativeLog.add_prompt_trace("Failed to create TCP server on PC") + return + + # step 2, create checker + serial_port_checker = SerialPortChecker(self.serial_reader) + tcp_server_checker = TCPServerChecker(server_sock) + serial_port_checker.start() + tcp_server_checker.start() + + # step 3, create 5 thread and do connection + utility = TCPConnectionUtility.Utility(self, server_port, server_port, pc_ip, target_ip) + work_thread = [] + for i in range(max_connection): + t = ConnectionHandler(utility, serial_port_checker, tcp_server_checker, + connect_method, disconnect_method, self) + work_thread.append(t) + t.start() + + # step 4, wait and exit + self.error_event.wait(execute_time) + # close all threads + for t in work_thread: + t.exit() + t.join() + serial_port_checker.exit() + tcp_server_checker.exit() + serial_port_checker.join() + tcp_server_checker.join() + + if self.error_event.isSet() is False: + # no error detected + self.set_result("Succeed") + pass + + +def main(): + pass + + +if __name__ == '__main__': + main() diff --git a/components/idf_test/integration_test/TestCaseScript/TCPStress/TCPConnectionUtility.py b/components/idf_test/integration_test/TestCaseScript/TCPStress/TCPConnectionUtility.py new file mode 100755 index 0000000000..f28218af0b --- /dev/null +++ b/components/idf_test/integration_test/TestCaseScript/TCPStress/TCPConnectionUtility.py @@ -0,0 +1,251 @@ +import random +import socket +import threading + +from NativeLog import NativeLog + +# from NativeLog import NativeLog + +# make sure target do not listen on this port +ERROR_PORT = 62685 + + +class Utility(object): + METHOD_RESULT = {"C_01": ("ESTABLISHED", "ESTABLISHED"), # target TCP peer state, PC TCP peer state + "C_02": ("SYNC_SENT", "CLOSED"), + "C_03": ("CLOSED", "CLOSED"), + "C_04": ("SYN_RCVD", "ESTABLISHED"), + "C_05": ("ESTABLISHED", "ESTABLISHED"), + "C_06": ("CLOSED", "CLOSED"), + "C_07": ("CLOSED", "CLOSED"), + "C_08": ("CLOSED", "CLOSED"), + "D_01": ("TIME_WAIT", "CLOSED"), + "D_02": ("TIME_WAIT", "TIME_WAIT"), + "D_03": ("FIN_WAIT_2", "CLOSE_WAIT"), + "D_04": ("FIN_WAIT_1", "CLOSE_WAIT"), + "D_05": ("CLOSED", "TIME_WAIT"), + "D_06": ("CLOSED", "CLOSED"), + "D_07": ("CLOSE_WAIT", "FIN_WAIT2"), + "D_08": ("TIME_WAIT", "CLOSED"), } + + SOC_CLOSED_STATE = ("FIN_WAIT_1", "FIN_WAIT_2", "CLOSING", "TIME_WAIT", "LAST_ACK", "CLOSED") + SOC_CREATED_STATE = ("SYNC_RCVD", "ESTABLISHED") + SOC_SEND_DATA_STATE = ("ESTABLISHED", "CLOSE_WAIT") + SOC_ESTABLISHED_STATE = ("ESTABLISHED", ) + + def __init__(self, tc_action, pc_server_port, target_server_port, pc_ip, target_ip): + self.tc_action = tc_action + self.pc_server_port = pc_server_port + self.target_server_port = target_server_port + self.pc_ip = pc_ip + self.target_ip = target_ip + self.pc_close_wait_socket_list = [] + self.sync_lock = threading.Lock() + pass + + # create a tcp socket, return True or False + def __create_tcp_socket(self, connection): + connection_handler = connection["Connection handler"] + connection["Target port"] = random.randint(10000, 60000) + connection_handler.add_checkers("BIND:(\d+),OK,%s,%s" + % (self.target_ip, connection["Target port"])) + self.tc_action.send_ssc_command("soc -B -t TCP -i %s -p %s" % (self.target_ip, connection["Target port"])) + serial_result, tcp_result = connection_handler.get_checker_results() + if serial_result is not None: + connection["Target socket id"] = serial_result.group(1) + return True + else: + return False + + # target do connect, return True or False + def __target_do_connect(self, connection, dest_ip, dest_port, timeout=20): + connection_handler = connection["Connection handler"] + connection_handler.add_checkers("CONNECT:%s,OK" % connection["Target socket id"], + connection["Target port"]) + self.tc_action.send_ssc_command("soc -C -s %s -i %s -p %s" + % (connection["Target socket id"], dest_ip, dest_port)) + serial_result, tcp_result = connection_handler.get_checker_results(timeout) + if serial_result is not None and tcp_result is not None: + connection["PC socket"] = tcp_result + return True + else: + return False + pass + + # pc do connect, return True or False + def __pc_do_connect(self, connection, dest_ip, dest_port, timeout=20): + connection_handler = connection["Connection handler"] + sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) + while True: + connection["PC port"] = random.randint(10000, 60000) + try: + sock.bind((self.pc_ip, connection["PC port"])) + break + except socket.error, e: + if e.errno == 10048: # socket port reuse + continue + sock.settimeout(timeout) + connection["PC socket"] = sock + connection_handler.add_checkers("ACCEPT:(\d+),\d+,%s,%s" + % (self.pc_ip, connection["PC port"])) + try: + sock.connect((dest_ip, dest_port)) + except socket.error: + pass + serial_result, tcp_result = connection_handler.get_checker_results() + if serial_result is not None: + connection["Target socket id"] = serial_result.group(1) + return True + else: + return False + pass + + def connect_c_01(self, connection): + if self.__create_tcp_socket(connection) is True: + return self.__target_do_connect(connection, self.pc_ip, self.pc_server_port) + else: + return False + + def connect_c_02(self, connection): + if self.__create_tcp_socket(connection) is True: + return not self.__target_do_connect(connection, self.pc_ip, ERROR_PORT, timeout=5) + else: + return False + + def connect_c_03(self, connection): + return False + + def connect_c_04(self, connection): + return False + + def connect_c_05(self, connection): + return self.__pc_do_connect(connection, self.target_ip, self.target_server_port) + + def connect_c_06(self, connection): + return False + + def connect_c_07(self, connection): + return not self.__pc_do_connect(connection, self.target_ip, ERROR_PORT) + + def connect_c_08(self, connection): + return False + + def __target_socket_close(self, connection): + connection_handler = connection["Connection handler"] + if connection["Target socket id"] is not None: + connection_handler.add_checkers("CLOSE:%s" % connection["Target socket id"]) + self.tc_action.send_ssc_command("soc -T -s %s" % connection["Target socket id"]) + serial_result, tcp_result = connection_handler.get_checker_results() + connection["Target socket id"] = None + else: + serial_result = None + return True if serial_result is not None else False + + @staticmethod + def __pc_socket_close(connection): + connection_handler = connection["Connection handler"] + if connection["PC socket"] is not None: + connection_handler.add_checkers("CLOSED:%s" % connection["Target socket id"]) + connection["PC socket"].close() + serial_result, tcp_result = connection_handler.get_checker_results() + connection["PC socket"] = None + else: + serial_result = None + return True if serial_result is not None else False + + def close_d_01(self, connection): + connection["PC socket"] = None + return self.__target_socket_close(connection) + + def close_d_02(self, connection): + pass + + def close_d_03(self, connection): + with self.sync_lock: + self.pc_close_wait_socket_list.append(connection["PC socket"]) + return self.__target_socket_close(connection) + pass + + def close_d_04(self, connection): + pass + + def close_d_05(self, connection): + return self.__pc_socket_close(connection) + + def close_d_06(self, connection): + # target send data to PC, PC don't recv and close socket + connection_handler = connection["Connection handler"] + connection_handler.add_checkers("SEND:%s,OK" % connection["Target socket id"]) + self.tc_action.send_ssc_command("soc -S -s %s -l 100" % connection["Target socket id"]) + serial_result, tcp_result = connection_handler.get_checker_results() + if serial_result is None: + return False + return self.__pc_socket_close(connection) + + def close_d_07(self, connection): + # PC shutdown WR + result = False + try: + connection["PC socket"].shutdown(socket.SHUT_WR) + result = True + except StandardError: + pass + return result + + def close_d_08(self, connection): + pass + + def close_connection(self, connection): + self.__target_socket_close(connection) + pass + + TCP_ACTION_DICT = {"C_01": connect_c_01, + "C_02": connect_c_02, + "C_03": connect_c_03, + "C_04": connect_c_04, + "C_05": connect_c_05, + "C_06": connect_c_06, + "C_07": connect_c_07, + "C_08": connect_c_08, + "D_01": close_d_01, + "D_02": close_d_02, + "D_03": close_d_03, + "D_04": close_d_04, + "D_05": close_d_05, + "D_06": close_d_06, + "D_07": close_d_07, + "D_08": close_d_08, + } + + def get_method_destination_state(self, method): + return self.METHOD_RESULT[method] + + def execute_tcp_method(self, method, connection): + if method in self.METHOD_RESULT: + result = self.TCP_ACTION_DICT[method](self, connection) + if result is True: + state = self.get_method_destination_state(method) + connection["Target state"] = state[0] + connection["PC state"] = state[1] + else: + NativeLog.add_prompt_trace("[TCPConnection] tcp method %s fail, connection is %s" + % (method, connection)) + NativeLog.add_trace_info("[TCPConnection][data cache][check item %s] %s" + % (connection["Connection handler"].serial_check_item_id, + connection["Connection handler"].data_cache)) + else: + raise StandardError("Not TCP connection method") + return result + + def is_established_state(self, connection): + return True if connection["Target state"] in self.SOC_CREATED_STATE else False + + def is_closed_state(self, connection): + return True if connection["Target state"] in self.SOC_CLOSED_STATE else False + + +def main(): + pass + +if __name__ == '__main__': + main() diff --git a/components/idf_test/integration_test/TestCaseScript/TCPStress/TCPDataValidation.py b/components/idf_test/integration_test/TestCaseScript/TCPStress/TCPDataValidation.py new file mode 100755 index 0000000000..a06824ce3e --- /dev/null +++ b/components/idf_test/integration_test/TestCaseScript/TCPStress/TCPDataValidation.py @@ -0,0 +1,243 @@ +import os +import random +import threading +import socket +import time +import re + +from TCAction import TCActionBase +from TCAction import PerformanceTCBase +from NativeLog import NativeLog + + +def calc_hash(index): + return (index & 0xffffffff) % 83 + (index & 0xffffffff) % 167 + + +def verify_data(data, start_index): + for i, c in enumerate(data): + if ord(c) != calc_hash(start_index + i): + NativeLog.add_trace_critical("[Data Validation Error] target sent data index %u is error." + " Sent data is %x, should be %x" + % (start_index + i, ord(c), calc_hash(start_index + i))) + return False + return True + + +def make_validation_data(length, start_index): + return bytes().join([chr(calc_hash(start_index + i)) for i in range(length)]) + + +class SendThread(threading.Thread): + def __init__(self, sock, send_len): + threading.Thread.__init__(self) + self.setDaemon(True) + self.sock = sock + self.send_len = send_len + self.exit_event = threading.Event() + pass + + def exit(self): + self.exit_event.set() + + def run(self): + index = 0 + while self.exit_event.isSet() is False: + data = make_validation_data(self.send_len, index) + try: + self.sock.send(data) + index += self.send_len + except StandardError: + # pass but not exit thread + time.sleep(1) + continue + pass + + +class RecvThread(threading.Thread): + def __init__(self, sock): + threading.Thread.__init__(self) + self.setDaemon(True) + self.sock = sock + self.exit_event = threading.Event() + + def exit(self): + self.exit_event.set() + + def run(self): + index = 0 + while self.exit_event.isSet() is False: + if self.sock is not None: + try: + data = self.sock.recv(8*1024) + except StandardError, e: + NativeLog.add_exception_log(e) + NativeLog.add_trace_critical("recv error, connection closed") + break + if verify_data(data, index) is not True: + break + index += len(data) + else: + time.sleep(1) + pass + + +class ValidationThread(threading.Thread): + def __init__(self, tc_action): + threading.Thread.__init__(self) + self.setDaemon(True) + self.tc_action = tc_action + self.exit_event = threading.Event() + + def exit(self): + self.exit_event.set() + + def run(self): + while self.exit_event.isSet() is False: + if self.tc_action.check_response("SSC1", "DATA_ERROR", 5) is True: + NativeLog.add_trace_critical("[Data Validation Error] target recv data error") + break + pass + + +class TestCase(PerformanceTCBase.PerformanceTCBase): + + def __init__(self, test_case, test_env, timeout=30, log_path=TCActionBase.LOG_PATH): + PerformanceTCBase.PerformanceTCBase.__init__(self, test_case, test_env, timeout=timeout, log_path=log_path) + self.send_len = 0 + self.tx_enable = None + self.rx_enable = None + self.conn_num = 0 + self.test_time = 0 + # load param from excel + cmd_set = test_case["cmd set"] + for i in range(1, len(cmd_set)): + if cmd_set[i][0] != "dummy": + cmd_string = "self." + cmd_set[i][0] + exec cmd_string + pass + + def execute(self): + TCActionBase.TCActionBase.execute(self) + + try: + # configurable params + send_len = self.send_len + tx_enable = self.tx_enable + rx_enable = self.rx_enable + conn_num = self.conn_num + test_time = self.test_time * 60 # convert minutes to seconds + # configurable params + except StandardError, e: + NativeLog.add_trace_critical("Error configuration for TCPThroughput script, error is %s" % e) + raise StandardError("Error configuration") + + # init throughput result data + test_item = "" + if tx_enable is True: + test_item += "Tx" + if rx_enable is True: + test_item += "Rx" + if test_item == "": + raise StandardError("no throughput test item") + + pc_ip = self.get_parameter("pc_ip") + tcp_port = random.randint(10000, 50000) + + # disable recv print during throughput test + self.serial_write_line("SSC1", "soc -R -o 0") + if self.check_response("SSC1", "+RECVPRINT", 2) is False: + NativeLog.add_trace_critical("Fail, Fail to disable recv print") + + # create server + server_sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) + server_sock.bind((pc_ip, tcp_port)) + server_sock.settimeout(5) + server_sock.listen(5) + + sock_id_list = [] + send_thread_list = [] + recv_thread_list = [] + + # step 4 create tcp connection + for i in range(conn_num): + self.serial_write_line("SSC1", "soc -B -t TCP") + match = self.check_regular_expression("SSC1", re.compile("\+BIND:(\d+),OK"), 2) + if match is None: + NativeLog.add_trace_critical("Fail, Fail to bind") + return + else: + sock_id_list.append(int(match.group(1))) + + self.serial_write_line("SSC1", "soc -V -s %s -o 3" % sock_id_list[-1]) + if self.check_regular_expression("SSC1", re.compile("\+DATA_VALIDATION:\d+,OK"), 2) is None: + NativeLog.add_trace_critical("Fail, Failed to enable validation") + return + + self.serial_write_line("SSC1", "soc -C -s %s -i %s -p %s" % (sock_id_list[-1], pc_ip, tcp_port)) + try: + sock, addr = server_sock.accept() + except socket.error, e: + NativeLog.add_trace_critical("%s" % e) + raise e + + if self.check_regular_expression("SSC1", re.compile("\+CONNECT:\d+,OK"), 5) is None: + NativeLog.add_trace_critical("Fail, Failed to connect") + return + + sock.settimeout(10) + + send_thread_list.append(SendThread(sock if rx_enable is True else None, send_len)) + recv_thread_list.append(RecvThread(sock if tx_enable is True else None)) + recv_thread_list[-1].start() + + # step 5 do test + validation_thread = ValidationThread(self) + validation_thread.start() + + for send_thread in send_thread_list: + send_thread.start() + + if tx_enable is True: + # do send from target + for sock_id in sock_id_list: + self.serial_write_line("SSC1", "soc -S -s %s -l %s -n 10000000" % (sock_id, send_len)) + + time1 = time.time() + exit_flag = False + + while time.time() - time1 < test_time and exit_flag is False: + for i in sock_id_list: + send_thread_list[i].join(0.5) + recv_thread_list[i].join(0.5) + validation_thread.join(0.5) + if send_thread_list[i].isAlive() is False \ + or recv_thread_list[i].isAlive() is False \ + or validation_thread.isAlive() is False: + NativeLog.add_trace_critical("validation error found") + exit_flag = True + break + + NativeLog.add_prompt_trace("time escape: %s" % (time.time() - time1)) + + if time.time() - time1 >= test_time: + self.set_result("Succeed") + else: + self.set_result("Failed") + + # exit all thread + for i in sock_id_list: + send_thread_list[i].exit() + recv_thread_list[i].exit() + send_thread_list[i].join() + send_thread_list[i].join() + + validation_thread.exit() + validation_thread.join() + + +def main(): + pass + +if __name__ == '__main__': + main() diff --git a/components/idf_test/integration_test/TestCaseScript/TCPStress/TCPMultiSTASendRecv.py b/components/idf_test/integration_test/TestCaseScript/TCPStress/TCPMultiSTASendRecv.py new file mode 100644 index 0000000000..56cb7e6928 --- /dev/null +++ b/components/idf_test/integration_test/TestCaseScript/TCPStress/TCPMultiSTASendRecv.py @@ -0,0 +1,165 @@ +from TCAction import TCActionBase +from NativeLog import NativeLog +import time +import random +import string + +TEST_COUNT_ONE_ROUND = 500 + + +class TestCase(TCActionBase.CommonTCActionBase): + def __init__(self, test_case, test_env, timeout=45, log_path=TCActionBase.LOG_PATH): + TCActionBase.CommonTCActionBase.__init__(self, test_case, test_env, timeout=timeout, log_path=log_path) + # load param from excel + cmd_set = test_case["cmd set"] + for i in range(1, len(cmd_set)): + if cmd_set[i][0] != "dummy": + cmd_string = "self." + cmd_set[i][0] + exec cmd_string + self.result_cntx = TCActionBase.ResultCheckContext(self, test_env, self.tc_name) + pass + + def execute(self): + TCActionBase.TCActionBase.execute(self) + self.result_cntx.start() + + try: + # configurable params + send_len = self.send_len + test_time = self.test_time * 60 + server_echo = self.server_echo + sta_number = self.sta_number + send_delay = self.send_delay + # configurable params + except StandardError, e: + NativeLog.add_trace_critical("Error configuration for TCPTransparent script, error is %s" % e) + raise StandardError("Error configuration") + + # step0 reboot + for i in range(sta_number + 1): + checker_stings = ["P SSC%d C ready!!!" % (i + 1)] + test_action_string = ["SSCC SSC%d restore" % (i + 1)] + fail_string = "Fail, Fail to restore" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # turn off recv print + for i in range(sta_number + 1): + checker_stings = ["P SSC%d C +RECVPRINT:0" % (i + 1)] + test_action_string = ["SSCC SSC%d soc -R -o 0" % (i + 1)] + fail_string = "Fail, Fail to turn off recv print" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # step1, set softap mode on SSC1 + checker_stings = ["R SSC1 C +MODE:OK"] + test_action_string = ["SSCC SSC1 op -S -o 2"] + fail_string = "Fail, Fail to set mode on SSC1" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # step2, set STA mode on SSC2-SSCn + for i in range(sta_number): + checker_stings = ["R SSC%d C +MODE:OK" % (i + 2)] + test_action_string = ["SSCC SSC%d op -S -o 1" % (i + 2)] + fail_string = "Fail, Fail to set mode on SSC%d" % (i + 2) + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + ssid = "".join([random.choice(string.lowercase) for m in range(10)]) + password = "".join([random.choice(string.lowercase) for m in range(10)]) + tcp_port = random.randint(40000, 50000) + + # step3, set ssid/password on SSC1 + checker_stings = ["R SSC1 C +SAP:OK"] + test_action_string = ["SSCC SSC1 ap -S -s %s -p %s -n 10 -t 0 -m 10" % (ssid, password)] + fail_string = "Fail, Fail to set ssid/password on SSC1" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # step4, SSC2-SSCn join SSC1(soft AP) + for i in range(sta_number): + checker_stings = ["P SSC%d C +JAP:CONNECTED,%s" % (i + 2, ssid)] + test_action_string = ["SSCC SSC%d ap -C -s %s -p %s" % (i + 2, ssid, password)] + fail_string = "Fail, SSC%d Fail to connect to SSC1" % (i + 2) + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string, check_time=450) is False: + return + + # step5, create server on SSC2 + checker_stings = ["R SSC2 A :BIND:(\d+),OK"] + test_action_string = ["SSCC SSC2 soc -B -t TCP -p %s" % tcp_port] + fail_string = "Fail, Fail to create server on SSC2 while binding" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + checker_stings = ["R SSC2 RE LISTEN:(\d+),OK"] + test_action_string = ["SSCC SSC2 soc -L -s "] + fail_string = "Fail, Fail to create server on SSC2 while listening" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # step6, create client on SSC3-SSCn + for i in range(sta_number - 1): + checker_stings = ["P SSC%d A :BIND:(\d+),OK" % (i + 3, i + 3)] + test_action_string = ["SSCC SSC%d soc -B -t TCP" % (i + 3)] + fail_string = "Fail, SSC%d Fail to connect to TCP server while binding" % (i + 3) + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + for i in range(sta_number - 1): + checker_stings = ["P SSC%d RE CONNECT:(\d+),OK" % (i + 3), "P SSC2 A :ACCEPT:(\d+),.+" % i] + test_action_string = [ + "SSCC SSC%d soc -C -s -i 192.168.4.2 -p %s" % (i + 3, i + 3, tcp_port)] + fail_string = "Fail, SSC%d Fail to connect to TCP server while connecting" % (i + 3) + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + start_time = time.time() + # step7, do send/recv, SSC2<---->other STAs + while time.time() - start_time < test_time: + checker_stings = [] + test_action_string = [] + # SSC2 send packets to SSC3-SSCn + if server_echo is True: + for i in range(sta_number - 1): + test_action_string.append("SSC SSC2 soc -S -s -l %d -n 1000 -j %d" % + (i, send_len, send_delay)) + checker_stings.append( + "P SSC2 RE \+SEND:%s,OK NC CLOSED NC ERROR" % self.get_parameter("accept_sock%d" % (i + 3))) + + # SSC3-SSCn send packets to SSC2 + for i in range(sta_number - 1): + checker_stings.append( + "P SSC%d RE \+SEND:%s,OK NC CLOSED NC ERROR" % (i + 3, self.get_parameter("client_sock%d" % i))) + test_action_string.append("SSC SSC%d soc -S -s -l %d -n 1000 -j %d" % + (i + 3, i + 3, send_len, send_delay)) + + fail_string = "Fail, Failed to send/recv data" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string, + check_freq=1, check_time=900) is False: + break + + # drop off the delay time if it's greater than 20ms + if send_delay > 20: + send_delay -= 10 + + NativeLog.add_trace_critical("Time escape: %d" % (time.time() - start_time)) + + if (time.time() - start_time) >= test_time: + self.result_cntx.set_result("Succeed") + else: + self.result_cntx.set_result("Failed") + + # finally, execute done + + def result_check(self, port_name, data): + TCActionBase.CommonTCActionBase.result_check(self, port_name, data) + self.result_cntx.append_data(port_name, data) + + +def main(): + pass + + +if __name__ == '__main__': + main() diff --git a/components/idf_test/integration_test/TestCaseScript/TCPStress/TCPRandomSend.py b/components/idf_test/integration_test/TestCaseScript/TCPStress/TCPRandomSend.py new file mode 100755 index 0000000000..e32c63bf42 --- /dev/null +++ b/components/idf_test/integration_test/TestCaseScript/TCPStress/TCPRandomSend.py @@ -0,0 +1,109 @@ +import os +import time +import random +import threading +import socket +from TCAction import TCActionBase +from NativeLog import NativeLog +from NativeLog import ThroughputResult + + +class TestCase(TCActionBase.CommonTCActionBase): + + def __init__(self, test_case, test_env, timeout=30, log_path=TCActionBase.LOG_PATH): + TCActionBase.CommonTCActionBase.__init__(self, test_case, test_env, timeout=timeout, log_path=log_path) + self.send_len_config = range(1460) + self.delay_config = [0, 0.01, 0.1, 0.5, 1] + # load param from excel + cmd_set = test_case["cmd set"] + for i in range(1, len(cmd_set)): + if cmd_set[i][0] != "dummy": + cmd_string = "self." + cmd_set[i][0] + exec cmd_string + self.result_cntx = TCActionBase.ResultCheckContext(self, test_env, self.tc_name) + pass + + def execute(self): + TCActionBase.TCActionBase.execute(self) + self.result_cntx.start() + + try: + # configurable params + send_len_config = self.send_len_config + delay_config = self.delay_config + send_count = self.send_count + test_time = self.test_time * 60 + # configurable params + except StandardError, e: + NativeLog.add_trace_critical("Error configuration for TCPThroughput script, error is %s" % e) + raise StandardError("Error configuration") + + # disable recv print during random send test + checker_stings = ["R SSC1 C +RECVPRINT"] + test_action_string = ["SSC SSC1 soc -R -o 0"] + fail_string = "Fail, Fail to disable recv print" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + pc_ip = self.get_parameter("pc_ip") + tcp_port = random.randint(50000, 60000) + + # step 0 create tcp connection + + server_sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) + server_sock.bind((pc_ip, tcp_port)) + server_sock.settimeout(1) + server_sock.listen(5) + + checker_stings = ["R SSC1 A :\+BIND:(\d+),OK"] + test_action_string = ["SSC SSC1 soc -B -t TCP"] + fail_string = "Fail, Fail bind" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + checker_stings = ["P SSC1 RE \+CONNECT:\d+,OK"] + test_action_string = ["SSC SSC1 soc -C -s -i %s -p %s" % (pc_ip, tcp_port)] + fail_string = "Fail, Fail to connect" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + sock, addr = server_sock.accept() + sock.settimeout(10) + # set no delay so that tcp segment will be send as soon as send called + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + + # step 1 start send + start_time = time.time() + while time.time() - start_time < test_time: + for delay in delay_config: + for i in xrange(send_count): + send_len = random.choice(send_len_config) + data = "A" * (send_len+1) + try: + sock.send(data) + except socket.error, e: + NativeLog.add_exception_log(e) + NativeLog.add_trace_critical("Fail to send packets") + return + pass + time.sleep(delay) + + NativeLog.add_prompt_trace("time escape: %s" % (time.time() - start_time)) + + if (time.time() - start_time) > test_time: + self.result_cntx.set_result("Succeed") + else: + self.result_cntx.set_result("Failed") + + # finally, execute done + + def result_check(self, port_name, data): + TCActionBase.CommonTCActionBase.result_check(self, port_name, data) + self.result_cntx.append_data(port_name, data) + + +def main(): + pass + +if __name__ == '__main__': + main() diff --git a/components/idf_test/integration_test/TestCaseScript/TCPStress/TCPSTAMuitiSockSendRecv.py b/components/idf_test/integration_test/TestCaseScript/TCPStress/TCPSTAMuitiSockSendRecv.py new file mode 100644 index 0000000000..5625756b94 --- /dev/null +++ b/components/idf_test/integration_test/TestCaseScript/TCPStress/TCPSTAMuitiSockSendRecv.py @@ -0,0 +1,159 @@ +from TCAction import TCActionBase +from NativeLog import NativeLog +import time +import random +import string + + +class TestCase(TCActionBase.CommonTCActionBase): + def __init__(self, test_case, test_env, timeout=45, log_path=TCActionBase.LOG_PATH): + TCActionBase.CommonTCActionBase.__init__(self, test_case, test_env, timeout, log_path) + # load param from excel + cmd_set = test_case["cmd set"] + for i in range(1, len(cmd_set)): + if cmd_set[i][0] != "dummy": + cmd_string = "self." + cmd_set[i][0] + exec cmd_string + self.result_cntx = TCActionBase.ResultCheckContext(self, test_env, self.tc_name) + pass + + def execute(self): + TCActionBase.TCActionBase.execute(self) + self.result_cntx.start() + + try: + # configurable params + send_len = self.send_len + server_echo = self.server_echo + conn_number = self.conn_number + sta_number = self.sta_number + test_time = self.test_time + send_delay = self.send_delay + # configurable params + except StandardError, e: + NativeLog.add_trace_critical("Error configuration for TCPTransparent script, error is %s" % e) + raise StandardError("Error configuration") + + # step0 reboot + for i in range(sta_number + 1): + checker_stings = ["P SSC%d C !!!ready!!!" % (i + 1)] + test_action_string = ["SSCC SSC%d reboot" % (i + 1)] + fail_string = "Fail, Fail to reboot" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # step1, set ap mode on SSC1, set STA mode on SSC2-SSC3 + checker_stings = ["R SSC1 C +MODE:OK"] + test_action_string = ["SSCC SSC1 op -S -o 2"] + fail_string = "Fail, Fail to set mode on SSC1" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + for i in range(sta_number): + checker_stings = ["R SSC%d C +MODE:OK" % (i + 2)] + test_action_string = ["SSCC SSC%d op -S -o 1" % (i + 2)] + fail_string = "Fail, Fail to set mode on SSC%d" % (i + 2) + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # turn off recv print + for i in range(sta_number + 1): + checker_stings = ["P SSC%d C +RECVPRINT:0" % (i + 1)] + test_action_string = ["SSCC SSC%d soc -R -o 0" % (i + 1)] + fail_string = "Fail, Fail to turn off recv print" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # step2, set ssid/password on SSC1 + ssid = "".join([random.choice(string.lowercase) for m in range(10)]) + password = "".join([random.choice(string.lowercase) for m in range(10)]) + tcp_port = random.randint(10000, 20000) + + checker_stings = ["R SSC1 C +SAP:OK"] + test_action_string = ["SSCC SSC1 ap -S -s %s -p %s -t 3 -m 8" % (ssid, password)] + fail_string = "Fail, Fail to set ssid/password on SSC1" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # step3, SSC2-SSC3 connect to SSC1 + checker_stings = [] + test_action_string = [] + for i in range(sta_number): + checker_stings.append("P SSC%d C +JAP:CONNECTED,%s" % (i + 2, ssid)) + test_action_string.append("SSCC SSC%d ap -C -s %s -p %s" % (i + 2, ssid, password)) + fail_string = "Fail, SSC%d Fail to connect to SoftAP" % (i + 2) + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string, check_time=450) is False: + return + + # step4, create tcp server on STA SSC2 + checker_stings = ["R SSC2 A :BIND:(\d+),OK"] + test_action_string = ["SSCC SSC2 soc -B -t TCP -p %s" % tcp_port] + fail_string = "Fail, Fail to create server on SSC2 while binding" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + checker_stings = ["R SSC2 RE LISTEN:(\d+),OK"] + test_action_string = ["SSCC SSC2 soc -L -s "] + fail_string = "Fail, Fail to create server on SSC2 while listening" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # step5, create multi client on SSC3 + for i in range(conn_number): + checker_stings = ["P SSC3 A :BIND:(\d+),OK" % i] + test_action_string = ["SSCC SSC3 soc -B -t TCP"] + fail_string = "Fail, Fail to create client on SSC3" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + checker_stings = ["P SSC3 RE CONNECT:(\d+),OK", "P SSC2 A :ACCEPT:(\d+),.+" % i] + test_action_string = ["SSCC SSC3 soc -C -s -i %s -p %s" % (i, "192.168.4.2", tcp_port)] + fail_string = "Fail, Fail to connect to SSC2 server while connecting" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + start_time = time.time() + # step6, do send/recv + while time.time() - start_time < test_time: + checker_stings = [] + test_action_string = [] + # SSC2 send packets to SSC3 + if server_echo is True: + for i in range(conn_number): + checker_stings.append("P SSC2 RE \+SEND:\d+,OK NC CLOSED NC ERROR") + test_action_string.append("SSC SSC2 soc -S -s -l %d -n 1000 -j %d" % + (i, send_len, send_delay)) + # SSC3 send packets to SSC2 + for i in range(conn_number): + test_action_string.append("SSC SSC3 soc -S -s -l %d -n 1000 -j %d" % + (i, send_len, send_delay)) + checker_stings.append("P SSC3 RE \+SEND:\d+,OK NC CLOSED NC ERROR") + + fail_string = "Fail, Failed to send/recv data" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string, + check_freq=1, check_time=600) is False: + break + + if send_delay > 20: + send_delay -= 10 + + NativeLog.add_trace_critical("Time escape: %d" % (time.time() - start_time)) + + if (time.time() - start_time) > test_time: + self.result_cntx.set_result("Succeed") + else: + self.result_cntx.set_result("Failed") + + # finally, execute done + + def result_check(self, port_name, data): + TCActionBase.CommonTCActionBase.result_check(self, port_name, data) + self.result_cntx.append_data(port_name, data) + + +def main(): + pass + + +if __name__ == '__main__': + main() diff --git a/components/idf_test/integration_test/TestCaseScript/TCPStress/TCPSTAsendrecv.py b/components/idf_test/integration_test/TestCaseScript/TCPStress/TCPSTAsendrecv.py new file mode 100644 index 0000000000..8fd0d7d6f7 --- /dev/null +++ b/components/idf_test/integration_test/TestCaseScript/TCPStress/TCPSTAsendrecv.py @@ -0,0 +1,167 @@ +from TCAction import TCActionBase +from NativeLog import NativeLog +import time +import random +import string + + +class TestCase(TCActionBase.CommonTCActionBase): + def __init__(self, test_case, test_env, timeout=45, log_path=TCActionBase.LOG_PATH): + TCActionBase.CommonTCActionBase.__init__(self, test_case, test_env, timeout, log_path) + # load param from excel + cmd_set = test_case["cmd set"] + for i in range(1, len(cmd_set)): + if cmd_set[i][0] != "dummy": + cmd_string = "self." + cmd_set[i][0] + exec cmd_string + self.result_cntx = TCActionBase.ResultCheckContext(self, test_env, self.tc_name) + pass + + def execute(self): + TCActionBase.TCActionBase.execute(self) + self.result_cntx.start() + + try: + # configurable params + send_len = self.send_len + server_echo = self.server_echo + conn_number = self.conn_number + sta_number = self.sta_number + test_time = self.test_time * 60 + send_delay = self.send_delay + # configurable params + except StandardError, e: + NativeLog.add_trace_critical("Error configuration for TCPTransparent script, error is %s" % e) + raise StandardError("Error configuration") + + # step0 reboot + for i in range(sta_number + 1): + checker_stings = ["P SSC%d C ready!!!" % (i + 1)] + test_action_string = ["SSCC SSC%d restore" % (i + 1)] + fail_string = "Fail, Fail to restore" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # step1, set ap mode on SSC1, set STA mode on SSC2-SSC3 + checker_stings = ["R SSC1 C +MODE:OK"] + test_action_string = ["SSCC SSC1 op -S -o 2"] + fail_string = "Fail, Fail to set mode on SSC1" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + for i in range(sta_number): + checker_stings = ["R SSC%d C +MODE:OK" % (i + 2)] + test_action_string = ["SSCC SSC%d op -S -o 1" % (i + 2)] + fail_string = "Fail, Fail to set mode on SSC%d" % (i + 2) + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # turn off recv print + for i in range(sta_number + 1): + checker_stings = ["P SSC%d C +RECVPRINT:0" % (i + 1)] + test_action_string = ["SSCC SSC%d soc -R -o 0" % (i + 1)] + fail_string = "Fail, Fail to turn off recv print" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # step2, set ssid/password on SSC1 + ssid = "".join([random.choice(string.lowercase) for m in range(10)]) + password = "".join([random.choice(string.lowercase) for m in range(10)]) + tcp_port = random.randint(10000, 20000) + + checker_stings = ["R SSC1 C +SAP:OK"] + test_action_string = ["SSCC SSC1 ap -S -s %s -p %s -n 10 -t 0 -m 10" % (ssid, password)] + fail_string = "Fail, Fail to set ssid/password on SSC1" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # step3, SSC2-SSC3 connect to SSC1 + checker_stings = [] + test_action_string = [] + for i in range(sta_number): + checker_stings.append("P SSC%d C +JAP:CONNECTED,%s" % (i + 2, ssid)) + test_action_string.append("SSCC SSC%d ap -C -s %s -p %s" % (i + 2, ssid, password)) + fail_string = "Fail, SSC%d Fail to connect to SoftAP" % (i + 2) + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string, check_time=450) is False: + return + + # step4, create tcp server on STA SSC2 + checker_stings = ["R SSC2 A :BIND:(\d+),OK"] + test_action_string = ["SSCC SSC2 soc -B -t TCP -p %s" % tcp_port] + fail_string = "Fail, Fail to create server on SSC2 while binding" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + checker_stings = ["R SSC2 RE LISTEN:(\d+),OK"] + test_action_string = ["SSCC SSC2 soc -L -s "] + fail_string = "Fail, Fail to create server on SSC2 while listening" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # step5, create multi client on SSC3 + for i in range(conn_number): + checker_stings = ["P SSC3 A :BIND:(\d+),OK" % i] + test_action_string = ["SSCC SSC3 soc -B -t TCP"] + fail_string = "Fail, Fail to create client on SSC3" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + checker_stings = ["P SSC3 RE CONNECT:(\d+),OK", "P SSC2 A :ACCEPT:(\d+),.+ NC ERROR" % i] + test_action_string = ["SSCC SSC3 soc -C -s -i %s -p %s" % (i, "192.168.4.2", tcp_port)] + fail_string = "Fail, Fail to connect to SSC2 server while connecting" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + start_time = time.time() + # step6, do send/recv + while time.time() - start_time < test_time: + checker_stings = [] + test_action_string = [] + # SSC2 send packets to SSC3 + if server_echo is True: + for i in range(conn_number): + checker_stings.append( + "P SSC2 RE \+SEND:%s,OK NC CLOSED NC ERROR NC unexpected" % self.get_parameter( + "accept_sock%d" % i)) + test_action_string.append("SSC SSC2 soc -S -s -l %d -n 1000 -j %d" % + (i, send_len, send_delay)) + # SSC3 send packets to SSC2 + for i in range(conn_number): + test_action_string.append("SSC SSC3 soc -S -s -l %d -n 1000 -j %d" % + (i, send_len, send_delay)) + checker_stings.append( + "P SSC3 RE \+SEND:%s,OK NC CLOSED NC ERROR" % self.get_parameter("client_sock%d" % i)) + + fail_string = "Fail, Failed to send/recv data" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string, + check_freq=1, check_time=900) is False: + # checker_stings = ["R SSC1 C +LSTADONE"] + # test_action_string = ["SSCC SSC1 ap -L"] + # fail_string = "" + # if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + # pass + break + + if send_delay > 20: + send_delay -= 10 + + NativeLog.add_trace_critical("Time escape: %d" % (time.time() - start_time)) + + if (time.time() - start_time) > test_time: + self.result_cntx.set_result("Succeed") + else: + self.result_cntx.set_result("Failed") + + # finally, execute done + + def result_check(self, port_name, data): + TCActionBase.CommonTCActionBase.result_check(self, port_name, data) + self.result_cntx.append_data(port_name, data) + + +def main(): + pass + + +if __name__ == '__main__': + main() diff --git a/components/idf_test/integration_test/TestCaseScript/TCPStress/TCPSendRecv.py b/components/idf_test/integration_test/TestCaseScript/TCPStress/TCPSendRecv.py new file mode 100755 index 0000000000..afc3804e78 --- /dev/null +++ b/components/idf_test/integration_test/TestCaseScript/TCPStress/TCPSendRecv.py @@ -0,0 +1,154 @@ +from TCAction import TCActionBase +from NativeLog import NativeLog +import time +import random +import string + +TEST_COUNT_ONE_ROUND = 1000 + + +class TestCase(TCActionBase.CommonTCActionBase): + + def __init__(self, test_case, test_env, timeout=30, log_path=TCActionBase.LOG_PATH): + TCActionBase.CommonTCActionBase.__init__(self, test_case, test_env, timeout=timeout, log_path=log_path) + # load param from excel + cmd_set = test_case["cmd set"] + for i in range(1, len(cmd_set)): + if cmd_set[i][0] != "dummy": + cmd_string = "self." + cmd_set[i][0] + exec cmd_string + self.result_cntx = TCActionBase.ResultCheckContext(self, test_env, self.tc_name) + pass + + def cleanup(self): + # step 0 turn on recv print + checker_stings = ["P SSC1 C +RECVPRINT:1", "P SSC2 C +RECVPRINT:1"] + test_action_string = ["SSC SSC1 soc -R -o 1", "SSC SSC2 soc -R -o 1"] + fail_string = "Fail, Fail to turn on recv print" + self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) + pass + + def execute(self): + TCActionBase.TCActionBase.execute(self) + self.result_cntx.start() + + try: + # configurable params + send_len = self.send_len + test_time = self.test_time * 60 + duplex = self.duplex + conn_num = self.conn_num + send_delay = self.send_delay + # configurable params + except StandardError, e: + NativeLog.add_trace_critical("Error configuration for TCPSendRecv script, error is %s" % e) + raise StandardError("Error configuration") + + ssid = "".join([random.choice(string.lowercase) for m in range(10)]) + password = "".join([random.choice(string.lowercase) for m in range(10)]) + tcp_port = random.randint(10000, 50000) + + # step 0 set ap + checker_stings = ["R SSC1 C +SAP:OK"] + test_action_string = ["SSC SSC1 ap -S -s %s -p %s -t 3" % (ssid, password)] + fail_string = "Fail, Fail to set ap" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + self.result_cntx.set_result("Fail") + return + + # step 1 connect to ap and turn off recv print + checker_stings = ["R SSC2 C +JAP:CONNECTED"] + test_action_string = ["SSC SSC2 sta -C -s %s -p %s" % (ssid, password)] + fail_string = "Fail, Fail to connect to server" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string, check_time=200) is False: + self.result_cntx.set_result("Fail") + return + + checker_stings = ["P SSC1 C +RECVPRINT:0", "P SSC2 C +RECVPRINT:0"] + test_action_string = ["SSC SSC1 soc -R -o 0", "SSC SSC2 soc -R -o 0"] + fail_string = "Fail, Fail to turn off recv print" + self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string, check_time=200) is False: + self.result_cntx.set_result("Fail") + return + + # step 2 create server on AP + checker_stings = ["R SSC1 A :\+BIND:(\d+),OK"] + test_action_string = ["SSC SSC1 soc -B -t TCP -p %s" % tcp_port] + fail_string = "Fail, Fail to create server" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + self.result_cntx.set_result("Fail") + return + + checker_stings = ["R SSC1 A :\+LISTEN:(\d+),OK"] + test_action_string = ["SSC SSC1 soc -L -s "] + fail_string = "Fail, Fail to create server" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + self.result_cntx.set_result("Fail") + return + + # step 3 create conn_num tcp connections + for i in range(conn_num): + checker_stings = ["R SSC2 A :\+BIND:(\d+),OK" % i] + test_action_string = ["SSC SSC2 soc -B -t TCP"] + fail_string = "Fail, Fail to bind" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + self.result_cntx.set_result("Fail") + return + + checker_stings = ["P SSC1 A :\+ACCEPT:(\d+),\d+" % i, + "P SSC2 RE \+CONNECT:\d+,OK"] + test_action_string = ["SSC SSC2 soc -C -s -i -p %s" % (i, tcp_port)] + fail_string = "Fail, Fail to connect" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + self.result_cntx.set_result("Fail") + return + + start_time = time.time() + # step 4, do send/recv + while time.time()-start_time < test_time: + + checker_stings = ["P SSC1 NC ERROR NC CLOSE NC ERROR"] + for i in range(conn_num): + test_action_string = ["SSC SSC2 soc -S -s -l %d -n %d -j %d" % + (i, send_len, TEST_COUNT_ONE_ROUND, send_delay)] + checker_stings.append("P SSC2 RE \"\+SEND:%%%%s,OK\"%%%%() NC ERROR NC CLOSE" % i) + + if duplex is True: + checker_stings.append("P SSC1 RE \"\+SEND:%%%%s,OK\"%%%%()" % i) + test_action_string.append("SSC SSC1 soc -S -s -l %d -n %d -j %d" % + (i, send_len, TEST_COUNT_ONE_ROUND, send_delay)) + + fail_string = "Fail, Failed on send command" + if self.load_and_exe_one_step([], test_action_string, fail_string) is False: + self.result_cntx.set_result("Fail") + break + + fail_string = "Fail, Failed to send/recv data" + if self.load_and_exe_one_step(checker_stings, ["DELAY 0.1"], fail_string, + check_freq=1, check_time=300) is False: + NativeLog.add_prompt_trace("time escape: %s" % (time.time() - start_time)) + self.result_cntx.set_result("Fail") + return + + NativeLog.add_prompt_trace("time escape: %s" % (time.time() - start_time)) + + if (time.time() - start_time) >= test_time: + self.result_cntx.set_result("Succeed") + else: + self.result_cntx.set_result("Failed") + + # finally, execute done + + def result_check(self, port_name, data): + TCActionBase.CommonTCActionBase.result_check(self, port_name, data) + self.result_cntx.append_data(port_name, data) + + +def main(): + pass + +if __name__ == '__main__': + main() + + diff --git a/components/idf_test/integration_test/TestCaseScript/TCPStress/TCPSoftAPSTASendRecv.py b/components/idf_test/integration_test/TestCaseScript/TCPStress/TCPSoftAPSTASendRecv.py new file mode 100644 index 0000000000..bd66559780 --- /dev/null +++ b/components/idf_test/integration_test/TestCaseScript/TCPStress/TCPSoftAPSTASendRecv.py @@ -0,0 +1,339 @@ +from TCAction import TCActionBase +from NativeLog import NativeLog +import time +import random +import string + +TEST_COUNT_ONE_ROUND = 500 + + +class TestCase(TCActionBase.CommonTCActionBase): + def __init__(self, test_case, test_env, timeout=45, log_path=TCActionBase.LOG_PATH): + TCActionBase.CommonTCActionBase.__init__(self, test_case, test_env, + timeout=timeout, log_path=log_path) + self.send_len = 1460 + self.server_port = random.randint(10000, 50000) + self.server_port_2 = random.randint(10000, 50000) + self.server_echo = True + self.test_time = 12 * 60 + self.sta_number = 3 + self.send_delay = 50 + # load param from excel + cmd_set = test_case["cmd set"] + for i in range(1, len(cmd_set)): + if cmd_set[i][0] != "dummy": + cmd_string = "self." + cmd_set[i][0] + exec cmd_string + self.result_cntx = TCActionBase.ResultCheckContext(self, test_env, self.tc_name) + pass + + def cleanup(self): + checker_stings = [] + test_action_strings = [] + for i in range(self.sta_number + 2): + checker_stings.append("R SSC%s C +RECVPRINT:1" % (i+1)) + test_action_strings.append("SSC SSC%s soc -R -o 1" % (i+1)) + fail_string = "Fail, Fail to turn on recv print" + self.load_and_exe_one_step(checker_stings, test_action_strings, fail_string) + pass + + def execute(self): + TCActionBase.TCActionBase.execute(self) + self.result_cntx.start() + + try: + # configurable params + send_len = self.send_len + test_time = self.test_time * 60 + # server port + server_port = self.server_port + server_port_t = self.server_port_2 + # ap ip + # ap_ip = self.ap_ip + # server echo + server_echo = self.server_echo + # station number + sta_number = self.sta_number + # send delay + send_delay = self.send_delay + # configurable params + except StandardError, e: + NativeLog.add_trace_critical("Error configuration for TCPTransparent script, error is %s" % e) + raise StandardError("Error configuration") + + # step0 reboot + checker_stings = [] + test_action_string = [] + + for i in range(sta_number + 2): + checker_stings.append("P SSC%d C !!!ready!!!" % (i + 1)) + test_action_string.append("SSCC SSC%d reboot" % (i + 1)) + + fail_string = "Fail, Fail to reboot" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # switch off recv print + checker_stings = [] + test_action_strings = [] + for i in range(self.sta_number + 2): + checker_stings.append("R SSC%s C +RECVPRINT:0" % (i+1)) + test_action_strings.append("SSC SSC%s soc -R -o 0" % (i+1)) + fail_string = "Fail, Fail to turn off recv print" + self.load_and_exe_one_step(checker_stings, test_action_strings, fail_string) + + # step1, set ap/STA mode on all target + for i in range(sta_number + 2): + checker_stings = ["R SSC%d C +MODE:OK" % (i + 1)] + test_action_string = ["SSCC SSC%d op -S -o 3" % (i + 1)] + fail_string = "Fail, Fail to set mode on SSC%d" % (i + 1) + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # set different getway for SSC1 softAP + checker_stings = ["R SSC1 C +DHCP:AP,OK"] + test_action_string = ["SSCC SSC1 dhcp -E -o 2"] + fail_string = "Fail, SSC1 Fail to disable DHCP" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + checker_stings = ["R SSC1 C +IP:OK"] + test_action_string = ["SSCC SSC1 ip -S -o 2 -i 192.168.6.1"] + fail_string = "Fail, SSC1 Fail to set IP" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + checker_stings = ["R SSC1 C +DHCP:AP,OK"] + test_action_string = ["SSCC SSC1 dhcp -S -o 2"] + fail_string = "Fail, SSC1 Fail to enable DHCP" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # set different getway for SSC2 softAP + checker_stings = ["R SSC2 C +DHCP:AP,OK"] + test_action_string = ["SSCC SSC2 dhcp -E -o 2"] + fail_string = "Fail, SSC2 Fail to disable DHCP" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + checker_stings = ["R SSC2 C +IP:OK"] + test_action_string = ["SSCC SSC2 ip -S -o 2 -i 192.168.5.1"] + fail_string = "Fail, SSC2 Fail to set IP" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + checker_stings = ["R SSC2 C +DHCP:AP,OK"] + test_action_string = ["SSCC SSC2 dhcp -S -o 2"] + fail_string = "Fail, SSC2 Fail to enable DHCP" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # step2, set ssid/password on SSC1 + ssid = "".join([random.choice(string.lowercase) for m in range(10)]) + password = "".join([random.choice(string.lowercase) for m in range(10)]) + checker_stings = ["R SSC1 C +SAP:OK"] + test_action_string = ["SSCC SSC1 ap -S -s %s -p %s -n 10 -t 0 -m 8" % (ssid, password)] + fail_string = "Fail, Fail to set ssid/password on SSC1" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # step3, set ssid/password on SSC2 + ssid_1 = "".join([random.choice(string.lowercase) for m in range(10)]) + password_1 = "".join([random.choice(string.lowercase) for m in range(10)]) + checker_stings = ["R SSC2 C +SAP:OK"] + test_action_string = ["SSCC SSC2 ap -S -s %s -p %s -n 10 -t 0 -m 8" % (ssid_1, password_1)] + fail_string = "Fail, Fail to set ap ssid/password on SSC2" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # step4, SSC2 join SSC1(soft AP) + checker_stings = [] + test_action_string = [] + checker_stings.append("P SSC2 C +JAP:CONNECTED,%s" % ssid) + test_action_string.append("SSCC SSC2 ap -C -s %s -p %s" % (ssid, password)) + fail_string = "Fail, Fail to connect to SSC1 SoftAP" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + # step5, create server on SSC1 + checker_stings = ["R SSC1 A :BIND:(\d+),OK"] + test_action_string = ["SSCC SSC1 soc -B -t TCP -p %s" % server_port] + fail_string = "Fail, Fail to create server on SSC1 while binding" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + checker_stings = ["R SSC1 RE LISTEN:(\d+),OK"] + test_action_string = ["SSCC SSC1 soc -L -s "] + fail_string = "Fail, Fail to create server on SSC1 while listening" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # step6, create client on SSC2 + checker_stings = [] + test_action_string = [] + checker_stings.append("P SSC2 A :BIND:(\d+),OK") + test_action_string.append("SSCC SSC2 soc -B -t TCP") + fail_string = "Fail, SSC2 Fail to connect to server while binding" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + checker_stings = ["P SSC2 RE CONNECT:(\d+),OK", "P SSC1 A :ACCEPT:(\d+),.+"] + test_action_string = ["SSCC SSC2 soc -C -s -i %s -p %s" % ("192.168.6.1", server_port)] + fail_string = "Fail, SSC2 Fail to connect to server while connecting" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # step7, SSC3 - SSC5 join SSC2 + checker_stings = [] + test_action_string = [] + for i in range(sta_number): + checker_stings.append("P SSC%d C +JAP:CONNECTED,%s" % (i + 3, ssid_1)) + test_action_string.append("SSCC SSC%d ap -C -s %s -p %s" % (i + 3, ssid_1, password_1)) + fail_string = "Fail, SSC%d Fail to connect to SSC2" % (i + 3) + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string, check_time=450) is False: + return + + # step8, create server on SSC2 + checker_stings = ["R SSC2 A :BIND:(\d+),OK"] + test_action_string = ["SSCC SSC2 soc -B -t TCP -p %s -i 192.168.5.1" % server_port_t] + fail_string = "Fail, Fail to create server one SSC2 while binding" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + checker_stings = ["R SSC2 RE LISTEN:(\d+),OK"] + test_action_string = ["SSCC SSC2 soc -L -s "] + fail_string = "Fail, Fail to create server one SSC2 while listening" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # step9, create client on SSC3 - SSC5 + checker_stings = [] + test_action_string = [] + for i in range(sta_number): + checker_stings.append("P SSC%d A :BIND:(\d+),OK" % (i + 3, i + 3)) + test_action_string.append("SSCC SSC%d soc -B -t TCP" % (i + 3)) + fail_string = "Fail, Fail to connect to SSC2 server while binding" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + for i in range(sta_number): + checker_stings = ["P SSC%d RE CONNECT:(\d+),OK" % (i + 3), + "P SSC2 A :ACCEPT:(\d+),.+" % (i + 3)] + test_action_string = ["SSCC SSC%d soc -C -s -i %s -p %s" % + (i + 3, i + 3, "192.168.5.1", server_port_t)] + fail_string = "Fail, Fail to connect to SSC2 server while connecting" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + start_time = time.time() + # step 10, do send/recv + while time.time() - start_time < test_time: + + checker_stings = [] + test_action_string = [] + if server_echo is True: + test_action_string.append("SSC SSC1 soc -S -s -l %d -n %d -j %d" % + (send_len, TEST_COUNT_ONE_ROUND, send_delay)) + checker_stings.append("P SSC1 RE \+SEND:\d+,OK NC CLOSED") + test_action_string.append("SSC SSC2 soc -S -s -l %d -n %d -j %d" % + (send_len, TEST_COUNT_ONE_ROUND, send_delay)) + checker_stings.append("P SSC2 RE \+SEND:\d+,OK NC CLOSED") + + for i in range(sta_number): + checker_stings.append("P SSC%d RE \+SEND:\d+,OK NC CLOSED" % (i + 3)) + test_action_string.append("SSC SSC%d soc -S -s -l %d -n %d -j %d" % + (i + 3, i + 3, send_len, TEST_COUNT_ONE_ROUND, send_delay)) + for i in range(sta_number): + test_action_string.append("SSC SSC2 soc -S -s -l %d -n %d -j %d" % + (i + 3, send_len, TEST_COUNT_ONE_ROUND, send_delay)) + checker_stings.append("P SSC2 RE \+SEND:\d+,OK NC CLOSED") + + fail_string = "Fail, Failed to send/recv data" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string, + check_freq=1, check_time=300) is False: + break + pass + + NativeLog.add_prompt_trace("time escape: %s" % (time.time() - start_time)) + + if (time.time() - start_time) > test_time: + self.result_cntx.set_result("Succeed") + else: + self.result_cntx.set_result("Failed") + checker_stings = [] + test_action_string = [] + for i in range(sta_number + 2): + checker_stings.append("P SSC%d C CLOSEALL" % (i + 1)) + test_action_string.append("SSCC SSC%d soc -T" % (i + 1)) + fail_string = "Fail, Fail to close socket" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + # re-set server on SSC1 + server_port = random.randint(20000, 30000) + checker_stings = ["R SSC1 A :BIND:(\d+),OK"] + test_action_string = ["SSCC SSC1 soc -B -t TCP -p %s" % server_port] + fail_string = "Fail, Fail to bind socket" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + checker_stings = ["R SSC1 RE LISTEN:(\d+),OK"] + test_action_string = ["SSCC SSC1 soc -L -s "] + fail_string = "Fail, Fail to listen" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + # SSC2 connnect SSC1 + checker_stings = [] + test_action_string = [] + checker_stings.append("P SSC2 A :BIND:(\d+),OK") + test_action_string.append("SSCC SSC2 soc -B -t TCP") + fail_string = "Fail, SSC2 Fail to bind sock" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + checker_stings = ["P SSC2 RE CONNECT:(\d+),OK", "P SSC1 A :ACCEPT:(\d+),.+"] + test_action_string = ["SSCC SSC2 soc -C -s -i %s -p %s" % ("192.168.6.1", server_port)] + fail_string = "Fail, SSC2 Fail to connect to SSC1 server" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + # create server on SSC2 + checker_stings = [] + test_action_string = [] + checker_stings.append("P SSC2 A :BIND:(\d+),OK") + test_action_string.append("SSCC SSC2 soc -B -t TCP -p %s -i 192.168.5.1" % server_port_t) + fail_string = "Fail, SSC2 Fail to bind" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + checker_stings = ["R SSC2 RE LISTEN:(\d+),OK"] + test_action_string = ["SSCC SSC2 soc -L -s "] + fail_string = "Fail, SSC2 Fail to listen" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + # create client on SSC3-SSC5 + checker_stings = [] + test_action_string = [] + for i in range(sta_number): + checker_stings.append("P SSC%d A :BIND:(\d+),OK" % (i + 3, i + 3)) + test_action_string.append("SSCC SSC%d soc -B -t TCP" % (i + 3)) + fail_string = "Fail, Fail to connect to SSC2 server while binding" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + for i in range(sta_number): + checker_stings = ["P SSC%d RE CONNECT:(\d+),OK" % (i + 3), + "P SSC2 A :ACCEPT:(\d+),.+" % (i + 3)] + test_action_string = ["SSCC SSC%d soc -C -s -i %s -p %s" % + (i + 3, i + 3, "192.168.5.1", server_port_t)] + fail_string = "Fail, Fail to connect to server" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + def result_check(self, port_name, data): + TCActionBase.CommonTCActionBase.result_check(self, port_name, data) + self.result_cntx.append_data(port_name, data) + + +def main(): + pass + + +if __name__ == '__main__': + main() diff --git a/components/idf_test/integration_test/TestCaseScript/TCPStress/TCPThroughput.py b/components/idf_test/integration_test/TestCaseScript/TCPStress/TCPThroughput.py new file mode 100755 index 0000000000..85759b702d --- /dev/null +++ b/components/idf_test/integration_test/TestCaseScript/TCPStress/TCPThroughput.py @@ -0,0 +1,325 @@ +import os +import time +import random +import threading +import socket + +from TCAction import TCActionBase +from NativeLog import NativeLog +from NativeLog import ThroughputResult +from Utility import RSSICalibrator + +LOG_FOLDER = os.path.join("Performance", "Throughput") + +AP_PROP_KEY = ("ssid", "password", "apc") + + +class SendThread(threading.Thread): + def __init__(self, sock, send_len): + threading.Thread.__init__(self) + self.setDaemon(True) + self.sock = sock + self.send_len = send_len + self.exit_event = threading.Event() + self.calc_event = threading.Event() + self.bytes_sent = 0 + pass + + def start_calc(self): + self.calc_event.set() + + def stop_calc(self): + self.calc_event.clear() + self.exit_event.set() + + def run(self): + data = "A" * self.send_len + if self.sock is None: + return + while True: + if self.exit_event.isSet() is True: + break + try: + self.sock.send(data) + except StandardError: + break + if self.calc_event.isSet() is True: + self.bytes_sent += self.send_len + + def get_bytes_sent(self): + return self.bytes_sent + + pass + + +class RecvThread(threading.Thread): + def __init__(self, sock): + threading.Thread.__init__(self) + self.setDaemon(True) + self.sock = sock + self.exit_event = threading.Event() + self.calc_event = threading.Event() + self.bytes_recv = 0 + + def start_calc(self): + self.calc_event.set() + + def stop_calc(self): + self.calc_event.clear() + self.exit_event.set() + + def run(self): + if self.sock is None: + return + while True: + if self.exit_event.isSet() is True: + break + try: + data = self.sock.recv(8 * 1024) + except StandardError: + break + if self.calc_event.isSet() is True: + self.bytes_recv += len(data) + + def get_bytes_recv(self): + return self.bytes_recv + + pass + + +class TestCase(TCActionBase.CommonTCActionBase): + def __init__(self, test_case, test_env, timeout=30, log_path=TCActionBase.LOG_PATH): + TCActionBase.CommonTCActionBase.__init__(self, test_case, test_env, timeout=timeout, log_path=log_path) + self.performance_folder_path = log_path + self.att_test_list = range(60) + cmd_set = test_case["cmd set"] + # load param from excel + for i in range(1, len(cmd_set)): + if cmd_set[i][0] != "dummy": + cmd_string = "self." + cmd_set[i][0] + exec cmd_string + self.result_cntx = TCActionBase.ResultCheckContext(self, test_env, self.tc_name) + pass + + def execute(self): + TCActionBase.TCActionBase.execute(self) + self.result_cntx.start() + + try: + # configurable params + ap_list = self.get_parameter("shield_box_ap_list") + pc_ip = self.get_parameter("pc_ip") + send_len = self.send_len + att_test_list = self.att_test_list + tx_enable = self.tx_enable + rx_enable = self.rx_enable + measure_period = self.measure_period + # configurable params + except StandardError, e: + NativeLog.add_trace_critical("Error configuration for TCPThroughput script, error is %s" % e) + raise StandardError("Error configuration") + + tcp_port = random.randint(40000, 50000) + + # init throughput result data + test_item = "" + if tx_enable is True: + test_item += "Tx" + if rx_enable is True: + test_item += "Rx" + if test_item == "": + raise StandardError("no throughput test item") + + folder_path = os.path.join(self.performance_folder_path, LOG_FOLDER) + file_name = os.path.join(folder_path, + "TCPThroughput_%s_%s" % (test_item, time.strftime("%d%H%M%S", time.localtime()))) + result = ThroughputResult.ThroughputResult(file_name, standard_required=True) + + # restart before executing throughput + checker_stings = ["R SSC1 C !!!ready!!!"] + test_action_string = ["SSC SSC1 reboot"] + fail_string = "Fail, Fail to reboot" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + self.result_cntx.set_result("Fail") + return + + # disable recv print during throughput test + checker_stings = ["R SSC1 C +RECVPRINT"] + test_action_string = ["SSC SSC1 soc -R -o 0"] + fail_string = "Fail, Fail to disable recv print" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + self.result_cntx.set_result("Fail") + return + + ret = True + for ap_prop in ap_list: + ssid = ap_prop[0] + password = ap_prop[1] + apc = ap_prop[2] + if ap_prop[1] == "": + # set a default string for open ap + password = "1" + # switch off all outlet, switch on AP outlet + outlet_config_dict = dict.fromkeys(range(1, 9), "OFF") + outlet_config_dict[apc] = "ON" + apc_cmd = "APC " + for outlet in outlet_config_dict: + apc_cmd += " %s %s" % (outlet_config_dict[outlet], outlet) + checker_stings = ["P PC_COM L OK"] + fail_string = "Fail, Fail to switch apc" + if self.load_and_exe_one_step(checker_stings, [apc_cmd], fail_string) is False: + ret = False + break + + # wait AP ready + time.sleep(20) + + # create server + server_sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) + server_sock.bind((pc_ip, tcp_port)) + server_sock.settimeout(5) + server_sock.listen(5) + + if tx_enable is True: + result.add_test_item(ssid + "_tx") + if rx_enable is True: + result.add_test_item(ssid + "_rx") + + # create RSSI Calibrator + calibrator = RSSICalibrator.Calibrator() + + for att_value in att_test_list: + # step 0 set att value + checker_stings = ["R PC_COM L OK"] + test_action_string = ["ATT %s" % att_value] + fail_string = "Fail, Fail to set att value" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + ret = False + break + # continue + + # step 1 get AP RSSI + checker_stings = ["R SSC1 A :\+SCAN:%s,[:\d\w]+,\d+,\d+,([-\d]+)" % ssid] + test_action_string = ["SSC SSC1 sta -S -s %s" % ssid] + fail_string = "Fail, Fail to scan" + rssi = scan_count = 0 + for i in range(3): + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + continue + rssi += int(self.test_env.get_variable_by_name("rssi")[1]) + scan_count += 1 + + rssi = calibrator.calibrate_rssi(float(rssi) / scan_count if scan_count > 0 else 0, att_value) + + # step 2 connect to AP + checker_stings = ["R SSC1 C +JAP:CONNECTED"] + test_action_string = ["SSC SSC1 sta -C -s %s -p %s" % (ssid, password)] + fail_string = "Fail, Fail to JAP" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string, + check_freq=1, check_time=30) is False: + if rssi < -89: + continue + else: + ret = False + break + + # step 3 close all connections + checker_stings = ["R SSC1 C +CLOSEALL"] + test_action_string = ["SSC SSC1 soc -T"] + fail_string = "Fail, Fail to close socket" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + ret = False + break + # continue + + # step 4 create tcp connection + + checker_stings = ["R SSC1 A :\+BIND:(\d+),OK"] + test_action_string = ["SSC SSC1 soc -B -t TCP"] + fail_string = "Fail, Fail to bind" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + ret = False + break + # continue + + checker_stings = ["P SSC1 RE \+CONNECT:\d+,OK"] + test_action_string = ["SSC SSC1 soc -C -s -i %s -p %s" % (pc_ip, tcp_port)] + fail_string = "Fail, Fail to connect socket" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + ret = False + break + # continue + + try: + sock, addr = server_sock.accept() + except socket.error, e: + NativeLog.add_trace_critical("%s" % e) + continue + sock.settimeout(measure_period) + + # step 5 do throughput test + send_thread = SendThread(sock if rx_enable is True else None, send_len) + send_thread.start() + + recv_thread = RecvThread(sock if tx_enable is True else None) + recv_thread.start() + + if tx_enable is True: + # do send from target + test_action_string = ["SSC SSC1 soc -S -s -l %s -n 10000000" % send_len] + fail_string = "Fail, Fail to send" + if self.load_and_exe_one_step([], test_action_string, fail_string) is False: + pass + + # start throughput calculate + send_thread.start_calc() + recv_thread.start_calc() + + # sleep for measure period + time.sleep(measure_period) + + # stop throughput calculate + send_thread.stop_calc() + recv_thread.stop_calc() + + send_thread.join() + recv_thread.join() + + sock.close() + + # output throughput result + # in Mbps + if send_thread.get_bytes_sent() > 0: + result.log_throughput(ssid + "_rx", rssi, att_value, + float(send_thread.get_bytes_sent() * 8) / (measure_period * 1000000)) + + if recv_thread.get_bytes_recv() > 0: + result.log_throughput(ssid + "_tx", rssi, att_value, + float(recv_thread.get_bytes_recv() * 8) / (measure_period * 1000000)) + + result.output_to_file() + pass + + server_sock.close() + if not ret: + NativeLog.add_trace_critical("Test SUC for %s" % ssid) + elif ret: + NativeLog.add_trace_critical("Test FAIL for %s!!!" % ssid) + if ret: + self.result_cntx.set_result("Succeed") + else: + self.result_cntx.set_result("Fail") + + # finally, execute done + + def result_check(self, port_name, data): + TCActionBase.CommonTCActionBase.result_check(self, port_name, data) + self.result_cntx.append_data(port_name, data) + + +def main(): + pass + + +if __name__ == '__main__': + main() diff --git a/components/idf_test/integration_test/TestCaseScript/TCPStress/__init__.py b/components/idf_test/integration_test/TestCaseScript/TCPStress/__init__.py new file mode 100755 index 0000000000..049c1b961f --- /dev/null +++ b/components/idf_test/integration_test/TestCaseScript/TCPStress/__init__.py @@ -0,0 +1 @@ +__all__ = ["TCPConnUtility", "TCPConnSingleMode", "TCPConnMixedMode"] \ No newline at end of file diff --git a/components/idf_test/integration_test/TestCaseScript/UDPStress/UDPBroadcast.py b/components/idf_test/integration_test/TestCaseScript/UDPStress/UDPBroadcast.py new file mode 100644 index 0000000000..9ced966a88 --- /dev/null +++ b/components/idf_test/integration_test/TestCaseScript/UDPStress/UDPBroadcast.py @@ -0,0 +1,342 @@ +import time +import random +import threading +import socket +import re + +from TCAction import PerformanceTCBase +from NativeLog import NativeLog +from Utility import Encoding + + +class SendThread(threading.Thread): + def __init__(self, sock, send_len, target_addr, delay): + threading.Thread.__init__(self) + self.sock = sock + self.send_len = send_len + self.target_addr = target_addr + self.delay = delay + self.exit_event = threading.Event() + self.send_count = 0 + pass + + def exit(self): + self.exit_event.set() + + def run(self): + data = "A" * self.send_len + if self.sock is None: + return + while True: + if self.exit_event.isSet() is True: + break + try: + self.sock.sendto(data, self.target_addr) + except StandardError: + break + self.send_count += 1 + time.sleep(self.delay * 0.001) + + pass + + def get_send_count(self): + return self.send_count + + +class RecvThread(threading.Thread): + def __init__(self, sock): + threading.Thread.__init__(self) + self.sock = sock + self.exit_event = threading.Event() + self.calc_event = threading.Event() + self.bytes_recv = 0 + self.Max = 0.0 + + def start_calc(self): + self.calc_event.set() + + def stop_calc(self): + self.calc_event.clear() + self.exit_event.set() + + def run(self): + if self.sock is None: + return + while True: + if self.exit_event.isSet() is True: + break + try: + data, addr = self.sock.recvfrom(2048) + except StandardError: + break + if self.calc_event.isSet() is True: + self.bytes_recv += len(data) + if len(data) == 0: + start = time.time() + while True: + try: + data, addr = self.sock.recvfrom(2048) + except StandardError: + break + if len(data) > 0: + if self.calc_event.isSet() is True: + self.bytes_recv += len(data) + end = time.time() + break + if end - start > self.Max: + self.Max = end - start + + def get_bytes_recv(self): + return self.bytes_recv + + pass + + def get_Max_time(self): + return self.Max + + pass + + +class device_check(threading.Thread): + def __init__(self, port): + threading.Thread.__init__(self) + self.Max = 0.0 + self.port = port + self.recv_data_cache = "" + self.cache_lock = threading.Lock() + self.exit_event = threading.Event() + + def data_recv_callback(self, data): + with self.cache_lock: + self.recv_data_cache += data + pass + + def exit(self): + self.exit_event.set() + pass + + def run(self): + while self.exit_event.isSet() is False: + while True: + if self.recv_data_cache: + match = re.search("\+RECVFROM:\d+,\d+,\d+\.\d+\.\d+\.\d+,\d+", self.recv_data_cache) + if match is not None: + self.recv_data_cache = self.recv_data_cache[len(match.group()):] + else: + start = time.time() + end = 0.0 + while True: + res = re.search("\+RECVFROM:\d+,\d+,\d+\.\d+\.\d+\.\d+,\d+", self.recv_data_cache) + if res is not None: + self.recv_data_cache = self.recv_data_cache[len(res.group()):] + end = time.time() + break + if end - start > self.Max: + self.Max = end - start + pass + + def get_max_time(self): + return self.Max + + +class TestCase(PerformanceTCBase.PerformanceTCBase): + def __init__(self, test_case, test_env, timeout, log_path): + PerformanceTCBase.PerformanceTCBase.__init__(self, test_case, test_env, timeout, log_path) + self.send_len = 0 + self.pc_send = 0 + self.target_send = 0 + self.test_time = 0 + self.delay = 0 + # load param from excel + cmd_set = test_case["cmd set"] + for i in range(1, len(cmd_set)): + if cmd_set[i][0] != "dummy": + cmd_string = "self." + cmd_set[i][0] + exec cmd_string + self.recv_cb_lock = threading.Lock() + self.recv_cb = dict.fromkeys(["SSC1"]) + pass + + def register_recv_callback(self, port_name, callback): + with self.recv_cb_lock: + if self.recv_cb[port_name] is None: + self.recv_cb[port_name] = [callback] + else: + self.recv_cb[port_name].append(callback) + pass + + def process(self): + try: + # configurable params + send_len = self.send_len + pc_send = self.pc_send + target_send = self.target_send + test_time = self.test_time + delay = self.delay + ap_ssid = self.get_parameter("ap_ssid") + ap_password = self.get_parameter("ap_password") + pc_ip = self.get_parameter("pc_ip") + target_ip = self.get_parameter("target_ip") + except StandardError, e: + NativeLog.add_trace_critical("Error configuration for UDP script, error is %s" % e) + raise StandardError("Error configuration") + + udp_port = random.randint(40000, 50000) + + # reboot before executing + self.flush_data("SSC1") + self.serial_write_line("SSC1", "reboot") + if self.check_response("SSC1", "ready!!!", 5) is False: + NativeLog.add_trace_critical("Fail to reboot") + return + + # set target as STA mode + self.flush_data("SSC1") + self.serial_write_line("SSC1", "op -S -o 1") + if self.check_response("SSC1", "+MODE:OK", 5) is False: + NativeLog.add_trace_critical("Fail to set mode") + return + + # connect to AP + self.flush_data("SSC1") + self.serial_write_line("SSC1", "sta -C -s %s -p %s" % (ap_ssid, ap_password)) + if self.check_response("SSC1", "+JAP:CONNECTED", 30) is False: + NativeLog.add_trace_critical("Fail to JAP") + return + + # disable recv print on target + self.flush_data("SSC1") + self.serial_write_line("SSC1", "soc -R -o 0") + if self.check_response("SSC1", "+RECVPRINT", 5) is False: + NativeLog.add_trace_critical("Fail to disable recv print") + return + + # get broadcast ip + res = re.search("(\d+\.\d+\.\d+\.)\d+", pc_ip) + if res is not None: + udp = res.group(1) + broadcast_ip = udp + "255" + else: + NativeLog.add_trace_critical("No ip addr found") + return + + # close all connection on target + self.flush_data("SSC1") + self.serial_write_line("SSC1", "soc -T") + if self.check_response("SSC1", "+CLOSEALL", 5) is False: + NativeLog.add_trace_critical("Fail to close sock") + return + + # create socket on pc + pc_sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM) + pc_sock.bind((pc_ip, udp_port)) + pc_sock.settimeout(1) + + # create socket on target + self.flush_data("SSC1") + self.serial_write_line("SSC1", "soc -B -t UDP -i %s -p %s" % (target_ip, udp_port)) + if self.check_response("SSC1", "+BIND:0,OK,", 5) is False: + NativeLog.add_trace_critical("Fail to bind") + return + + thread_dict = dict.fromkeys(["SSC1"]) + thread_dict["SSC1"] = dict(zip(["check"], [None])) + thread_dict["SSC1"]["check"] = device_check(self.test_env.get_port_by_name("SSC1")) + self.register_recv_callback("SSC1", thread_dict["SSC1"]["check"].data_recv_callback) + + send_thread = SendThread(pc_sock if pc_send is True else None, send_len, (broadcast_ip, udp_port), delay) + send_thread.start() + + recv_thread = RecvThread(pc_sock if target_send is True else None) + recv_thread.start() + + # start calculate + recv_thread.start_calc() + thread_dict["SSC1"]["check"].start() + send_count = 0 + if target_send is True: + # do send from target + start = time.time() + while time.time() - start < test_time * 60: + self.flush_data("SSC1") + self.serial_write_line("SSC1", "soc -S -s 0 -l %s -n 1000 -i %s -p %s -j %s" % ( + send_len, broadcast_ip, udp_port, delay)) + if self.check_response("SSC1", "+SEND:0,OK", 300) is False: + NativeLog.add_trace_critical("Fail to send") + return + send_count += 1000 + else: + time.sleep(test_time * 60) + + send_thread.exit() + send_thread.join() + + # stop throughput calculate + while True: + if recv_thread.isAlive() is False: + recv_thread.stop_calc() + recv_thread.join() + break + + Max = 0.0 + recv_count = 0 + if pc_send is True: + send_count = send_thread.get_send_count() + start = time.time() + rx_data_len = 0 + suc_time = 0 + while time.time() - start < 30: + self.flush_data("SSC1") + self.serial_write_line("SSC1", "soc -Q -s 0 -o 1") + time.sleep(0.05) + data = self.serial_read_data("SSC1") + if data is not None: + res = re.search("\+RECVLEN:(\d+)", data) + if res is not None: + if rx_data_len < int(res.group(1)): + rx_data_len = int(res.group(1)) + time.sleep(0.5) + else: + suc_time += 1 + if suc_time > 5: + break + + if (rx_data_len * 8 % send_len) > 0: + recv_count = rx_data_len / send_len + 1 + else: + recv_count = rx_data_len / send_len + + if recv_thread.get_bytes_recv() > 0: + if (recv_thread.get_bytes_recv() % send_len) > 0: + recv_count = recv_thread.get_bytes_recv() / send_len + 1 + else: + recv_count = recv_thread.get_bytes_recv() / send_len + Max = recv_thread.get_Max_time() + + thread_dict["SSC1"]["check"].exit() + pc_sock.close() + + self.set_result("Succeed") + NativeLog.add_trace_critical("send_count is %s, recv_count is %s" % (send_count, recv_count)) + NativeLog.add_trace_critical( + "UDP Broadcast lose rate is %.2f%%" % (float(send_count - recv_count) / send_count * 100)) + NativeLog.add_trace_critical("UDP Broadcast lose test MAX time is %.4f" % Max) + + @Encoding.encode_utf8(3) + def result_check(self, port_name, data): + PerformanceTCBase.PerformanceTCBase.result_check(self, port_name, data) + if port_name in self.recv_cb: + with self.recv_cb_lock: + callback_list = self.recv_cb[port_name] + if callback_list is not None: + for callback in callback_list: + callback(data) + pass + + +def main(): + pass + + +if __name__ == '__main__': + main() diff --git a/components/idf_test/integration_test/TestCaseScript/UDPStress/UDPMultiSTASendRecv.py b/components/idf_test/integration_test/TestCaseScript/UDPStress/UDPMultiSTASendRecv.py new file mode 100644 index 0000000000..82c615d4ee --- /dev/null +++ b/components/idf_test/integration_test/TestCaseScript/UDPStress/UDPMultiSTASendRecv.py @@ -0,0 +1,156 @@ +from TCAction import TCActionBase +from NativeLog import NativeLog +import time +import random +import string + + +class TestCase(TCActionBase.CommonTCActionBase): + def __init__(self, test_case, test_env, timeout=45, log_path=TCActionBase.LOG_PATH): + TCActionBase.CommonTCActionBase.__init__(self, test_case, test_env, timeout=timeout, log_path=log_path) + # load param from excel + cmd_set = test_case["cmd set"] + for i in range(1, len(cmd_set)): + if cmd_set[i][0] != "dummy": + cmd_string = "self." + cmd_set[i][0] + exec cmd_string + self.result_cntx = TCActionBase.ResultCheckContext(self, test_env, self.tc_name) + pass + + def execute(self): + TCActionBase.TCActionBase.execute(self) + self.result_cntx.start() + + try: + # configurable params + send_len = self.send_len + test_time = self.test_time * 60 + server_echo = self.server_echo + sta_number = self.sta_number + send_delay = self.send_delay + # configurable params + except StandardError, e: + NativeLog.add_trace_critical("Error configuration for TCPTransparent script, error is %s" % e) + raise StandardError("Error configuration") + + # step0 reboot + for i in range(sta_number + 1): + checker_stings = ["P SSC%d C ready!!!" % (i + 1)] + test_action_string = ["SSCC SSC%d restore" % (i + 1)] + fail_string = "Fail, Fail to restore" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # turn off recv print + for i in range(sta_number + 1): + checker_stings = ["P SSC%d C +RECVPRINT:0" % (i + 1)] + test_action_string = ["SSCC SSC%d soc -R -o 0" % (i + 1)] + fail_string = "Fail, Fail to turn off recv print" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # step1, set softAP mode on SSC1 + checker_stings = ["R SSC1 C +MODE:OK"] + test_action_string = ["SSCC SSC1 op -S -o 2"] + fail_string = "Fail, Fail to set mode on SSC1" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # step2, set STA mode on SSC2-SSCn + for i in range(sta_number): + checker_stings = ["R SSC%d C +MODE:OK" % (i + 2)] + test_action_string = ["SSCC SSC%d op -S -o 1" % (i + 2)] + fail_string = "Fail, Fail to set mode on SSC%d" % (i + 2) + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # step3, set ssid/password on SSC1 + ssid = "".join([random.choice(string.lowercase) for m in range(10)]) + password = "".join([random.choice(string.lowercase) for m in range(10)]) + udp_port = random.randint(10000, 20000) + + checker_stings = ["R SSC1 C +SAP:OK"] + test_action_string = ["SSCC SSC1 ap -S -s %s -p %s -n 10 -t 0 -m 10" % (ssid, password)] + fail_string = "Fail, Fail to set ssid/password on SSC1" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # step4, all STA join SSC1(soft AP) + for i in range(sta_number): + checker_stings = ["R SSC%d C +JAP:CONNECTED,%s" % (i + 2, ssid)] + test_action_string = ["SSCC SSC%d ap -C -s %s -p %s" % (i + 2, ssid, password)] + fail_string = "Fail, Fail to connect to SSC1" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string, check_time=450) is False: + return + + # step5, get all the STA ip + for i in range(sta_number): + checker_stings = ["R SSC%d A :\+STAIP:192.168.4.(\d+)" % (i + 2, i + 2)] + test_action_string = ["SSCC SSC%d ip -Q" % (i + 2)] + fail_string = "Fail, Fail to get SSC%d ip" % (i + 2) + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + # else: + # print "SSC%d ip is:" % (i + 2), self.get_parameter("SSC%d_IP" % (i + 2)) + + # step6, create UDP socket on all targets + for i in range(sta_number): + checker_stings = ["R SSC%d A :\+BIND:(\d+),OK" % (i + 2, i + 2)] + test_action_string = ["SSCC SSC%d soc -B -t UDP -p %s" % (i + 2, udp_port + i + 2)] + fail_string = "Fail, SSC%d Fail to bind" % (i + 2) + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + start_time = time.time() + # step7, do send/recv, SSC2<---->other STAs + while time.time() - start_time < test_time: + checker_stings = [] + test_action_string = [] + if server_echo is True: + + # SSC2 send packets to SSC3-SSCn + for i in range(sta_number - 1): + ip = "192.168.4." + self.get_parameter("SSC%d_IP" % (i + 3)) + test_action_string.append( + "SSC SSC2 soc -S -s -i %s -p %s -l %d -n 1000 -j %d" % ( + i + 3, ip, udp_port + i + 3, send_len, send_delay)) + checker_stings.append( + "P SSC2 RE \+SEND:%s,OK NC CLOSED NC ERROR" % self.get_parameter("sock%d" % (i + 3))) + + # SSC3-SSCn send packets to SSC2 + ssc2_ip = "192.168.4." + self.get_parameter("SSC2_IP") + for i in range(sta_number - 1): + test_action_string.append( + "SSC SSC%d soc -S -s -i %s -p %s -l %d -n 1000 -j %d" % ( + i + 3, i + 3, ssc2_ip, udp_port + 2, send_len, send_delay)) + checker_stings.append( + "P SSC%d RE \+SEND:%s,OK NC CLOSED NC ERROR" % (i + 3, self.get_parameter("sock%d" % (i + 3)))) + + fail_string = "Fail, Failed to send/recv data" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string, + check_freq=1, check_time=300) is False: + break + + # drop off the delay time if it's greater than 20ms + if send_delay > 20: + send_delay -= 10 + + NativeLog.add_trace_critical("time escape: %s" % (time.time() - start_time)) + if (time.time() - start_time) >= test_time: + self.result_cntx.set_result("Succeed") + else: + self.result_cntx.set_result("Failed") + + # finally, execute done + + def result_check(self, port_name, data): + TCActionBase.CommonTCActionBase.result_check(self, port_name, data) + self.result_cntx.append_data(port_name, data) + + +def main(): + pass + + +if __name__ == '__main__': + main() diff --git a/components/idf_test/integration_test/TestCaseScript/UDPStress/UDPPacketLose.py b/components/idf_test/integration_test/TestCaseScript/UDPStress/UDPPacketLose.py new file mode 100644 index 0000000000..4cb0c1e8d8 --- /dev/null +++ b/components/idf_test/integration_test/TestCaseScript/UDPStress/UDPPacketLose.py @@ -0,0 +1,246 @@ +import os +import time +import random +import threading +import socket + +from TCAction import TCActionBase +from NativeLog import NativeLog + +AP_PROP_KEY = ("ssid", "password", "apc") + + +class SendThread(threading.Thread): + def __init__(self, sock, send_len, target_addr, delay): + threading.Thread.__init__(self) + self.sock = sock + self.send_len = send_len + self.target_addr = target_addr + self.delay = delay + self.count = 0 + self.exit_event = threading.Event() + pass + + def exit(self): + self.exit_event.set() + + def run(self): + data = "A" * self.send_len + if self.sock is None: + return + while True: + if self.exit_event.isSet() is True: + break + try: + self.sock.sendto(data, self.target_addr) + except StandardError: + break + self.count += 1 + time.sleep(self.delay * 0.001) + + def calculate(self): + return self.count + + +class RecvThread(threading.Thread): + def __init__(self, sock): + threading.Thread.__init__(self) + self.sock = sock + self.exit_event = threading.Event() + self.calc_event = threading.Event() + self.bytes_recv = 0 + + def start_calc(self): + self.calc_event.set() + + def stop_calc(self): + self.calc_event.clear() + self.exit_event.set() + + def run(self): + if self.sock is None: + return + ret = True + while ret: + if self.exit_event.isSet() is True: + break + try: + data, addr = self.sock.recvfrom(65535) + except StandardError: + break + if self.calc_event.isSet() is True: + self.bytes_recv += len(data) + if len(data) == 0: + start = time.time() + while time.time() - start < 30: + try: + data, addr = self.sock.recvfrom(65535) + except StandardError: + break + if len(data) == 0: + break + else: + self.bytes_recv += len(data) + else: + ret = False + + def get_bytes_recv(self): + return self.bytes_recv + + pass + + +class TestCase(TCActionBase.CommonTCActionBase): + def __init__(self, test_case, test_env, timeout=30, log_path=TCActionBase.LOG_PATH): + TCActionBase.CommonTCActionBase.__init__(self, test_case, test_env, timeout, log_path) + self.att_test_list = range(60) + # load param from excel + cmd_set = test_case["cmd set"] + for i in range(1, len(cmd_set)): + if cmd_set[i][0] != "dummy": + cmd_string = "self." + cmd_set[i][0] + exec cmd_string + self.result_cntx = TCActionBase.ResultCheckContext(self, test_env, self.tc_name) + pass + + def execute(self): + TCActionBase.TCActionBase.execute(self) + self.result_cntx.start() + + try: + # configurable params + send_len = self.send_len + pc_send = self.pc_send + target_send = self.target_send + test_time = self.test_time + delay = self.delay + ap_ssid = self.get_parameter("ap_ssid") + ap_password = self.get_parameter("ap_password") + pc_ip = self.get_parameter("pc_ip") + target_ip = self.get_parameter("target_ip") + # configurable params + except StandardError, e: + NativeLog.add_trace_critical("Error configuration for UDP script, error is %s" % e) + raise StandardError("Error configuration") + + udp_port = random.randint(40000, 50000) + + # reboot before executing + checker_stings = ["R SSC1 C ready!!!"] + test_action_string = ["SSC SSC1 reboot"] + fail_string = "Fail, Fail to reboot" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # disable recv print on target + checker_stings = ["R SSC1 C +RECVPRINT"] + test_action_string = ["SSC SSC1 soc -R -o 0"] + fail_string = "Fail, Fail to disable recv print" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # create socket on pc + udp_sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM) + udp_sock.bind((pc_ip, udp_port)) + udp_sock.settimeout(1) + + # connect to AP + checker_stings = ["R SSC1 C +JAP:CONNECTED"] + test_action_string = ["SSC SSC1 sta -C -s %s -p %s" % (ap_ssid, ap_password)] + fail_string = "Fail, Fail to JAP" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string, + check_freq=1, check_time=30) is False: + return + + # close all connection + checker_stings = ["R SSC1 C +CLOSEALL"] + test_action_string = ["SSC SSC1 soc -T"] + fail_string = "Fail, Fail to create server" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # create UDP socket on target + checker_stings = ["R SSC1 A :\+BIND:(\d+),OK"] + test_action_string = ["SSC SSC1 soc -B -t UDP -p %s" % udp_port] + fail_string = "Fail, Fail bind" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + send_thread = SendThread(udp_sock if pc_send is True else None, + send_len, (target_ip, udp_port), delay) + send_thread.start() + + recv_thread = RecvThread(udp_sock if target_send is True else None) + recv_thread.start() + + # start calculate + recv_thread.start_calc() + send_count = 0 + if target_send is True: + # do send from target + start = time.time() + while time.time() - start < test_time * 60: + checker_stings = ["P SSC1 RE \+SEND:0,OK"] + test_action_string = ["SSC SSC1 soc -S -s -l %s -n 1000 -i %s -p %s -j %s" % ( + send_len, pc_ip, udp_port, delay)] + fail_string = "Fail, Fail to send" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string, check_freq=0.1, + check_time=3000) is False: + return + send_count += 1000 + else: + time.sleep(test_time * 60) + + send_thread.exit() + send_thread.join() + + # stop throughput calculate + while True: + if recv_thread.isAlive() is False: + recv_thread.stop_calc() + recv_thread.join() + break + + recv_count = 0 + if pc_send is True: + # get received data len from PC + self.load_and_exe_one_step(["R SSC1 A :RECVLEN:(\d+)"], + ["SSC SSC1 soc -Q -s -o 1"], + "Fail, Fail to get recv data len") + try: + rx_data_len = int(self.get_parameter("recv_len")) + except StandardError: + rx_data_len = 0 + + if (rx_data_len % send_len) > 0: + recv_count = rx_data_len / send_len + 1 + else: + recv_count = rx_data_len / send_len + + send_count = send_thread.calculate() + + if recv_thread.get_bytes_recv() > 0: + if (recv_thread.get_bytes_recv() % send_len) > 0: + recv_count = recv_thread.get_bytes_recv() / send_len + 1 + else: + recv_count = recv_thread.get_bytes_recv() / send_len + udp_sock.close() + + NativeLog.add_trace_critical("send_count is %s, recv_count is %s" % (send_count, recv_count)) + self.result_cntx.set_result("Succeed") + NativeLog.add_trace_critical( + "UDP Packet lose rate is %.2f%%" % (float(send_count - recv_count) / send_count * 100)) + + # finally, execute done + + def result_check(self, port_name, data): + TCActionBase.CommonTCActionBase.result_check(self, port_name, data) + self.result_cntx.append_data(port_name, data) + + +def main(): + pass + + +if __name__ == '__main__': + main() diff --git a/components/idf_test/integration_test/TestCaseScript/UDPStress/UDPSTAMuitiSocketSendRecv.py b/components/idf_test/integration_test/TestCaseScript/UDPStress/UDPSTAMuitiSocketSendRecv.py new file mode 100644 index 0000000000..ca94572617 --- /dev/null +++ b/components/idf_test/integration_test/TestCaseScript/UDPStress/UDPSTAMuitiSocketSendRecv.py @@ -0,0 +1,163 @@ +from TCAction import TCActionBase +from NativeLog import NativeLog +import time +import random +import string + + +class TestCase(TCActionBase.CommonTCActionBase): + def __init__(self, test_case, test_env, timeout=45, log_path=TCActionBase.LOG_PATH): + TCActionBase.CommonTCActionBase.__init__(self, test_case, test_env, timeout=timeout, log_path=log_path) + # load param from excel + cmd_set = test_case["cmd set"] + for i in range(1, len(cmd_set)): + if cmd_set[i][0] != "dummy": + cmd_string = "self." + cmd_set[i][0] + exec cmd_string + self.result_cntx = TCActionBase.ResultCheckContext(self, test_env, self.tc_name) + pass + + def execute(self): + TCActionBase.TCActionBase.execute(self) + self.result_cntx.start() + + try: + # configurable params + send_len = self.send_len + test_time = self.test_time * 60 + server_echo = self.server_echo + conn_number = self.conn_number + sta_number = self.sta_number + send_delay = self.send_delay + # configurable params + except StandardError, e: + NativeLog.add_trace_critical("Error configuration for TCPTransparent script, error is %s" % e) + raise StandardError("Error configuration") + + # step0 reboot + for i in range(sta_number + 1): + checker_stings = ["P SSC%d C !!!ready!!!" % (i + 1)] + test_action_string = ["SSCC SSC%d reboot" % (i + 1)] + fail_string = "Fail, Fail to reboot" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # turn off recv print + for i in range(sta_number + 1): + checker_stings = ["P SSC%d C +RECVPRINT:0" % (i + 1)] + test_action_string = ["SSCC SSC%d soc -R -o 0" % (i + 1)] + fail_string = "Fail, Fail to turn off recv print" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # step1, set softAP mode on SSC1 + checker_stings = ["R SSC1 C +MODE:OK"] + test_action_string = ["SSCC SSC1 op -S -o 2"] + fail_string = "Fail, Fail to set mode on SSC1" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # step2, set STA mode on SSC2-SSCn + for i in range(sta_number): + checker_stings = ["R SSC%d C +MODE:OK" % (i + 2)] + test_action_string = ["SSCC SSC%d op -S -o 1" % (i + 2)] + fail_string = "Fail, Fail to set mode on SSC%d" % (i + 2) + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # step3, set ssid/password on SSC1 + ssid = "".join([random.choice(string.lowercase) for m in range(10)]) + password = "".join([random.choice(string.lowercase) for m in range(10)]) + + checker_stings = ["R SSC1 C +SAP:OK"] + test_action_string = ["SSCC SSC1 ap -S -s %s -p %s -n 10 -t 0 -m 8" % (ssid, password)] + fail_string = "Fail, Fail to set ssid/password on SSC1" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # step4, all STA join SSC1(soft AP) + for i in range(sta_number): + checker_stings = ["R SSC%d C +JAP:CONNECTED,%s" % (i + 2, ssid)] + test_action_string = ["SSCC SSC%d ap -C -s %s -p %s" % (i + 2, ssid, password)] + fail_string = "Fail, Fail to connect to SSC1" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string, check_time=450) is False: + return + + # step5, get all the STA ip + for i in range(sta_number): + checker_stings = ["R SSC%d A :\+STAIP:192.168.4.(\d+)" % (i + 2, i + 2)] + test_action_string = ["SSCC SSC%d ip -Q" % (i + 2)] + fail_string = "Fail, Fail to get SSC%d ip" % (i + 2) + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + else: + print "SSC%d ip is:" % (i + 2), self.get_parameter("SSC%d_IP" % (i + 2)) + + udp_port_list = [] + # step6, create multi UDP socket on all targets + for i in range(conn_number): + + udp_port = random.randint(10000, 20000) + udp_port_list.append(udp_port) + + checker_stings = ["R SSC2 A :\+BIND:(\d+),OK" % i] + test_action_string = ["SSCC SSC2 soc -B -t UDP -p %s" % udp_port] + fail_string = "Fail, SSC2 Fail to bind" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + checker_stings = ["R SSC3 A :\+BIND:(\d+),OK" % i] + test_action_string = ["SSCC SSC3 soc -B -t UDP -p %s" % (udp_port + 1)] + fail_string = "Fail, SSC3 Fail to bind" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + start_time = time.time() + # step7, do send/recv, SSC2<---->other STAs + while time.time() - start_time < test_time: + # drop off the delay time if it's greater than 20ms + if send_delay > 20: + send_delay -= 10 + + checker_stings = [] + test_action_string = [] + if server_echo is True: + + # SSC2 send packets to SSC3 + ssc3_ip = "192.168.4." + self.get_parameter("SSC3_IP") + for i in range(conn_number): + test_action_string.append("SSC SSC2 soc -S -s -i %s -p %s -l %d -n 1000 -j %d" % ( + i, ssc3_ip, udp_port_list[i] + 1, send_len, send_delay)) + checker_stings.append("P SSC2 RE \+SEND:\d+,OK NC CLOSED NC ERROR") + + # SSC3 send packets to SSC2 + ssc2_ip = "192.168.4." + self.get_parameter("SSC2_IP") + for i in range(conn_number): + test_action_string.append("SSC SSC3 soc -S -s -i %s -p %s -l %d -n 1000 -j %d" % ( + i, ssc2_ip, udp_port_list[i], send_len, send_delay)) + checker_stings.append("P SSC3 RE \+SEND:\d+,OK NC CLOSED NC ERROR") + + fail_string = "Fail, Failed to send/recv data" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string, + check_freq=1, check_time=300) is False: + break + + NativeLog.add_trace_critical("time escape: %s" % (time.time() - start_time)) + if (time.time() - start_time) >= test_time: + self.result_cntx.set_result("Succeed") + else: + self.result_cntx.set_result("Failed") + + # finally, execute done + + def result_check(self, port_name, data): + TCActionBase.CommonTCActionBase.result_check(self, port_name, data) + self.result_cntx.append_data(port_name, data) + + +def main(): + pass + + +if __name__ == '__main__': + main() diff --git a/components/idf_test/integration_test/TestCaseScript/UDPStress/UDPSendRecv.py b/components/idf_test/integration_test/TestCaseScript/UDPStress/UDPSendRecv.py new file mode 100755 index 0000000000..032062ad25 --- /dev/null +++ b/components/idf_test/integration_test/TestCaseScript/UDPStress/UDPSendRecv.py @@ -0,0 +1,133 @@ +from TCAction import TCActionBase +from NativeLog import NativeLog +import time +import random +import string + +TEST_COUNT_ONE_ROUND = 1000 + + +class TestCase(TCActionBase.CommonTCActionBase): + + def __init__(self, test_case, test_env, timeout=30, log_path=TCActionBase.LOG_PATH): + TCActionBase.CommonTCActionBase.__init__(self, test_case, test_env, timeout=timeout, log_path=log_path) + # load param from excel + cmd_set = test_case["cmd set"] + for i in range(1, len(cmd_set)): + if cmd_set[i][0] != "dummy": + cmd_string = "self." + cmd_set[i][0] + exec cmd_string + self.result_cntx = TCActionBase.ResultCheckContext(self, test_env, self.tc_name) + pass + + def cleanup(self): + # step 0 turn on recv print + checker_stings = ["R SSC1 C +RECVPRINT:1"] + test_action_string = ["SSC SSC1 soc -R -o 1"] + fail_string = "Fail, Fail to turn on recv print" + self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) + pass + + def execute(self): + TCActionBase.TCActionBase.execute(self) + self.result_cntx.start() + + try: + # configurable params + send_len = self.send_len + test_time = self.test_time * 60 + duplex = self.duplex + conn_num = self.conn_num + send_delay = self.send_delay + # configurable params + except StandardError, e: + NativeLog.add_trace_critical("Error configuration for UDPSendRecv script, error is %s" % e) + raise StandardError("Error configuration") + + ssid = "".join([random.choice(string.lowercase) for m in range(10)]) + password = "".join([random.choice(string.lowercase) for m in range(10)]) + + # step 0 set ap + checker_stings = ["R SSC1 C +SAP:OK"] + test_action_string = ["SSC SSC1 ap -S -s %s -p %s -t 3" % (ssid, password)] + fail_string = "Fail, Fail to set ap" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + # step 1 connect to ap and turn off recv print + checker_stings = ["R SSC2 C +JAP:CONNECTED"] + test_action_string = ["SSC SSC2 sta -C -s %s -p %s" % (ssid, password)] + fail_string = "Fail, Fail to connect to server" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string, check_time=200) is False: + return + + checker_stings = ["R SSC2 A :\+STAIP:(\d+\.\d+\.\d+\.\d+)\r"] + test_action_string = ["SSC SSC2 ip -Q -o 1"] + fail_string = "Fail, Fail to connect to server" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string, check_time=200) is False: + return + + checker_stings = ["P SSC1 C +RECVPRINT:0", "P SSC2 C +RECVPRINT:0"] + test_action_string = ["SSC SSC1 soc -R -o 0", "SSC SSC2 soc -R -o 0"] + fail_string = "Fail, Fail to turn off recv print" + self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string, check_time=200) is False: + return + + # step 2 create conn_num udp socket + for i in range(1, conn_num+1): + checker_stings = ["R SSC1 A :\+BIND:(\d+),OK" % i, + "R SSC2 A :\+BIND:(\d+),OK" % i] + test_action_string = ["SSC SSC1 soc -B -t UDP -p " % i, + "SSC SSC2 soc -B -t UDP -p " % i] + fail_string = "Fail, Fail to create socket" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + return + + start_time = time.time() + # step 3, do send/recv + while time.time()-start_time < test_time: + + checker_stings = ["P SSC1 NC ERROR NC CLOSE NC ERROR"] + for i in range(1, conn_num+1): + test_action_string = ["SSC SSC2 soc -S -s -l %d -n %d -j %d " + "-i -p " % + (i, send_len, TEST_COUNT_ONE_ROUND, send_delay, i)] + checker_stings.append("P SSC2 RE \"\+SEND:%%%%s,OK\"%%%%() NC ERROR NC CLOSE NC ERROR" % i) + if duplex is True: + test_action_string.append("SSC SSC1 soc -S -s -l %d -n %d -j %d" + " -i -p " % + (i, send_len, TEST_COUNT_ONE_ROUND, send_delay, i)) + checker_stings.append("P SSC1 RE \"\+SEND:%%%%s,OK\"%%%%()" % i) + + fail_string = "Fail, Failed on send command" + if self.load_and_exe_one_step([], test_action_string, fail_string) is False: + break + time.sleep(1) + + fail_string = "Fail, Failed to send/recv data" + if self.load_and_exe_one_step(checker_stings, ["DELAY 0.1"], fail_string, + check_freq=1, check_time=300) is False: + break + pass + + NativeLog.add_prompt_trace("time escape: %s" % (time.time() - start_time)) + if time.time() - start_time >= test_time: + self.result_cntx.set_result("Succeed") + else: + self.result_cntx.set_result("Fail") + + # finally, execute done + + def result_check(self, port_name, data): + TCActionBase.CommonTCActionBase.result_check(self, port_name, data) + self.result_cntx.append_data(port_name, data) + + +def main(): + pass + +if __name__ == '__main__': + main() + + diff --git a/components/idf_test/integration_test/TestCaseScript/UDPStress/UDPThroughput.py b/components/idf_test/integration_test/TestCaseScript/UDPStress/UDPThroughput.py new file mode 100755 index 0000000000..861f6010d8 --- /dev/null +++ b/components/idf_test/integration_test/TestCaseScript/UDPStress/UDPThroughput.py @@ -0,0 +1,321 @@ +import os +import time +import random +import threading +import socket + +from TCAction import TCActionBase +from NativeLog import NativeLog +from NativeLog import ThroughputResult +from Utility import RSSICalibrator +from Utility import MakeFolder + + +LOG_FOLDER = os.path.join("Performance", "Throughput") + + +AP_PROP_KEY = ("ssid", "password", "apc") + + +class SendThread(threading.Thread): + def __init__(self, sock, send_len, target_addr): + threading.Thread.__init__(self) + self.setDaemon(True) + self.sock = sock + self.send_len = send_len + self.target_addr = target_addr + self.exit_event = threading.Event() + pass + + def exit(self): + self.exit_event.set() + + def run(self): + data = "A" * self.send_len + if self.sock is None: + return + while True: + if self.exit_event.isSet() is True: + break + try: + self.sock.sendto(data, self.target_addr) + except StandardError: + break + pass + + +class RecvThread(threading.Thread): + def __init__(self, sock): + threading.Thread.__init__(self) + self.setDaemon(True) + self.sock = sock + self.exit_event = threading.Event() + self.calc_event = threading.Event() + self.bytes_recv = 0 + + def start_calc(self): + self.calc_event.set() + + def stop_calc(self): + self.calc_event.clear() + self.exit_event.set() + + def run(self): + if self.sock is None: + return + while True: + if self.exit_event.isSet() is True: + break + try: + data, addr = self.sock.recvfrom(65535) + except StandardError: + break + if self.calc_event.isSet() is True: + self.bytes_recv += len(data) + + def get_bytes_recv(self): + return self.bytes_recv + pass + + +class TestCase(TCActionBase.CommonTCActionBase): + + def __init__(self, test_case, test_env, timeout=30, log_path=TCActionBase.LOG_PATH): + TCActionBase.CommonTCActionBase.__init__(self, test_case, test_env, timeout=timeout, log_path=log_path) + self.performance_folder_path = log_path + self.att_test_list = range(60) + # load param from excel + cmd_set = test_case["cmd set"] + for i in range(1, len(cmd_set)): + if cmd_set[i][0] != "dummy": + cmd_string = "self." + cmd_set[i][0] + exec cmd_string + self.result_cntx = TCActionBase.ResultCheckContext(self, test_env, self.tc_name) + pass + + def execute(self): + TCActionBase.TCActionBase.execute(self) + self.result_cntx.start() + + try: + # configurable params + ap_list = self.get_parameter("shield_box_ap_list") + pc_ip = self.get_parameter("pc_ip") + send_len = self.send_len + att_test_list = self.att_test_list + tx_enable = self.tx_enable + rx_enable = self.rx_enable + measure_period = self.measure_period + # configurable params + except StandardError, e: + NativeLog.add_trace_critical("Error configuration for TCPThroughput script, error is %s" % e) + raise StandardError("Error configuration") + + udp_port = random.randint(40000, 50000) + + # init throughput result data + test_item = "" + if tx_enable is True: + test_item += "Tx" + if rx_enable is True: + test_item += "Rx" + if test_item == "": + raise StandardError("no throughput test item") + + folder_path = os.path.join(self.performance_folder_path, LOG_FOLDER) + file_name = os.path.join(folder_path, + "UDPThroughput_%s_%s" % (test_item, time.strftime("%d%H%M%S", time.localtime()))) + + result = ThroughputResult.ThroughputResult(file_name) + + # restart before executing throughput + checker_stings = ["R SSC1 C !!!ready!!!"] + test_action_string = ["SSC SSC1 reboot"] + fail_string = "Fail, Fail to reboot" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + self.result_cntx.set_result("Fail") + return + + # disable recv print during throughput test + checker_stings = ["R SSC1 C +RECVPRINT"] + test_action_string = ["SSC SSC1 soc -R -o 0"] + fail_string = "Fail, Fail to disable recv print" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + self.result_cntx.set_result("Fail") + return + + ret = True + for ap_prop in ap_list: + ssid = ap_prop[0] + password = ap_prop[1] + apc = ap_prop[2] + if ap_prop[1] == "": + # set a default string for open ap + password = "1" + + # switch off all outlet, switch on AP outlet + outlet_config_dict = dict.fromkeys(range(1, 9), "OFF") + outlet_config_dict[apc] = "ON" + apc_cmd = "APC " + for outlet in outlet_config_dict: + apc_cmd += " %s %s" % (outlet_config_dict[outlet], outlet) + checker_stings = ["P PC_COM L OK"] + fail_string = "Fail, Fail to switch apc" + if self.load_and_exe_one_step(checker_stings, [apc_cmd], fail_string) is False: + ret = False + break + + # wait AP ready + time.sleep(20) + + # create server + udp_sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM) + udp_sock.bind((pc_ip, udp_port)) + udp_sock.settimeout(1) + + if tx_enable is True: + result.add_test_item(ssid + "_tx") + if rx_enable is True: + result.add_test_item(ssid + "_rx") + + # create RSSI Calibrator + calibrator = RSSICalibrator.Calibrator() + + for att_value in att_test_list: + # step 0 set att value + checker_stings = ["R PC_COM L OK"] + test_action_string = ["ATT %s" % att_value] + fail_string = "Fail, Fail to set att value" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + ret = False + break + # continue + # step 1 get AP RSSI + checker_stings = ["R SSC1 A :\+SCAN:%s,[:\d\w]+,\d+,\d+,([-\d]+)\r" % ssid] + test_action_string = ["SSC SSC1 sta -S -s %s" % ssid] + fail_string = "Fail, Fail to scan" + rssi = scan_count = 0 + for i in range(3): + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + continue + rssi += int(self.test_env.get_variable_by_name("rssi")[1]) + scan_count += 1 + + rssi = calibrator.calibrate_rssi(float(rssi)/scan_count if scan_count > 0 else 0, att_value) + # step 2 connect to AP + checker_stings = ["R SSC1 C +JAP:CONNECTED"] + test_action_string = ["SSC SSC1 sta -C -s %s -p %s" % (ssid, password)] + fail_string = "Fail, Fail to JAP" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string, + check_freq=1, check_time=30) is False: + if rssi < -89: + continue + else: + ret = False + break + # continue + checker_stings = ["R SSC1 A :STAIP:(\d+\.\d+\.\d+\.\d+)"] + test_action_string = ["SSC SSC1 ip -Q"] + fail_string = "Fail, Fail to get ip" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string, + check_freq=1, check_time=30) is False: + if rssi < -89: + continue + else: + ret = False + break + # continue + target_ip = self.get_parameter("target_ip") + + # step 3 close all connections + checker_stings = ["R SSC1 C +CLOSEALL"] + test_action_string = ["SSC SSC1 soc -T"] + fail_string = "Fail, Fail to close socket" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + ret = False + break + # continue + + # step 4 create UDP socket + checker_stings = ["R SSC1 A :\+BIND:(\d+),OK"] + test_action_string = ["SSC SSC1 soc -B -t UDP -i %s -p %s" % (target_ip, udp_port)] + fail_string = "Fail, Fail to bind" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + ret = False + break + # continue + + # step 5 do throughput test + send_thread = SendThread(udp_sock if rx_enable is True else None, + send_len, (target_ip, udp_port)) + send_thread.start() + + recv_thread = RecvThread(udp_sock if tx_enable is True else None) + recv_thread.start() + + if tx_enable is True: + # do send from target + test_action_string = ["SSC SSC1 soc -S -s -l %s -n 10000000 -i %s -p %s" + % (send_len, pc_ip, udp_port)] + fail_string = "Fail, Fail to send" + if self.load_and_exe_one_step([], test_action_string, fail_string) is False: + pass + + # start throughput calculate + recv_thread.start_calc() + + # sleep for measure period + time.sleep(measure_period) + + # stop throughput calculate + recv_thread.stop_calc() + send_thread.exit() + + send_thread.join() + recv_thread.join() + + # output throughput result + # in Mbps + if rx_enable is True: + # get received data len from PC + self.load_and_exe_one_step(["R SSC1 A :RECVLEN:(\d+)"], + ["SSC SSC1 soc -Q -s -o 1"], + "Fail, Fail to get recv data len") + try: + rx_data_len = int(self.get_parameter("recv_len")) + except StandardError: + rx_data_len = 0 + + result.log_throughput(ssid + "_rx", rssi, att_value, + float(rx_data_len * 8) / (measure_period * 1000000)) + + if recv_thread.get_bytes_recv() > 0: + result.log_throughput(ssid + "_tx", rssi, att_value, + float(recv_thread.get_bytes_recv() * 8) / (measure_period * 1000000)) + + result.output_to_file() + pass + + udp_sock.close() + if not ret: + NativeLog.add_trace_critical("Test SUC for %s" % ssid) + elif ret: + NativeLog.add_trace_critical("Test FAIL for %s!!!" % ssid) + + if ret: + self.result_cntx.set_result("Succeed") + else: + self.result_cntx.set_result("Fail") + + # finally, execute done + + def result_check(self, port_name, data): + TCActionBase.CommonTCActionBase.result_check(self, port_name, data) + self.result_cntx.append_data(port_name, data) + + +def main(): + pass + +if __name__ == '__main__': + main() diff --git a/components/idf_test/integration_test/TestCaseScript/UDPStress/__init__.py b/components/idf_test/integration_test/TestCaseScript/UDPStress/__init__.py new file mode 100755 index 0000000000..d29ee405a4 --- /dev/null +++ b/components/idf_test/integration_test/TestCaseScript/UDPStress/__init__.py @@ -0,0 +1 @@ +__all__ = ["UDPSendRecv", ] diff --git a/components/idf_test/integration_test/TestCaseScript/WiFiStress/SoftAPNSTA.py b/components/idf_test/integration_test/TestCaseScript/WiFiStress/SoftAPNSTA.py new file mode 100755 index 0000000000..b8eb75a589 --- /dev/null +++ b/components/idf_test/integration_test/TestCaseScript/WiFiStress/SoftAPNSTA.py @@ -0,0 +1,178 @@ +import random +import time +import string +import threading + +from TCAction import TCActionBase +from NativeLog import NativeLog +from TCAction import PerformanceTCBase +from Utility import Encoding + + +class STAJAPThread(threading.Thread): + def __init__(self, test_action, port_name, ssid, password, delay1, delay2, change_mac): + threading.Thread.__init__(self) + self.setDaemon(True) + self.test_action = test_action + self.port_name = port_name + self.ssid = ssid + self.password = password + self.delay1 = delay1 + self.delay2 = delay2 + self.change_mac = change_mac + self.exit_flag = threading.Event() + pass + + def exit(self): + self.exit_flag.set() + pass + + def run(self): + total_test_count = 0 + fail_count = 0 + while self.exit_flag.isSet() is False: + # change mac + if self.change_mac is True: + mac = Encoding.generate_random_mac() + self.test_action.serial_write_line(self.port_name, "mac -S -o 1 -m %s" % mac) + self.test_action.check_response(self.port_name, "+MAC:STA,OK") + + time.sleep(1) + + # JAP + total_test_count += 1 + # flush current port data + self.test_action.flush_data(self.port_name) + self.test_action.serial_write_line(self.port_name, "sta -C -s %s -p %s" % (self.ssid, self.password)) + if self.test_action.check_response(self.port_name, "+JAP:CONNECTED", 45) is False: + fail_count += 1 + NativeLog.add_trace_critical("[%s] Failed to JAP, Failed/Total : %d/%d" + % (self.port_name, fail_count, total_test_count)) + continue + time.sleep(random.randint(self.delay1[0], self.delay1[1])) + + # QAP + self.test_action.serial_write_line(self.port_name, "sta -D") + if self.test_action.check_response(self.port_name, "+QAP:OK", 5) is False: + NativeLog.add_trace_critical("[%s] Failed to QAP" % self.port_name) + time.sleep(random.randint(self.delay2[0], self.delay2[1])) + + # make sure quit AP + self.test_action.serial_write_line(self.port_name, "sta -D") + pass + pass + + +class TestCase(PerformanceTCBase.PerformanceTCBase): + + def __init__(self, test_case, test_env, timeout=30, log_path=TCActionBase.LOG_PATH): + PerformanceTCBase.PerformanceTCBase.__init__(self, test_case, test_env, timeout=timeout, log_path=log_path) + self.sta_num = 0 + self.max_sta = 4 + self.test_time = 60 + self.delay1 = [5, 30] + self.delay2 = [5, 10] + self.change_mac = True + self.channel = 11 + # load param from excel + cmd_set = test_case["cmd set"] + for i in range(1, len(cmd_set)): + if cmd_set[i][0] != "dummy" and cmd_set[i][0] != "": + cmd_string = "self." + cmd_set[i][0] + exec cmd_string + pass + + def process(self): + # configurable parameters + try: + sta_num = self.sta_num + max_sta = self.max_sta + test_time = self.test_time + # delay between JAP succeed and QAP + delay1 = self.delay1 + # delay between QAP and JAP + delay2 = self.delay2 + # if change mac each time before JAP + change_mac = self.change_mac + # channel + channel = self.channel + except StandardError, e: + raise StandardError("miss mandatory parameters") + + # step 0, set op mode and enable dhcp + self.serial_write_line("SSC1", "op -S -o 2") + if self.check_response("SSC1", "+MODE:OK", 2) is False: + NativeLog.add_trace_critical("Failed to set ap mode") + return + self.serial_write_line("SSC1", "dhcp -E -o 2") + if self.check_response("SSC1", "+DHCP:AP,OK", 2) is False: + NativeLog.add_trace_critical("Failed to enable ap dhcp") + return + self.serial_write_line("SSC1", "dhcp -L -s 192.168.4.2 -e 192.168.4.100 -t 1") + if self.check_response("SSC1", "+DHCP:LEASE,OK", 2) is False: + NativeLog.add_trace_critical("Failed to enable ap dhcp") + return + self.serial_write_line("SSC1", "dhcp -S -o 2") + if self.check_response("SSC1", "+DHCP:AP,OK", 2) is False: + NativeLog.add_trace_critical("Failed to enable ap dhcp") + return + + for i in range(sta_num): + self.serial_write_line("SSC%d" % (i+2), "op -S -o 1") + if self.check_response("SSC%d" % (i+2), "+MODE:OK", 2) is False: + NativeLog.add_trace_critical("Failed to set sta mode") + return + self.serial_write_line("SSC%d" % (i+2), "dhcp -S -o 1") + if self.check_response("SSC%d" % (i+2), "+DHCP:STA,OK", 2) is False: + NativeLog.add_trace_critical("Failed to enable sta dhcp") + + # step 1, set ap config and load + ap_ssid = "".join([random.choice(string.uppercase) for m in range(15)]) + ap_password = "".join([random.choice(string.lowercase) for m in range(15)]) + + self.serial_write_line("SSC1", "ap -S -s %s -p %s -t 3 -m %s -n %s" + % (ap_ssid, ap_password, max_sta, channel)) + if self.check_response("SSC1", "+SAP:OK", 2) is False: + NativeLog.add_trace_critical("Failed to set AP") + return + + # step 2, start thread to let STA JAP + sta_thread_list = [] + for i in range(sta_num): + sta_thread_list.append(STAJAPThread(self, "SSC%d" % (i+2), + ap_ssid, ap_password, delay1, delay2, change_mac)) + for sta_thread in sta_thread_list: + sta_thread.start() + + # step 3, sleep for test time + for i in range(test_time): + self.flush_data("SSC1") + time.sleep(60) + + # step 4, close all thread, will disconnect when exit thread + for sta_thread in sta_thread_list: + sta_thread.exit() + for sta_thread in sta_thread_list: + sta_thread.join() + # wait and make sure disconnect done + time.sleep(1) + + # step 5, join AP and check + sta_num_temp = max_sta if sta_num > max_sta else sta_num + + for i in range(sta_num_temp): + self.serial_write_line("SSC%d" % (i+2), "sta -C -s %s -p %s" % (ap_ssid, ap_password)) + if self.check_response("SSC%d" % (i+2), "+JAP:CONNECTED", 45) is False: + self.set_result("Fail") + break + pass + else: + self.set_result("Succeed") + + +def main(): + pass + +if __name__ == '__main__': + main() + diff --git a/components/idf_test/integration_test/TestCaseScript/WiFiStress/WifiConnUtility.py b/components/idf_test/integration_test/TestCaseScript/WiFiStress/WifiConnUtility.py new file mode 100755 index 0000000000..24702bfc8d --- /dev/null +++ b/components/idf_test/integration_test/TestCaseScript/WiFiStress/WifiConnUtility.py @@ -0,0 +1,240 @@ +from NativeLog import NativeLog +import time +import random + + +ERROR_AP_PROP = {"ssid": "123456789012345678901234567890", + "ssid_len": 30, + "pwd": "12345678901234567890", + "pwd_len": 20, + "channel": 10, + "enc": 3, + "apc": 9, # invalid apc count + } + + +class WifiConnUtilError(StandardError): + pass + + +class WifiConnUtility(object): + + def __init__(self, tc_action): + self.tc_action = tc_action + self.target_type = tc_action.target_type + pass + + def set_mode(self, mode): + ret = True + fail_string = "set mode fail" + cmd = [] + checker_stings = [] + for i in range(2): + if self.target_type[0] == "SSC": + cmd.append("SSCC SSC%d op -S -o %d" % (i+1, mode[i])) + checker_stings.append("SSCP SSC%d C +MODE:OK" % (i+1)) + pass + else: + cmd.append("ATC AT%d CWMODE %d" % (i+1, mode[i])) + checker_stings.append("ATP AT%d L OK" % (i+1)) + pass + if self.tc_action.load_and_exe_one_step(checker_stings, cmd, + fail_string, check_time=50) is False: + NativeLog.add_trace_critical("Failed to set mode") + ret = False + return ret + pass + + def _apc_switch(self, outlet_list, action_list): + checker_stings = ["R PC_COM C OK"] + switch_cmd = "APC " + fail_string = "Error when switching APC" + ret = True + + for [_outlet, _action] in zip(action_list, outlet_list): + switch_cmd += " %s %d" % (_action, _outlet) + + if self.tc_action.load_and_exe_one_step(checker_stings, [switch_cmd], + fail_string, check_time=50) is False: + NativeLog.add_trace_critical("Error when switching APC") + ret = False + return ret + pass + + def _set_target_ap(self, ap_prop): + ret = True + fail_string = "set target ap fail, %s, %s" % (ap_prop["ssid"], ap_prop["pwd"]) + if self.target_type[1] == "SSC": + if ap_prop["pwd"] == "": + cmd = ["SSCC SSC2 ap -S -s %s -t %d" % (ap_prop["ssid"], + ap_prop["enc"]) + ] + else: + cmd = ["SSCC SSC2 ap -S -s %s -p %s -t %d" % (ap_prop["ssid"], + ap_prop["pwd"], + ap_prop["enc"]) + ] + checker_stings = ["SSCP SSC2 C +SAP:OK"] + pass + else: + cmd = ["ATC AT2 CWSAP \"%s\" \"%s\" %d %d" % (ap_prop["ssid"], + ap_prop["pwd"], + ap_prop["channel"], + ap_prop["enc"]) + ] + checker_stings = ["ATR AT2 L OK"] + pass + if self.tc_action.load_and_exe_one_step(checker_stings, cmd, + fail_string, check_time=50) is False: + NativeLog.add_trace_critical("set target ap fail") + ret = False + return ret + pass + + def setup_ap(self, ap_type, ap_prop): + if ap_type == "target": + ret = self._set_target_ap(ap_prop) + pass + else: + ret = self._apc_switch(["ON"], [ap_prop["apc"]]) + # delay for 5 seconds, wait AP ready + time.sleep(5) + pass + return ret + + def do_scan(self, ap_prop): + fail_string = "Scan fail" + ret = True + # do not check if the set AP can be scanned + if self.target_type[1] == "SSC": + cmd = ["SSCC SSC1 sta -S"] + checker_stings = ["SSCR SSC1 C +SCANDONE"] + pass + else: + cmd = ["ATS AT1 AT+CWLAP"] + checker_stings = ["ATR AT1 L OK"] + pass + if self.tc_action.load_and_exe_one_step(checker_stings, cmd, + fail_string, check_time=100) is False: + NativeLog.add_trace_critical("Scan fail") + ret = False + return ret + pass + + def _switch_off_target_ap(self, delay): + time.sleep(delay) + self._set_target_ap(ERROR_AP_PROP) + pass + + def _switch_on_target_ap(self, ap_prop, delay): + time.sleep(delay) + self._set_target_ap(ap_prop) + pass + + def _switch_off_ap(self, ap_type, ap_prop, delay_range): + delay = random.randint(delay_range[0]*10, delay_range[1]*10)/10 + if ap_type == "target": + self._switch_off_target_ap(delay) + else: + delay -= 1.5 + time.sleep(delay if delay > 0 else 0) + self._apc_switch(["OFF"], [ap_prop["apc"]]) + pass + + def _switch_on_ap(self, ap_type, ap_prop, delay_range): + delay = random.randint(delay_range[0]*10, delay_range[1]*10)/10 + if ap_type == "target": + self._switch_on_target_ap(ap_prop, delay) + else: + delay -= 1.5 + time.sleep(delay if delay > 0 else 0) + self._apc_switch(["ON"], [ap_prop["apc"]]) + pass + + def _join_ap(self, ap_prop, test_method): + fail_string = "join target ap fail, %s, %s" % (ap_prop["ssid"], ap_prop["pwd"]) + if self.target_type[1] == "SSC": + cmd = ["SSCC SSC1 ap -C -s %s -p %s" % (ap_prop["ssid"], + ap_prop["pwd"],) + ] + checker_stings = ["SSCR SSC1 C +JAP:CONNECTED"] + pass + else: + cmd = ["ATC AT1 CWJAP \"%s\" \"%s\"" % (ap_prop["ssid"], + ap_prop["pwd"]) + ] + checker_stings = ["ATR AT1 NC ERROR NC FAIL L OK"] + pass + if test_method == "Normal": + ret = self.tc_action.load_and_exe_one_step(checker_stings, cmd, + fail_string, check_freq=0.1, check_time=350) + if ret is not False: + ret *= 0.1 + else: + ret = self.tc_action.load_and_exe_one_step([], cmd, fail_string) + return ret + pass + + def _check_join_ap_result(self, ap_prop): + ret = False + fail_string = "join ap fail, %s, %s" % (ap_prop["ssid"], ap_prop["pwd"]) + + if self.target_type[1] == "SSC": + checker_stings = ["SSCR SSC1 C +JAP:CONNECTED"] + ret = self.tc_action.load_and_exe_one_step(checker_stings, ["DELAY 0"], + fail_string, check_freq=1, check_time=120) + pass + else: + cmd = ["ATS AT1 AT+CWJAP?"] + checker_stings = ["ATR AT1 NC busy NC No%20AP C +CWJAP"] + for i in range(3): + ret = self.tc_action.load_and_exe_one_step(checker_stings, cmd, + fail_string, check_freq=1, check_time=2) + if ret is not False: + break + time.sleep(15) + + return ret + pass + + def join_ap(self, join_test_method, ap_type, ap_prop, delay): + + if join_test_method == "WRONG_PROP": + _prop = ERROR_AP_PROP + else: + _prop = ap_prop + + ret = self._join_ap(_prop, join_test_method) + + if join_test_method == "OFF_ON": + self._switch_off_ap(ap_type, ap_prop, delay[0]) + self._switch_on_ap(ap_type, ap_prop, delay[1]) + ret = self._check_join_ap_result(_prop) + pass + elif join_test_method == "OFF": + self._switch_off_ap(ap_type, ap_prop, delay[0]) + time.sleep(25) + pass + + return ret + pass + + def do_reconnect(self, reconnect_test_method, ap_type, ap_prop, delay): + ret = True + if reconnect_test_method == "OFF_ON": + self._switch_off_ap(ap_type, ap_prop, delay[0]) + self._switch_on_ap(ap_type, ap_prop, delay[1]) + ret = self._check_join_ap_result(ap_prop) + pass + elif reconnect_test_method == "OFF": + self._switch_off_ap(ap_type, ap_prop, delay[0]) + pass + return ret + pass + + +def main(): + pass + +if __name__ == '__main__': + main() diff --git a/components/idf_test/integration_test/TestCaseScript/WiFiStress/WifiJAP.py b/components/idf_test/integration_test/TestCaseScript/WiFiStress/WifiJAP.py new file mode 100755 index 0000000000..64346be17f --- /dev/null +++ b/components/idf_test/integration_test/TestCaseScript/WiFiStress/WifiJAP.py @@ -0,0 +1,219 @@ +import os +import random +import time + +import WifiConnUtility +from NativeLog import NativeLog +from TCAction import TCActionBase +from Utility import Encoding +from Utility import MakeFolder + +STEPS = {"SCAN1": 0x01, "JAP": 0x02, "SCAN2": 0x04, "RECONNECT": 0x08} + +AP_PROP = ("ssid", "ssid_len", "pwd", + "pwd_len", "channel", "enc", "apc") + +JAP_TEST_METHOD = ("Normal", "OFF_ON", "OFF", "WRONG_PROP") + +RECONNECT_TEST_METHOD = ("OFF_ON", "OFF") + +LOG_FOLDER = os.path.join("Performance", "JAP") + + +SSID_LEN_RANGE = (1, 32) # in bytes +ENC_TYPE = (0, 2, 3, 4) # do not support WEP for 8266 soft AP +PWD_RANGE = {0: [0, 0], + 1: [5, 5], + 2: [8, 63], + 3: [8, 63], + 4: [8, 63], + } + + +class TestCase(TCActionBase.CommonTCActionBase): + + def __init__(self, test_case, test_env, timeout=30, log_path=TCActionBase.LOG_PATH): + TCActionBase.CommonTCActionBase.__init__(self, test_case, test_env, timeout=timeout, log_path=log_path) + # default value for optional configurable params + self.performance_folder_path = log_path + self.pwd_len = [8, 64] + self.step_config = [0x03, 0x01, 0x02, 0x0B, 0x0F] + self.join_test_method = ["Normal"] + self.join_delay = [[1.5, 5], [1.5, 5]] + self.reconnect_test_method = ["OFF_ON"] + self.reconnect_delay = [[1.5, 5], [1.5, 6]] + # load param from excel + cmd_set = test_case["cmd set"] + for i in range(1, len(cmd_set)): + if cmd_set[i][0] != "dummy" and cmd_set[i][0] != "": + cmd_string = "self." + cmd_set[i][0] + exec cmd_string + # read AP list + self.ap_list = [] + for i in range(1, len(cmd_set)): + for j in range(len(cmd_set[i][1])): + if cmd_set[i][1][j] != "": + cmd_string = "self.ap_list.append(dict(zip(AP_PROP, " + cmd_set[i][1][j] + ")))" + exec cmd_string + + folder_path = MakeFolder.make_folder(self.performance_folder_path + "\\" + LOG_FOLDER) + file_name = "JAP_log_%s.log" % (time.strftime("%m%d%H%M%S", time.localtime())) + self._performance_log_file = os.path.join(folder_path, file_name) + + # test statistics + self._succeed_count = self._fail_count = self._time_cost_count = 0 + self._total_time = self._longest_time = 0 + + self.result_cntx = TCActionBase.ResultCheckContext(self, test_env, self.tc_name) + # get target type "SSC" or "AT" + self.target_type = ["SSC" if test_env.get_port_by_name("AT1") is None else "AT"] + self.target_type.append("SSC" if test_env.get_port_by_name("AT2") is None else "AT") + self._utility = WifiConnUtility.WifiConnUtility(self) + pass + + def _generate_random_ap_prop(self): + ap_prop = dict.fromkeys(AP_PROP) + # generate target ap_value + ap_prop["ssid_len"] = random.randint(SSID_LEN_RANGE[0], SSID_LEN_RANGE[1]) + ap_prop["channel"] = random.choice(range(1, 14)) + ap_prop["enc"] = random.choice(ENC_TYPE) + ap_prop["pwd_len"] = random.randint(PWD_RANGE[ap_prop["enc"]][0], PWD_RANGE[ap_prop["enc"]][1]) + # generate string + if self.target_type[0] == self.target_type[1] == "AT": + ap_prop["ssid"] = Encoding.generate_random_utf8_str(ap_prop["ssid_len"]) + ap_prop["pwd"] = Encoding.generate_random_utf8_str(ap_prop["pwd_len"]) + # NativeLog.add_trace_info("ssid hex is : %x" % ap_prop["ssid"]) + # NativeLog.add_trace_info("pwd hex is : %x" % ap_prop["pwd"]) + else: + ap_prop["ssid"] = Encoding.generate_random_printable_str(ap_prop["ssid_len"]) + ap_prop["pwd"] = Encoding.generate_random_printable_str(ap_prop["pwd_len"]) + + return ap_prop + + def _logging_performance(self, ssid, join_method="Normal", time_cost=0): + # log performance to performance log file + with open(self._performance_log_file, "ab+") as f: + # log time and ssid + f.write("\r\n[%s]:\r\n[AP name] %s\r\n" % + (time.strftime("%m-%d %H:%M:%S", time.localtime()), ssid)) + if join_method == "Normal" or join_method == "OFF_ON": + if time_cost is not False: + self._succeed_count += 1 + if join_method == "Normal": + f.write("[Succeed][%f]\r\n" % time_cost) + self._longest_time = (time_cost > self._longest_time and + [time_cost] or [self._longest_time])[0] + self._time_cost_count += 1 + self._total_time += time_cost + else: + f.write("[Succeed][%s]\r\n" % join_method) + else: + self._fail_count += 1 + f.write("[Fail][%s]\r\n" % join_method) + pass + + def _logging_fail_step(self, ssid, step): + with open(self._performance_log_file, "ab+") as f: + f.write("\r\n[%s]:\r\n[AP name] %s\r\n" % + (time.strftime("%m-%d %H:%M:%S", time.localtime()), ssid)) + f.write("[Fail][%s]\r\n" % step) + pass + + def _generate_performance_report(self): + with open(self._performance_log_file, "ab+") as f: + f.write("[Test report] Succeed: %d\r\n" % self._succeed_count) + f.write("[Test report] Failed: %d\r\n" % self._fail_count) + if self._succeed_count > 0 or self._fail_count > 0: + f.write("[Test report] Pass Rate: %f\r\n" % + (self._succeed_count/(self._fail_count+self._succeed_count))) + if self._time_cost_count > 0: + f.write("[Test report] Average time: %f\r\n" % (self._total_time/self._time_cost_count)) + f.write("[Test report] Longest time: %f\r\n" % self._longest_time) + + def execute(self): + TCActionBase.TCActionBase.execute(self) + self.result_cntx.start() + + # mandatory configurable params + try: + target_ap_num = self.target_ap_num + test_count = self.test_count + except StandardError, e: + NativeLog.add_trace_critical("Error configuration for WifiJAP script, error is %s" % e) + raise StandardError("Error configuration") + + # prepare ap list + _ap_list = [["target", None]] * target_ap_num + for _ap_prop in self.ap_list: + _ap_list.append(["AP", _ap_prop]) + + # set to correct mode first + self._utility.set_mode([1, 2]) + + for i in xrange(test_count): + _ap = random.choice(_ap_list) + # arrange ap + _ap_type = _ap[0] + _ap_prop = _ap[1] + if _ap_type == "target": + _ap_prop = self._generate_random_ap_prop() + pass + + # step 1 : mandatory step, set up AP + if self._utility.setup_ap(_ap_type, _ap_prop) is False: + self._logging_fail_step(_ap_prop["ssid"], "Set AP") + NativeLog.add_prompt_trace("[Step1] setup AP Fail") + continue + step_config = random.choice(self.step_config) + NativeLog.add_prompt_trace("[Step1] setup AP succeed") + + # step 2 : optional step, do scan before connect + if step_config & STEPS["SCAN1"] != 0: # check option + if self._utility.do_scan(_ap_prop) is False: + self._logging_fail_step(_ap_prop["ssid"], "Scan before JAP") + NativeLog.add_prompt_trace("[Step2] Scan Done") + + # step 3 : mandatory step, join AP + if step_config & STEPS["JAP"] != 0: # check option + _join_test_method = random.choice(self.join_test_method) + time_cost = self._utility.join_ap(_join_test_method, _ap_type, _ap_prop, self.join_delay) + # log performance to performance log file + self._logging_performance(_ap_prop["ssid"], _join_test_method, time_cost) + if time_cost is False: + # do scan once to check if AP exist + self._utility.do_scan(_ap_prop) + continue + NativeLog.add_prompt_trace("[Step3] Join AP done") + + # step 4 : optional step, scan after join AP + if step_config & STEPS["SCAN2"] != 0: # check option + if self._utility.do_scan(_ap_prop) is False: + self._logging_fail_step(_ap_prop["ssid"], "Scan after JAP") + NativeLog.add_prompt_trace("[Step4] Scan done") + + # step 5 : optional step, reconnect test + if step_config & STEPS["RECONNECT"] != 0: # check option + _reconnect_test_method = random.choice(self.reconnect_test_method) + if self._utility.do_reconnect(_reconnect_test_method, + _ap_type, _ap_prop, self.reconnect_delay) is False: + self._logging_fail_step(_ap_prop["ssid"], "Reconnect") + + NativeLog.add_prompt_trace("[Step5] Reconnect done") + # continue to next loop + NativeLog.add_prompt_trace("[WifiJAP] Test count %d done" % i) + + # generate report and cleanup + self._generate_performance_report() + + self.result_cntx.set_result("Succeed") + + def result_check(self, port_name, data): + TCActionBase.CommonTCActionBase.result_check(self, port_name, data) + self.result_cntx.append_data(port_name, data) + + +def main(): + pass + +if __name__ == '__main__': + main() diff --git a/components/idf_test/integration_test/TestCaseScript/WiFiStress/WifiJAPAtt.py b/components/idf_test/integration_test/TestCaseScript/WiFiStress/WifiJAPAtt.py new file mode 100755 index 0000000000..23eeba3f0e --- /dev/null +++ b/components/idf_test/integration_test/TestCaseScript/WiFiStress/WifiJAPAtt.py @@ -0,0 +1,193 @@ +import os +import time +from TCAction import TCActionBase +from NativeLog import NativeLog +from Utility import RSSICalibrator +from Utility import MakeFolder + +MAX_RSSI = 0 +MIN_RSSI = -110 +MAX_ATT = 60 +LOG_FOLDER = os.path.join("Performance", "JAP") +AP_PROP_KEY = ("ssid", "password", "apc") + + +class Performance(object): + RSSI_THRESHOLD = -90 + + def __init__(self): + self.succeed_rssi = dict.fromkeys(range(MIN_RSSI, MAX_RSSI), 0) + self.failed_rssi = dict.fromkeys(range(MIN_RSSI, MAX_RSSI), 0) + self.failed_att = dict.fromkeys(range(MAX_ATT), 0) + self.result = True + pass + + def log_performance(self, result, att, rssi): + if result == "Succeed": + self.succeed_rssi[rssi] += 1 + else: + if rssi == 0: + self.failed_att[att] += 1 + else: + self.failed_rssi[rssi] += 1 + if rssi > self.RSSI_THRESHOLD: + self.result = False + pass + + def get_result(self): + return self.result + + def __str__(self): + data = "Succeed:\r\n" + for rssi in self.succeed_rssi: + if self.succeed_rssi[rssi] > 0: + data += "\tRSSI%4d: %2d times\r\n" % (rssi, self.succeed_rssi[rssi]) + + data += "Failed during scan:\r\n" + for att in self.failed_att: + if self.failed_att[att] > 0: + data += "\tATT%3d: %2d times\r\n" % (att, self.failed_att[att]) + + data += "Failed during JAP:\r\n" + for rssi in self.failed_rssi: + if self.failed_rssi[rssi] > 0: + data += "\tRSSI%4d: %2d times\r\n" % (rssi, self.failed_rssi[rssi]) + + return data + + pass + + +class TestCase(TCActionBase.CommonTCActionBase): + def __init__(self, test_case, test_env, timeout=30, log_path=TCActionBase.LOG_PATH): + TCActionBase.CommonTCActionBase.__init__(self, test_case, test_env, timeout=timeout, log_path=log_path) + self.att_test_list = range(60) + self.performance_folder_path = log_path + # load param from excel + cmd_set = test_case["cmd set"] + for i in range(1, len(cmd_set)): + if cmd_set[i][0] != "dummy": + cmd_string = "self." + cmd_set[i][0] + exec cmd_string + + self.ap_list = self.get_parameter("shield_box_ap_list") + + self.performance = dict([(ap_prop["ssid"], Performance()) for ap_prop in self.ap_list]) + # create log file + folder_path = MakeFolder.make_folder(self.performance_folder_path + "\\" + LOG_FOLDER) + self.performance_log = os.path.join(folder_path, + "JAP_Att_%s.log" % time.strftime("%d%H%M%S", time.localtime())) + + self.result_cntx = TCActionBase.ResultCheckContext(self, test_env, self.tc_name) + pass + + def log_performance(self, att, rssi, ssid, result): + NativeLog.add_prompt_trace("[%s][ssid %s] [att %s] [rssi %s]" % (result, ssid, att, rssi)) + data = "" + self.performance[ssid].log_performance(result, att, rssi) + for _ssid in self.performance: + data += "[ssid] %s\r\n%s\r\n" % (_ssid, self.performance[_ssid]) + with open(self.performance_log, "wb+") as f: + f.write(data) + pass + + def execute(self): + TCActionBase.TCActionBase.execute(self) + self.result_cntx.start() + + try: + # configurable params + ap_list = self.ap_list + att_test_list = self.att_test_list + test_count = self.test_count + # configurable params + except StandardError, e: + NativeLog.add_trace_critical("Error configuration for JAPAtt script, error is %s" % e) + raise StandardError("Error configuration") + + for x in xrange(test_count): + for ap_prop in ap_list: + ssid = ap_prop[0] + password = ap_prop[1] + apc = ap_prop[2] + if ap_prop[1] == "": + # set a default string for open ap + password = "1" + + # switch off all outlet, switch on AP outlet + outlet_config_dict = dict.fromkeys(range(1, 9), "OFF") + outlet_config_dict[apc] = "ON" + apc_cmd = "APC " + for outlet in outlet_config_dict: + apc_cmd += " %s %s" % (outlet_config_dict[outlet], outlet) + checker_stings = ["P PC_COM L OK"] + fail_string = "Fail, Fail to switch apc" + if self.load_and_exe_one_step(checker_stings, [apc_cmd], fail_string) is False: + return + + # wait AP ready + time.sleep(20) + + # create RSSI Calibrator + calibrator = RSSICalibrator.Calibrator() + + ret = True + for att_value in att_test_list: + # step 0 set att value + checker_stings = ["R PC_COM L OK"] + test_action_string = ["ATT %s" % att_value] + fail_string = "Fail, Fail to set att value" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string) is False: + continue + + # step 1 get AP RSSI + checker_stings = ["R SSC1 A :\+SCAN:%s,[:\d\w]+,\d+,\d+,([-\d]+)" % ssid] + test_action_string = ["SSC SSC1 sta -S -s %s" % ssid] + fail_string = "Fail, Fail to scan" + rssi = scan_count = 0 + for i in range(3): + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string, + check_freq=1, check_time=5) is False: + self.log_performance(att_value, 0, ssid, "Failed to measure RSSI") + continue + rssi += int(self.test_env.get_variable_by_name("rssi")[1]) + scan_count += 1 + + rssi = calibrator.calibrate_rssi(float(rssi) / scan_count if scan_count > 0 else 0, att_value) + + # step 2 connect to AP + checker_stings = ["R SSC1 C +JAP:CONNECTED"] + test_action_string = ["SSC SSC1 sta -C -s %s -p %s" % (ssid, password)] + fail_string = "Fail, Fail to JAP" + if self.load_and_exe_one_step(checker_stings, test_action_string, fail_string, + check_freq=1, check_time=45) is False: + self.log_performance(att_value, rssi, ssid, "Failed to JAP") + if rssi < -90: + continue + else: + ret = False + break + if ret: + self.log_performance(att_value, rssi, ssid, "Succeed") + else: + self.log_performance(att_value, rssi, ssid, "Failed") + + # finally, execute done + for ssid in self.performance: + if self.performance[ssid].get_result() is False: + self.result_cntx.set_result("Failed") + break + else: + self.result_cntx.set_result("Succeed") + + def result_check(self, port_name, data): + TCActionBase.CommonTCActionBase.result_check(self, port_name, data) + self.result_cntx.append_data(port_name, data) + + +def main(): + pass + + +if __name__ == '__main__': + main() diff --git a/components/idf_test/integration_test/TestCaseScript/WiFiStress/WifiSmartConfig.py b/components/idf_test/integration_test/TestCaseScript/WiFiStress/WifiSmartConfig.py new file mode 100755 index 0000000000..830de766c9 --- /dev/null +++ b/components/idf_test/integration_test/TestCaseScript/WiFiStress/WifiSmartConfig.py @@ -0,0 +1,274 @@ +import random +import os +import time +import copy + +from TCAction import TCActionBase +from NativeLog import NativeLog +from Utility import Encoding +from Utility import MakeFolder + +AP_PROP = ("ssid", "ssid_len", "pwd", + "pwd_len", "channel", "enc", "apc") + +SMART_TYPE = ("esp-touch", "airkiss") + +TEST_METHOD = ("ssid_broadcast", "ssid_hidden") + +HT = ("ht20", "ht40") + +TEST_STAT = ("total count", "fail count", "total time", "longest time") + +_TEST_STAT_INIT_DICT = {"total count": 0, + "fail count": 0, + "total time": 0, + "longest time": 0, + } + +LOG_FOLDER = os.path.join("Performance", "SmartConfig") + + +SSID_LEN_RANGE = (1, 32) # in bytes +ENC_TYPE = (0, 2, 3, 4) # do not support WEP for 8266 soft AP +PWD_RANGE = {0: [0, 0], + 1: [5, 5], + 2: [8, 32], + 3: [8, 32], + 4: [8, 32], + } + + +class TestCase(TCActionBase.CommonTCActionBase): + + def __init__(self, test_case, test_env, timeout=30, log_path=TCActionBase.LOG_PATH): + TCActionBase.CommonTCActionBase.__init__(self, test_case, test_env, timeout=timeout, log_path=log_path) + self.performance_folder_path = log_path + # default value for optional configurable params + self.test_method = ["ssid_hidden", "ssid_broadcast"] + self.bssid = "ff:ff:ff:ff:ff:ff" + self.ht_ap = dict(zip(HT, [("", ""), + ("", "")])) + self.ap_channel = {"ht20": 1, "ht40": 1} + self.delay_time = 3 # default 3s, wait for scan done + # load param from excel + cmd_set = test_case["cmd set"] + for i in range(1, len(cmd_set)): + if cmd_set[i][0] != "dummy" and cmd_set[i][0] != "": + cmd_string = "self." + cmd_set[i][0] + exec cmd_string + + folder_path = MakeFolder.make_folder(self.performance_folder_path + "\\" + LOG_FOLDER) + file_name = "SmartConfig_log_%s.log" % (time.strftime("%m%d%H%M%S", time.localtime())) + self._performance_log_file = os.path.join(folder_path, file_name) + + # type + self.target_type = ["SSC" if test_env.get_port_by_name("AT1") is None else "AT"] + self.target_type.append("SSC" if test_env.get_port_by_name("AT2") is None else "AT") + + # test statistics + # better ways to create? + _test_stat = dict.fromkeys(TEST_STAT, 0) + _test_method = dict.fromkeys(TEST_METHOD) + _test_ht = dict.fromkeys(HT) + self.test_stat = dict.fromkeys(SMART_TYPE) + for i in SMART_TYPE: + self.test_stat[i] = copy.deepcopy(_test_ht) + for j in HT: + self.test_stat[i][j] = copy.deepcopy(_test_method) + for k in TEST_METHOD: + self.test_stat[i][j][k] = copy.deepcopy(_test_stat) + + self.result_cntx = TCActionBase.ResultCheckContext(self, test_env, self.tc_name) + pass + + def _generate_random_ap_prop(self, ht_type): + ap_prop = dict.fromkeys(AP_PROP) + # generate target ap_value + ap_prop["ssid_len"] = random.randint(SSID_LEN_RANGE[0], SSID_LEN_RANGE[1]) + ap_prop["channel"] = self.ap_channel[ht_type] + ap_prop["enc"] = random.choice(ENC_TYPE) + ap_prop["pwd_len"] = random.randint(PWD_RANGE[ap_prop["enc"]][0], PWD_RANGE[ap_prop["enc"]][1]) + ap_prop["ssid"] = Encoding.generate_random_printable_str(ap_prop["ssid_len"]) + ap_prop["pwd"] = Encoding.generate_random_printable_str(ap_prop["pwd_len"]) + + return ap_prop + + def _logging_performance(self, time_cost, ssid, password, smart_type, test_method, ht_type): + # update test statistics + stat = self.test_stat[smart_type][ht_type][test_method] + stat["total count"] += 1 + # log performance to performance log file + with open(self._performance_log_file, "ab+") as f: + # log time and ssid + if time_cost is not False: + time_tmp = float(time_cost)/10 + f.write("\r\n[%s]:\r\n[Succeed] [%.2f]\r\n" % + (time.strftime("%m-%d %H:%M:%S", time.localtime()), time_tmp)) + stat["total time"] += time_tmp + stat["longest time"] = time_tmp if time_tmp > stat["longest time"] else stat["longest time"] + else: + f.write("\r\n[%s]:\r\n[Fail]\r\n" % + time.strftime("%m-%d %H:%M:%S", time.localtime())) + stat["fail count"] += 1 + + f.write("[%s] [%s] [%s]\r\n" % + (smart_type, test_method, ht_type)) + f.write("[ssid] %s \r\n[password] %s\r\n" % + (ssid, password)) + pass + + def _generate_performance_report(self): + with open(self._performance_log_file, "ab+") as f: + for i in SMART_TYPE: + for j in HT: + for k in TEST_METHOD: + stat = self.test_stat[i][j][k] + f.write("\r\n[Test report] [%s] [%s] [%s]\r\n" % (i, j, k)) + if stat["total count"] > 0: + f.write("[Total]: %d\r\n" % stat["total count"]) + f.write("[Failed]: %d\r\n" % stat["fail count"]) + f.write("[Fail ratio]: %.2f%%\r\n" % + (float(stat["fail count"])/stat["total count"] * 100)) + f.write("[Longest time cost]: %.2f\r\n" % stat["longest time"]) + if (stat["total count"] - stat["fail count"]) > 0: + f.write("[Average time cost]: %.2f\r\n" % + (stat["total time"]/(stat["total count"]-stat["fail count"]))) + + @staticmethod + def cmd_exception_catcher(e): + raise e + pass + + def execute(self): + TCActionBase.TCActionBase.execute(self) + self.result_cntx.start() + + # mandatory configurable params + try: + test_count = self.test_count + delay_time = self.delay_time + except StandardError, e: + NativeLog.add_trace_critical("Error configuration for WifiJAP script, error is %s" % e) + raise StandardError("Error configuration") + + # step 0 : set AT1 mode + fail_string = "Fail to restore init condition" + if self.target_type[0] == "AT": + cmd = ["ATS AT1 AT+CWMODE=1"] + checker_stings = ["R AT1 L OK"] + else: + cmd = ["SSC SSC1 op -S -o 1"] + checker_stings = ["R SSC1 C +MODE:OK"] + if self.target_type[1] == "AT": + cmd.append("ATS AT2 AT+CWMODE=2") + checker_stings.append("R AT2 L OK") + else: + cmd.append("SSC SSC2 op -S -o 2") + checker_stings.append("R SSC2 C +MODE:OK") + + if self.load_and_exe_one_step(checker_stings, cmd, + fail_string, check_time=150) is False: + NativeLog.add_trace_critical(fail_string) + return + + for i in xrange(test_count): + _method = random.choice(self.test_method) + _ht = random.choice(self.ht) + _ap_prop = self._generate_random_ap_prop(_ht) + _smart_type = random.choice(self.smart_type) + _ht_ap = self.ht_ap[_ht] + is_hidden = 0 if _method == "ssid_broadcast" else 1 + # get ip and + + # step 1 : restore init condition + fail_string = "Fail to restore init condition" + if self.target_type[0] == "AT": + cmd = ["ATS AT1 AT+CWSTOPSMART", "WIFI CONN %s %s " % (_ht_ap[0], _ht_ap[1])] + checker_stings = ["P AT1 L OK", "P PC_COM L OK"] + else: + cmd = ["SSC SSC1 smart -E", "WIFI CONN %s %s " % (_ht_ap[0], _ht_ap[1])] + checker_stings = ["P SSC1 C +SC:OK", "P PC_COM L OK"] + + if self.load_and_exe_one_step(checker_stings, cmd, + fail_string, check_time=200) is False: + NativeLog.add_trace_critical(fail_string) + continue + NativeLog.add_prompt_trace("Step1 Done") + + # step 2 : test method is ssid_broadcast, then set AP on target 2 + if _method == "ssid_broadcast": + fail_string = "Fail to set AP" + if self.target_type[1] == "AT": + cmd = ["ATS AT2 AT+CWSAP=\"%s\",\"%s\",%d,%d" % (_ap_prop["ssid"], _ap_prop["pwd"], + _ap_prop["channel"], _ap_prop["enc"])] + checker_stings = ["R AT2 L OK"] + else: + cmd = ["SSC SSC2 ap -S -s %s -p %s -n %d -t %d" % (_ap_prop["ssid"], _ap_prop["pwd"], + _ap_prop["channel"], _ap_prop["enc"])] + checker_stings = ["R SSC2 C +SAP:OK"] + + if self.load_and_exe_one_step(checker_stings, cmd, + fail_string, check_time=50) is False: + NativeLog.add_trace_critical(fail_string) + continue + NativeLog.add_prompt_trace("Step2 Done") + + # step 3 : start SMART + fail_string = "Fail to start smart config" + if self.target_type[0] == "AT": + cmd = ["ATS AT1 AT+CWSTARTSMART"] + checker_stings = ["R AT1 L OK"] + else: + cmd = ["SSC SSC1 smart -S -a 0"] + checker_stings = ["R SSC1 C +SC:OK"] + + if self.load_and_exe_one_step(checker_stings, cmd, + fail_string, check_time=50) is False: + NativeLog.add_trace_critical(fail_string) + continue + # sleep for delay_time seconds to wait scan done or simulate delay config situation + time.sleep(delay_time) + NativeLog.add_prompt_trace("Step3 Done") + + # step 4 : do smart config + fail_string = "Fail in smart config" + cmd = ["SMART %s %s %s %s %d" + % (_smart_type, _ap_prop["ssid"], _ap_prop["pwd"], self.bssid, is_hidden)] + if self.target_type[0] == "AT": + checker_stings = ["P AT1 C Smart%20get%20wifi%20info", + "P LOG1 C %s C %s" % (_ap_prop["ssid"], _ap_prop["pwd"])] + else: + checker_stings = ["P SSC1 C %s C %s" % (_ap_prop["ssid"], _ap_prop["pwd"])] + + try: + time_cost = self.load_and_exe_one_step(checker_stings, cmd, + fail_string, check_time=400, + cmd_exception_catcher=self.cmd_exception_catcher) + except StandardError: + NativeLog.add_prompt_trace("Exception occurred during executing cmd") + continue + pass + self._logging_performance(time_cost, _ap_prop["ssid"], _ap_prop["pwd"], + _smart_type, _method, _ht) + if time_cost is False: + NativeLog.add_prompt_trace(fail_string) + continue + + # continue to next loop + NativeLog.add_prompt_trace("[WifiSmartConfig] Test count %d done" % i) + + # generate report and cleanup + self._generate_performance_report() + + self.result_cntx.set_result("Succeed") + + def result_check(self, port_name, data): + TCActionBase.CommonTCActionBase.result_check(self, port_name, data) + self.result_cntx.append_data(port_name, data) + + +def main(): + pass + +if __name__ == '__main__': + main() diff --git a/components/idf_test/integration_test/TestCaseScript/WiFiStress/__init__.py b/components/idf_test/integration_test/TestCaseScript/WiFiStress/__init__.py new file mode 100755 index 0000000000..7960a3ce80 --- /dev/null +++ b/components/idf_test/integration_test/TestCaseScript/WiFiStress/__init__.py @@ -0,0 +1 @@ +__all__ = ["WifiJAP", ] \ No newline at end of file diff --git a/components/idf_test/unit_test/TestCaseAll.yml b/components/idf_test/unit_test/TestCaseAll.yml index 6f974e0bf5..4e5c01f219 100644 --- a/components/idf_test/unit_test/TestCaseAll.yml +++ b/components/idf_test/unit_test/TestCaseAll.yml @@ -7,9 +7,9 @@ test cases: category: Function cmd set: - IDFUnitTest/UnitTest - - - test_case = "check if ROM is used for functions" + - - test_case = "check if ROM or Flash is used for functions" - [dummy] - comment: check if ROM is used for functions + comment: check if ROM or Flash is used for functions execution time: 0 expected result: 1. set succeed initial condition: UTINIT1 diff --git a/components/mbedtls/Kconfig b/components/mbedtls/Kconfig index 5a719ff33a..e6619e36c4 100644 --- a/components/mbedtls/Kconfig +++ b/components/mbedtls/Kconfig @@ -61,14 +61,6 @@ config MBEDTLS_MPI_USE_INTERRUPT This allows other code to run on the CPU while an MPI operation is pending. Otherwise the CPU busy-waits. -config MBEDTLS_MPI_INTERRUPT_NUM - int "MPI Interrupt number" - depends on MBEDTLS_MPI_USE_INTERRUPT - default 18 - help - CPU interrupt number for MPI interrupt to connect to. Must be otherwise unused. - Eventually this assignment will be handled automatically at runtime. - config MBEDTLS_HARDWARE_SHA bool "Enable hardware SHA acceleration" default y diff --git a/components/mbedtls/port/esp_bignum.c b/components/mbedtls/port/esp_bignum.c index 2fe4d920c3..5cce103ea3 100644 --- a/components/mbedtls/port/esp_bignum.c +++ b/components/mbedtls/port/esp_bignum.c @@ -32,6 +32,7 @@ #include "esp_system.h" #include "esp_log.h" #include "esp_intr.h" +#include "esp_intr_alloc.h" #include "esp_attr.h" #include "soc/dport_reg.h" @@ -59,10 +60,7 @@ static void rsa_isr_initialise() { if (op_complete_sem == NULL) { op_complete_sem = xSemaphoreCreateBinary(); - intr_matrix_set(xPortGetCoreID(), ETS_RSA_INTR_SOURCE, CONFIG_MBEDTLS_MPI_INTERRUPT_NUM); - xt_set_interrupt_handler(CONFIG_MBEDTLS_MPI_INTERRUPT_NUM, &rsa_complete_isr, NULL); - xthal_set_intclear(1 << CONFIG_MBEDTLS_MPI_INTERRUPT_NUM); - xt_ints_on(1 << CONFIG_MBEDTLS_MPI_INTERRUPT_NUM); + esp_intr_alloc(ETS_RSA_INTR_SOURCE, 0, rsa_complete_isr, NULL, NULL); } } diff --git a/components/newlib/component.mk b/components/newlib/component.mk index 432780f650..8715c475a4 100644 --- a/components/newlib/component.mk +++ b/components/newlib/component.mk @@ -5,6 +5,10 @@ else LIBC_PATH := $(COMPONENT_PATH)/lib/libc.a endif -COMPONENT_ADD_LDFLAGS := $(LIBC_PATH) $(COMPONENT_PATH)/lib/libm.a -lnewlib +LIBM_PATH := $(COMPONENT_PATH)/lib/libm.a + +COMPONENT_ADD_LDFLAGS := $(LIBC_PATH) $(LIBM_PATH) -lnewlib + +COMPONENT_ADD_LINKER_DEPS := $(LIBC_PATH) $(LIBM_PATH) COMPONENT_ADD_INCLUDEDIRS := include platform_include diff --git a/components/newlib/time.c b/components/newlib/time.c index 1baa955759..363e17b3eb 100644 --- a/components/newlib/time.c +++ b/components/newlib/time.c @@ -22,6 +22,7 @@ #include #include #include "esp_attr.h" +#include "esp_intr_alloc.h" #include "soc/soc.h" #include "soc/rtc_cntl_reg.h" #include "soc/frc_timer_reg.h" @@ -105,9 +106,7 @@ void esp_setup_time_syscalls() SET_PERI_REG_MASK(FRC_TIMER_CTRL_REG(0), FRC_TIMER_ENABLE | \ FRC_TIMER_INT_ENABLE); - intr_matrix_set(xPortGetCoreID(), ETS_TIMER1_INTR_SOURCE, ETS_FRC1_INUM); - xt_set_interrupt_handler(ETS_FRC1_INUM, &frc_timer_isr, NULL); - xt_ints_on(1 << ETS_FRC1_INUM); + esp_intr_alloc(ETS_TIMER1_INTR_SOURCE, 0, &frc_timer_isr, NULL, NULL); #endif // WITH_FRC1 } diff --git a/components/partition_table/Makefile.projbuild b/components/partition_table/Makefile.projbuild index 5bd13f766d..dbc9d36053 100644 --- a/components/partition_table/Makefile.projbuild +++ b/components/partition_table/Makefile.projbuild @@ -11,6 +11,7 @@ # NB: gen_esp32part.py lives in the sdk/bin/ dir not component dir GEN_ESP32PART := $(PYTHON) $(COMPONENT_PATH)/gen_esp32part.py -q +# Has a matching value in bootloader_support esp_flash_partitions.h PARTITION_TABLE_OFFSET := 0x8000 # Path to partition CSV file is relative to project path for custom @@ -20,11 +21,11 @@ PARTITION_TABLE_CSV_PATH := $(call dequote,$(abspath $(PARTITION_TABLE_ROOT)/$(s PARTITION_TABLE_BIN := $(BUILD_DIR_BASE)/$(notdir $(PARTITION_TABLE_CSV_PATH:.csv=.bin)) -ifdef CONFIG_SECURE_BOOTLOADER_ENABLED +ifdef CONFIG_SECURE_BOOT_ENABLED PARTITION_TABLE_BIN_UNSIGNED := $(PARTITION_TABLE_BIN:.bin=-unsigned.bin) # add an extra signing step for secure partition table -$(PARTITION_TABLE_BIN): $(PARTITION_TABLE_BIN_UNSIGNED) - $(Q) $(ESPSECUREPY) sign_data --keyfile $(SECURE_BOOT_SIGNING_KEY) -o $@ $< +$(PARTITION_TABLE_BIN): $(PARTITION_TABLE_BIN_UNSIGNED) $(SDKCONFIG_MAKEFILE) $(SECURE_BOOT_SIGNING_KEY) + $(ESPSECUREPY) sign_data --keyfile $(SECURE_BOOT_SIGNING_KEY) -o $@ $< else # secure bootloader disabled, both files are the same PARTITION_TABLE_BIN_UNSIGNED := $(PARTITION_TABLE_BIN) diff --git a/components/partition_table/gen_esp32part.py b/components/partition_table/gen_esp32part.py index 5ead13adc7..a4412ad45b 100755 --- a/components/partition_table/gen_esp32part.py +++ b/components/partition_table/gen_esp32part.py @@ -90,11 +90,11 @@ class PartitionTable(list): for o in range(0,len(b),32): data = b[o:o+32] if len(data) != 32: - raise InputError("Ran out of partition table data before reaching end marker") + raise InputError("Partition table length must be a multiple of 32 bytes") if data == '\xFF'*32: - break # end of partition table + return result # got end marker result.append(PartitionDefinition.from_binary(data)) - return result + raise InputError("Partition table is missing an end-of-table marker") def to_binary(self): result = "".join(e.to_binary() for e in self) @@ -105,7 +105,7 @@ class PartitionTable(list): def to_csv(self, simple_formatting=False): rows = [ "# Espressif ESP32 Partition Table", - "# Name, Type, SubType, Offset, Size" ] + "# Name, Type, SubType, Offset, Size, Flags" ] rows += [ x.to_csv(simple_formatting) for x in self ] return "\n".join(rows) + "\n" @@ -140,6 +140,12 @@ class PartitionDefinition(object): DATA_TYPE : 0x04, } + # dictionary maps flag name (as used in CSV flags list, property name) + # to bit set in flags words in binary format + FLAGS = { + "encrypted" : 1 + } + # add subtypes for the 16 OTA slot values ("ota_XXX, etc.") for ota_slot in range(16): SUBTYPES[TYPES["app"]]["ota_%d" % ota_slot] = 0x10 + ota_slot @@ -150,11 +156,12 @@ class PartitionDefinition(object): self.subtype = None self.offset = None self.size = None + self.encrypted = False @classmethod def from_csv(cls, line): """ Parse a line from the CSV """ - line_w_defaults = line + ",,," # lazy way to support default fields + line_w_defaults = line + ",,,," # lazy way to support default fields fields = [ f.strip() for f in line_w_defaults.split(",") ] res = PartitionDefinition() @@ -165,6 +172,14 @@ class PartitionDefinition(object): res.size = res.parse_address(fields[4]) if res.size is None: raise InputError("Size field can't be empty") + + flags = fields[5].split(":") + for flag in flags: + if flag in cls.FLAGS: + setattr(res, flag, True) + elif len(flag) > 0: + raise InputError("CSV flag column contains unknown flag '%s'" % (flag)) + return res def __eq__(self, other): @@ -220,22 +235,30 @@ class PartitionDefinition(object): raise InputError("Partition definition length must be exactly 32 bytes. Got %d bytes." % len(b)) res = cls() (magic, res.type, res.subtype, res.offset, - res.size, res.name, reserved) = struct.unpack(cls.STRUCT_FORMAT, b) + res.size, res.name, flags) = struct.unpack(cls.STRUCT_FORMAT, b) if "\x00" in res.name: # strip null byte padding from name string res.name = res.name[:res.name.index("\x00")] if magic != cls.MAGIC_BYTES: raise InputError("Invalid magic bytes (%r) for partition definition" % magic) - if reserved != 0: - critical("WARNING: Partition definition had unexpected reserved value 0x%08x. Newer binary format?" % reserved) + for flag,bit in cls.FLAGS.items(): + if flags & (1< +#include #include #include "esp_err.h" #include "sdkconfig.h" @@ -79,14 +80,33 @@ esp_err_t spi_flash_erase_range(size_t start_address, size_t size); * @note If source address is in DROM, this function will return * ESP_ERR_INVALID_ARG. * - * @param dest destination address in Flash - * @param src pointer to the source buffer - * @param size length of data, in bytes + * @param dest destination address in Flash. Must be a multiple of 4 bytes. + * @param src pointer to the source buffer. + * @param size length of data, in bytes. Must be a multiple of 4 bytes. * * @return esp_err_t */ esp_err_t spi_flash_write(size_t dest, const void *src, size_t size); + +/** + * @brief Write data encrypted to Flash. + * + * @note Flash encryption must be enabled for this function to work. + * + * @note Address in flash, dest, has to be 32-byte aligned. + * + * @note If source address is in DROM, this function will return + * ESP_ERR_INVALID_ARG. + * + * @param dest destination address in Flash. Must be a multiple of 32 bytes. + * @param src pointer to the source buffer. + * @param size length of data, in bytes. Must be a multiple of 32 bytes. + * + * @return esp_err_t + */ +esp_err_t spi_flash_write_encrypted(size_t dest, const void *src, size_t size); + /** * @brief Read data from Flash. * diff --git a/components/spi_flash/partition.c b/components/spi_flash/partition.c index c54da24caa..490efbb32c 100644 --- a/components/spi_flash/partition.c +++ b/components/spi_flash/partition.c @@ -22,6 +22,7 @@ #include "esp_flash_data_types.h" #include "esp_spi_flash.h" #include "esp_partition.h" +#include "esp_flash_encrypt.h" #include "esp_log.h" @@ -164,7 +165,11 @@ static esp_err_t load_partitions() item->info.size = it->pos.size; item->info.type = it->type; item->info.subtype = it->subtype; - item->info.encrypted = false; + item->info.encrypted = it->flags & PART_FLAG_ENCRYPTED; + if (esp_flash_encryption_enabled() && it->type == PART_TYPE_APP) { + /* All app partitions are encrypted if encryption is turned on */ + item->info.encrypted = true; + } // it->label may not be zero-terminated strncpy(item->info.label, (const char*) it->label, sizeof(it->label)); item->info.label[sizeof(it->label)] = 0; @@ -201,7 +206,24 @@ esp_err_t esp_partition_read(const esp_partition_t* partition, if (src_offset + size > partition->size) { return ESP_ERR_INVALID_SIZE; } - return spi_flash_read(partition->address + src_offset, dst, size); + + if (!partition->encrypted) { + return spi_flash_read(partition->address + src_offset, dst, size); + } else { + /* Encrypted partitions need to be read via a cache mapping */ + const void *buf; + spi_flash_mmap_handle_t handle; + esp_err_t err; + + err = esp_partition_mmap(partition, src_offset, size, + SPI_FLASH_MMAP_DATA, &buf, &handle); + if (err != ESP_OK) { + return err; + } + memcpy(dst, buf, size); + spi_flash_munmap(handle); + return ESP_OK; + } } esp_err_t esp_partition_write(const esp_partition_t* partition, @@ -218,7 +240,12 @@ esp_err_t esp_partition_write(const esp_partition_t* partition, if (dst_offset + size > partition->size) { return ESP_ERR_INVALID_SIZE; } - return spi_flash_write(partition->address + dst_offset, src, size); + dst_offset = partition->address + dst_offset; + if (partition->encrypted) { + return spi_flash_write_encrypted(dst_offset, src, size); + } else { + return spi_flash_write(dst_offset, src, size); + } } esp_err_t esp_partition_erase_range(const esp_partition_t* partition, diff --git a/docs/Doxyfile b/docs/Doxyfile index c1aebcd308..bdb91a4dce 100755 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -27,7 +27,8 @@ INPUT = ../components/esp32/include/esp_wifi.h \ ../components/esp32/include/esp_task_wdt.h \ ../components/app_update/include/esp_ota_ops.h \ ../components/ethernet/include/esp_eth.h \ - ../components/ulp/include/esp32/ulp.h + ../components/ulp/include/esp32/ulp.h \ + ../components/esp32/include/esp_intr_alloc.h ## Get warnings for functions that have no documentation for their parameters or return value ## diff --git a/docs/api/intr_alloc.rst b/docs/api/intr_alloc.rst new file mode 100644 index 0000000000..4d2f21abac --- /dev/null +++ b/docs/api/intr_alloc.rst @@ -0,0 +1,111 @@ +Interrupt allocation +==================== + +Overview +-------- + +The ESP32 has two cores, with 32 interrupts each. Each interrupt has a certain priority level, most (but not all) interrupts are connected +to the interrupt mux. Because there are more interrupt sources than interrupts, sometimes it makes sense to share an interrupt in +multiple drivers. The esp_intr_alloc abstraction exists to hide all these implementation details. + +A driver can allocate an interrupt for a certain peripheral by calling esp_intr_alloc (or esp_intr_alloc_sintrstatus). It can use +the flags passed to this function to set the type of interrupt allocated, specifying a specific level or trigger method. The +interrupt allocation code will then find an applicable interrupt, use the interrupt mux to hook it up to the peripheral, and +install the given interrupt handler and ISR to it. + +This code has two different types of interrupts it handles differently: Shared interrupts and non-shared interrupts. The simplest +of the two are non-shared interrupts: a separate interrupt is allocated per esp_intr_alloc call and this interrupt is solely used for +the peripheral attached to it, with only one ISR that will get called. Non-shared interrupts can have multiple peripherals triggering +it, with multiple ISRs being called when one of the peripherals attached signals an interrupt. Thus, ISRs that are intended for shared +interrupts should check the interrupt status of the peripheral they service in order to see if any action is required. + +Non-shared interrupts can be either level- or edge-triggered. Shared interrupts can +only be level interrupts (because of the chance of missed interrupts when edge interrupts are +used.) +(The logic behind this: DevA and DevB share an int. DevB signals an int. Int line goes high. ISR handler +calls code for DevA -> does nothing. ISR handler calls code for DevB, but while doing that, +DevA signals an int. ISR DevB is done, clears int for DevB, exits interrupt code. Now an +interrupt for DevA is still pending, but because the int line never went low (DevA kept it high +even when the int for DevB was cleared) the interrupt is never serviced.) + + +Multicore issues +---------------- + +Peripherals that can generate interrupts can be divided in two types: external peripherals, outside the Xtensa +cores in the ESP32, and internal peripherals, inside the ESP32. Interrupt handling differs slightly between +these two types of peripherals. + +Each Xtensa core has its own set of internal peripherals: three timer comparators, a performance monitor and two +software interrupts. These peripherals can only be configured from the core they are associated with. When +generating an interrupt, the interrupt they generate is hard-wired to their associated core; it's not possible +to have e.g. an internal timer comparator of one core generate an interrupt on another core. That is why these +sources can only be managed using a task running on that specific core. Internal interrupt sources are still +allocatable using esp_intr_alloc as normal, but they cannot be shared and will always have a fixed interrupt +level (namely, the one associated in hardware with the peripheral). Internal interrupt sources are defined +in esp_intr_alloc.h as ETS_INTERNAL_*_INTR_SOURCE. + +The remaining interrupt slots in both cores are wired to an interrupt multiplexer, which can be used to +route any external interrupt source to any of these interrupt slots. Allocating an external interrupt will always +allocate it on the core that does the allocation, and freeing the interrupt should always happen on the same +core. Disabling and enabling the interrupt from another core is allowed, however. External interrupts can +share an interrupt slot bu passing ESP_INTR_FLAG_SHARED as a flag to esp_intr_alloc. External interrupt sources +are defined in soc/soc.h as ETS_*_INTR_SOURCE. + +Care should be taken when allocating an interrupt using a task not pinned to a certain core; while running +code not in a critical secion, these tasks can migrate between cores at any moment, possibly making an +interrupt operation fail because of the reasons mentioned above. It is advised to always use +xTaskCreatePinnedToCore with a specific CoreID argument to create tasks that will handle interrupts. + +Application Example +------------------- + +API Reference +------------- + +Header Files +^^^^^^^^^^^^ + + * `esp_intr_alloc.h `_ + + +Macros +^^^^^^ + +.. doxygendefine:: ESP_INTR_FLAG_LEVEL1 +.. doxygendefine:: ESP_INTR_FLAG_LEVEL2 +.. doxygendefine:: ESP_INTR_FLAG_LEVEL3 +.. doxygendefine:: ESP_INTR_FLAG_LEVEL4 +.. doxygendefine:: ESP_INTR_FLAG_LEVEL5 +.. doxygendefine:: ESP_INTR_FLAG_LEVEL6 +.. doxygendefine:: ESP_INTR_FLAG_NMI +.. doxygendefine:: ESP_INTR_FLAG_LOWMED +.. doxygendefine:: ESP_INTR_FLAG_HIGH +.. doxygendefine:: ESP_INTR_FLAG_SHARED +.. doxygendefine:: ESP_INTR_FLAG_EDGE +.. doxygendefine:: ESP_INTR_FLAG_IRAM +.. doxygendefine:: ESP_INTR_FLAG_INTRDISABLED + +Type Definitions +^^^^^^^^^^^^^^^^ + +Enumerations +^^^^^^^^^^^^ + +Structures +^^^^^^^^^^ + +Functions +^^^^^^^^^ + +.. doxygenfunction:: esp_intr_mark_shared +.. doxygenfunction:: esp_intr_reserve +.. doxygenfunction:: esp_intr_alloc +.. doxygenfunction:: esp_intr_alloc_intrstatus +.. doxygenfunction:: esp_intr_free +.. doxygenfunction:: esp_intr_get_cpu +.. doxygenfunction:: esp_intr_get_intno +.. doxygenfunction:: esp_intr_disable +.. doxygenfunction:: esp_intr_enable +.. doxygenfunction:: esp_intr_noniram_disable +.. doxygenfunction:: esp_intr_noniram_enable diff --git a/docs/build_system.rst b/docs/build_system.rst index 75f48bb248..6687fa69ed 100644 --- a/docs/build_system.rst +++ b/docs/build_system.rst @@ -188,6 +188,10 @@ The following variables can be set inside ``component.mk`` to control build sett are available at all times. It is necessary if one component generates an include file which you then want to include in another component. Most components do not need to set this variable. +- ``COMPONENT_ADD_LINKER_DEPS``: Optional list of component-relative paths + to files which should trigger a re-link of the ELF file if they change. + Typically used for linker script files and binary libraries. Most components do + not need to set this variable. The following variable only works for components that are part of esp-idf itself: diff --git a/docs/index.rst b/docs/index.rst index 4c48255ae2..4e58cfe02f 100755 --- a/docs/index.rst +++ b/docs/index.rst @@ -110,6 +110,7 @@ Contents: Non-Volatile Storage Virtual Filesystem Ethernet + Interrupt Allocation deep-sleep-stub Template diff --git a/docs/partition-tables.rst b/docs/partition-tables.rst index c9709029d8..55b8870857 100644 --- a/docs/partition-tables.rst +++ b/docs/partition-tables.rst @@ -4,7 +4,7 @@ Partition Tables Overview -------- -A single ESP32's flash can contain multiple apps, as well as many different kinds of data (calibration data, filesystems, parameter storage, etc). For this reason a partition table is flashed to offset 0x4000 in the flash. +A single ESP32's flash can contain multiple apps, as well as many different kinds of data (calibration data, filesystems, parameter storage, etc). For this reason a partition table is flashed to offset 0x8000 in the flash. Partition table length is 0xC00 bytes (maximum 95 partition table entries). If the partition table is signed due to `secure boot`, the signature is appended after the table data. diff --git a/docs/security/flash-encryption.rst b/docs/security/flash-encryption.rst new file mode 100644 index 0000000000..b73d8a9ad3 --- /dev/null +++ b/docs/security/flash-encryption.rst @@ -0,0 +1,321 @@ +Flash Encryption +================ + +Flash Encryption is a feature for encrypting the contents of the ESP32's attached SPI flash. When flash encryption is enabled, physical readout of the SPI flash is not sufficient to recover most flash contents. + +Flash Encryption is separate from the `Secure Boot` feature, and you can use flash encryption without enabling secure boot. However we recommend using both features together for a secure environment. + +**IMPORTANT: Enabling flash encryption limits your options for further updates of your ESP32. Make sure to read this document (including `Limitations of Flash Encryption` and understand the implications of enabling flash encryption.** + +**IMPORTANT: Flash Encryption feature is currently enabled for development use only, with a key generated on the host. The recommended production configuration, where the flash encryption key is generated by the device on first boot, is currently disabled while final testing is done. This documentation refers to flash encryption keys being generated on first boot, however for now it is necessary to follow the additional steps shown under `Precalculated Flash Encryption Key`.** + + +Background +---------- + +- The contents of the flash are encrypted using AES with a 256 bit key. The flash encryption key is stored in efuse internal to the chip, and is (by default) protected from software access. + +- Flash access is transparent via the flash cache mapping feature of ESP32 - any flash regions which are mapped to the address space will be transparently decrypted when read. + +- Encryption is applied by flashing the ESP32 with plaintext data, and (if encryption is enabled) the bootloader encrypts the data in place on first boot. + +- Not all of the flash is encrypted. The following kinds of flash data are encrypted: + - Bootloader + - Secure boot bootloader digest (if secure boot is enabled) + - Partition Table + - All "app" type partitions + - Any partition marked with the "encrypt" flag in the partition table + + It may be desirable for some data partitions to remain unencrypted for ease of access, or to use flash-friendly update algorithms that are ineffective if the data is encrypted. "NVS" partitions for non-volatile storage cannot be encrypted. + +- The flash encryption key is stored in efuse key block 1, internal to the ESP32 chip. By default, this key is read- and write-protected so software cannot access it or change it. + +- The `flash encryption algorithm` is AES-256, where the key is "tweaked" with the offset address of each 32 byte block of flash. This means every 32 byte block (two consecutive 16 byte AES blocks) is encrypted with a unique key derived from the flash encryption key. + +- Although software running on the chip can transparently decrypt flash contents, by default it is made possible for the UART bootloader to decrypt (or encrypt) data when flash encryption is enabled. + +Flash Encryption Initialisation +------------------------------- + +This is the default (and recommended) flash encryption initialisation process. It is possible to customise this process for development or other purposes, see `Flash Encryption Advanced Features` for details. + +**IMPORTANT: Once flash encryption is enabled on first boot, the hardware allows a maximum of 3 subsequent flash updates via physical re-flashing. If secure boot is enabled, no physical re-flashes are possible. OTA updates can be used to update flash content without counting towards this limit. When enabling flash encryption in development, use a `precalculated flash encryption key` to allow physically re-flashing an unlimited number of times with pre-encrypted data.** + +- The bootloader must be compiled with flash encryption support enabled. In ``make menuconfig``, navigate to "Security Features" and select "Yes" for "Enable flash nencryption on boot". + +- If enabling Secure Boot at the same time, you can simultaneously select those options now. See the `Secure Boot` documentation for details. + +- Build and flash the bootloader, partition table and factory app image as normal. These partitions are initially written to the flash unencrypted. + +- On first boot, the bootloader sees ``FLASH_CRYPT_CNT`` efuse is set to 0 so it generates a flash encryption key using the hardware random number generator. This key is stored in efuse. The key is read and write protected against further software access. + +- All of the encrypted partitions are then encrypted in-place by the bootloader. Encrypting in-place can take some time (up to a minute for large partitions.) + +**IMPORTANT: Do not interrupt power to the ESP32 while the first boot encryption pass is running. If power is interrupted, the flash contents will be corrupted and require flashing with unencrypted data again. This re-flash will not count towards the flashing limit, as ``FLASH_CRYPT_CNT`` is only updated after this process finishes.** + +- Once flashing is complete. efuses are blown (by default) to disable encrypted flash access while the UART bootloader is running. + +- If not already write-protected, the ``FLASH_CRYPT_CONFIG`` efuse is also burned to the maximum value (``0xF``) to maximise the number of key bits which are tweaked in the flash algorithm. See `Setting FLASH_CRYPT_CONFIG` for details of this efuse. + +- Finally, the ``FLASH_CRYPT_CNT`` efuse is burned with the initial value 1. It is this efuse which activates the transparent flash encryption layer, and limits the number of subsequent reflashes. See the `Updating Encrypted Flash` section for details about ``FLASH_CRYPT_CNT``. + +- The bootloader resets itself to reboot from the newly encrypted flash. + + +Encrypted Flash Access +---------------------- + +Reading Encrypted Flash +^^^^^^^^^^^^^^^^^^^^^^^ + +Whenever the ``FLASH_CRYPT_CNT`` efuse is set to a value with an odd number of bits set, all flash content which is accessed via the MMU's flash cache is transparently decrypted. This includes: + +- Executable application code in flash (IROM). +- All read-only data stored in flash (DROM). +- Any data accessed via ``esp_spi_flash_mmap``. +- The software bootloader image when it is read by the ROM bootloader. + +**IMPORTANT: The MMU flash cache unconditionally decrypts all data. Data which is stored unencrypted in the flash will be "transparently decrypted" via the flash cache and appear to software like random garbage.** + +To read data without using a flash cache MMU mapping, we recommend using the partition read function ``esp_partition_read``. When using this function, data will only be decrypted when it is read from an encrypted partition. Other partitions will be read unencrypted. In this way, software can access encrypted and non-encrypted flash in the same way. + +Data which is read via other SPI read APIs are not decrypted: + +- Data read via ``esp_spi_flash_read`` is not decrypted +- Data read via ROM function ``SPIRead`` is not decrypted (this function is not supported in esp-idf apps). +- Data stored using the Non-Volatile Storage (NVS) API is always stored decrypted. + + +Writing Encrypted Flash +^^^^^^^^^^^^^^^^^^^^^^^ + +Where possible, we recommend using the partition write function ``esp_partition_write``. When using this function, data will only be encrypted when writing to encrypted partitions. Data will be written to other partitions unencrypted. In this way, software can access encrypted and non-encrypted flash in the same way. + +The ``esp_spi_flash_write`` function will write data when the write_encrypted parameter is set to true. Otherwise, data will be written unencrypted. + +The ROM function ``SPI_Encrypt_Write`` will write encrypted data to flash, the ROM function ``SPIWrite`` will write unencrypted to flash. (these function are not supported in esp-idf apps). + +The minimum write size for unencrypted data is 4 bytes (and the alignment is 4 bytes). Because data is encrypted in blocks, the minimum write size for encrypted data is 32 bytes (and the alignment is 32 bytes.) + +Updating Encrypted Flash +------------------------ + +OTA Updates +^^^^^^^^^^^ + +OTA updates to encrypted partitions will automatically write encrypted, as long as the ``esp_partition_write`` function is used. + +Serial Flashing +^^^^^^^^^^^^^^^ + +Provided secure boot is not used, the ``FLASH_CRYPT_CNT`` registers allow the flash to be updated with new plaintext data via serial flashing (or other physical methods), up to 3 additional times. ``FLASH_CRYPT_CNT`` efuse is an 8-bit value, and the flash encryption enables or disables based on the number of bits which are set to "1": + +- Even number (0-6) bits are set: Transparent reading of encrypted flash is disabled, any encrypted data cannot be decrypted. If the bootloader was built with "Enable flash encryption on boot" then it will see this situation and immediately re-encrypt the flash wherever it finds unencrypted data. Once done, it sets another bit in the efuse to '1' meaning an odd number of bits are now set. + +- Odd number (1-7) bits are set: Transparent reading of encrypted flash is enabled. + +- All 8 bits are set (valuye 0: Transparent reading of encrypted flash is disabled, any encrypted data is inaccessible. Bootloader will normally detect this condition and halt. To avoid use of this state to load unauthorised code, secure boot must be used or ``FLASH_CRYPT_CNT`` must be write-protected. + +The espefuse.py tool can be used to manually change the number of bits set in ``FLASH_CRYPT_CNT``, via serial bootloader. + +Limited Updates +^^^^^^^^^^^^^^^ + +Only 4 physical flash updates (writing plaintext data which is then encrypted) are possible: + +1. On first plaintext boot, bit count has brand new value 0 and bootloader changes to 1 (0x01) following encryption. +2. On next plaintext flash update, bit count is manually updated to 2 (0x03) and bootloader changes to 4 (0x07) following encryption. +3. Then bit count is manually updated to 4 (0x0F) and the bootloader changes efuse bit count to 5 (0x1F). +4. Finally bootloader is manually updated to 6 (0x3F) and bootloader changes efuse bit count to 7 (0x7F). + +Cautions With Re-Flashing +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- When reflashing via serial, reflash every partition that was previously written with plaintext (including bootloader). It is possible to skip app partitions which are not the "currently selected" OTA partition (these will not be re-encrypted unless a plaintext app image is found there.) However any partition marked with the "encrypt" flag will be unconditionally re-encrypted, meaning that any already encrypted data will be encrypted twice and corrupted. + +- If secure boot is enabled, you can't reflash via serial at all unless you used chosen the "Reflashable" option for Secure Boot, pre-generated a key and burned it to the ESP32. In this case you can re-flash a plaintext secure boot digest and bootloader image at offset 0 (see `Secure Boot` documentation.) In production secure boot configuration, the secure boot digest is stored encrypted - so if ``FLASH_CRYPT_CNT`` is set to an even value then the ROM bootloader will read the encrypted digest as-is and therefore will fail to verify any bootloader image as valid. + +Re-Flashing Procedure +^^^^^^^^^^^^^^^^^^^^^ + +The steps to update a device with plaintext via UART bootloader, when flash encryption is enabled are: + +- Build the application as usual. + +- Burn the ``FLASH_CRYPT_CNT`` efuse by running the command ``espefuse.py burn_efuse FLASH_CRYPT_CNT``. espefuse.py will automatically increment the bit count by 1. + +- Flash the device with plaintext data as usual (``make flash`` or ``esptool.py`` commands.) Flash all previously encrypted partitions, including the bootloader. If secure boot is enabled, it must be enabled in "Reflashable" mode and a pre-generated key burned to the ESP32 - flash the bootloader-reflash-digest.bin file at offset 0x0. + +- Reset the device and it will re-encrypt plaintext partitions, burn the ``FLASH_CRYPT_CNT`` flag to re-enable encryption. + + +Disabling Updates +^^^^^^^^^^^^^^^^^ + +To prevent further plaintext updates via physical access, use espefuse.py to write protect the ``FLASH_CRYPT_CNT`` efuse after flash encryption has been enabled (ie after first boot is complete):: + + espefuse.py write_protect_efuse FLASH_CRYPT_CNT + +This prevents any further modifications to disable or re-enable flash encryption. + +Limitations of Flash Encryption +------------------------------- + +Flash Encryption prevents plaintext readout of the encrypted flash, to protect firmware against unauthorised readout and modification. It is important to understand the limitations of the flash encryption system: + +- Flash encryption is only as strong as the key. For this reason, we recommend keys are generated on the device during first boot (default behaviour). If generating keys off-device to burn with ``esp_efuse.py burn_key``, ensure they are generated from a quality random number source, kept secure, and never shared between devices. + +- Not all data is stored encrypted. If storing data on flash, check if the method you are using (library, API, etc.) supports flash encryption. + +- Flash encryption does not prevent an attacker from understanding the high-level layout of the flash. This is because the same AES key is used for every two 16 byte AES blocks. When both adjacent 16 byte blocks contain identical content (such as empty or padding areas), these blocks will encrypt to produce matching pairs of encrypted blocks. This may allow an attacker to make high-level comparisons between encrypted devices (ie to tell if two devices are probably running the same firmware version). + +- For the same reason, an attacker can always guess when two adjacent 16 byte blocks (32 byte aligned) contain identical content. Keep this in mind if storing sensitive data on the flash, design your flash storage so this doesn't happen (using a counter byte or some other non-identical value every 16 bytes is sufficient). + +- Flash encryption alone may not prevent an attacker from modifying the firmware of the device. Always use flash encryption in combination with Secure Boot. + + +Flash Encryption Advanced Features +---------------------------------- + +Encrypted Partition Flag +^^^^^^^^^^^^^^^^^^^^^^^^ + +In the `partition table` description CSV files, there is a field for flags. + +Usually left blank, if you write "encrypted" in this field then the partition will be marked as encrypted in the partition table, and data written here will be treated as encrypted (same as an app partition):: + + # Name, Type, SubType, Offset, Size, Flags + nvs, data, nvs, 0x9000, 0x6000 + phy_init, data, phy, 0xf000, 0x1000 + factory, app, factory, 0x10000, 1M + secret_data, 0x40, 0x01, 0x20000, 256K, encrypted + +- None of the default partition formats have any encrypted data partitions. + +- It is not necessary to mark "app" partitions as encrypted, they are always treated as encrypted. + +- The "encrypted" flag does nothing if flash encryption is not enabled. + +- It is possible to mark the optional ``phy`` partition with ``phy_init`` data as encrypted, if you wish to protect this data from physical access readout or modification. + +- It is not possible to mark the ``nvs`` partition as encrypted. + +Precalculated Flash Encryption Key +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +It is possible to pre-generate a flash encryption key on the host computer and burn it into the ESP32 efuse. This allows data to be per-encrypted on the host and flashed to the ESP32 without needing a plaintext flash update. + +This is useful for development, because it removes the 4 flash limit and allows reflashing with secure boot enabled. + +**IMPORTANT** This method is intended to assist with development only, not for production devices. If pre-generating flash encryption for production, ensure the keys are generated from a high quality random number source and do not share the same flash encryption key across multiple devices. + +Obtaining Flash Encryption Key +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Flash encryption keys are 32 bytes of random data. You can generate a random key with espsecure.py:: + + espsecure.py generate_flash_encryption_key my_flash_encryption_key.bin + +(The randomness of this data is only as good as the OS and it's Python installation's random data source.) + +Alternatively, if you're using `secure boot` and have a secure boot signing key then you can generate a deterministic SHA-256 digest of the secure boot private key to use:: + + espsecure.py digest_private-key --keyfile secure_boot_signing_key.pem my_flash_encryption_key.bin + +The same key is used as the secure boot digest key if you enabled "Reflashable" mode for secure boot. + +This means you can always re-calculate the flash encryption key from the secure boot private signing key. This method is **not at all suitable** for production devices. + +Burning Flash Encryption Key +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Once you have generated a flash encryption key, you need to burn it to efuse on the device. This **must be done before first boot**, otherwise the ESP32 will generate a random key that software can't access. + +To burn a key to the device (possible one time only):: + + espefuse.py burn_key flash_encryption my_flash_encryption_key.bin + +First Flash +~~~~~~~~~~~ + +For the first flash, follow the same steps as for default `Flash Encryption Initialisation` and flash a plaintext image. The bootloader will enable flash encryption using the pre-burned key and encrypt all partitions. + +Reflashing +~~~~~~~~~~ + +To reflash an encrypted image requires an additional manual update step, to encrypt the data you wish to flash. + +Suppose that this is the normal flashing non-encrypted flashing step:: + + esptool.py --port /dev/ttyUSB0 --baud 115200 write_flash -z 0x10000 build/my-app.bin + +The data needs to be pre-encrypted with knowledge of the address (0x10000) and the binary file name:: + + espsecure.py encrypt_flash_data --keyfile my_flash_encryption_key.bin --address 0x10000 -o build/my-app-encrypted.bin build/my-app.bin + +This step will encrypt ``my-app.bin`` using the supplied key, and produce an encrypted file ``my-app-encrypted.bin``. Be sure that the address argument matches the address where you plan to flash the binary. + +Then, flash the encrypted binary with esptool.py:: + + esptool.py --port /dev/ttyUSB0 --baud 115200 write_flash -z 0x10000 build/my-app-encrypted.bin + +Enabling UART Bootloader Encryption/Decryption +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +By default, on first boot the flash encryption process will burn efuses ``DISABLE_DL_ENCRYPT``, ``DISABLE_DL_DECRYPT`` and ``DISABLE_DL_CACHE``. + +- ``DISABLE_DL_ENCRYPT`` disables the flash encryption operations when running in UART bootloader boot mode. +- ``DISABLE_DL_DECRYPT`` disables transparent flash decryption when running in UART bootloader mode, even if ``FLASH_CRYPT_CNT`` is set to enable it in normal operation. +- ``DISABLE_DL_CACHE`` disables the entire MMU flash cache when running in UART bootloader mode. + +It is possible to burn only some of these efuses, and write-protect the rest (with unset value 0) before the first boot, in order to preserve them:: + + espefuse.py burn_efuse DISABLE_DL_DECRYPT + espefuse.py write_protect_efuse DISABLE_DL_ENCRYPT + +(Note that all 3 of these efuses are disabled via one write protect bit, so write protecting one will write protect all of them.) + +Write protecting these efuses when they are unset (0) is not currently useful, as ``esptool.py`` does not support flash encryption functions. + +However, note that write protecting ``DISABLE_DL_DECRYPT`` when it is unset (0) effectively makes flash encryption useless, as an attacker with physical access can use UART bootloader mode to read out the flash. + +Technical Details +----------------- + +Flash Encryption Algorithm +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- AES-256 operates on 16 byte blocks of data. The flash encryption engine encrypts and decrypts data in 32 byte blocks, two AES blocks in series. + +- AES algorithm is used inverted in flash encryption, so the flash encryption "encrypt" operation is AES decrypt and the "decrypt" operation is AES encrypt. This is for performance reasons and does not alter the effectiveness of the algorithm. + +- The main flash encryption key is stored in efuse (BLK2) and by default is protected from further writes or software readout. + +- Each 32 byte block is encrypted with a unique key which is derived from this main flash encryption key XORed with the offset of this block in the flash (a "key tweak"). + +- The specific tweak depends on the setting of ``FLASH_CRYPT_CONFIG`` efuse. This is a 4 bit efuse, where each bit enables XORing of a particular range of the key bits: + - Bit 1, bits 0-66 of the key are XORed. + - Bit 2, bits 67-131 of the key are XORed. + - Bit 3, bits 132-194 of the key are XORed. + - Bit 4, bits 195-256 of the key are XORed. +It is recommended that ``FLASH_CRYPT_CONFIG`` is always left to set the default value `0xF`, so that all key bits are XORed with the block offset. See `Setting FLASH_CRYPT_CONFIG` for details. + +- The high 19 bits of the block offset (bit 5 to bit 23) are XORed with the main flash encryption key. This range is chosen for two reasons: the maximum flash size is 16MB (24 bits), and each block is 32 bytes so the least significant 5 bits are always zero. + +- There is a particular mapping from each of the 19 block offset bits to the 256 bits of the flash encryption key, to determine which bit is XORed with which. See the variable _FLASH_ENCRYPTION_TWEAK_PATTERN in espsecure.py for a list of these. + +- For the full algorithm implemented in Python, see `_flash_encryption_operation()` in the espsecure.py source code. + +Setting FLASH_CRYPT_CONFIG +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``FLASH_CRYPT_CONFIG`` efuse determines the number of bits in the flash encryption key which are "tweaked" with the block offset. See `Flash Encryption Algorithm` for details. + +First boot of the bootloader always sets this value to the maximum `0xF`. + +It is possible to write these efuse manually, and write protect it before first boot in order to select different tweak values. This is not recommended. + +It is strongly recommended to never write protect ``FLASH_CRYPT_CONFIG`` when it the value is zero. If this efuse is set to zero, no bits in the flash encryption key are tweaked and the flash encryption algorithm is equivalent to AES ECB mode. + +.. _Secure Boot: secure-boot.rst +.. _partition table: ../partition-tables.rst diff --git a/docs/security/secure-boot.rst b/docs/security/secure-boot.rst index 113d250902..65b6bab48f 100644 --- a/docs/security/secure-boot.rst +++ b/docs/security/secure-boot.rst @@ -3,9 +3,9 @@ Secure Boot Secure Boot is a feature for ensuring only your code can run on the chip. Data loaded from flash is verified on each reset. -Secure Boot is separate from the Encrypted Flash feature, and you can use secure boot without encrypting the flash contents. However we recommend using both features together for a secure environment. +Secure Boot is separate from the `Flash Encryption` feature, and you can use secure boot without encrypting the flash contents. However we recommend using both features together for a secure environment. -**IMPORTANT: As Encrypted Flash feature and related security features are not yet released, Secure Boot should not be considered sufficient for a secure device and we strongly recommend not enabling the one-time secure bootloader feature until it is mature.** +**IMPORTANT: Secure Boot feature is currently enabled for development use only, with a key generated on the host. The recommended production configuration, where the secure boot key is generated by the device on first boot, is currently disabled while final testing is done. This documentation refers to "One-Time Flashable" mode (where keys are generated on the device), but for now only the `Re-Flashable Software Bootloader` mode is available.** Background ---------- @@ -175,5 +175,26 @@ Deterministic ECDSA as specified by `RFC6979`. - In the bootloader, the public key (for signature verification) is flashed as 64 raw bytes. - Image signature is 68 bytes - a 4 byte version word (currently zero), followed by a 64 bytes of signature data. These 68 bytes are appended to an app image or partition table data. +Manual Commands +~~~~~~~~~~~~~~~ + +Secure boot is integrated into the esp-idf build system, so `make` will automatically sign an app image if secure boot is enabled. `make bootloader` will produce a bootloader digest if menuconfig is configured for it. + +However, it is possible to use the `espsecure.py` tool to make standalone signatures and digests. + +To sign a binary image:: + + espsecure.py sign_data --keyfile ./my_signing_key.pem --output ./image_signed.bin image-unsigned.bin + +Keyfile is the PEM file containing an ECDSA private signing key. + +To generate a bootloader digest:: + + espsecure.py digest_secure_bootloader --keyfile ./securebootkey.bin --output ./bootloader-digest.bin build/bootloader/bootloader.bin + +Keyfile is the 32 byte raw secure boot key for the device. To flash this digest onto the device:: + + esptool.py write_flash 0x0 bootloader-digest.bin .. _RFC6979: https://tools.ietf.org/html/rfc6979 +.. _Flash Encryption: flash-encryption.rst diff --git a/examples/11_rmt_nec_tx_rx/main/infrared_nec.c b/examples/11_rmt_nec_tx_rx/main/infrared_nec.c index dbd11fbae5..d2f7b091fa 100644 --- a/examples/11_rmt_nec_tx_rx/main/infrared_nec.c +++ b/examples/11_rmt_nec_tx_rx/main/infrared_nec.c @@ -43,7 +43,6 @@ static const char* NEC_TAG = "NEC"; #define RMT_TX_GPIO_NUM 16 /*!< GPIO number for transmitter signal */ #define RMT_RX_CHANNEL 0 /*!< RMT channel for receiver */ #define RMT_RX_GPIO_NUM 19 /*!< GPIO number for receiver */ -#define RMT_INTR_NUM 19 /*!< RMT interrupt number, select from soc.h */ #define RMT_CLK_DIV 100 /*!< RMT counter clock divider */ #define RMT_TICK_10_US (80000000/RMT_CLK_DIV/100000) /*!< RMT counter value for 10 us.(Source clock is APB clock) */ @@ -254,7 +253,7 @@ static void rmt_tx_init() rmt_tx.tx_config.idle_output_en = true; rmt_tx.rmt_mode = 0; rmt_config(&rmt_tx); - rmt_driver_install(rmt_tx.channel, 0, RMT_INTR_NUM); + rmt_driver_install(rmt_tx.channel, 0, 0); } /* @@ -272,7 +271,7 @@ void rmt_rx_init() rmt_rx.rx_config.filter_ticks_thresh = 100; rmt_rx.rx_config.idle_threshold = rmt_item32_tIMEOUT_US / 10 * (RMT_TICK_10_US); rmt_config(&rmt_rx); - rmt_driver_install(rmt_rx.channel, 1000, RMT_INTR_NUM); + rmt_driver_install(rmt_rx.channel, 1000, 0); } /** diff --git a/examples/13_timer_group/main/timer_group.c b/examples/13_timer_group/main/timer_group.c index 15d1ca2c05..9db471054d 100644 --- a/examples/13_timer_group/main/timer_group.c +++ b/examples/13_timer_group/main/timer_group.c @@ -16,8 +16,6 @@ #include "driver/periph_ctrl.h" #include "driver/timer.h" -#define TIMER_INTR_NUM_0 17 /*!< Interrupt number for hardware timer 0 */ -#define TIMER_INTR_NUM_1 18 /*!< Interrupt number for hardware timer 1*/ #define TIMER_INTR_SEL TIMER_INTR_LEVEL /*!< Timer level interrupt */ #define TIMER_GROUP TIMER_GROUP_0 /*!< Test on timer group 0 */ #define TIMER_DIVIDER 16 /*!< Hardware timer clock divider */ @@ -88,7 +86,9 @@ void IRAM_ATTR timer_group0_isr(void *para) /*Timer0 is an example that don't reload counter value*/ TIMERG0.hw_timer[timer_idx].update = 1; - /*We don't call a API here because they are not declared with IRAM_ATTR*/ + /* We don't call a API here because they are not declared with IRAM_ATTR. + If we're okay with the timer irq not being serviced while SPI flash cache is disabled, + we can alloc this interrupt without the ESP_INTR_FLAG_IRAM flag and use the normal API. */ TIMERG0.int_clr_timers.t0 = 1; uint64_t timer_val = ((uint64_t) TIMERG0.hw_timer[timer_idx].cnt_high) << 32 | TIMERG0.hw_timer[timer_idx].cnt_low; @@ -157,7 +157,7 @@ void tg0_timer0_init() /*Enable timer interrupt*/ timer_enable_intr(timer_group, timer_idx); /*Set ISR handler*/ - timer_isr_register(timer_group, timer_idx, TIMER_INTR_NUM_0, TIMER_INTR_SEL, timer_group0_isr, (void*) timer_idx); + timer_isr_register(timer_group, timer_idx, timer_group0_isr, (void*) timer_idx, ESP_INTR_FLAG_IRAM, NULL); /*Start timer counter*/ timer_start(timer_group, timer_idx); } @@ -187,7 +187,7 @@ void tg0_timer1_init() /*Enable timer interrupt*/ timer_enable_intr(timer_group, timer_idx); /*Set ISR handler*/ - timer_isr_register(timer_group, timer_idx, TIMER_INTR_NUM_1, TIMER_INTR_SEL, timer_group0_isr, (void*) timer_idx); + timer_isr_register(timer_group, timer_idx, timer_group0_isr, (void*) timer_idx, ESP_INTR_FLAG_IRAM, NULL); /*Start timer counter*/ timer_start(timer_group, timer_idx); } diff --git a/examples/16_pcnt/main/pcnt_test.c b/examples/16_pcnt/main/pcnt_test.c index 5ed5fc7b1d..b8489ecb2f 100644 --- a/examples/16_pcnt/main/pcnt_test.c +++ b/examples/16_pcnt/main/pcnt_test.c @@ -41,7 +41,6 @@ #define PCNT_L_LIM_VAL (-10) #define PCNT_THRESH1_VAL (5) #define PCNT_THRESH0_VAL (-5) -#define PCNT_INTR_NUM (18) #define PCNT_INPUT_SIG_IO (4) #define PCNT_INPUT_CTRL_IO (5) #define LEDC_OUPUT_IO (18) @@ -176,7 +175,7 @@ static void pcnt_init(void) /*Reset counter value*/ pcnt_counter_clear(PCNT_TEST_UNIT); /*Register ISR handler*/ - pcnt_isr_register(PCNT_INTR_NUM, pcnt_intr_handler, NULL); + pcnt_isr_register(pcnt_intr_handler, NULL, 0, NULL); /*Enable interrupt for PCNT unit*/ pcnt_intr_enable(PCNT_TEST_UNIT); /*Resume counting*/ diff --git a/make/common.mk b/make/common.mk index 4d40f7abc3..c0487d2737 100644 --- a/make/common.mk +++ b/make/common.mk @@ -52,5 +52,5 @@ endef # # example $(call resolvepath,$(CONFIG_PATH),$(CONFIG_DIR)) define resolvepath -$(if $(filter /%,$(1)),$(1),$(subst //,/,$(2)/$(1))) +$(foreach dir,$(1),$(if $(filter /%,$(dir)),$(dir),$(subst //,/,$(2)/$(dir)))) endef diff --git a/make/component_wrapper.mk b/make/component_wrapper.mk index 75bb4b1a03..a84208267a 100644 --- a/make/component_wrapper.mk +++ b/make/component_wrapper.mk @@ -95,7 +95,7 @@ COMPONENT_INCLUDES := $(OWN_INCLUDES) $(filter-out $(OWN_INCLUDES),$(COMPONENT_I # # This means if directories move (breaking absolute paths), don't need to 'make clean' define MakeVariablePath -$(subst $(IDF_PATH),$$(IDF_PATH),$(subst $(PROJECT_PATH),$$(PROJECT_PATH),$(subst $(BUILD_DIR_BASE),\$$(BUILD_DIR_BASE),$(1)))) +$(subst $(IDF_PATH),$$(IDF_PATH),$(subst $(PROJECT_PATH),$$(PROJECT_PATH),$(subst $(BUILD_DIR_BASE),$$(BUILD_DIR_BASE),$(1)))) endef # component_project_vars.mk target for the component. This is used to @@ -116,6 +116,7 @@ component_project_vars.mk:: @echo '# Automatically generated build file. Do not edit.' > $@ @echo 'COMPONENT_INCLUDES += $(call MakeVariablePath,$(addprefix $(COMPONENT_PATH)/,$(COMPONENT_ADD_INCLUDEDIRS)))' >> $@ @echo 'COMPONENT_LDFLAGS += $(call MakeVariablePath,$(COMPONENT_ADD_LDFLAGS))' >> $@ + @echo 'COMPONENT_LINKER_DEPS += $(call MakeVariablePath,$(call resolvepath,$(COMPONENT_ADD_LINKER_DEPS),$(COMPONENT_PATH)))' >> $@ @echo 'COMPONENT_SUBMODULES += $(call MakeVariablePath,$(addprefix $(COMPONENT_PATH)/,$(COMPONENT_SUBMODULES)))' >> $@ @echo '$(COMPONENT_NAME)-build: $(addsuffix -build,$(COMPONENT_DEPENDS))' >> $@ diff --git a/make/project.mk b/make/project.mk index 181152f118..0548002277 100644 --- a/make/project.mk +++ b/make/project.mk @@ -10,7 +10,7 @@ # where this file is located. # -.PHONY: build-components menuconfig defconfig all build clean all_binaries check-submodules +.PHONY: build-components menuconfig defconfig all build clean all_binaries check-submodules size all: all_binaries # see below for recipe of 'all' target # @@ -28,6 +28,7 @@ help: @echo "make all - Build app, bootloader, partition table" @echo "make flash - Flash all components to a fresh chip" @echo "make clean - Remove all build output" + @echo "make size - Display the memory footprint of the app" @echo "" @echo "make app - Build just the app" @echo "make app-flash - Flash just the app" @@ -139,7 +140,7 @@ export COMPONENT_INCLUDES include $(IDF_PATH)/make/common.mk all: -ifdef CONFIG_SECURE_BOOTLOADER_ENABLED +ifdef CONFIG_SECURE_BOOT_ENABLED @echo "(Secure boot enabled, so bootloader not flashed automatically. See 'make bootloader' output)" @echo "To flash app & partition table, run 'make flash' or:" else @@ -150,8 +151,6 @@ endif # Set default LDFLAGS LDFLAGS ?= -nostdlib \ - -L$(IDF_PATH)/lib \ - -L$(IDF_PATH)/ld \ $(addprefix -L$(BUILD_DIR_BASE)/,$(COMPONENTS) $(TEST_COMPONENT_NAMES) $(SRCDIRS) ) \ -u call_user_start_cpu0 \ $(EXTRA_LDFLAGS) \ @@ -173,7 +172,7 @@ LDFLAGS ?= -nostdlib \ # CPPFLAGS used by C preprocessor # If any flags are defined in application Makefile, add them at the end. -CPPFLAGS := -DESP_PLATFORM $(CPPFLAGS) $(EXTRA_CPPFLAGS) +CPPFLAGS := -DESP_PLATFORM -MMD -MP $(CPPFLAGS) $(EXTRA_CPPFLAGS) # Warnings-related flags relevant both for C and C++ COMMON_WARNING_FLAGS = -Wall -Werror=all \ @@ -189,8 +188,7 @@ COMMON_FLAGS = \ -ffunction-sections -fdata-sections \ -fstrict-volatile-bitfields \ -mlongcalls \ - -nostdlib \ - -MMD -MP + -nostdlib # Optimization flags are set based on menuconfig choice ifneq ("$(CONFIG_OPTIMIZATION_LEVEL_RELEASE)","") @@ -232,7 +230,8 @@ HOSTCC := $(CC) HOSTLD := $(LD) HOSTAR := $(AR) HOSTOBJCOPY := $(OBJCOPY) -export HOSTCC HOSTLD HOSTAR HOSTOBJCOPY +HOSTSIZE := $(SIZE) +export HOSTCC HOSTLD HOSTAR HOSTOBJCOPY SIZE # Set target compiler. Defaults to whatever the user has # configured as prefix + ye olde gcc commands @@ -241,7 +240,8 @@ CXX := $(call dequote,$(CONFIG_TOOLPREFIX))c++ LD := $(call dequote,$(CONFIG_TOOLPREFIX))ld AR := $(call dequote,$(CONFIG_TOOLPREFIX))ar OBJCOPY := $(call dequote,$(CONFIG_TOOLPREFIX))objcopy -export CC CXX LD AR OBJCOPY +SIZE := $(call dequote,$(CONFIG_TOOLPREFIX))size +export CC CXX LD AR OBJCOPY SIZE PYTHON=$(call dequote,$(CONFIG_PYTHON)) @@ -273,7 +273,10 @@ COMPONENT_LIBRARIES = $(filter $(notdir $(COMPONENT_PATHS_BUILDABLE)) $(TEST_COM # ELF depends on the library archive files for COMPONENT_LIBRARIES # the rules to build these are emitted as part of GenerateComponentTarget below -$(APP_ELF): $(foreach libcomp,$(COMPONENT_LIBRARIES),$(BUILD_DIR_BASE)/$(libcomp)/lib$(libcomp).a) +# +# also depends on additional dependencies (linker scripts & binary libraries) +# stored in COMPONENT_LINKER_DEPS, built via component.mk files' COMPONENT_ADD_LINKER_DEPS variable +$(APP_ELF): $(foreach libcomp,$(COMPONENT_LIBRARIES),$(BUILD_DIR_BASE)/$(libcomp)/lib$(libcomp).a) $(COMPONENT_LINKER_DEPS) $(summary) LD $(notdir $@) $(CC) $(LDFLAGS) -o $@ -Wl,-Map=$(APP_MAP) @@ -342,6 +345,9 @@ $(foreach component,$(TEST_COMPONENT_PATHS),$(eval $(call GenerateComponentTarge app-clean: $(addsuffix -clean,$(notdir $(COMPONENT_PATHS_BUILDABLE))) $(summary) RM $(APP_ELF) rm -f $(APP_ELF) $(APP_BIN) $(APP_MAP) + +size: $(APP_ELF) + $(SIZE) $(APP_ELF) # NB: this ordering is deliberate (app-clean before config-clean), # so config remains valid during all component clean targets @@ -368,7 +374,7 @@ $(IDF_PATH)/$(1)/.git: # Parse 'git submodule status' output for out-of-date submodule. # Status output prefixes status line with '+' if the submodule commit doesn't match ifneq ("$(shell cd ${IDF_PATH} && git submodule status $(1) | grep '^+')","") -$$(info WARNING: git submodule $(1) may be out of date. Run 'git submodule update' to update.) +$$(info WARNING: esp-idf git submodule $(1) may be out of date. Run 'git submodule update' in IDF_PATH dir to update.) endif endef diff --git a/make/test_build_system.sh b/make/test_build_system.sh index d08ae6c8a2..5d24e2a948 100755 --- a/make/test_build_system.sh +++ b/make/test_build_system.sh @@ -141,6 +141,30 @@ function run_tests() assert_built ${APP_BINS} ${BOOTLOADER_BINS} partitions_singleapp.bin [ -f ${BUILD}/partition*.bin ] || failure "A partition table should have been built in CRLF mode" + print_status "Touching rom ld file should re-link app and bootloader" + make + take_build_snapshot + touch ${IDF_PATH}/components/esp32/ld/esp32.rom.ld + make + assert_rebuilt ${APP_BINS} ${BOOTLOADER_BINS} + + print_status "Touching peripherals ld file should only re-link app" + take_build_snapshot + touch ${IDF_PATH}/components/esp32/ld/esp32.peripherals.ld + make + assert_rebuilt ${APP_BINS} + assert_not_rebuilt ${BOOTLOADER_BINS} + + print_status "sdkconfig update triggers recompiles" + make + take_build_snapshot + touch sdkconfig + make + # pick one each of .c, .cpp, .S that #includes sdkconfig.h + # and therefore should rebuild + assert_rebuilt newlib/syscall_table.o + assert_rebuilt nvs_flash/src/nvs_api.o + assert_rebuilt freertos/xtensa_vectors.o print_status "All tests completed" if [ -n "${FAILURES}" ]; then diff --git a/tools/kconfig/confdata.c b/tools/kconfig/confdata.c index dd243d2abd..1e3d1f35b2 100644 --- a/tools/kconfig/confdata.c +++ b/tools/kconfig/confdata.c @@ -771,7 +771,7 @@ int conf_write(const char *name) sprintf(newname, "%s%s", dirname, basename); env = getenv("KCONFIG_OVERWRITECONFIG"); if (!env || !*env) { - sprintf(tmpname, "%s.tmpconfig.%d", dirname, (int)getpid()); + sprintf(tmpname, "%s.tmpconfig.%d", newname, (int)getpid()); out = fopen(tmpname, "w"); } else { *tmpname = 0; diff --git a/tools/unit-test-app/sdkconfig b/tools/unit-test-app/sdkconfig index d82f83561d..1b9db99518 100644 --- a/tools/unit-test-app/sdkconfig +++ b/tools/unit-test-app/sdkconfig @@ -74,6 +74,7 @@ CONFIG_OPTIMIZATION_LEVEL_DEBUG=y # # Component config # +CONFIG_BTC_TASK_STACK_SIZE=2048 CONFIG_BT_RESERVE_DRAM=0 # @@ -85,7 +86,6 @@ CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240 # CONFIG_ESP32_ENABLE_STACK_WIFI is not set # CONFIG_ESP32_ENABLE_STACK_BT is not set -CONFIG_ESP32_ENABLE_STACK_NONE=y CONFIG_MEMMAP_SMP=y # CONFIG_MEMMAP_TRACEMEM is not set CONFIG_TRACEMEM_RESERVE_DRAM=0x0 @@ -93,6 +93,11 @@ CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32 CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2048 CONFIG_MAIN_TASK_STACK_SIZE=4096 CONFIG_NEWLIB_STDOUT_ADDCR=y +CONFIG_CONSOLE_UART_DEFAULT=y +# CONFIG_CONSOLE_UART_CUSTOM is not set +# CONFIG_CONSOLE_UART_NONE is not set +CONFIG_CONSOLE_UART_NUM=0 +CONFIG_CONSOLE_UART_BAUDRATE=115200 CONFIG_ULP_COPROC_ENABLED=y CONFIG_ULP_COPROC_RESERVE_MEM=512 # CONFIG_ESP32_PANIC_PRINT_HALT is not set @@ -165,7 +170,6 @@ CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=16384 CONFIG_MBEDTLS_HARDWARE_AES=y CONFIG_MBEDTLS_HARDWARE_MPI=y CONFIG_MBEDTLS_MPI_USE_INTERRUPT=y -CONFIG_MBEDTLS_MPI_INTERRUPT_NUM=18 CONFIG_MBEDTLS_HARDWARE_SHA=y #