diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index 91be3a6d6e..50c95f9fec 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -8,7 +8,7 @@ # basically runs Make in the src/ directory but it needs to zero some variables # the ESP-IDF project.mk makefile exports first, to not let them interfere. # -ifeq ("$(IS_BOOTLOADER_BUILD)","") +ifndef IS_BOOTLOADER_BUILD BOOTLOADER_COMPONENT_PATH := $(COMPONENT_PATH) BOOTLOADER_BUILD_DIR=$(abspath $(BUILD_DIR_BASE)/bootloader) diff --git a/components/bootloader/src/Makefile b/components/bootloader/src/Makefile index f30e314a5f..add9c15d61 100644 --- a/components/bootloader/src/Makefile +++ b/components/bootloader/src/Makefile @@ -4,12 +4,13 @@ # PROJECT_NAME := bootloader -COMPONENTS := esptool_py bootloader log +COMPONENTS := esptool_py bootloader log spi_flash # The bootloader pseudo-component is also included in this build, for its Kconfig.projbuild to be included. # # IS_BOOTLOADER_BUILD tells the component Makefile.projbuild to be a no-op IS_BOOTLOADER_BUILD := 1 +export IS_BOOTLOADER_BUILD #We cannot include the esp32 component directly but we need its includes. #This is fixed by adding CFLAGS from Makefile.projbuild diff --git a/components/bootloader/src/main/bootloader_config.h b/components/bootloader/src/main/bootloader_config.h index 709ff41b16..8a837693c2 100644 --- a/components/bootloader/src/main/bootloader_config.h +++ b/components/bootloader/src/main/bootloader_config.h @@ -20,12 +20,13 @@ extern "C" { #endif + +#include "esp_flash_data_types.h" + #define BOOT_VERSION "V0.1" #define SPI_SEC_SIZE 0x1000 #define MEM_CACHE(offset) (uint8_t *)(0x3f400000 + (offset)) #define CACHE_READ_32(offset) ((uint32_t *)(0x3f400000 + (offset))) -#define PARTITION_ADD 0x4000 -#define PARTITION_MAGIC 0x50AA #define IROM_LOW 0x400D0000 #define IROM_HIGH 0x40400000 #define DROM_LOW 0x3F400000 @@ -35,73 +36,6 @@ extern "C" #define RTC_DATA_LOW 0x50000000 #define RTC_DATA_HIGH 0x50002000 -/*spi mode,saved in third byte in flash */ -enum { - SPI_MODE_QIO, - SPI_MODE_QOUT, - SPI_MODE_DIO, - SPI_MODE_DOUT, - SPI_MODE_FAST_READ, - SPI_MODE_SLOW_READ -}; -/* spi speed*/ -enum { - SPI_SPEED_40M, - SPI_SPEED_26M, - SPI_SPEED_20M, - SPI_SPEED_80M = 0xF -}; -/*suppport flash size in esp32 */ -enum { - SPI_SIZE_1MB = 0, - SPI_SIZE_2MB, - SPI_SIZE_4MB, - SPI_SIZE_8MB, - SPI_SIZE_16MB, - SPI_SIZE_MAX -}; - - -struct flash_hdr { - char magic; - char blocks; - char spi_mode; /* flag of flash read mode in unpackage and usage in future */ - char spi_speed: 4; /* low bit */ - char spi_size: 4; - unsigned int entry_addr; - uint8_t encrypt_flag; /* encrypt flag */ - uint8_t secury_boot_flag; /* secury boot flag */ - char extra_header[14]; /* ESP32 additional header, unused by second bootloader */ -}; - -/* each header of flash bin block */ -struct block_hdr { - unsigned int load_addr; - unsigned int data_len; -}; - -/* OTA selection structure (two copies in the OTA data partition.) - - Size of 32 bytes is friendly to flash encryption */ -typedef struct { - uint32_t ota_seq; - uint8_t seq_label[24]; - uint32_t crc; /* CRC32 of ota_seq field only */ -} ota_select; - -typedef struct { - uint32_t offset; - uint32_t size; -} partition_pos_t; - -typedef struct { - uint16_t magic; - uint8_t type; /* partition Type */ - uint8_t subtype; /* part_subtype */ - partition_pos_t pos; - uint8_t label[16]; /* label for the partition */ - uint8_t reserved[4]; /* reserved */ -} partition_info_t; #define PART_TYPE_APP 0x00 #define PART_SUBTYPE_FACTORY 0x00 @@ -120,10 +54,10 @@ typedef struct { #define SPI_ERROR_LOG "spi flash error" typedef struct { - partition_pos_t ota_info; - partition_pos_t factory; - partition_pos_t test; - partition_pos_t ota[16]; + esp_partition_pos_t ota_info; + esp_partition_pos_t factory; + esp_partition_pos_t test; + esp_partition_pos_t ota[16]; uint32_t app_count; uint32_t selected_subtype; } bootloader_state_t; diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index a61ea77d59..5b1e152070 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -3,7 +3,7 @@ // 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 @@ -49,8 +49,8 @@ flash cache is down and the app CPU is in reset. We do have a stack, so we can d extern void Cache_Flush(int); void bootloader_main(); -void unpack_load_app(const partition_pos_t *app_node); -void print_flash_info(struct flash_hdr* pfhdr); +void unpack_load_app(const esp_partition_pos_t *app_node); +void print_flash_info(const esp_image_header_t* pfhdr); void IRAM_ATTR set_cache_and_start_app(uint32_t drom_addr, uint32_t drom_load_addr, uint32_t drom_size, @@ -58,6 +58,7 @@ void IRAM_ATTR set_cache_and_start_app(uint32_t drom_addr, uint32_t irom_load_addr, uint32_t irom_size, uint32_t entry_addr); +static void update_flash_config(const esp_image_header_t* pfhdr); void IRAM_ATTR call_start_cpu0() @@ -153,7 +154,7 @@ void boot_cache_redirect( uint32_t pos, size_t size ) */ bool load_partition_table(bootloader_state_t* bs, uint32_t addr) { - partition_info_t partition; + esp_partition_info_t partition; uint32_t end = addr + 0x1000; int index = 0; char *partition_usage; @@ -167,7 +168,7 @@ bool load_partition_table(bootloader_state_t* bs, uint32_t addr) ESP_LOGD(TAG, "type=%x subtype=%x", partition.type, partition.subtype); partition_usage = "unknown"; - if (partition.magic == PARTITION_MAGIC) { /* valid partition definition */ + if (partition.magic == ESP_PARTITION_MAGIC) { /* valid partition definition */ switch(partition.type) { case PART_TYPE_APP: /* app partition */ switch(partition.subtype) { @@ -230,12 +231,12 @@ bool load_partition_table(bootloader_state_t* bs, uint32_t addr) return true; } -static uint32_t ota_select_crc(const ota_select *s) +static uint32_t ota_select_crc(const esp_ota_select_entry_t *s) { return crc32_le(UINT32_MAX, (uint8_t*)&s->ota_seq, 4); } -static bool ota_select_valid(const ota_select *s) +static bool ota_select_valid(const esp_ota_select_entry_t *s) { return s->ota_seq != UINT32_MAX && s->crc == ota_select_crc(s); } @@ -251,30 +252,32 @@ void bootloader_main() { ESP_LOGI(TAG, "Espressif ESP32 2nd stage bootloader v. %s", BOOT_VERSION); - struct flash_hdr fhdr; + esp_image_header_t fhdr; bootloader_state_t bs; SpiFlashOpResult spiRet1,spiRet2; - ota_select sa,sb; + esp_ota_select_entry_t sa,sb; memset(&bs, 0, sizeof(bs)); ESP_LOGI(TAG, "compile time " __TIME__ ); - /* close watch dog here */ + /* disable watch dog here */ REG_CLR_BIT( RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_FLASHBOOT_MOD_EN ); REG_CLR_BIT( TIMG_WDTCONFIG0_REG(0), TIMG_WDT_FLASHBOOT_MOD_EN ); SPIUnlock(); /*register first sector in drom0 page 0 */ boot_cache_redirect( 0, 0x5000 ); - memcpy((unsigned int *) &fhdr, MEM_CACHE(0x1000), sizeof(struct flash_hdr) ); + memcpy((unsigned int *) &fhdr, MEM_CACHE(0x1000), sizeof(esp_image_header_t) ); print_flash_info(&fhdr); - if (!load_partition_table(&bs, PARTITION_ADD)) { + update_flash_config(&fhdr); + + if (!load_partition_table(&bs, ESP_PARTITION_TABLE_ADDR)) { ESP_LOGE(TAG, "load partition table error!"); return; } - partition_pos_t load_part_pos; + esp_partition_pos_t load_part_pos; if (bs.ota_info.offset != 0) { // check if partition table has OTA info partition //ESP_LOGE("OTA info sector handling is not implemented"); @@ -290,14 +293,14 @@ void bootloader_main() sb.crc = ota_select_crc(&sb); Cache_Read_Disable(0); - spiRet1 = SPIEraseSector(bs.ota_info.offset/0x1000); - spiRet2 = SPIEraseSector(bs.ota_info.offset/0x1000+1); + spiRet1 = SPIEraseSector(bs.ota_info.offset/0x1000); + spiRet2 = SPIEraseSector(bs.ota_info.offset/0x1000+1); if (spiRet1 != SPI_FLASH_RESULT_OK || spiRet2 != SPI_FLASH_RESULT_OK ) { ESP_LOGE(TAG, SPI_ERROR_LOG); return; } - spiRet1 = SPIWrite(bs.ota_info.offset,(uint32_t *)&sa,sizeof(ota_select)); - spiRet2 = SPIWrite(bs.ota_info.offset + 0x1000,(uint32_t *)&sb,sizeof(ota_select)); + spiRet1 = SPIWrite(bs.ota_info.offset,(uint32_t *)&sa,sizeof(esp_ota_select_entry_t)); + spiRet2 = SPIWrite(bs.ota_info.offset + 0x1000,(uint32_t *)&sb,sizeof(esp_ota_select_entry_t)); if (spiRet1 != SPI_FLASH_RESULT_OK || spiRet2 != SPI_FLASH_RESULT_OK ) { ESP_LOGE(TAG, SPI_ERROR_LOG); return; @@ -326,7 +329,7 @@ void bootloader_main() } ESP_LOGI(TAG, "Loading app partition at offset %08x", load_part_pos); - if(fhdr.secury_boot_flag == 0x01) { + if(fhdr.secure_boot_flag == 0x01) { /* protect the 2nd_boot */ if(false == secure_boot()){ ESP_LOGE(TAG, "secure boot failed"); @@ -347,12 +350,12 @@ void bootloader_main() } -void unpack_load_app(const partition_pos_t* partition) +void unpack_load_app(const esp_partition_pos_t* partition) { boot_cache_redirect(partition->offset, partition->size); uint32_t pos = 0; - struct flash_hdr image_header; + esp_image_header_t image_header; memcpy(&image_header, MEM_CACHE(pos), sizeof(image_header)); pos += sizeof(image_header); @@ -364,7 +367,7 @@ void unpack_load_app(const partition_pos_t* partition) uint32_t irom_size = 0; /* Reload the RTC memory sections whenever a non-deepsleep reset - is occuring */ + is occurring */ bool load_rtc_memory = rtc_get_reset_reason(0) != DEEPSLEEP_RESET; ESP_LOGD(TAG, "bin_header: %u %u %u %u %08x", image_header.magic, @@ -376,7 +379,7 @@ void unpack_load_app(const partition_pos_t* partition) for (uint32_t section_index = 0; section_index < image_header.blocks; ++section_index) { - struct block_hdr section_header = {0}; + esp_image_section_header_t section_header = {0}; memcpy(§ion_header, MEM_CACHE(pos), sizeof(section_header)); pos += sizeof(section_header); @@ -482,67 +485,84 @@ void IRAM_ATTR set_cache_and_start_app( (*entry)(); } +static void update_flash_config(const esp_image_header_t* pfhdr) +{ + uint32_t size; + switch(pfhdr->spi_size) { + case ESP_IMAGE_FLASH_SIZE_1MB: + size = 1; + break; + case ESP_IMAGE_FLASH_SIZE_2MB: + size = 2; + break; + case ESP_IMAGE_FLASH_SIZE_4MB: + size = 4; + break; + case ESP_IMAGE_FLASH_SIZE_8MB: + size = 8; + break; + case ESP_IMAGE_FLASH_SIZE_16MB: + size = 16; + break; + default: + size = 2; + } + Cache_Read_Disable( 0 ); + // Set flash chip size + SPIParamCfg(g_rom_flashchip.deviceId, size * 0x100000, 0x10000, 0x1000, 0x100, 0xffff); + // TODO: set mode + // TODO: set frequency + Cache_Flush(0); + Cache_Read_Enable( 0 ); +} -void print_flash_info(struct flash_hdr* pfhdr) +void print_flash_info(const esp_image_header_t* phdr) { #if (BOOT_LOG_LEVEL >= BOOT_LOG_LEVEL_NOTICE) - struct flash_hdr fhdr = *pfhdr; - - ESP_LOGD(TAG, "magic %02x", fhdr.magic ); - ESP_LOGD(TAG, "blocks %02x", fhdr.blocks ); - ESP_LOGD(TAG, "spi_mode %02x", fhdr.spi_mode ); - ESP_LOGD(TAG, "spi_speed %02x", fhdr.spi_speed ); - ESP_LOGD(TAG, "spi_size %02x", fhdr.spi_size ); + ESP_LOGD(TAG, "magic %02x", phdr->magic ); + ESP_LOGD(TAG, "blocks %02x", phdr->blocks ); + ESP_LOGD(TAG, "spi_mode %02x", phdr->spi_mode ); + ESP_LOGD(TAG, "spi_speed %02x", phdr->spi_speed ); + ESP_LOGD(TAG, "spi_size %02x", phdr->spi_size ); const char* str; - switch ( fhdr.spi_speed ) { - case SPI_SPEED_40M: + switch ( phdr->spi_speed ) { + case ESP_IMAGE_SPI_SPEED_40M: str = "40MHz"; break; - - case SPI_SPEED_26M: + case ESP_IMAGE_SPI_SPEED_26M: str = "26.7MHz"; break; - - case SPI_SPEED_20M: + case ESP_IMAGE_SPI_SPEED_20M: str = "20MHz"; break; - - case SPI_SPEED_80M: + case ESP_IMAGE_SPI_SPEED_80M: str = "80MHz"; break; - default: str = "20MHz"; break; } ESP_LOGI(TAG, "SPI Speed : %s", str ); - - - switch ( fhdr.spi_mode ) { - case SPI_MODE_QIO: + switch ( phdr->spi_mode ) { + case ESP_IMAGE_SPI_MODE_QIO: str = "QIO"; break; - - case SPI_MODE_QOUT: + case ESP_IMAGE_SPI_MODE_QOUT: str = "QOUT"; break; - - case SPI_MODE_DIO: + case ESP_IMAGE_SPI_MODE_DIO: str = "DIO"; break; - - case SPI_MODE_DOUT: + case ESP_IMAGE_SPI_MODE_DOUT: str = "DOUT"; break; - - case SPI_MODE_FAST_READ: + case ESP_IMAGE_SPI_MODE_FAST_READ: str = "FAST READ"; break; - - case SPI_MODE_SLOW_READ: + case ESP_IMAGE_SPI_MODE_SLOW_READ: str = "SLOW READ"; break; default: @@ -551,31 +571,24 @@ void print_flash_info(struct flash_hdr* pfhdr) } ESP_LOGI(TAG, "SPI Mode : %s", str ); - - - switch ( fhdr.spi_size ) { - case SPI_SIZE_1MB: + switch ( phdr->spi_size ) { + case ESP_IMAGE_FLASH_SIZE_1MB: str = "1MB"; break; - - case SPI_SIZE_2MB: + case ESP_IMAGE_FLASH_SIZE_2MB: str = "2MB"; break; - - case SPI_SIZE_4MB: + case ESP_IMAGE_FLASH_SIZE_4MB: str = "4MB"; break; - - case SPI_SIZE_8MB: + case ESP_IMAGE_FLASH_SIZE_8MB: str = "8MB"; break; - - case SPI_SIZE_16MB: + case ESP_IMAGE_FLASH_SIZE_16MB: str = "16MB"; break; - default: - str = "1MB"; + str = "2MB"; break; } ESP_LOGI(TAG, "SPI Flash Size : %s", str ); diff --git a/components/bootloader/src/main/flash_encrypt.c b/components/bootloader/src/main/flash_encrypt.c index 26e66aa039..2fb57a987d 100644 --- a/components/bootloader/src/main/flash_encrypt.c +++ b/components/bootloader/src/main/flash_encrypt.c @@ -128,7 +128,7 @@ bool flash_encrypt(bootloader_state_t *bs) return false; } /* encrypt partition table */ - if (false == flash_encrypt_write(PARTITION_ADD, SPI_SEC_SIZE)) { + if (false == flash_encrypt_write(ESP_PARTITION_TABLE_ADDR, SPI_SEC_SIZE)) { ESP_LOGE(TAG, "encrypt partition table error"); return false; } diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index 4aacd937ab..b5a8d2f2dd 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -81,8 +81,10 @@ config TRACEMEM_RESERVE_DRAM default 0x4000 if MEMMAP_TRACEMEM && !MEMMAP_TRACEMEM_TWOBANKS default 0x0 +# Not implemented and/or needs new silicon rev to work config MEMMAP_SPISRAM bool "Use external SPI SRAM chip as main memory" + depends on ESP32_NEEDS_NEW_SILICON_REV default "n" help The ESP32 can control an external SPI SRAM chip, adding the memory it contains to the @@ -152,4 +154,171 @@ config ULP_COPROC_RESERVE_MEM default 0 depends on !ULP_COPROC_ENABLED + +choice ESP32_PANIC + prompt "Panic handler behaviour" + default ESP32_PANIC_PRINT_REBOOT + help + If FreeRTOS detects unexpected behaviour or an unhandled exception, the panic handler is + invoked. Configure the panic handlers action here. + +config ESP32_PANIC_PRINT_HALT + bool "Print registers and halt" + help + Outputs the relevant registers over the serial port and halt the + processor. Needs a manual reset to restart. + +config ESP32_PANIC_PRINT_REBOOT + bool "Print registers and reboot" + help + Outputs the relevant registers over the serial port and immediately + reset the processor. + +config ESP32_PANIC_SILENT_REBOOT + bool "Silent reboot" + help + Just resets the processor without outputting anything + +config ESP32_PANIC_GDBSTUB + bool "Invoke GDBStub" + help + Invoke gdbstub on the serial port, allowing for gdb to attach to it to do a postmortem + of the crash. +endchoice + +config ESP32_DEBUG_OCDAWARE + bool "Make exception and panic handlers JTAG/OCD aware" + default y + help + The FreeRTOS panic and unhandled exception handers can detect a JTAG OCD debugger and + instead of panicking, have the debugger stop on the offending instruction. + + +config INT_WDT + bool "Interrupt watchdog" + default y + help + This watchdog timer can detect if the FreeRTOS tick interrupt has not been called for a certain time, + either because a task turned off interrupts and did not turn them on for a long time, or because an + interrupt handler did not return. It will try to invoke the panic handler first and failing that + reset the SoC. + +config INT_WDT_TIMEOUT_MS + int "Interrupt watchdog timeout (ms)" + depends on INT_WDT + default 300 + range 10 10000 + help + The timeout of the watchdog, in miliseconds. Make this higher than the FreeRTOS tick rate. + +config INT_WDT_CHECK_CPU1 + bool "Also watch CPU1 tick interrupt" + depends on INT_WDT && !FREERTOS_UNICORE + default y + help + Also detect if interrupts on CPU 1 are disabled for too long. + +config TASK_WDT + bool "Task watchdog" + default y + help + This watchdog timer can be used to make sure individual tasks are still running. + +config TASK_WDT_PANIC + bool "Invoke panic handler when Task Watchdog is triggered" + depends on TASK_WDT + default n + help + Normally, the Task Watchdog will only print out a warning if it detects it has not + been fed. If this is enabled, it will invoke the panic handler instead, which + can then halt or reboot the chip. + +config TASK_WDT_TIMEOUT_S + int "Task watchdog timeout (seconds)" + depends on TASK_WDT + range 1 60 + default 5 + help + Timeout for the task WDT, in seconds. + +config TASK_WDT_CHECK_IDLE_TASK + bool "Task watchdog watches CPU0 idle task" + depends on TASK_WDT + default y + help + With this turned on, the task WDT can detect if the idle task is not called within the task + watchdog timeout period. The idle task not being called usually is a symptom of another + task hoarding the CPU. It is also a bad thing because FreeRTOS household tasks depend on the + idle task getting some runtime every now and then. Take Care: With this disabled, this + watchdog will trigger if no tasks register themselves within the timeout value. + +config TASK_WDT_CHECK_IDLE_TASK_CPU1 + bool "Task watchdog also watches CPU1 idle task" + depends on TASK_WDT_CHECK_IDLE_TASK && !FREERTOS_UNICORE + default y + help + Also check the idle task that runs on CPU1. + +#The brownout detector code is disabled (by making it depend on a nonexisting symbol) because the current revision of ESP32 +#silicon has a bug in the brown-out detector, rendering it unusable for resetting the CPU. +config BROWNOUT_DET + bool "Hardware brownout detect & reset" + default y + depends on NEEDS_ESP32_NEW_SILICON_REV + help + The ESP32 has a built-in brownout detector which can detect if the voltage is lower than + a specific value. If this happens, it will reset the chip in order to prevent unintended + behaviour. + +choice BROWNOUT_DET_LVL_SEL + prompt "Brownout voltage level" + depends on BROWNOUT_DET + default BROWNOUT_DET_LVL_SEL_25 + help + The brownout detector will reset the chip when the supply voltage is below this level. + +#The voltage levels here are estimates, more work needs to be done to figure out the exact voltages +#of the brownout threshold levels. +config BROWNOUT_DET_LVL_SEL_0 + bool "2.1V" +config BROWNOUT_DET_LVL_SEL_1 + bool "2.2V" +config BROWNOUT_DET_LVL_SEL_2 + bool "2.3V" +config BROWNOUT_DET_LVL_SEL_3 + bool "2.4V" +config BROWNOUT_DET_LVL_SEL_4 + bool "2.5V" +config BROWNOUT_DET_LVL_SEL_5 + bool "2.6V" +config BROWNOUT_DET_LVL_SEL_6 + bool "2.7V" +config BROWNOUT_DET_LVL_SEL_7 + bool "2.8V" +endchoice + +config BROWNOUT_DET_LVL + int + default 0 if BROWNOUT_DET_LVL_SEL_0 + default 1 if BROWNOUT_DET_LVL_SEL_1 + default 2 if BROWNOUT_DET_LVL_SEL_2 + default 3 if BROWNOUT_DET_LVL_SEL_3 + default 4 if BROWNOUT_DET_LVL_SEL_4 + default 5 if BROWNOUT_DET_LVL_SEL_5 + default 6 if BROWNOUT_DET_LVL_SEL_6 + default 7 if BROWNOUT_DET_LVL_SEL_7 + + +config BROWNOUT_DET_RESETDELAY + int "Brownout reset delay (in uS)" + depends on BROWNOUT_DET + range 0 6820 + default 1000 + help + The brownout detector can reset the chip after a certain delay, in order to make sure e.g. a voltage dip has entirely passed + before trying to restart the chip. You can set the delay here. + + + + endmenu diff --git a/components/esp32/brownout.c b/components/esp32/brownout.c new file mode 100644 index 0000000000..1dcde078e3 --- /dev/null +++ b/components/esp32/brownout.c @@ -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. + + +#include +#include +#include +#include +#include "sdkconfig.h" +#include "soc/soc.h" +#include "soc/rtc_cntl_reg.h" + + +#if CONFIG_BROWNOUT_DET +/* +This file is included in esp-idf, but the menuconfig option for this is disabled because a silicon bug +prohibits the brownout detector from functioning correctly on the ESP32. +*/ + +void esp_brownout_init() { + WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, + RTC_CNTL_BROWN_OUT_ENA | (CONFIG_BROWNOUT_DET_LVL << RTC_CNTL_DBROWN_OUT_THRES_S) | + RTC_CNTL_BROWN_OUT_RST_ENA | (((CONFIG_BROWNOUT_DET_RESETDELAY*150)/1000) << RTC_CNTL_BROWN_OUT_RST_WAIT_S) | + RTC_CNTL_BROWN_OUT_PD_RF_ENA|RTC_CNTL_BROWN_OUT_CLOSE_FLASH_ENA); + +} + +#endif \ No newline at end of file diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 5c7a411c8e..d94a4e6e79 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -41,8 +41,13 @@ #include "esp_event.h" #include "esp_spi_flash.h" #include "esp_ipc.h" +#include "esp_crosscore_int.h" #include "esp_log.h" - +#include "esp_vfs_dev.h" +#include "esp_newlib.h" +#include "esp_brownout.h" +#include "esp_int_wdt.h" +#include "esp_task_wdt.h" #include "trax.h" void start_cpu0(void) __attribute__((weak, alias("start_cpu0_default"))); @@ -56,7 +61,6 @@ static bool app_cpu_started = false; static void do_global_ctors(void); static void main_task(void* args); -extern void ets_setup_syscalls(void); extern void app_main(void); extern int _bss_start; @@ -99,6 +103,10 @@ void IRAM_ATTR call_start_cpu0() #if !CONFIG_FREERTOS_UNICORE ESP_EARLY_LOGI(TAG, "Starting app cpu, entry point is %p", call_start_cpu1); + //Un-stall the app cpu; the panic handler may have stalled it. + CLEAR_PERI_REG_MASK(RTC_CNTL_SW_CPU_STALL_REG, RTC_CNTL_SW_STALL_APPCPU_C1_M); + CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_STALL_APPCPU_C0_M); + //Enable clock gating and reset the app cpu. SET_PERI_REG_MASK(DPORT_APPCPU_CTRL_B_REG, DPORT_APPCPU_CLKGATE_EN); CLEAR_PERI_REG_MASK(DPORT_APPCPU_CTRL_C_REG, DPORT_APPCPU_RUNSTALL); SET_PERI_REG_MASK(DPORT_APPCPU_CTRL_A_REG, DPORT_APPCPU_RESETTING); @@ -144,10 +152,29 @@ void start_cpu0_default(void) #endif esp_set_cpu_freq(); // set CPU frequency configured in menuconfig uart_div_modify(0, (APB_CLK_FREQ << 4) / 115200); - ets_setup_syscalls(); +#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_syscalls(); + esp_vfs_dev_uart_register(); + esp_reent_init(_GLOBAL_REENT); + const char* default_uart_dev = "/dev/uart/0"; + _GLOBAL_REENT->_stdout = fopen(default_uart_dev, "w"); + _GLOBAL_REENT->_stderr = fopen(default_uart_dev, "w"); + _GLOBAL_REENT->_stdin = fopen(default_uart_dev, "r"); do_global_ctors(); +#if !CONFIG_FREERTOS_UNICORE + esp_crosscore_int_init(); +#endif esp_ipc_init(); spi_flash_init(); + xTaskCreatePinnedToCore(&main_task, "main", ESP_TASK_MAIN_STACK, NULL, ESP_TASK_MAIN_PRIO, NULL, 0); @@ -165,6 +192,7 @@ void start_cpu1_default(void) while (port_xSchedulerRunning[0] == 0) { ; } + esp_crosscore_int_init(); ESP_LOGI(TAG, "Starting scheduler on APP CPU."); xPortStartScheduler(); } diff --git a/components/esp32/crosscore_int.c b/components/esp32/crosscore_int.c new file mode 100644 index 0000000000..60a45da402 --- /dev/null +++ b/components/esp32/crosscore_int.c @@ -0,0 +1,103 @@ +// 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 + +#include "esp_attr.h" +#include "esp_err.h" +#include "esp_intr.h" + +#include "rom/ets_sys.h" +#include "rom/uart.h" + +#include "soc/cpu.h" +#include "soc/dport_reg.h" +#include "soc/io_mux_reg.h" +#include "soc/rtc_cntl_reg.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" +#include "freertos/portmacro.h" + + +#define REASON_YIELD (1<<0) + +static portMUX_TYPE reasonSpinlock = portMUX_INITIALIZER_UNLOCKED; +static volatile uint32_t reason[ portNUM_PROCESSORS ]; + + +/* +ToDo: There is a small chance the CPU already has yielded when this ISR is serviced. In that case, it's running the intended task but +the ISR will cause it to switch _away_ from it. portYIELD_FROM_ISR will probably just schedule the task again, but have to check that. +*/ +static void esp_crosscore_isr(void *arg) { + uint32_t myReasonVal; +#if 0 + //A pointer to the correct reason array item is passed to this ISR. + volatile uint32_t *myReason=arg; +#else + //The previous line does not work yet, the interrupt code needs work to understand two separate interrupt and argument + //tables... this is a valid but slightly less optimal replacement. + volatile uint32_t *myReason=&reason[xPortGetCoreID()]; +#endif + //Clear the interrupt first. + if (xPortGetCoreID()==0) { + WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_0_REG, 0); + } else { + WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_1_REG, 0); + } + //Grab the reason and clear it. + portENTER_CRITICAL(&reasonSpinlock); + myReasonVal=*myReason; + *myReason=0; + portEXIT_CRITICAL(&reasonSpinlock); + + //Check what we need to do. + if (myReasonVal&REASON_YIELD) { + portYIELD_FROM_ISR(); + } +} + +//Initialize the crosscore interrupt on this core. Call this once +//on each active core. +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); + } else { + intr_matrix_set(xPortGetCoreID(), ETS_FROM_CPU_INTR1_SOURCE, ETS_FROM_CPU_INUM); + } + 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) { + assert(coreId + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define ESP_PARTITION_TABLE_ADDR 0x4000 +#define ESP_PARTITION_MAGIC 0x50AA + +/* SPI flash mode, used in esp_image_header_t */ +typedef enum { + ESP_IMAGE_SPI_MODE_QIO, + ESP_IMAGE_SPI_MODE_QOUT, + ESP_IMAGE_SPI_MODE_DIO, + ESP_IMAGE_SPI_MODE_DOUT, + ESP_IMAGE_SPI_MODE_FAST_READ, + ESP_IMAGE_SPI_MODE_SLOW_READ +} esp_image_spi_mode_t; + +/* SPI flash clock frequency */ +enum { + ESP_IMAGE_SPI_SPEED_40M, + ESP_IMAGE_SPI_SPEED_26M, + ESP_IMAGE_SPI_SPEED_20M, + ESP_IMAGE_SPI_SPEED_80M = 0xF +} esp_image_spi_freq_t; + +/* Supported SPI flash sizes */ +typedef enum { + ESP_IMAGE_FLASH_SIZE_1MB = 0, + ESP_IMAGE_FLASH_SIZE_2MB, + ESP_IMAGE_FLASH_SIZE_4MB, + ESP_IMAGE_FLASH_SIZE_8MB, + ESP_IMAGE_FLASH_SIZE_16MB, + ESP_IMAGE_FLASH_SIZE_MAX +} esp_image_flash_size_t; + +/* Main header of binary image */ +typedef struct { + uint8_t magic; + uint8_t blocks; + uint8_t spi_mode; /* flash read mode (esp_image_spi_mode_t as uint8_t) */ + uint8_t spi_speed: 4; /* flash frequency (esp_image_spi_freq_t as uint8_t) */ + uint8_t spi_size: 4; /* flash chip size (esp_image_flash_size_t as uint8_t) */ + uint32_t entry_addr; + uint8_t encrypt_flag; /* encrypt flag */ + uint8_t secure_boot_flag; /* secure boot flag */ + uint8_t extra_header[14]; /* ESP32 additional header, unused by second bootloader */ +} esp_image_header_t; + +/* Header of binary image segment */ +typedef struct { + uint32_t load_addr; + uint32_t data_len; +} esp_image_section_header_t; + + +/* OTA selection structure (two copies in the OTA data partition.) + Size of 32 bytes is friendly to flash encryption */ +typedef struct { + uint32_t ota_seq; + uint8_t seq_label[24]; + uint32_t crc; /* CRC32 of ota_seq field only */ +} esp_ota_select_entry_t; + + +typedef struct { + uint32_t offset; + uint32_t size; +} esp_partition_pos_t; + +/* Structure which describes the layout of partition table entry. + * See docs/partition_tables.rst for more information about individual fields. + */ +typedef struct { + uint16_t magic; + uint8_t type; + uint8_t subtype; + esp_partition_pos_t pos; + uint8_t label[16]; + uint8_t reserved[4]; +} esp_partition_info_t; + + +#ifdef __cplusplus +} +#endif + +#endif //__ESP_BIN_TYPES_H__ diff --git a/components/freertos/gdbstub.h b/components/esp32/include/esp_gdbstub.h similarity index 93% rename from components/freertos/gdbstub.h rename to components/esp32/include/esp_gdbstub.h index 5e97213c92..bc26f2941a 100644 --- a/components/freertos/gdbstub.h +++ b/components/esp32/include/esp_gdbstub.h @@ -17,6 +17,6 @@ #include #include "freertos/xtensa_api.h" -void gdbstubPanicHandler(XtExcFrame *frame); +void esp_gdbstub_panic_handler(XtExcFrame *frame); #endif \ No newline at end of file diff --git a/components/esp32/include/esp_int_wdt.h b/components/esp32/include/esp_int_wdt.h new file mode 100644 index 0000000000..4387400396 --- /dev/null +++ b/components/esp32/include/esp_int_wdt.h @@ -0,0 +1,60 @@ +// 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_INT_WDT_H +#define __ESP_INT_WDT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** @addtogroup Watchdog_APIs + * @{ + */ + +/* +This routine enables a watchdog to catch instances of processes disabling +interrupts for too long, or code within interrupt handlers taking too long. +It does this by setting up a watchdog which gets fed from the FreeRTOS +task switch interrupt. When this watchdog times out, initially it will call +a high-level interrupt routine that will panic FreeRTOS in order to allow +for forensic examination of the state of the CPU. When this interrupt +handler is not called and the watchdog times out a second time, it will +reset the SoC. + +This uses the TIMERG1 WDT. +*/ + + +/** + * @brief Initialize the interrupt watchdog. This is called in the init code if + * the interrupt watchdog is enabled in menuconfig. + * + * @param null + * + * @return null + */ +void esp_int_wdt_init(); + + +/** + * @} + */ + + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/components/esp32/include/esp_panic.h b/components/esp32/include/esp_panic.h new file mode 100644 index 0000000000..6aba6c5f48 --- /dev/null +++ b/components/esp32/include/esp_panic.h @@ -0,0 +1,21 @@ +#ifndef PANIC_H +#define PANIC_H + + +#define PANIC_RSN_NONE 0 +#define PANIC_RSN_DEBUGEXCEPTION 1 +#define PANIC_RSN_DOUBLEEXCEPTION 2 +#define PANIC_RSN_KERNELEXCEPTION 3 +#define PANIC_RSN_COPROCEXCEPTION 4 +#define PANIC_RSN_INTWDT_CPU0 5 +#define PANIC_RSN_INTWDT_CPU1 6 +#define PANIC_RSN_MAX 6 + + +#ifndef __ASSEMBLER__ + +void esp_set_breakpoint_if_jtag(void *fn); + +#endif + +#endif \ No newline at end of file diff --git a/components/esp32/include/esp_task_wdt.h b/components/esp32/include/esp_task_wdt.h new file mode 100644 index 0000000000..bbc4995674 --- /dev/null +++ b/components/esp32/include/esp_task_wdt.h @@ -0,0 +1,83 @@ +// 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_TASK_WDT_H +#define __ESP_TASK_WDT_H + +#ifdef __cplusplus +extern "C" { +#endif + + +/** \defgroup Watchdog_APIs Watchdog APIs + * @brief Watchdog APIs + */ + +/** @addtogroup Watchdog_APIs + * @{ + */ + +/* +This routine enables a more general-purpose task watchdog: tasks can individually +feed the watchdog and the watchdog will bark if one or more tasks haven't fed the +watchdog within the specified time. Optionally, the idle tasks can also configured +to feed the watchdog in a similar fashion, to detect CPU starvation. + +This uses the TIMERG0 WDT. +*/ + + +/** + * @brief Initialize the task watchdog. This is called in the init code, if the + * task watchdog is enabled in menuconfig. + * + * @param null + * + * @return null + */ +void esp_task_wdt_init(); + +/** + * @brief Feed the watchdog. After the first feeding session, the watchdog will expect the calling + * task to keep feeding the watchdog until task_wdt_delete() is called. + * + * @param null + * + * @return null + */ + +void esp_task_wdt_feed(); + + +/** + * @brief Delete the watchdog for the current task. + * + * @param null + * + * @return null + */ +void esp_task_wdt_delete(); + +/** + * @} + */ + + +#ifdef __cplusplus +} +#endif + + + +#endif \ No newline at end of file diff --git a/components/esp32/include/esp_wifi.h b/components/esp32/include/esp_wifi.h index 12378f3346..80ced5dc61 100644 --- a/components/esp32/include/esp_wifi.h +++ b/components/esp32/include/esp_wifi.h @@ -191,14 +191,14 @@ esp_err_t esp_wifi_disconnect(void); esp_err_t esp_wifi_clear_fast_connect(void); /** - * @brief Kick the all station or associated id equals to aid + * @brief deauthenticate all stations or associated id equals to aid * - * @param uint16_t aid : when aid is 0, kick all stations, otherwise kick station whose associated id is aid + * @param uint16_t aid : when aid is 0, deauthenticate all stations, otherwise deauthenticate station whose associated id is aid * * @return ESP_OK : succeed * @return others : fail */ -esp_err_t esp_wifi_kick_station(uint16_t aid); +esp_err_t esp_wifi_deauth_sta(uint16_t aid); /** * @brief Scan all available APs. @@ -235,19 +235,30 @@ esp_err_t esp_wifi_scan_stop(void); * @return ESP_OK : succeed * @return others : fail */ -esp_err_t esp_wifi_get_ap_num(uint16_t *number); +esp_err_t esp_wifi_scan_get_ap_num(uint16_t *number); /** * @brief Get AP list found in last scan * - * @param uint16_t *number : as input param, it stores max AP number ap_list can hold, as output param, it store + * @param uint16_t *number : as input param, it stores max AP number ap_records can hold, as output param, it store the actual AP number this API returns - * @param wifi_ap_list_t *ap_list : a list to hold the found APs + * @param wifi_ap_record_t *ap_records: wifi_ap_record_t array to hold the found APs * * @return ESP_OK : succeed * @return others : fail */ -esp_err_t esp_wifi_get_ap_list(uint16_t *number, wifi_ap_list_t *ap_list); +esp_err_t esp_wifi_scan_get_ap_records(uint16_t *number, wifi_ap_record_t *ap_records); + + +/** + * @brief Get information of AP associated with ESP32 station + * + * @param wifi_ap_record_t *ap_info: the wifi_ap_record_t to hold station assocated AP + * + * @return ESP_OK : succeed + * @return others : fail + */ +esp_err_t esp_wifi_sta_get_ap_info(wifi_ap_record_t *ap_info); /** * @brief Set current power save type @@ -471,14 +482,13 @@ esp_err_t esp_wifi_get_config(wifi_interface_t ifx, wifi_config_t *conf); * * @attention SSC only API * - * @param struct station_info **station : station list + * @param wifi_sta_list_t *sta: station list * * @return ESP_OK : succeed * @return others : fail */ -esp_err_t esp_wifi_get_station_list(struct station_info **station); +esp_err_t esp_wifi_ap_get_sta_list(wifi_sta_list_t *sta); -esp_err_t esp_wifi_free_station_list(void); /** * @brief Set the WiFi API configuration storage type diff --git a/components/esp32/include/esp_wifi_internal.h b/components/esp32/include/esp_wifi_internal.h new file mode 100644 index 0000000000..217d5f6d1f --- /dev/null +++ b/components/esp32/include/esp_wifi_internal.h @@ -0,0 +1,80 @@ +// 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. + +/* + * All the APIs declared here are internal only APIs, it can only be used by + * espressif internal modules, such as SSC, LWIP, TCPIP adapter etc, espressif + * customers are not recommended to use them. + * + * If someone really want to use specified APIs declared in here, please contact + * espressif AE/developer to make sure you know the limitations or risk of + * the API, otherwise you may get unexpected behavior!!! + * + */ + + +#ifndef __ESP_WIFI_INTERNAL_H__ +#define __ESP_WIFI_INTERNAL_H__ + +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "rom/queue.h" +#include "esp_err.h" +#include "esp_wifi_types.h" +#include "esp_event.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief get whether the wifi driver is allowed to transmit data or not + * + * @param none + * + * @return true : upper layer should stop to transmit data to wifi driver + * @return false : upper layer can transmit data to wifi driver + */ +bool esp_wifi_internal_tx_is_stop(void); + +/** + * @brief free the rx buffer which allocated by wifi driver + * + * @param void* buffer: rx buffer pointer + * + * @return nonoe + */ +void esp_wifi_internal_free_rx_buffer(void* buffer); + +/** + * @brief transmit the buffer via wifi driver + * + * @attention1 TODO should modify the return type from bool to int + * + * @param wifi_interface_t wifi_if : wifi interface id + * @param void *buffer : the buffer to be tansmit + * @param u16_t len : the length of buffer + * + * @return True : success transmit the buffer to wifi driver + * False : failed to transmit the buffer to wifi driver + */ +bool esp_wifi_internal_tx(wifi_interface_t wifi_if, void *buffer, u16_t len); + +#ifdef __cplusplus +} +#endif + +#endif /* __ESP_WIFI_H__ */ diff --git a/components/esp32/include/esp_wifi_types.h b/components/esp32/include/esp_wifi_types.h index b3474769e8..fb19aa8fc3 100644 --- a/components/esp32/include/esp_wifi_types.h +++ b/components/esp32/include/esp_wifi_types.h @@ -109,7 +109,7 @@ typedef struct { wifi_second_chan_t second; /**< second channel of AP */ int8_t rssi; /**< signal strength of AP */ wifi_auth_mode_t authmode; /**< authmode of AP */ -} wifi_ap_list_t; +} wifi_ap_record_t; typedef enum { WIFI_PS_NONE, /**< No power save */ @@ -150,10 +150,15 @@ typedef union { wifi_sta_config_t sta; /**< configuration of STA */ } wifi_config_t; -struct station_info { - STAILQ_ENTRY(station_info) next; - uint8_t bssid[6]; -}; +typedef struct { + uint8_t mac[6]; /**< mac address of sta that associated with ESP32 soft-AP */ +}wifi_sta_info_t; + +#define ESP_WIFI_MAX_CONN_NUM (10) /**< max number of stations which can connect to ESP32 soft-AP */ +typedef struct { + wifi_sta_info_t sta[ESP_WIFI_MAX_CONN_NUM]; /**< station list */ + int num; /**< number of station that associated with ESP32 soft-AP */ +}wifi_sta_list_t; typedef enum { WIFI_STORAGE_FLASH, /**< all configuration will strore in both memory and flash */ diff --git a/components/esp32/include/rom/spi_flash.h b/components/esp32/include/rom/spi_flash.h index ba8eebc2ce..1f14c6617a 100644 --- a/components/esp32/include/rom/spi_flash.h +++ b/components/esp32/include/rom/spi_flash.h @@ -218,7 +218,7 @@ void SelectSpiFunction(uint32_t ishspi); void spi_flash_attach(uint32_t ishspi, bool legacy); /** - * @brief SPI Read Flash status register. We use CMD 0x05. + * @brief SPI Read Flash status register. We use CMD 0x05 (RDSR). * Please do not call this function in SDK. * * @param SpiFlashChip *spi : The information for Flash, which is exported from ld file. @@ -232,7 +232,7 @@ void spi_flash_attach(uint32_t ishspi, bool legacy); SpiFlashOpResult SPI_read_status(SpiFlashChip *spi, uint32_t *status); /** - * @brief SPI Read Flash status register high 16 bit. We use CMD 0x35. + * @brief SPI Read Flash status register bits 8-15. We use CMD 0x35 (RDSR2). * Please do not call this function in SDK. * * @param SpiFlashChip *spi : The information for Flash, which is exported from ld file. @@ -243,7 +243,7 @@ SpiFlashOpResult SPI_read_status(SpiFlashChip *spi, uint32_t *status); * SPI_FLASH_RESULT_ERR : read error. * SPI_FLASH_RESULT_TIMEOUT : read timeout. */ -SpiFlashOpResult SPI_read_status_high(SpiFlashChip *spi, uint32_t *status); +SpiFlashOpResult SPI_read_status_high(uint32_t *status); /** * @brief Write status to Falsh status register. @@ -503,6 +503,12 @@ void SPI_Write_Encrypt_Disable(void); */ SpiFlashOpResult SPI_Encrypt_Write(uint32_t flash_addr, uint32_t *data, uint32_t len); + +/** @brief Global SpiFlashChip structure used by ROM functions + * + */ +extern SpiFlashChip g_rom_flashchip; + /** * @} */ diff --git a/components/esp32/include/soc/cpu.h b/components/esp32/include/soc/cpu.h index c74ba317c8..ee2907493e 100644 --- a/components/esp32/include/soc/cpu.h +++ b/components/esp32/include/soc/cpu.h @@ -15,6 +15,9 @@ #ifndef _SOC_CPU_H #define _SOC_CPU_H +#include +#include +#include #include "xtensa/corebits.h" /* C macros for xtensa special register read/write/exchange */ diff --git a/components/esp32/include/soc/dport_reg.h b/components/esp32/include/soc/dport_reg.h index d65d9edbce..0c43c08740 100644 --- a/components/esp32/include/soc/dport_reg.h +++ b/components/esp32/include/soc/dport_reg.h @@ -3830,6 +3830,11 @@ #define DPORT_DATE_S 0 #define DPORT_DPORT_DATE_VERSION 0x1605190 +/* Flash MMU table for PRO CPU */ +#define DPORT_PRO_FLASH_MMU_TABLE ((volatile uint32_t*) 0x3FF10000) + +/* Flash MMU table for APP CPU */ +#define DPORT_APP_FLASH_MMU_TABLE ((volatile uint32_t*) 0x3FF12000) diff --git a/components/esp32/include/soc/rtc_cntl_reg.h b/components/esp32/include/soc/rtc_cntl_reg.h index 47328611e0..bb4e2afced 100644 --- a/components/esp32/include/soc/rtc_cntl_reg.h +++ b/components/esp32/include/soc/rtc_cntl_reg.h @@ -14,6 +14,9 @@ #ifndef _SOC_RTC_CNTL_REG_H_ #define _SOC_RTC_CNTL_REG_H_ +/* The value that needs to be written to RTC_CNTL_WDT_WKEY to write-enable the wdt registers */ +#define RTC_CNTL_WDT_WKEY_VALUE 0x50D83AA1 + #include "soc.h" #define RTC_CNTL_OPTIONS0_REG (DR_REG_RTCCNTL_BASE + 0x0) diff --git a/components/esp32/include/soc/timer_group_reg.h b/components/esp32/include/soc/timer_group_reg.h index 96a5eb7905..2db2a7e3f1 100644 --- a/components/esp32/include/soc/timer_group_reg.h +++ b/components/esp32/include/soc/timer_group_reg.h @@ -15,6 +15,16 @@ #define __TIMG_REG_H__ #include "soc.h" +/* The value that needs to be written to TIMG_WDT_WKEY to write-enable the wdt registers */ +#define TIMG_WDT_WKEY_VALUE 0x50D83AA1 + +/* Possible values for TIMG_WDT_STGx */ +#define TIMG_WDT_STG_SEL_OFF 0 +#define TIMG_WDT_STG_SEL_INT 1 +#define TIMG_WDT_STG_SEL_RESET_CPU 2 +#define TIMG_WDT_STG_SEL_RESET_SYSTEM 3 + + #define REG_TIMG_BASE(i) (DR_REG_TIMERGROUP0_BASE + i*0x1000) #define TIMG_T0CONFIG_REG(i) (REG_TIMG_BASE(i) + 0x0000) /* TIMG_T0_EN : R/W ;bitpos:[31] ;default: 1'h0 ; */ diff --git a/components/esp32/int_wdt.c b/components/esp32/int_wdt.c new file mode 100644 index 0000000000..11de8f20d2 --- /dev/null +++ b/components/esp32/int_wdt.c @@ -0,0 +1,96 @@ +// 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 "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include +#include "esp_err.h" +#include "esp_intr.h" +#include "esp_attr.h" +#include "soc/timer_group_struct.h" +#include "soc/timer_group_reg.h" + +#include "esp_int_wdt.h" + +#if CONFIG_INT_WDT + + +#define WDT_INT_NUM 24 + + +void esp_int_wdt_init() { + TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; + TIMERG1.wdt_config0.sys_reset_length=7; //3.2uS + TIMERG1.wdt_config0.cpu_reset_length=7; //3.2uS + TIMERG1.wdt_config0.level_int_en=1; + TIMERG1.wdt_config0.stg0=TIMG_WDT_STG_SEL_INT; //1st stage timeout: interrupt + TIMERG1.wdt_config0.stg1=TIMG_WDT_STG_SEL_RESET_SYSTEM; //2nd stage timeout: reset system + TIMERG1.wdt_config1.clk_prescale=80*500; //Prescaler: wdt counts in ticks of 0.5mS + //The timer configs initially are set to 5 seconds, to make sure the CPU can start up. The tick hook sets + //it to their actual value. + TIMERG1.wdt_config2=10000; + TIMERG1.wdt_config3=10000; + TIMERG1.wdt_config0.en=1; + TIMERG1.wdt_feed=1; + TIMERG1.wdt_wprotect=0; + TIMERG1.int_clr_timers.wdt=1; + TIMERG1.int_ena.wdt=1; + ESP_INTR_DISABLE(WDT_INT_NUM); + intr_matrix_set(xPortGetCoreID(), ETS_TG1_WDT_LEVEL_INTR_SOURCE, WDT_INT_NUM); + //We do not register a handler for the interrupt because it is interrupt level 4 which + //is not servicable from C. Instead, xtensa_vectors.S has a call to the panic handler for + //this interrupt. + ESP_INTR_ENABLE(WDT_INT_NUM); +} + + +//Take care: the tick hook can also be called before esp_int_wdt_init() is called. +#if CONFIG_INT_WDT_CHECK_CPU1 +//Not static; the ISR assembly checks this. +bool int_wdt_app_cpu_ticked=false; + +void IRAM_ATTR vApplicationTickHook(void) { + if (xPortGetCoreID()!=0) { + int_wdt_app_cpu_ticked=true; + } else { + //Only feed wdt if app cpu also ticked. + if (int_wdt_app_cpu_ticked) { + TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; + TIMERG1.wdt_config2=CONFIG_INT_WDT_TIMEOUT_MS*2; //Set timeout before interrupt + TIMERG1.wdt_config3=CONFIG_INT_WDT_TIMEOUT_MS*4; //Set timeout before reset + TIMERG1.wdt_feed=1; + TIMERG1.wdt_wprotect=0; + int_wdt_app_cpu_ticked=false; + } + } +} +#else +void IRAM_ATTR vApplicationTickHook(void) { + if (xPortGetCoreID()!=0) return; + TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; + TIMERG1.wdt_config2=CONFIG_INT_WDT_TIMEOUT_MS*2; //Set timeout before interrupt + TIMERG1.wdt_config3=CONFIG_INT_WDT_TIMEOUT_MS*4; //Set timeout before reset + TIMERG1.wdt_feed=1; + TIMERG1.wdt_wprotect=0; +} +#endif + +#endif \ No newline at end of file diff --git a/components/esp32/ld/esp32.common.ld b/components/esp32/ld/esp32.common.ld index a3c6367840..7b14b6da1f 100644 --- a/components/esp32/ld/esp32.common.ld +++ b/components/esp32/ld/esp32.common.ld @@ -47,6 +47,7 @@ SECTIONS _iram_text_start = ABSOLUTE(.); *(.iram1 .iram1.*) *libfreertos.a:(.literal .text .literal.* .text.*) + *libesp32.a:panic.o(.literal .text .literal.* .text.*) *libphy.a:(.literal .text .literal.* .text.*) *librtc.a:(.literal .text .literal.* .text.*) *libpp.a:(.literal .text .literal.* .text.*) @@ -92,6 +93,7 @@ SECTIONS KEEP(*(.gnu.linkonce.s2.*)) KEEP(*(.jcr)) *(.dram1 .dram1.*) + *libesp32.a:panic.o(.rodata .rodata.*) _data_end = ABSOLUTE(.); . = ALIGN(4); _heap_start = ABSOLUTE(.); diff --git a/components/esp32/ld/esp32.rom.ld b/components/esp32/ld/esp32.rom.ld index cc14d3258a..5266a679be 100644 --- a/components/esp32/ld/esp32.rom.ld +++ b/components/esp32/ld/esp32.rom.ld @@ -286,6 +286,7 @@ PROVIDE ( _global_impure_ptr = 0x3ffae0b0 ); PROVIDE ( gmtime = 0x40059848 ); PROVIDE ( gmtime_r = 0x40059868 ); PROVIDE ( g_phyFuns_instance = 0x3ffae0c4 ); +PROVIDE ( g_rom_flashchip = 0x3ffae270 ); PROVIDE ( gpio_init = 0x40009c20 ); PROVIDE ( gpio_input_get = 0x40009b88 ); PROVIDE ( gpio_input_get_high = 0x40009b9c ); @@ -1584,6 +1585,8 @@ PROVIDE ( SPIEraseBlock = 0x40062c4c ); PROVIDE ( SPIEraseChip = 0x40062c14 ); PROVIDE ( SPIEraseSector = 0x40062ccc ); PROVIDE ( spi_flash_attach = 0x40062a6c ); +/* NB: SPIUnlock @ 0x400628b0 has been replaced with an updated + version in the "spi_flash" component */ PROVIDE ( SPILock = 0x400628f0 ); PROVIDE ( SPIMasterReadModeCnfig = 0x40062b64 ); PROVIDE ( spi_modes = 0x3ff99270 ); @@ -1595,9 +1598,8 @@ PROVIDE ( SPIReadModeCnfig = 0x40062944 ); PROVIDE ( SPI_read_status = 0x4006226c ); /* This is static function, but can be used, not generated by script*/ PROVIDE ( SPI_read_status_high = 0x40062448 ); -PROVIDE ( SPIUnlock = 0x400628b0 ); PROVIDE ( SPI_user_command_read = 0x400621b0 ); -PROVIDE ( spi_w25q16 = 0x3ffae270 ); +PROVIDE ( SPI_flashchip_data = 0x3ffae270 ); PROVIDE ( SPIWrite = 0x40062d50 ); /* This is static function, but can be used, not generated by script*/ PROVIDE ( SPI_write_enable = 0x40062320 ); diff --git a/components/esp32/lib b/components/esp32/lib index a1e5f8b953..9d18fd1a8f 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit a1e5f8b953c7934677ba7a6ed0a6dd2da0e6bd0f +Subproject commit 9d18fd1a8f7610130e69f8be74ec68f6399221b1 diff --git a/components/freertos/panic.c b/components/esp32/panic.c similarity index 66% rename from components/freertos/panic.c rename to components/esp32/panic.c index 6a87679d13..c5d8aa669e 100644 --- a/components/freertos/panic.c +++ b/components/esp32/panic.c @@ -25,8 +25,12 @@ #include "soc/io_mux_reg.h" #include "soc/dport_reg.h" #include "soc/rtc_cntl_reg.h" +#include "soc/timer_group_struct.h" +#include "soc/timer_group_reg.h" -#include "gdbstub.h" +#include "esp_gdbstub.h" +#include "esp_panic.h" +#include "esp_attr.h" /* Panic handlers; these get called when an unhandled exception occurs or the assembly-level @@ -34,7 +38,11 @@ task switching / interrupt code runs into an unrecoverable error. The default ta overflow handler also is in here. */ -#if !CONFIG_FREERTOS_PANIC_SILENT_REBOOT +/* +Note: The linker script will put everything in this file in IRAM/DRAM, so it also works with flash cache disabled. +*/ + +#if !CONFIG_ESP32_PANIC_SILENT_REBOOT //printf may be broken, so we fix our own printing fns... inline static void panicPutchar(char c) { while (((READ_PERI_REG(UART_STATUS_REG(0))>>UART_TXFIFO_CNT_S)&UART_TXFIFO_CNT)>=126) ; @@ -104,22 +112,22 @@ void commonErrorHandler(XtExcFrame *frame); static void haltOtherCore() { if (xPortGetCoreID()==0) { //Kill app cpu - CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_STALL_APPCPU_C1< +#include +#include +#include +#include +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include +#include "esp_err.h" +#include "esp_intr.h" +#include "esp_attr.h" +#include "soc/timer_group_struct.h" +#include "soc/timer_group_reg.h" +#include "esp_log.h" + +#include "esp_task_wdt.h" + +#if CONFIG_TASK_WDT + +static const char* TAG = "task_wdt"; + +typedef struct wdt_task_t wdt_task_t; +struct wdt_task_t { + TaskHandle_t task_handle; + bool fed_watchdog; + wdt_task_t *next; +}; + +static wdt_task_t *wdt_task_list=NULL; + +static void IRAM_ATTR task_wdt_isr(void *arg) { + wdt_task_t *wdttask; + const char *cpu; + //Feed the watchdog so we do not reset + TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; + TIMERG0.wdt_feed=1; + TIMERG0.wdt_wprotect=0; + //Ack interrupt + TIMERG0.int_clr_timers.wdt=1; + //Watchdog got triggered because at least one task did not report in. + ets_printf("Task watchdog got triggered. The following tasks did not feed the watchdog in time:\n"); + for (wdttask=wdt_task_list; wdttask!=NULL; wdttask=wdttask->next) { + if (!wdttask->fed_watchdog) { + cpu=xTaskGetAffinity(wdttask->task_handle)==0?"CPU 0":"CPU 1"; + if (xTaskGetAffinity(wdttask->task_handle)==tskNO_AFFINITY) cpu="CPU 0/1"; + printf(" - %s (%s)\n", pcTaskGetTaskName(wdttask->task_handle), cpu); + } + } + ets_printf("Tasks currently running:\n"); + for (int x=0; xnext) { + //See if we are at the current task. + if (wdttask->task_handle == handle) { + wdttask->fed_watchdog=true; + found_task=true; + } + //If even one task in the list doesn't have the do_feed_wdt var set, we do not feed the watchdog. + if (!wdttask->fed_watchdog) do_feed_wdt=false; + } + + if (!found_task) { + //This is the first time the task calls the task_wdt_feed function. Create a new entry for it in + //the linked list. + wdt_task_t *newtask=malloc(sizeof(wdt_task_t)); + memset(newtask, 0, sizeof(wdt_task_t)); + newtask->task_handle=handle; + newtask->fed_watchdog=true; + if (wdt_task_list == NULL) { + wdt_task_list=newtask; + } else { + for (wdttask=wdt_task_list; wdttask->next!=NULL; wdttask=wdttask->next) ; + wdttask->next=newtask; + } + } + if (do_feed_wdt) { + //All tasks have checked in; time to feed the hw watchdog. + TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; + TIMERG0.wdt_feed=1; + TIMERG0.wdt_wprotect=0; + //Reset fed_watchdog status + for (wdttask=wdt_task_list; wdttask->next!=NULL; wdttask=wdttask->next) wdttask->fed_watchdog=false; + } +} + +void esp_task_wdt_delete() { + TaskHandle_t handle=xTaskGetCurrentTaskHandle(); + wdt_task_t *wdttask=wdt_task_list; + //Wdt task list can't be empty + if (!wdt_task_list) { + ESP_LOGE(TAG, "task_wdt_delete: No tasks in list?"); + return; + } + if (handle==wdt_task_list) { + //Current task is first on list. + wdt_task_list=wdt_task_list->next; + free(wdttask); + } else { + //Find current task in list + while (wdttask->next!=NULL && wdttask->next->task_handle!=handle) wdttask=wdttask->next; + if (!wdttask->next) { + ESP_LOGE(TAG, "task_wdt_delete: Task never called task_wdt_feed!"); + return; + } + wdt_task_t *freeme=wdttask->next; + wdttask->next=wdttask->next->next; + free(freeme); + } +} + +void esp_task_wdt_init() { + TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; + TIMERG0.wdt_config0.sys_reset_length=7; //3.2uS + TIMERG0.wdt_config0.cpu_reset_length=7; //3.2uS + TIMERG0.wdt_config0.level_int_en=1; + TIMERG0.wdt_config0.stg0=TIMG_WDT_STG_SEL_INT; //1st stage timeout: interrupt + TIMERG0.wdt_config0.stg1=TIMG_WDT_STG_SEL_RESET_SYSTEM; //2nd stage timeout: reset system + TIMERG0.wdt_config1.clk_prescale=80*500; //Prescaler: wdt counts in ticks of 0.5mS + TIMERG0.wdt_config2=CONFIG_TASK_WDT_TIMEOUT_S*2000; //Set timeout before interrupt + TIMERG0.wdt_config3=CONFIG_TASK_WDT_TIMEOUT_S*4000; //Set timeout before reset + TIMERG0.wdt_config0.en=1; + TIMERG0.wdt_feed=1; + TIMERG0.wdt_wprotect=0; + ESP_INTR_DISABLE(ETS_T0_WDT_INUM); + intr_matrix_set(xPortGetCoreID(), ETS_TG0_WDT_LEVEL_INTR_SOURCE, ETS_T0_WDT_INUM); + xt_set_interrupt_handler(ETS_T0_WDT_INUM, task_wdt_isr, NULL); + TIMERG0.int_clr_timers.wdt=1; + TIMERG0.int_ena.wdt=1; + ESP_INTR_ENABLE(ETS_T0_WDT_INUM); +} + +#if CONFIG_TASK_WDT_CHECK_IDLE_TASK +void vApplicationIdleHook(void) { +#if !CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1 + if (xPortGetCoreID()!=0) return; +#endif + esp_task_wdt_feed(); +} +#endif + +#endif \ No newline at end of file diff --git a/components/esptool_py/Kconfig.projbuild b/components/esptool_py/Kconfig.projbuild index 3da802296a..8bab51225e 100644 --- a/components/esptool_py/Kconfig.projbuild +++ b/components/esptool_py/Kconfig.projbuild @@ -94,4 +94,31 @@ config ESPTOOLPY_FLASHFREQ default "26m" if ESPTOOLPY_FLASHFREQ_26M default "20m" if ESPTOOLPY_FLASHFREQ_20M + +choice ESPTOOLPY_FLASHSIZE + prompt "Flash size" + default ESPTOOLPY_FLASHSIZE_2MB + help + SPI flash size, in megabytes + +config ESPTOOLPY_FLASHSIZE_1MB + bool "1 MB" +config ESPTOOLPY_FLASHSIZE_2MB + bool "2 MB" +config ESPTOOLPY_FLASHSIZE_4MB + bool "4 MB" +config ESPTOOLPY_FLASHSIZE_8MB + bool "8 MB" +config ESPTOOLPY_FLASHSIZE_16MB + bool "16 MB" +endchoice + +config ESPTOOLPY_FLASHSIZE + string + default "1MB" if ESPTOOLPY_FLASHSIZE_1MB + default "2MB" if ESPTOOLPY_FLASHSIZE_2MB + default "4MB" if ESPTOOLPY_FLASHSIZE_4MB + default "8MB" if ESPTOOLPY_FLASHSIZE_8MB + default "16MB" if ESPTOOLPY_FLASHSIZE_16MB + endmenu diff --git a/components/esptool_py/Makefile.projbuild b/components/esptool_py/Makefile.projbuild index 4d0dd1b3e5..69c01e1e7f 100644 --- a/components/esptool_py/Makefile.projbuild +++ b/components/esptool_py/Makefile.projbuild @@ -4,6 +4,7 @@ ESPPORT ?= $(CONFIG_ESPTOOLPY_PORT) ESPBAUD ?= $(CONFIG_ESPTOOLPY_BAUD) ESPFLASHMODE ?= $(CONFIG_ESPTOOLPY_FLASHMODE) ESPFLASHFREQ ?= $(CONFIG_ESPTOOLPY_FLASHFREQ) +ESPFLASHSIZE ?= $(CONFIG_ESPTOOLPY_FLASHSIZE) PYTHON ?= $(call dequote,$(CONFIG_PYTHON)) @@ -15,13 +16,15 @@ ESPTOOLPY_SRC := $(COMPONENT_PATH)/esptool/esptool.py ESPTOOLPY := $(PYTHON) $(ESPTOOLPY_SRC) --chip esp32 ESPTOOLPY_SERIAL := $(ESPTOOLPY) --port $(ESPPORT) --baud $(ESPBAUD) +ESPTOOL_FLASH_OPTIONS := --flash_mode $(ESPFLASHMODE) --flash_freq $(ESPFLASHFREQ) --flash_size $(ESPFLASHSIZE) + # the no-stub argument is temporary until esptool.py fully supports compressed uploads -ESPTOOLPY_WRITE_FLASH=$(ESPTOOLPY_SERIAL) write_flash $(if $(CONFIG_ESPTOOLPY_COMPRESSED),-z) --flash_mode $(ESPFLASHMODE) --flash_freq $(ESPFLASHFREQ) +ESPTOOLPY_WRITE_FLASH=$(ESPTOOLPY_SERIAL) write_flash $(if $(CONFIG_ESPTOOLPY_COMPRESSED),-z) $(ESPTOOL_FLASH_OPTIONS) ESPTOOL_ALL_FLASH_ARGS += $(CONFIG_APP_OFFSET) $(APP_BIN) $(APP_BIN): $(APP_ELF) $(ESPTOOLPY_SRC) - $(Q) $(ESPTOOLPY) elf2image --flash_mode $(ESPFLASHMODE) --flash_freq $(ESPFLASHFREQ) -o $@ $< + $(Q) $(ESPTOOLPY) elf2image $(ESPTOOL_FLASH_OPTIONS) -o $@ $< flash: all_binaries $(ESPTOOLPY_SRC) @echo "Flashing binaries to serial port $(ESPPORT) (app at offset $(CONFIG_APP_OFFSET))..." diff --git a/components/esptool_py/esptool b/components/esptool_py/esptool index 197ba605fe..5c6962e894 160000 --- a/components/esptool_py/esptool +++ b/components/esptool_py/esptool @@ -1 +1 @@ -Subproject commit 197ba605fe0c05e16bf4c5ec07b726adc8d86abc +Subproject commit 5c6962e894e0a118c9a4b5760876433493449260 diff --git a/components/freertos/FreeRTOS-openocd.c b/components/freertos/FreeRTOS-openocd.c index 6177f02057..d74564495e 100644 --- a/components/freertos/FreeRTOS-openocd.c +++ b/components/freertos/FreeRTOS-openocd.c @@ -18,6 +18,6 @@ #define USED #endif -#ifdef CONFIG_FREERTOS_DEBUG_OCDAWARE +#ifdef CONFIG_ESP32_DEBUG_OCDAWARE const int USED uxTopUsedPriority = configMAX_PRIORITIES - 1; #endif \ No newline at end of file diff --git a/components/freertos/Kconfig b/components/freertos/Kconfig index 1a65e1eeb7..91e824b2dc 100644 --- a/components/freertos/Kconfig +++ b/components/freertos/Kconfig @@ -93,45 +93,6 @@ config FREERTOS_THREAD_LOCAL_STORAGE_POINTERS If using the WiFi stack, this value must be at least 1. -#This still needs to be implemented. -choice FREERTOS_PANIC - prompt "Panic handler behaviour" - default FREERTOS_PANIC_PRINT_REBOOT - help - If FreeRTOS detects unexpected behaviour or an unhandled exception, the panic handler is - invoked. Configure the panic handlers action here. - -config FREERTOS_PANIC_PRINT_HALT - bool "Print registers and halt" - help - Outputs the relevant registers over the serial port and halt the - processor. Needs a manual reset to restart. - -config FREERTOS_PANIC_PRINT_REBOOT - bool "Print registers and reboot" - help - Outputs the relevant registers over the serial port and immediately - reset the processor. - -config FREERTOS_PANIC_SILENT_REBOOT - bool "Silent reboot" - help - Just resets the processor without outputting anything - -config FREERTOS_PANIC_GDBSTUB - bool "Invoke GDBStub" - help - Invoke gdbstub on the serial port, allowing for gdb to attach to it to do a postmortem - of the crash. -endchoice - -config FREERTOS_DEBUG_OCDAWARE - bool "Make exception and panic handlers JTAG/OCD aware" - default y - help - The FreeRTOS panic and unhandled exception handers can detect a JTAG OCD debugger and - instead of panicking, have the debugger stop on the offending instruction. - choice FREERTOS_ASSERT prompt "FreeRTOS assertions" default FREERTOS_ASSERT_FAIL_ABORT @@ -160,7 +121,7 @@ endchoice config FREERTOS_BREAK_ON_SCHEDULER_START_JTAG bool "Stop program on scheduler start when JTAG/OCD is detected" - depends on FREERTOS_DEBUG_OCDAWARE + depends on ESP32_DEBUG_OCDAWARE default y help If JTAG/OCD is connected, stop execution when the scheduler is started and the first @@ -172,6 +133,14 @@ menuconfig ENABLE_MEMORY_DEBUG help Enable this option to show malloc heap block and memory crash detect +config FREERTOS_ISR_STACKSIZE + int "ISR stack size" + range 1536 32768 + default 1536 + help + The interrupt handlers have their own stack. The size of the stack can be defined here. + Each processor has its own stack, so the total size occupied will be twice this. + menuconfig FREERTOS_DEBUG_INTERNALS bool "Debug FreeRTOS internals" default n @@ -197,6 +166,8 @@ config FREERTOS_PORTMUX_DEBUG_RECURSIVE If enabled, additional debug information will be printed for recursive portMUX usage. + + endif # FREERTOS_DEBUG_INTERNALS endmenu diff --git a/components/freertos/include/freertos/FreeRTOS.h b/components/freertos/include/freertos/FreeRTOS.h index 04b39b65e9..f6c9aa497d 100644 --- a/components/freertos/include/freertos/FreeRTOS.h +++ b/components/freertos/include/freertos/FreeRTOS.h @@ -74,6 +74,7 @@ * Include the generic headers required for the FreeRTOS port being used. */ #include +#include "sys/reent.h" /* * If stdint.h cannot be located then: @@ -739,6 +740,20 @@ extern "C" { #define portTICK_TYPE_IS_ATOMIC 0 #endif +#ifndef configSUPPORT_STATIC_ALLOCATION + /* Defaults to 0 for backward compatibility. */ + #define configSUPPORT_STATIC_ALLOCATION 0 +#endif + +#ifndef configSUPPORT_DYNAMIC_ALLOCATION + /* Defaults to 1 for backward compatibility. */ + #define configSUPPORT_DYNAMIC_ALLOCATION 1 +#endif + +#if( ( configSUPPORT_STATIC_ALLOCATION == 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 0 ) ) + #error configSUPPORT_STATIC_ALLOCATION and configSUPPORT_DYNAMIC_ALLOCATION cannot both be 0, but can both be 1. +#endif + #if( portTICK_TYPE_IS_ATOMIC == 0 ) /* Either variables of tick type cannot be read atomically, or portTICK_TYPE_IS_ATOMIC was not set - map the critical sections used when @@ -791,6 +806,153 @@ V8 if desired. */ #define configESP32_PER_TASK_DATA 1 #endif +/* + * In line with software engineering best practice, FreeRTOS implements a strict + * data hiding policy, so the real structures used by FreeRTOS to maintain the + * state of tasks, queues, semaphores, etc. are not accessible to the application + * code. However, if the application writer wants to statically allocate such + * an object then the size of the object needs to be know. Dummy structures + * that are guaranteed to have the same size and alignment requirements of the + * real objects are used for this purpose. The dummy list and list item + * structures below are used for inclusion in such a dummy structure. + */ +struct xSTATIC_LIST_ITEM +{ + TickType_t xDummy1; + void *pvDummy2[ 4 ]; +}; +typedef struct xSTATIC_LIST_ITEM StaticListItem_t; + +/* See the comments above the struct xSTATIC_LIST_ITEM definition. */ +struct xSTATIC_MINI_LIST_ITEM +{ + TickType_t xDummy1; + void *pvDummy2[ 2 ]; +}; +typedef struct xSTATIC_MINI_LIST_ITEM StaticMiniListItem_t; + +/* See the comments above the struct xSTATIC_LIST_ITEM definition. */ +typedef struct xSTATIC_LIST +{ + UBaseType_t uxDummy1; + void *pvDummy2; + StaticMiniListItem_t xDummy3; +} StaticList_t; + +/* + * In line with software engineering best practice, especially when supplying a + * library that is likely to change in future versions, FreeRTOS implements a + * strict data hiding policy. This means the Task structure used internally by + * FreeRTOS is not accessible to application code. However, if the application + * writer wants to statically allocate the memory required to create a task then + * the size of the task object needs to be know. The StaticTask_t structure + * below is provided for this purpose. Its sizes and alignment requirements are + * guaranteed to match those of the genuine structure, no matter which + * architecture is being used, and no matter how the values in FreeRTOSConfig.h + * are set. Its contents are somewhat obfuscated in the hope users will + * recognise that it would be unwise to make direct use of the structure members. + */ +typedef struct xSTATIC_TCB +{ + void *pxDummy1; + #if ( portUSING_MPU_WRAPPERS == 1 ) + xMPU_SETTINGS xDummy2; + #endif + StaticListItem_t xDummy3[ 2 ]; + UBaseType_t uxDummy5; + void *pxDummy6; + uint8_t ucDummy7[ configMAX_TASK_NAME_LEN ]; + UBaseType_t uxDummyCoreId; + #if ( portSTACK_GROWTH > 0 ) + void *pxDummy8; + #endif + #if ( portCRITICAL_NESTING_IN_TCB == 1 ) + UBaseType_t uxDummy9; + uint32_t OldInterruptState; + #endif + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxDummy10[ 2 ]; + #endif + #if ( configUSE_MUTEXES == 1 ) + UBaseType_t uxDummy12[ 2 ]; + #endif + #if ( configUSE_APPLICATION_TASK_TAG == 1 ) + void *pxDummy14; + #endif + #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) + void *pvDummy15[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ]; + #if ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS ) + void *pvDummyLocalStorageCallBack[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ]; + #endif + #endif + #if ( configGENERATE_RUN_TIME_STATS == 1 ) + uint32_t ulDummy16; + #endif + #if ( configUSE_NEWLIB_REENTRANT == 1 ) + struct _reent xDummy17; + #endif + #if ( configUSE_TASK_NOTIFICATIONS == 1 ) + uint32_t ulDummy18; + uint32_t ucDummy19; + #endif + #if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + uint8_t uxDummy20; + #endif + +} StaticTask_t; + +/* + * In line with software engineering best practice, especially when supplying a + * library that is likely to change in future versions, FreeRTOS implements a + * strict data hiding policy. This means the Queue structure used internally by + * FreeRTOS is not accessible to application code. However, if the application + * writer wants to statically allocate the memory required to create a queue + * then the size of the queue object needs to be know. The StaticQueue_t + * structure below is provided for this purpose. Its sizes and alignment + * requirements are guaranteed to match those of the genuine structure, no + * matter which architecture is being used, and no matter how the values in + * FreeRTOSConfig.h are set. Its contents are somewhat obfuscated in the hope + * users will recognise that it would be unwise to make direct use of the + * structure members. + */ +typedef struct xSTATIC_QUEUE +{ + void *pvDummy1[ 3 ]; + + union + { + void *pvDummy2; + UBaseType_t uxDummy2; + } u; + + StaticList_t xDummy3[ 2 ]; + UBaseType_t uxDummy4[ 3 ]; + BaseType_t ucDummy5[ 2 ]; + + #if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + uint8_t ucDummy6; + #endif + + #if ( configUSE_QUEUE_SETS == 1 ) + void *pvDummy7; + #endif + + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxDummy8; + uint8_t ucDummy9; + #endif + + struct { + volatile uint32_t mux; + #ifdef CONFIG_FREERTOS_PORTMUX_DEBUG + const char *lastLockedFn; + int lastLockedLine; + #endif + } mux; + +} StaticQueue_t; +typedef StaticQueue_t StaticSemaphore_t; + #ifdef __cplusplus } #endif diff --git a/components/freertos/include/freertos/FreeRTOSConfig.h b/components/freertos/include/freertos/FreeRTOSConfig.h index d1958e7701..224857c86c 100644 --- a/components/freertos/include/freertos/FreeRTOSConfig.h +++ b/components/freertos/include/freertos/FreeRTOSConfig.h @@ -152,9 +152,9 @@ *----------------------------------------------------------*/ #define configUSE_PREEMPTION 1 -#define configUSE_IDLE_HOOK 0 +#define configUSE_IDLE_HOOK ( CONFIG_TASK_WDT_CHECK_IDLE_TASK ) -#define configUSE_TICK_HOOK 0 +#define configUSE_TICK_HOOK ( CONFIG_INT_WDT ) #define configTICK_RATE_HZ ( CONFIG_FREERTOS_HZ ) @@ -180,7 +180,7 @@ /* The Xtensa port uses a separate interrupt stack. Adjust the stack size */ /* to suit the needs of your specific application. */ #ifndef configISR_STACK_SIZE -#define configISR_STACK_SIZE 1024//2048 +#define configISR_STACK_SIZE CONFIG_FREERTOS_ISR_STACKSIZE #endif /* Minimal heap size to make sure examples can run on memory limited @@ -231,6 +231,7 @@ #define INCLUDE_vTaskDelayUntil 1 #define INCLUDE_vTaskDelay 1 #define INCLUDE_uxTaskGetStackHighWaterMark 1 +#define INCLUDE_pcTaskGetTaskName 1 #if CONFIG_ENABLE_MEMORY_DEBUG #define configENABLE_MEMORY_DEBUG 1 @@ -251,6 +252,8 @@ #define configUSE_NEWLIB_REENTRANT 1 +#define configSUPPORT_DYNAMIC_ALLOCATION 1 + /* Test FreeRTOS timers (with timer task) and more. */ /* Some files don't compile if this flag is disabled */ #define configUSE_TIMERS 1 diff --git a/components/freertos/include/freertos/panic.h b/components/freertos/include/freertos/panic.h deleted file mode 100644 index 9e902ed20b..0000000000 --- a/components/freertos/include/freertos/panic.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef PANIC_H -#define PANIC_H - -void setBreakpointIfJtag(void *fn); - - -#endif \ No newline at end of file diff --git a/components/freertos/include/freertos/portable.h b/components/freertos/include/freertos/portable.h index a3d39bd5a2..9ed378a8ab 100644 --- a/components/freertos/include/freertos/portable.h +++ b/components/freertos/include/freertos/portable.h @@ -179,6 +179,14 @@ BaseType_t xPortStartScheduler( void ) PRIVILEGED_FUNCTION; */ void vPortEndScheduler( void ) PRIVILEGED_FUNCTION; + +/* + * Send an interrupt to another core in order to make the task running + * on it yield for a higher-priority task. + */ + +void vPortYieldOtherCore( BaseType_t coreid) PRIVILEGED_FUNCTION; + /* * The structures and methods of manipulating the MPU are contained within the * port layer. @@ -192,7 +200,7 @@ void vPortEndScheduler( void ) PRIVILEGED_FUNCTION; #endif /* Multi-core: get current core ID */ -inline uint32_t xPortGetCoreID() { +static inline uint32_t xPortGetCoreID() { int id; asm volatile( "rsr.prid %0\n" diff --git a/components/freertos/include/freertos/portmacro.h b/components/freertos/include/freertos/portmacro.h index 5e2386d721..f20a4a1e26 100644 --- a/components/freertos/include/freertos/portmacro.h +++ b/components/freertos/include/freertos/portmacro.h @@ -234,7 +234,7 @@ static inline unsigned portENTER_CRITICAL_NESTED() { unsigned state = XTOS_SET_I * *bitwise inverse* of the old mem if the mem wasn't written. This doesn't seem to happen on the * ESP32, though. (Would show up directly if it did because the magic wouldn't match.) */ -inline void uxPortCompareSet(volatile uint32_t *addr, uint32_t compare, uint32_t *set) { +static inline void uxPortCompareSet(volatile uint32_t *addr, uint32_t compare, uint32_t *set) { __asm__ __volatile__( "WSR %2,SCOMPARE1 \n" "ISYNC \n" diff --git a/components/freertos/include/freertos/queue.h b/components/freertos/include/freertos/queue.h index 2095c59b0c..876f1a1b30 100644 --- a/components/freertos/include/freertos/queue.h +++ b/components/freertos/include/freertos/queue.h @@ -170,7 +170,95 @@ typedef void * QueueSetMemberHandle_t; * \defgroup xQueueCreate xQueueCreate * \ingroup QueueManagement */ -#define xQueueCreate( uxQueueLength, uxItemSize ) xQueueGenericCreate( uxQueueLength, uxItemSize, queueQUEUE_TYPE_BASE ) +#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + #define xQueueCreate( uxQueueLength, uxItemSize ) xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) ) +#endif + +/** + * queue. h + *
+ QueueHandle_t xQueueCreateStatic(
+							  UBaseType_t uxQueueLength,
+							  UBaseType_t uxItemSize,
+							  uint8_t *pucQueueStorageBuffer,
+							  StaticQueue_t *pxQueueBuffer
+						  );
+ * 
+ * + * Creates a new queue instance, and returns a handle by which the new queue + * can be referenced. + * + * Internally, within the FreeRTOS implementation, queues use two blocks of + * memory. The first block is used to hold the queue's data structures. The + * second block is used to hold items placed into the queue. If a queue is + * created using xQueueCreate() then both blocks of memory are automatically + * dynamically allocated inside the xQueueCreate() function. (see + * http://www.freertos.org/a00111.html). If a queue is created using + * xQueueCreateStatic() then the application writer must provide the memory that + * will get used by the queue. xQueueCreateStatic() therefore allows a queue to + * be created without using any dynamic memory allocation. + * + * http://www.FreeRTOS.org/Embedded-RTOS-Queues.html + * + * @param uxQueueLength The maximum number of items that the queue can contain. + * + * @param uxItemSize The number of bytes each item in the queue will require. + * Items are queued by copy, not by reference, so this is the number of bytes + * that will be copied for each posted item. Each item on the queue must be + * the same size. + * + * @param pucQueueStorageBuffer If uxItemSize is not zero then + * pucQueueStorageBuffer must point to a uint8_t array that is at least large + * enough to hold the maximum number of items that can be in the queue at any + * one time - which is ( uxQueueLength * uxItemsSize ) bytes. If uxItemSize is + * zero then pucQueueStorageBuffer can be NULL. + * + * @param pxQueueBuffer Must point to a variable of type StaticQueue_t, which + * will be used to hold the queue's data structure. + * + * @return If the queue is created then a handle to the created queue is + * returned. If pxQueueBuffer is NULL then NULL is returned. + * + * Example usage: +
+ struct AMessage
+ {
+	char ucMessageID;
+	char ucData[ 20 ];
+ };
+
+ #define QUEUE_LENGTH 10
+ #define ITEM_SIZE sizeof( uint32_t )
+
+ // xQueueBuffer will hold the queue structure.
+ StaticQueue_t xQueueBuffer;
+
+ // ucQueueStorage will hold the items posted to the queue.  Must be at least
+ // [(queue length) * ( queue item size)] bytes long.
+ uint8_t ucQueueStorage[ QUEUE_LENGTH * ITEM_SIZE ];
+
+ void vATask( void *pvParameters )
+ {
+ QueueHandle_t xQueue1;
+
+	// Create a queue capable of containing 10 uint32_t values.
+	xQueue1 = xQueueCreate( QUEUE_LENGTH, // The number of items the queue can hold.
+							ITEM_SIZE	  // The size of each item in the queue
+							&( ucQueueStorage[ 0 ] ), // The buffer that will hold the items in the queue.
+							&xQueueBuffer ); // The buffer that will hold the queue structure.
+
+	// The queue is guaranteed to be created successfully as no dynamic memory
+	// allocation is used.  Therefore xQueue1 is now a handle to a valid queue.
+
+	// ... Rest of task code.
+ }
+ 
+ * \defgroup xQueueCreateStatic xQueueCreateStatic + * \ingroup QueueManagement + */ +#if( configSUPPORT_STATIC_ALLOCATION == 1 ) + #define xQueueCreateStatic( uxQueueLength, uxItemSize, pucQueueStorage, pxQueueBuffer ) xQueueGenericCreateStatic( ( uxQueueLength ), ( uxItemSize ), ( pucQueueStorage ), ( pxQueueBuffer ), ( queueQUEUE_TYPE_BASE ) ) +#endif /* configSUPPORT_STATIC_ALLOCATION */ /** * queue. h @@ -1479,7 +1567,9 @@ BaseType_t xQueueCRReceive( QueueHandle_t xQueue, void *pvBuffer, TickType_t xTi * these functions directly. */ QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType ) PRIVILEGED_FUNCTION; +QueueHandle_t xQueueCreateMutexStatic( const uint8_t ucQueueType, StaticQueue_t *pxStaticQueue ) PRIVILEGED_FUNCTION; QueueHandle_t xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount ) PRIVILEGED_FUNCTION; +QueueHandle_t xQueueCreateCountingSemaphoreStatic( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount, StaticQueue_t *pxStaticQueue ) PRIVILEGED_FUNCTION; void* xQueueGetMutexHolder( QueueHandle_t xSemaphore ) PRIVILEGED_FUNCTION; /* @@ -1538,10 +1628,22 @@ BaseType_t xQueueGiveMutexRecursive( QueueHandle_t pxMutex ) PRIVILEGED_FUNCTION #endif /* - * Generic version of the queue creation function, which is in turn called by - * any queue, semaphore or mutex creation function or macro. + * Generic version of the function used to creaet a queue using dynamic memory + * allocation. This is called by other functions and macros that create other + * RTOS objects that use the queue structure as their base. */ -QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType ) PRIVILEGED_FUNCTION; +#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType ) PRIVILEGED_FUNCTION; +#endif + +/* + * Generic version of the function used to creaet a queue using dynamic memory + * allocation. This is called by other functions and macros that create other + * RTOS objects that use the queue structure as their base. + */ +#if( configSUPPORT_STATIC_ALLOCATION == 1 ) + QueueHandle_t xQueueGenericCreateStatic( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, StaticQueue_t *pxStaticQueue, const uint8_t ucQueueType ) PRIVILEGED_FUNCTION; +#endif /* * Queue sets provide a mechanism to allow a task to block (pend) on a read diff --git a/components/freertos/include/freertos/semphr.h b/components/freertos/include/freertos/semphr.h index 5866ab1ec5..6343d0190a 100644 --- a/components/freertos/include/freertos/semphr.h +++ b/components/freertos/include/freertos/semphr.h @@ -128,19 +128,37 @@ typedef QueueHandle_t SemaphoreHandle_t; * \defgroup vSemaphoreCreateBinary vSemaphoreCreateBinary * \ingroup Semaphores */ -#define vSemaphoreCreateBinary( xSemaphore ) \ - { \ - ( xSemaphore ) = xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE ); \ - if( ( xSemaphore ) != NULL ) \ - { \ - ( void ) xSemaphoreGive( ( xSemaphore ) ); \ - } \ - } +#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + #define vSemaphoreCreateBinary( xSemaphore ) \ + { \ + ( xSemaphore ) = xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE ); \ + if( ( xSemaphore ) != NULL ) \ + { \ + ( void ) xSemaphoreGive( ( xSemaphore ) ); \ + } \ + } +#endif /** * semphr. h *
SemaphoreHandle_t xSemaphoreCreateBinary( void )
* + * Creates a new binary semaphore instance, and returns a handle by which the + * new semaphore can be referenced. + * + * In many usage scenarios it is faster and more memory efficient to use a + * direct to task notification in place of a binary semaphore! + * http://www.freertos.org/RTOS-task-notifications.html + * + * Internally, within the FreeRTOS implementation, binary semaphores use a block + * of memory, in which the semaphore structure is stored. If a binary semaphore + * is created using xSemaphoreCreateBinary() then the required memory is + * automatically dynamically allocated inside the xSemaphoreCreateBinary() + * function. (see http://www.freertos.org/a00111.html). If a binary semaphore + * is created using xSemaphoreCreateBinaryStatic() then the application writer + * must provide the memory. xSemaphoreCreateBinaryStatic() therefore allows a + * binary semaphore to be created without using any dynamic memory allocation. + * * The old vSemaphoreCreateBinary() macro is now deprecated in favour of this * xSemaphoreCreateBinary() function. Note that binary semaphores created using * the vSemaphoreCreateBinary() macro are created in a state such that the @@ -182,7 +200,68 @@ typedef QueueHandle_t SemaphoreHandle_t; * \defgroup vSemaphoreCreateBinary vSemaphoreCreateBinary * \ingroup Semaphores */ -#define xSemaphoreCreateBinary() xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE ) +#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + #define xSemaphoreCreateBinary() xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE ) +#endif + +/** + * semphr. h + *
SemaphoreHandle_t xSemaphoreCreateBinaryStatic( StaticSemaphore_t *pxSemaphoreBuffer )
+ * + * Creates a new binary semaphore instance, and returns a handle by which the + * new semaphore can be referenced. + * + * NOTE: In many usage scenarios it is faster and more memory efficient to use a + * direct to task notification in place of a binary semaphore! + * http://www.freertos.org/RTOS-task-notifications.html + * + * Internally, within the FreeRTOS implementation, binary semaphores use a block + * of memory, in which the semaphore structure is stored. If a binary semaphore + * is created using xSemaphoreCreateBinary() then the required memory is + * automatically dynamically allocated inside the xSemaphoreCreateBinary() + * function. (see http://www.freertos.org/a00111.html). If a binary semaphore + * is created using xSemaphoreCreateBinaryStatic() then the application writer + * must provide the memory. xSemaphoreCreateBinaryStatic() therefore allows a + * binary semaphore to be created without using any dynamic memory allocation. + * + * This type of semaphore can be used for pure synchronisation between tasks or + * between an interrupt and a task. The semaphore need not be given back once + * obtained, so one task/interrupt can continuously 'give' the semaphore while + * another continuously 'takes' the semaphore. For this reason this type of + * semaphore does not use a priority inheritance mechanism. For an alternative + * that does use priority inheritance see xSemaphoreCreateMutex(). + * + * @param pxSemaphoreBuffer Must point to a variable of type StaticSemaphore_t, + * which will then be used to hold the semaphore's data structure, removing the + * need for the memory to be allocated dynamically. + * + * @return If the semaphore is created then a handle to the created semaphore is + * returned. If pxSemaphoreBuffer is NULL then NULL is returned. + * + * Example usage: +
+ SemaphoreHandle_t xSemaphore = NULL;
+ StaticSemaphore_t xSemaphoreBuffer;
+
+ void vATask( void * pvParameters )
+ {
+    // Semaphore cannot be used before a call to xSemaphoreCreateBinary().
+    // The semaphore's data structures will be placed in the xSemaphoreBuffer
+    // variable, the address of which is passed into the function.  The
+    // function's parameter is not NULL, so the function will not attempt any
+    // dynamic memory allocation, and therefore the function will not return
+    // return NULL.
+    xSemaphore = xSemaphoreCreateBinary( &xSemaphoreBuffer );
+
+    // Rest of task code goes here.
+ }
+ 
+ * \defgroup xSemaphoreCreateBinaryStatic xSemaphoreCreateBinaryStatic + * \ingroup Semaphores + */ +#if( configSUPPORT_STATIC_ALLOCATION == 1 ) + #define xSemaphoreCreateBinaryStatic( pxStaticSemaphore ) xQueueGenericCreateStatic( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, NULL, pxStaticSemaphore, queueQUEUE_TYPE_BINARY_SEMAPHORE ) +#endif /* configSUPPORT_STATIC_ALLOCATION */ /** * semphr. h @@ -652,9 +731,18 @@ typedef QueueHandle_t SemaphoreHandle_t; * Macro that implements a mutex semaphore by using the existing queue * mechanism. * - * Mutexes created using this macro can be accessed using the xSemaphoreTake() + * Internally, within the FreeRTOS implementation, mutex semaphores use a block + * of memory, in which the mutex structure is stored. If a mutex is created + * using xSemaphoreCreateMutex() then the required memory is automatically + * dynamically allocated inside the xSemaphoreCreateMutex() function. (see + * http://www.freertos.org/a00111.html). If a mutex is created using + * xSemaphoreCreateMutexStatic() then the application writer must provided the + * memory. xSemaphoreCreateMutexStatic() therefore allows a mutex to be created + * without using any dynamic memory allocation. + * + * Mutexes created using this function can be accessed using the xSemaphoreTake() * and xSemaphoreGive() macros. The xSemaphoreTakeRecursive() and - * xSemaphoreGiveRecursive() macros should not be used. + * xSemaphoreGiveRecursive() macros must not be used. * * This type of semaphore uses a priority inheritance mechanism so a task * 'taking' a semaphore MUST ALWAYS 'give' the semaphore back once the @@ -667,8 +755,9 @@ typedef QueueHandle_t SemaphoreHandle_t; * semaphore and another always 'takes' the semaphore) and from within interrupt * service routines. * - * @return xSemaphore Handle to the created mutex semaphore. Should be of type - * SemaphoreHandle_t. + * @return If the mutex was successfully created then a handle to the created + * semaphore is returned. If there was not enough heap to allocate the mutex + * data structures then NULL is returned. * * Example usage:
@@ -690,19 +779,93 @@ typedef QueueHandle_t SemaphoreHandle_t;
  * \defgroup vSemaphoreCreateMutex vSemaphoreCreateMutex
  * \ingroup Semaphores
  */
-#define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )
+#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
+	#define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )
+#endif
+
+/**
+ * semphr. h
+ * 
SemaphoreHandle_t xSemaphoreCreateMutexStatic( StaticSemaphore_t *pxMutexBuffer )
+ * + * Creates a new mutex type semaphore instance, and returns a handle by which + * the new mutex can be referenced. + * + * Internally, within the FreeRTOS implementation, mutex semaphores use a block + * of memory, in which the mutex structure is stored. If a mutex is created + * using xSemaphoreCreateMutex() then the required memory is automatically + * dynamically allocated inside the xSemaphoreCreateMutex() function. (see + * http://www.freertos.org/a00111.html). If a mutex is created using + * xSemaphoreCreateMutexStatic() then the application writer must provided the + * memory. xSemaphoreCreateMutexStatic() therefore allows a mutex to be created + * without using any dynamic memory allocation. + * + * Mutexes created using this function can be accessed using the xSemaphoreTake() + * and xSemaphoreGive() macros. The xSemaphoreTakeRecursive() and + * xSemaphoreGiveRecursive() macros must not be used. + * + * This type of semaphore uses a priority inheritance mechanism so a task + * 'taking' a semaphore MUST ALWAYS 'give' the semaphore back once the + * semaphore it is no longer required. + * + * Mutex type semaphores cannot be used from within interrupt service routines. + * + * See xSemaphoreCreateBinary() for an alternative implementation that can be + * used for pure synchronisation (where one task or interrupt always 'gives' the + * semaphore and another always 'takes' the semaphore) and from within interrupt + * service routines. + * + * @param pxMutexBuffer Must point to a variable of type StaticSemaphore_t, + * which will be used to hold the mutex's data structure, removing the need for + * the memory to be allocated dynamically. + * + * @return If the mutex was successfully created then a handle to the created + * mutex is returned. If pxMutexBuffer was NULL then NULL is returned. + * + * Example usage: +
+ SemaphoreHandle_t xSemaphore;
+ StaticSemaphore_t xMutexBuffer;
+
+ void vATask( void * pvParameters )
+ {
+    // A mutex cannot be used before it has been created.  xMutexBuffer is
+    // into xSemaphoreCreateMutexStatic() so no dynamic memory allocation is
+    // attempted.
+    xSemaphore = xSemaphoreCreateMutexStatic( &xMutexBuffer );
+
+    // As no dynamic memory allocation was performed, xSemaphore cannot be NULL,
+    // so there is no need to check it.
+ }
+ 
+ * \defgroup xSemaphoreCreateMutexStatic xSemaphoreCreateMutexStatic + * \ingroup Semaphores + */ + #if( configSUPPORT_STATIC_ALLOCATION == 1 ) + #define xSemaphoreCreateMutexStatic( pxMutexBuffer ) xQueueCreateMutexStatic( queueQUEUE_TYPE_MUTEX, ( pxMutexBuffer ) ) +#endif /* configSUPPORT_STATIC_ALLOCATION */ /** * semphr. h *
SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void )
* - * Macro that implements a recursive mutex by using the existing queue - * mechanism. + * Creates a new recursive mutex type semaphore instance, and returns a handle + * by which the new recursive mutex can be referenced. + * + * Internally, within the FreeRTOS implementation, recursive mutexs use a block + * of memory, in which the mutex structure is stored. If a recursive mutex is + * created using xSemaphoreCreateRecursiveMutex() then the required memory is + * automatically dynamically allocated inside the + * xSemaphoreCreateRecursiveMutex() function. (see + * http://www.freertos.org/a00111.html). If a recursive mutex is created using + * xSemaphoreCreateRecursiveMutexStatic() then the application writer must + * provide the memory that will get used by the mutex. + * xSemaphoreCreateRecursiveMutexStatic() therefore allows a recursive mutex to + * be created without using any dynamic memory allocation. * * Mutexes created using this macro can be accessed using the * xSemaphoreTakeRecursive() and xSemaphoreGiveRecursive() macros. The - * xSemaphoreTake() and xSemaphoreGive() macros should not be used. + * xSemaphoreTake() and xSemaphoreGive() macros must not be used. * * A mutex used recursively can be 'taken' repeatedly by the owner. The mutex * doesn't become available again until the owner has called @@ -745,14 +908,104 @@ typedef QueueHandle_t SemaphoreHandle_t; * \defgroup vSemaphoreCreateMutex vSemaphoreCreateMutex * \ingroup Semaphores */ -#define xSemaphoreCreateRecursiveMutex() xQueueCreateMutex( queueQUEUE_TYPE_RECURSIVE_MUTEX ) +#if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configUSE_RECURSIVE_MUTEXES == 1 ) ) + #define xSemaphoreCreateRecursiveMutex() xQueueCreateMutex( queueQUEUE_TYPE_RECURSIVE_MUTEX ) +#endif + +/** + * semphr. h + *
SemaphoreHandle_t xSemaphoreCreateRecursiveMutexStatic( StaticSemaphore_t *pxMutexBuffer )
+ * + * Creates a new recursive mutex type semaphore instance, and returns a handle + * by which the new recursive mutex can be referenced. + * + * Internally, within the FreeRTOS implementation, recursive mutexs use a block + * of memory, in which the mutex structure is stored. If a recursive mutex is + * created using xSemaphoreCreateRecursiveMutex() then the required memory is + * automatically dynamically allocated inside the + * xSemaphoreCreateRecursiveMutex() function. (see + * http://www.freertos.org/a00111.html). If a recursive mutex is created using + * xSemaphoreCreateRecursiveMutexStatic() then the application writer must + * provide the memory that will get used by the mutex. + * xSemaphoreCreateRecursiveMutexStatic() therefore allows a recursive mutex to + * be created without using any dynamic memory allocation. + * + * Mutexes created using this macro can be accessed using the + * xSemaphoreTakeRecursive() and xSemaphoreGiveRecursive() macros. The + * xSemaphoreTake() and xSemaphoreGive() macros must not be used. + * + * A mutex used recursively can be 'taken' repeatedly by the owner. The mutex + * doesn't become available again until the owner has called + * xSemaphoreGiveRecursive() for each successful 'take' request. For example, + * if a task successfully 'takes' the same mutex 5 times then the mutex will + * not be available to any other task until it has also 'given' the mutex back + * exactly five times. + * + * This type of semaphore uses a priority inheritance mechanism so a task + * 'taking' a semaphore MUST ALWAYS 'give' the semaphore back once the + * semaphore it is no longer required. + * + * Mutex type semaphores cannot be used from within interrupt service routines. + * + * See xSemaphoreCreateBinary() for an alternative implementation that can be + * used for pure synchronisation (where one task or interrupt always 'gives' the + * semaphore and another always 'takes' the semaphore) and from within interrupt + * service routines. + * + * @param pxMutexBuffer Must point to a variable of type StaticSemaphore_t, + * which will then be used to hold the recursive mutex's data structure, + * removing the need for the memory to be allocated dynamically. + * + * @return If the recursive mutex was successfully created then a handle to the + * created recursive mutex is returned. If pxMutexBuffer was NULL then NULL is + * returned. + * + * Example usage: +
+ SemaphoreHandle_t xSemaphore;
+ StaticSemaphore_t xMutexBuffer;
+
+ void vATask( void * pvParameters )
+ {
+    // A recursive semaphore cannot be used before it is created.  Here a
+    // recursive mutex is created using xSemaphoreCreateRecursiveMutexStatic().
+    // The address of xMutexBuffer is passed into the function, and will hold
+    // the mutexes data structures - so no dynamic memory allocation will be
+    // attempted.
+    xSemaphore = xSemaphoreCreateRecursiveMutexStatic( &xMutexBuffer );
+
+    // As no dynamic memory allocation was performed, xSemaphore cannot be NULL,
+    // so there is no need to check it.
+ }
+ 
+ * \defgroup xSemaphoreCreateRecursiveMutexStatic xSemaphoreCreateRecursiveMutexStatic + * \ingroup Semaphores + */ +#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configUSE_RECURSIVE_MUTEXES == 1 ) ) + #define xSemaphoreCreateRecursiveMutexStatic( pxStaticSemaphore ) xQueueCreateMutexStatic( queueQUEUE_TYPE_RECURSIVE_MUTEX, pxStaticSemaphore ) +#endif /* configSUPPORT_STATIC_ALLOCATION */ /** * semphr. h *
SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount )
* - * Macro that creates a counting semaphore by using the existing - * queue mechanism. + * Creates a new counting semaphore instance, and returns a handle by which the + * new counting semaphore can be referenced. + * + * In many usage scenarios it is faster and more memory efficient to use a + * direct to task notification in place of a counting semaphore! + * http://www.freertos.org/RTOS-task-notifications.html + * + * Internally, within the FreeRTOS implementation, counting semaphores use a + * block of memory, in which the counting semaphore structure is stored. If a + * counting semaphore is created using xSemaphoreCreateCounting() then the + * required memory is automatically dynamically allocated inside the + * xSemaphoreCreateCounting() function. (see + * http://www.freertos.org/a00111.html). If a counting semaphore is created + * using xSemaphoreCreateCountingStatic() then the application writer can + * instead optionally provide the memory that will get used by the counting + * semaphore. xSemaphoreCreateCountingStatic() therefore allows a counting + * semaphore to be created without using any dynamic memory allocation. * * Counting semaphores are typically used for two things: * @@ -808,7 +1061,94 @@ typedef QueueHandle_t SemaphoreHandle_t; * \defgroup xSemaphoreCreateCounting xSemaphoreCreateCounting * \ingroup Semaphores */ -#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) ) +#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + #define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) ) +#endif + +/** + * semphr. h + *
SemaphoreHandle_t xSemaphoreCreateCountingStatic( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount, StaticSemaphore_t *pxSemaphoreBuffer )
+ * + * Creates a new counting semaphore instance, and returns a handle by which the + * new counting semaphore can be referenced. + * + * In many usage scenarios it is faster and more memory efficient to use a + * direct to task notification in place of a counting semaphore! + * http://www.freertos.org/RTOS-task-notifications.html + * + * Internally, within the FreeRTOS implementation, counting semaphores use a + * block of memory, in which the counting semaphore structure is stored. If a + * counting semaphore is created using xSemaphoreCreateCounting() then the + * required memory is automatically dynamically allocated inside the + * xSemaphoreCreateCounting() function. (see + * http://www.freertos.org/a00111.html). If a counting semaphore is created + * using xSemaphoreCreateCountingStatic() then the application writer must + * provide the memory. xSemaphoreCreateCountingStatic() therefore allows a + * counting semaphore to be created without using any dynamic memory allocation. + * + * Counting semaphores are typically used for two things: + * + * 1) Counting events. + * + * In this usage scenario an event handler will 'give' a semaphore each time + * an event occurs (incrementing the semaphore count value), and a handler + * task will 'take' a semaphore each time it processes an event + * (decrementing the semaphore count value). The count value is therefore + * the difference between the number of events that have occurred and the + * number that have been processed. In this case it is desirable for the + * initial count value to be zero. + * + * 2) Resource management. + * + * In this usage scenario the count value indicates the number of resources + * available. To obtain control of a resource a task must first obtain a + * semaphore - decrementing the semaphore count value. When the count value + * reaches zero there are no free resources. When a task finishes with the + * resource it 'gives' the semaphore back - incrementing the semaphore count + * value. In this case it is desirable for the initial count value to be + * equal to the maximum count value, indicating that all resources are free. + * + * @param uxMaxCount The maximum count value that can be reached. When the + * semaphore reaches this value it can no longer be 'given'. + * + * @param uxInitialCount The count value assigned to the semaphore when it is + * created. + * + * @param pxSemaphoreBuffer Must point to a variable of type StaticSemaphore_t, + * which will then be used to hold the semaphore's data structure, removing the + * need for the memory to be allocated dynamically. + * + * @return If the counting semaphore was successfully created then a handle to + * the created counting semaphore is returned. If pxSemaphoreBuffer was NULL + * then NULL is returned. + * + * Example usage: +
+ SemaphoreHandle_t xSemaphore;
+ StaticSemaphore_t xSemaphoreBuffer;
+
+ void vATask( void * pvParameters )
+ {
+ SemaphoreHandle_t xSemaphore = NULL;
+
+    // Counting semaphore cannot be used before they have been created.  Create
+    // a counting semaphore using xSemaphoreCreateCountingStatic().  The max
+    // value to which the semaphore can count is 10, and the initial value
+    // assigned to the count will be 0.  The address of xSemaphoreBuffer is
+    // passed in and will be used to hold the semaphore structure, so no dynamic
+    // memory allocation will be used.
+    xSemaphore = xSemaphoreCreateCounting( 10, 0, &xSemaphoreBuffer );
+
+    // No memory allocation was attempted so xSemaphore cannot be NULL, so there
+    // is no need to check its value.
+ }
+ 
+ * \defgroup xSemaphoreCreateCountingStatic xSemaphoreCreateCountingStatic + * \ingroup Semaphores + */ +#if( configSUPPORT_STATIC_ALLOCATION == 1 ) + #define xSemaphoreCreateCountingStatic( uxMaxCount, uxInitialCount, pxSemaphoreBuffer ) xQueueCreateCountingSemaphoreStatic( ( uxMaxCount ), ( uxInitialCount ), ( pxSemaphoreBuffer ) ) +#endif /* configSUPPORT_STATIC_ALLOCATION */ /** * semphr. h diff --git a/components/freertos/include/freertos/task.h b/components/freertos/include/freertos/task.h index 9f3f3d659e..f7b9181fcf 100644 --- a/components/freertos/include/freertos/task.h +++ b/components/freertos/include/freertos/task.h @@ -177,6 +177,7 @@ typedef struct xTASK_STATUS UBaseType_t uxCurrentPriority; /* The priority at which the task was running (may be inherited) when the structure was populated. */ UBaseType_t uxBasePriority; /* The priority to which the task will return if the task's current priority has been inherited to avoid unbounded priority inversion when obtaining a mutex. Only valid if configUSE_MUTEXES is defined as 1 in FreeRTOSConfig.h. */ uint32_t ulRunTimeCounter; /* The total run time allocated to the task so far, as defined by the run time stats clock. See http://www.freertos.org/rtos-run-time-stats.html. Only valid when configGENERATE_RUN_TIME_STATS is defined as 1 in FreeRTOSConfig.h. */ + StackType_t *pxStackBase; /* Points to the lowest address of the task's stack area. */ uint16_t usStackHighWaterMark; /* The minimum amount of stack space that has remained for the task since the task was created. The closer this value is to zero the closer the task has come to overflowing its stack. */ } TaskStatus_t; @@ -281,8 +282,19 @@ is used in assert() statements. */ );
* * Create a new task and add it to the list of tasks that are ready to run. - * On multicore environments, this will give no specific affinity to the task. - * Use xTaskCreatePinnedToCore to give affinity. + * + * Internally, within the FreeRTOS implementation, tasks use two blocks of + * memory. The first block is used to hold the task's data structures. The + * second block is used by the task as its stack. If a task is created using + * xTaskCreate() then both blocks of memory are automatically dynamically + * allocated inside the xTaskCreate() function. (see + * http://www.freertos.org/a00111.html). If a task is created using + * xTaskCreateStatic() then the application writer must provide the required + * memory. xTaskCreateStatic() therefore allows a task to be created without + * using any dynamic memory allocation. + * + * See xTaskCreateStatic() for a version that does not use any dynamic memory + * allocation. * * xTaskCreate() can only be used to create a task that has unrestricted * access to the entire microcontroller memory map. Systems that include MPU @@ -350,8 +362,139 @@ is used in assert() statements. */ * \defgroup xTaskCreate xTaskCreate * \ingroup Tasks */ -#define xTaskCreate( pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask ) xTaskGenericCreate( ( pvTaskCode ), ( pcName ), ( usStackDepth ), ( pvParameters ), ( uxPriority ), ( pxCreatedTask ), ( NULL ), ( NULL ), tskNO_AFFINITY ) -#define xTaskCreatePinnedToCore( pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask, xCoreID ) xTaskGenericCreate( ( pvTaskCode ), ( pcName ), ( usStackDepth ), ( pvParameters ), ( uxPriority ), ( pxCreatedTask ), ( NULL ), ( NULL ), xCoreID ) +#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + BaseType_t xTaskCreatePinnedToCore( TaskFunction_t pxTaskCode, + const char * const pcName, + const uint16_t usStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + TaskHandle_t * const pxCreatedTask, + const BaseType_t xCoreID); + +#define xTaskCreate( pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask ) xTaskCreatePinnedToCore( ( pvTaskCode ), ( pcName ), ( usStackDepth ), ( pvParameters ), ( uxPriority ), ( pxCreatedTask ), tskNO_AFFINITY ) +#endif + +/** + * task. h + *
+ TaskHandle_t xTaskCreateStatic( TaskFunction_t pvTaskCode,
+								 const char * const pcName,
+								 uint32_t ulStackDepth,
+								 void *pvParameters,
+								 UBaseType_t uxPriority,
+								 StackType_t *pxStackBuffer,
+								 StaticTask_t *pxTaskBuffer,
+                                 const BaseType_t xCoreID );
+ + * + * Create a new task and add it to the list of tasks that are ready to run. + * + * Internally, within the FreeRTOS implementation, tasks use two blocks of + * memory. The first block is used to hold the task's data structures. The + * second block is used by the task as its stack. If a task is created using + * xTaskCreate() then both blocks of memory are automatically dynamically + * allocated inside the xTaskCreate() function. (see + * http://www.freertos.org/a00111.html). If a task is created using + * xTaskCreateStatic() then the application writer must provide the required + * memory. xTaskCreateStatic() therefore allows a task to be created without + * using any dynamic memory allocation. + * + * @param pvTaskCode Pointer to the task entry function. Tasks + * must be implemented to never return (i.e. continuous loop). + * + * @param pcName A descriptive name for the task. This is mainly used to + * facilitate debugging. The maximum length of the string is defined by + * configMAX_TASK_NAME_LEN in FreeRTOSConfig.h. + * + * @param ulStackDepth The size of the task stack specified as the number of + * variables the stack can hold - not the number of bytes. For example, if + * the stack is 32-bits wide and ulStackDepth is defined as 100 then 400 bytes + * will be allocated for stack storage. + * + * @param pvParameters Pointer that will be used as the parameter for the task + * being created. + * + * @param uxPriority The priority at which the task will run. + * + * @param pxStackBuffer Must point to a StackType_t array that has at least + * ulStackDepth indexes - the array will then be used as the task's stack, + * removing the need for the stack to be allocated dynamically. + * + * @param pxTaskBuffer Must point to a variable of type StaticTask_t, which will + * then be used to hold the task's data structures, removing the need for the + * memory to be allocated dynamically. + * + * @return If neither pxStackBuffer or pxTaskBuffer are NULL, then the task will + * be created and pdPASS is returned. If either pxStackBuffer or pxTaskBuffer + * are NULL then the task will not be created and + * errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY is returned. + * + * Example usage: +
+
+    // Dimensions the buffer that the task being created will use as its stack.
+    // NOTE:  This is the number of words the stack will hold, not the number of
+    // bytes.  For example, if each stack item is 32-bits, and this is set to 100,
+    // then 400 bytes (100 * 32-bits) will be allocated.
+    #define STACK_SIZE 200
+
+    // Structure that will hold the TCB of the task being created.
+    StaticTask_t xTaskBuffer;
+
+    // Buffer that the task being created will use as its stack.  Note this is
+    // an array of StackType_t variables.  The size of StackType_t is dependent on
+    // the RTOS port.
+    StackType_t xStack[ STACK_SIZE ];
+
+    // Function that implements the task being created.
+    void vTaskCode( void * pvParameters )
+    {
+        // The parameter value is expected to be 1 as 1 is passed in the
+        // pvParameters value in the call to xTaskCreateStatic().
+        configASSERT( ( uint32_t ) pvParameters == 1UL );
+
+        for( ;; )
+        {
+            // Task code goes here.
+        }
+    }
+
+    // Function that creates a task.
+    void vOtherFunction( void )
+    {
+        TaskHandle_t xHandle = NULL;
+
+        // Create the task without using any dynamic memory allocation.
+        xHandle = xTaskCreateStatic(
+                      vTaskCode,       // Function that implements the task.
+                      "NAME",          // Text name for the task.
+                      STACK_SIZE,      // Stack size in words, not bytes.
+                      ( void * ) 1,    // Parameter passed into the task.
+                      tskIDLE_PRIORITY,// Priority at which the task is created.
+                      xStack,          // Array to use as the task's stack.
+                      &xTaskBuffer );  // Variable to hold the task's data structure.
+
+        // puxStackBuffer and pxTaskBuffer were not NULL, so the task will have
+        // been created, and xHandle will be the task's handle.  Use the handle
+        // to suspend the task.
+        vTaskSuspend( xHandle );
+    }
+   
+ * \defgroup xTaskCreateStatic xTaskCreateStatic + * \ingroup Tasks + */ +#if( configSUPPORT_STATIC_ALLOCATION == 1 ) + TaskHandle_t xTaskCreateStaticPinnedToCore( TaskFunction_t pxTaskCode, + const char * const pcName, + const uint32_t ulStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + StackType_t * const puxStackBuffer, + StaticTask_t * const pxTaskBuffer, + const BaseType_t xCoreID ); + +#define xTaskCreateStatic( pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxStackBuffer, pxTaskBuffer ) xTaskCreateStaticPinnedToCore( ( pvTaskCode ), ( pcName ), ( usStackDepth ), ( pvParameters ), ( uxPriority ), ( pxStackBuffer ), ( pxTaskBuffer ), tskNO_AFFINITY ) +#endif /* configSUPPORT_STATIC_ALLOCATION */ /** * task. h @@ -420,7 +563,9 @@ TaskHandle_t xHandle; * \defgroup xTaskCreateRestricted xTaskCreateRestricted * \ingroup Tasks */ -#define xTaskCreateRestricted( x, pxCreatedTask ) xTaskGenericCreate( ((x)->pvTaskCode), ((x)->pcName), ((x)->usStackDepth), ((x)->pvParameters), ((x)->uxPriority), (pxCreatedTask), ((x)->puxStackBuffer), ((x)->xRegions) ) +#if( portUSING_MPU_WRAPPERS == 1 ) + BaseType_t xTaskCreateRestricted( const TaskParameters_t * const pxTaskDefinition, TaskHandle_t *pxCreatedTask ) PRIVILEGED_FUNCTION; +#endif /** * task. h @@ -1933,6 +2078,17 @@ TickType_t uxTaskResetEventItemValue( void ) PRIVILEGED_FUNCTION; */ TaskHandle_t xTaskGetCurrentTaskHandle( void ) PRIVILEGED_FUNCTION; + + +/* + * Return the handle of the task running on a certain CPU. Because of + * the nature of SMP processing, there is no guarantee that this + * value will still be valid on return and should only be used for + * debugging purposes. + */ +TaskHandle_t xTaskGetCurrentTaskHandleForCPU( BaseType_t cpuid ); + + /* * Capture the current time status for future reference. */ @@ -1968,12 +2124,6 @@ void vTaskPriorityInherit( TaskHandle_t const pxMutexHolder ) PRIVILEGED_FUNCTIO */ BaseType_t xTaskPriorityDisinherit( TaskHandle_t const pxMutexHolder ) PRIVILEGED_FUNCTION; -/* - * Generic version of the task creation function which is in turn called by the - * xTaskCreate() and xTaskCreateRestricted() macros. - */ -BaseType_t xTaskGenericCreate( TaskFunction_t pxTaskCode, const char * const pcName, const uint16_t usStackDepth, void * const pvParameters, UBaseType_t uxPriority, TaskHandle_t * const pxCreatedTask, StackType_t * const puxStackBuffer, const MemoryRegion_t * const xRegions, const BaseType_t xCoreID) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ - /* * Get the uxTCBNumber assigned to the task referenced by the xTask parameter. */ diff --git a/components/freertos/port.c b/components/freertos/port.c index a982db7d42..25480ed475 100644 --- a/components/freertos/port.c +++ b/components/freertos/port.c @@ -101,7 +101,9 @@ #include "FreeRTOS.h" #include "task.h" -#include "panic.h" +#include "esp_panic.h" + +#include "esp_crosscore_int.h" /* Defined in portasm.h */ extern void _frxt_tick_timer_init(void); @@ -228,6 +230,12 @@ BaseType_t xPortSysTickHandler( void ) return ret; } + + +void vPortYieldOtherCore( BaseType_t coreid ) { + esp_crosscore_int_send_yield( coreid ); +} + /*-----------------------------------------------------------*/ /* @@ -375,7 +383,7 @@ portBASE_TYPE vPortCPUReleaseMutex(portMUX_TYPE *mux) { #if CONFIG_FREERTOS_BREAK_ON_SCHEDULER_START_JTAG void vPortFirstTaskHook(TaskFunction_t function) { - setBreakpointIfJtag(function); + esp_set_breakpoint_if_jtag(function); } #endif diff --git a/components/freertos/queue.c b/components/freertos/queue.c index 168f09f1a1..f404a243e6 100644 --- a/components/freertos/queue.c +++ b/components/freertos/queue.c @@ -158,15 +158,19 @@ typedef struct QueueDefinition UBaseType_t uxLength; /*< The length of the queue defined as the number of items it will hold, not the number of bytes. */ UBaseType_t uxItemSize; /*< The size of each items that the queue will hold. */ - #if ( configUSE_TRACE_FACILITY == 1 ) - UBaseType_t uxQueueNumber; - uint8_t ucQueueType; + #if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the memory used by the queue was statically allocated to ensure no attempt is made to free the memory. */ #endif #if ( configUSE_QUEUE_SETS == 1 ) struct QueueDefinition *pxQueueSetContainer; #endif + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxQueueNumber; + uint8_t ucQueueType; + #endif + portMUX_TYPE mux; } xQUEUE; @@ -238,6 +242,21 @@ static void prvCopyDataFromQueue( Queue_t * const pxQueue, void * const pvBuffer static BaseType_t prvNotifyQueueSetContainer( const Queue_t * const pxQueue, const BaseType_t xCopyPosition ) PRIVILEGED_FUNCTION; #endif +/* + * Called after a Queue_t structure has been allocated either statically or + * dynamically to fill in the structure's members. + */ +static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, const uint8_t ucQueueType, Queue_t *pxNewQueue ) PRIVILEGED_FUNCTION; + +/* + * Mutexes are a special type of queue. When a mutex is created, first the + * queue is created, then prvInitialiseMutex() is called to configure the queue + * as a mutex. + */ +#if( configUSE_MUTEXES == 1 ) + static void prvInitialiseMutex( Queue_t *pxNewQueue ) PRIVILEGED_FUNCTION; +#endif + BaseType_t xQueueGenericReset( QueueHandle_t xQueue, BaseType_t xNewQueue ) { Queue_t * const pxQueue = ( Queue_t * ) xQueue; @@ -293,132 +312,165 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; } /*-----------------------------------------------------------*/ -QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType ) -{ -Queue_t *pxNewQueue; -size_t xQueueSizeInBytes; -QueueHandle_t xReturn = NULL; -int8_t *pcAllocatedBuffer; +#if( configSUPPORT_STATIC_ALLOCATION == 1 ) + QueueHandle_t xQueueGenericCreateStatic( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, StaticQueue_t *pxStaticQueue, const uint8_t ucQueueType ) + { + Queue_t *pxNewQueue; + + configASSERT( uxQueueLength > ( UBaseType_t ) 0 ); + + /* The StaticQueue_t structure and the queue storage area must be + supplied. */ + configASSERT( pxStaticQueue != NULL ); + + /* A queue storage area should be provided if the item size is not 0, and + should not be provided if the item size is 0. */ + configASSERT( !( ( pucQueueStorage != NULL ) && ( uxItemSize == 0 ) ) ); + configASSERT( !( ( pucQueueStorage == NULL ) && ( uxItemSize != 0 ) ) ); + + #if( configASSERT_DEFINED == 1 ) + { + /* Sanity check that the size of the structure used to declare a + variable of type StaticQueue_t or StaticSemaphore_t equals the size of + the real queue and semaphore structures. */ + volatile size_t xSize = sizeof( StaticQueue_t ); + configASSERT( xSize == sizeof( Queue_t ) ); + } + #endif /* configASSERT_DEFINED */ + + /* The address of a statically allocated queue was passed in, use it. + The address of a statically allocated storage area was also passed in + but is already set. */ + pxNewQueue = ( Queue_t * ) pxStaticQueue; /*lint !e740 Unusual cast is ok as the structures are designed to have the same alignment, and the size is checked by an assert. */ + + if( pxNewQueue != NULL ) + { + #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + { + /* Queues can be allocated wither statically or dynamically, so + note this queue was allocated statically in case the queue is + later deleted. */ + pxNewQueue->ucStaticallyAllocated = pdTRUE; + } + #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ + + prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue ); + } + + return pxNewQueue; + } + +#endif /* configSUPPORT_STATIC_ALLOCATION */ +/*-----------------------------------------------------------*/ + +#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + + QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType ) + { + Queue_t *pxNewQueue; + size_t xQueueSizeInBytes; + uint8_t *pucQueueStorage; + + configASSERT( uxQueueLength > ( UBaseType_t ) 0 ); + + if( uxItemSize == ( UBaseType_t ) 0 ) + { + /* There is not going to be a queue storage area. */ + xQueueSizeInBytes = ( size_t ) 0; + } + else + { + /* Allocate enough space to hold the maximum number of items that + can be in the queue at any time. */ + xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + } + + pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes ); + + if( pxNewQueue != NULL ) + { + /* Jump past the queue structure to find the location of the queue + storage area. */ + pucQueueStorage = ( ( uint8_t * ) pxNewQueue ) + sizeof( Queue_t ); + + #if( configSUPPORT_STATIC_ALLOCATION == 1 ) + { + /* Queues can be created either statically or dynamically, so + note this task was created dynamically in case it is later + deleted. */ + pxNewQueue->ucStaticallyAllocated = pdFALSE; + } + #endif /* configSUPPORT_STATIC_ALLOCATION */ + + prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue ); + } + + return pxNewQueue; + } + +#endif /* configSUPPORT_STATIC_ALLOCATION */ +/*-----------------------------------------------------------*/ + +static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, const uint8_t ucQueueType, Queue_t *pxNewQueue ) +{ /* Remove compiler warnings about unused parameters should configUSE_TRACE_FACILITY not be set to 1. */ ( void ) ucQueueType; - configASSERT( uxQueueLength > ( UBaseType_t ) 0 ); - if( uxItemSize == ( UBaseType_t ) 0 ) { - /* There is not going to be a queue storage area. */ - xQueueSizeInBytes = ( size_t ) 0; + /* No RAM was allocated for the queue storage area, but PC head cannot + be set to NULL because NULL is used as a key to say the queue is used as + a mutex. Therefore just set pcHead to point to the queue as a benign + value that is known to be within the memory map. */ + pxNewQueue->pcHead = ( int8_t * ) pxNewQueue; } else { - /* The queue is one byte longer than asked for to make wrap checking - easier/faster. */ - xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize ) + ( size_t ) 1; /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + /* Set the head to the start of the queue storage area. */ + pxNewQueue->pcHead = ( int8_t * ) pucQueueStorage; } - /* Allocate the new queue structure and storage area. */ - pcAllocatedBuffer = ( int8_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes ); + /* Initialise the queue members as described where the queue type is + defined. */ + pxNewQueue->uxLength = uxQueueLength; + pxNewQueue->uxItemSize = uxItemSize; + ( void ) xQueueGenericReset( pxNewQueue, pdTRUE ); - if( pcAllocatedBuffer != NULL ) + #if ( configUSE_TRACE_FACILITY == 1 ) { - pxNewQueue = ( Queue_t * ) pcAllocatedBuffer; /*lint !e826 MISRA The buffer cannot be to small because it was dimensioned by sizeof( Queue_t ) + xQueueSizeInBytes. */ - - if( uxItemSize == ( UBaseType_t ) 0 ) - { - /* No RAM was allocated for the queue storage area, but PC head - cannot be set to NULL because NULL is used as a key to say the queue - is used as a mutex. Therefore just set pcHead to point to the queue - as a benign value that is known to be within the memory map. */ - pxNewQueue->pcHead = ( int8_t * ) pxNewQueue; - } - else - { - /* Jump past the queue structure to find the location of the queue - storage area - adding the padding bytes to get a better alignment. */ - pxNewQueue->pcHead = pcAllocatedBuffer + sizeof( Queue_t ); - } - - /* Initialise the queue members as described above where the queue type - is defined. */ - pxNewQueue->uxLength = uxQueueLength; - pxNewQueue->uxItemSize = uxItemSize; - ( void ) xQueueGenericReset( pxNewQueue, pdTRUE ); - - #if ( configUSE_TRACE_FACILITY == 1 ) - { - pxNewQueue->ucQueueType = ucQueueType; - } - #endif /* configUSE_TRACE_FACILITY */ - - #if( configUSE_QUEUE_SETS == 1 ) - { - pxNewQueue->pxQueueSetContainer = NULL; - } - #endif /* configUSE_QUEUE_SETS */ - - traceQUEUE_CREATE( pxNewQueue ); - xReturn = pxNewQueue; + pxNewQueue->ucQueueType = ucQueueType; } - else + #endif /* configUSE_TRACE_FACILITY */ + + #if( configUSE_QUEUE_SETS == 1 ) { - mtCOVERAGE_TEST_MARKER(); + pxNewQueue->pxQueueSetContainer = NULL; } + #endif /* configUSE_QUEUE_SETS */ - configASSERT( xReturn ); - - return xReturn; + traceQUEUE_CREATE( pxNewQueue ); } /*-----------------------------------------------------------*/ -#if ( configUSE_MUTEXES == 1 ) +#if( configUSE_MUTEXES == 1 ) - QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType ) + static void prvInitialiseMutex( Queue_t *pxNewQueue ) { - Queue_t *pxNewQueue; - - /* Prevent compiler warnings about unused parameters if - configUSE_TRACE_FACILITY does not equal 1. */ - ( void ) ucQueueType; - - /* Allocate the new queue structure. */ - pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) ); if( pxNewQueue != NULL ) { - /* Information required for priority inheritance. */ + /* The queue create function will set all the queue structure members + correctly for a generic queue, but this function is creating a + mutex. Overwrite those members that need to be set differently - + in particular the information required for priority inheritance. */ pxNewQueue->pxMutexHolder = NULL; pxNewQueue->uxQueueType = queueQUEUE_IS_MUTEX; - /* Queues used as a mutex no data is actually copied into or out - of the queue. */ - pxNewQueue->pcWriteTo = NULL; - pxNewQueue->u.pcReadFrom = NULL; + /* In case this is a recursive mutex. */ + pxNewQueue->u.uxRecursiveCallCount = 0; - /* Each mutex has a length of 1 (like a binary semaphore) and - an item size of 0 as nothing is actually copied into or out - of the mutex. */ - pxNewQueue->uxMessagesWaiting = ( UBaseType_t ) 0U; - pxNewQueue->uxLength = ( UBaseType_t ) 1U; - pxNewQueue->uxItemSize = ( UBaseType_t ) 0U; - - #if ( configUSE_TRACE_FACILITY == 1 ) - { - pxNewQueue->ucQueueType = ucQueueType; - } - #endif - - #if ( configUSE_QUEUE_SETS == 1 ) - { - pxNewQueue->pxQueueSetContainer = NULL; - } - #endif - - /* Ensure the event queues start with the correct state. */ - vListInitialise( &( pxNewQueue->xTasksWaitingToSend ) ); - vListInitialise( &( pxNewQueue->xTasksWaitingToReceive ) ); - - vPortCPUInitializeMutex(&pxNewQueue->mux); + vPortCPUInitializeMutex(&pxNewQueue->mux); traceCREATE_MUTEX( pxNewQueue ); @@ -429,8 +481,41 @@ int8_t *pcAllocatedBuffer; { traceCREATE_MUTEX_FAILED(); } + } + +#endif /* configUSE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if( ( configUSE_MUTEXES == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + + QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType ) + { + Queue_t *pxNewQueue; + const UBaseType_t uxMutexLength = ( UBaseType_t ) 1, uxMutexSize = ( UBaseType_t ) 0; + + pxNewQueue = ( Queue_t * ) xQueueGenericCreate( uxMutexLength, uxMutexSize, ucQueueType ); + prvInitialiseMutex( pxNewQueue ); + + return pxNewQueue; + } + +#endif /* configUSE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if( ( configUSE_MUTEXES == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + + QueueHandle_t xQueueCreateMutexStatic( const uint8_t ucQueueType, StaticQueue_t *pxStaticQueue ) + { + Queue_t *pxNewQueue; + const UBaseType_t uxMutexLength = ( UBaseType_t ) 1, uxMutexSize = ( UBaseType_t ) 0; + + /* Prevent compiler warnings about unused parameters if + configUSE_TRACE_FACILITY does not equal 1. */ + ( void ) ucQueueType; + + pxNewQueue = ( Queue_t * ) xQueueGenericCreateStatic( uxMutexLength, uxMutexSize, NULL, pxStaticQueue, ucQueueType ); + prvInitialiseMutex( pxNewQueue ); - configASSERT( pxNewQueue ); return pxNewQueue; } @@ -565,7 +650,35 @@ int8_t *pcAllocatedBuffer; #endif /* configUSE_RECURSIVE_MUTEXES */ /*-----------------------------------------------------------*/ -#if ( configUSE_COUNTING_SEMAPHORES == 1 ) +#if( ( configUSE_COUNTING_SEMAPHORES == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + + QueueHandle_t xQueueCreateCountingSemaphoreStatic( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount, StaticQueue_t *pxStaticQueue ) + { + QueueHandle_t xHandle; + + configASSERT( uxMaxCount != 0 ); + configASSERT( uxInitialCount <= uxMaxCount ); + + xHandle = xQueueGenericCreateStatic( uxMaxCount, queueSEMAPHORE_QUEUE_ITEM_LENGTH, NULL, pxStaticQueue, queueQUEUE_TYPE_COUNTING_SEMAPHORE ); + + if( xHandle != NULL ) + { + ( ( Queue_t * ) xHandle )->uxMessagesWaiting = uxInitialCount; + + traceCREATE_COUNTING_SEMAPHORE(); + } + else + { + traceCREATE_COUNTING_SEMAPHORE_FAILED(); + } + + return xHandle; + } + +#endif /* ( ( configUSE_COUNTING_SEMAPHORES == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) */ +/*-----------------------------------------------------------*/ + +#if( ( configUSE_COUNTING_SEMAPHORES == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) QueueHandle_t xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount ) { @@ -591,7 +704,7 @@ int8_t *pcAllocatedBuffer; return xHandle; } -#endif /* configUSE_COUNTING_SEMAPHORES */ +#endif /* ( ( configUSE_COUNTING_SEMAPHORES == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) */ /*-----------------------------------------------------------*/ BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition ) @@ -1685,7 +1798,33 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; vQueueUnregisterQueue( pxQueue ); } #endif - vPortFree( pxQueue ); + + #if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) ) + { + /* The queue can only have been allocated dynamically - free it + again. */ + vPortFree( pxQueue ); + } + #elif( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + { + /* The queue could have been allocated statically or dynamically, so + check before attempting to free the memory. */ + if( pxQueue->ucStaticallyAllocated == ( uint8_t ) pdFALSE ) + { + vPortFree( pxQueue ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #else + { + /* The queue must have been statically allocated, so is not going to be + deleted. Avoid compiler warnings about the unused parameter. */ + ( void ) pxQueue; + } + #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ } /*-----------------------------------------------------------*/ @@ -2263,7 +2402,7 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; #endif /* configUSE_TIMERS */ /*-----------------------------------------------------------*/ -#if ( configUSE_QUEUE_SETS == 1 ) +#if( ( configUSE_QUEUE_SETS == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQueueLength ) { diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index 95e7811dde..b79d3a98ba 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -77,6 +77,7 @@ task.h is included from an application file. */ #define MPU_WRAPPERS_INCLUDED_FROM_API_FILE #include "rom/ets_sys.h" +#include "esp_newlib.h" /* FreeRTOS includes. */ #include "FreeRTOS.h" @@ -85,7 +86,6 @@ task.h is included from an application file. */ #include "StackMacros.h" #include "portmacro.h" #include "semphr.h" -#include "sys/reent.h" /* Lint e961 and e750 are suppressed as a MISRA exception justified because the MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined for the @@ -140,6 +140,26 @@ typedef enum eNotified } eNotifyValue; +/* Sometimes the FreeRTOSConfig.h settings only allow a task to be created using +dynamically allocated RAM, in which case when any task is deleted it is known +that both the task's stack and TCB need to be freed. Sometimes the +FreeRTOSConfig.h settings only allow a task to be created using statically +allocated RAM, in which case when any task is deleted it is known that neither +the task's stack or TCB should be freed. Sometimes the FreeRTOSConfig.h +settings allow a task to be created using either statically or dynamically +allocated RAM, in which case a member of the TCB is used to record whether the +stack and/or TCB were allocated statically or dynamically, so when a task is +deleted the RAM that was allocated dynamically is freed again and no attempt is +made to free the RAM that was allocated statically. +tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE is only true if it is possible for a +task to be created using either statically or dynamically allocated RAM. Note +that if portUSING_MPU_WRAPPERS is 1 then a protected task can be created with +a statically allocated stack and a dynamically allocated TCB. */ +#define tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE ( ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) || ( portUSING_MPU_WRAPPERS == 1 ) ) +#define tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB ( ( uint8_t ) 0 ) +#define tskSTATICALLY_ALLOCATED_STACK_ONLY ( ( uint8_t ) 1 ) +#define tskSTATICALLY_ALLOCATED_STACK_AND_TCB ( ( uint8_t ) 2 ) + /* * Task control block. A task control block (TCB) is allocated for each task, * and stores task state information, including a pointer to the task's context @@ -151,7 +171,6 @@ typedef struct tskTaskControlBlock #if ( portUSING_MPU_WRAPPERS == 1 ) xMPU_SETTINGS xMPUSettings; /*< The MPU settings are defined as part of the port layer. THIS MUST BE THE SECOND MEMBER OF THE TCB STRUCT. */ - BaseType_t xUsingStaticallyAllocatedStack; /* Set to pdTRUE if the stack is a statically allocated array, and pdFALSE if the stack is dynamically allocated. */ #endif ListItem_t xGenericListItem; /*< The list that the state list item of a task is reference from denotes the state of that task (Ready, Blocked, Suspended ). */ @@ -211,6 +230,12 @@ typedef struct tskTaskControlBlock volatile eNotifyValue eNotifyState; #endif + /* See the comments above the definition of + tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE. */ + #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) + uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the task is a statically allocated to ensure no attempt is made to free the memory. */ + #endif + } tskTCB; /* The old tskTCB name is maintained above then typedefed to the new TCB_t name @@ -236,7 +261,7 @@ PRIVILEGED_DATA static List_t xDelayedTaskList1; /*< Delayed tasks. */ PRIVILEGED_DATA static List_t xDelayedTaskList2; /*< Delayed tasks (two lists are used - one for delays that have overflowed the current tick count. */ PRIVILEGED_DATA static List_t * volatile pxDelayedTaskList; /*< Points to the delayed task list currently being used. */ PRIVILEGED_DATA static List_t * volatile pxOverflowDelayedTaskList; /*< Points to the delayed task list currently being used to hold tasks that have overflowed the current tick count. */ -PRIVILEGED_DATA static List_t xPendingReadyList; /*< Tasks that have been readied while the scheduler was suspended. They will be moved to the ready list when the scheduler is resumed. */ +PRIVILEGED_DATA static List_t xPendingReadyList[ portNUM_PROCESSORS ]; /*< Tasks that have been readied while the scheduler was suspended. They will be moved to the ready list when the scheduler is resumed. */ #if ( INCLUDE_vTaskDelete == 1 ) @@ -263,7 +288,7 @@ PRIVILEGED_DATA static volatile TickType_t xTickCount = ( TickType_t ) 0U; PRIVILEGED_DATA static volatile UBaseType_t uxTopReadyPriority = tskIDLE_PRIORITY; PRIVILEGED_DATA static volatile BaseType_t xSchedulerRunning = pdFALSE; PRIVILEGED_DATA static volatile UBaseType_t uxPendedTicks = ( UBaseType_t ) 0U; -PRIVILEGED_DATA static volatile BaseType_t xYieldPending = pdFALSE; +PRIVILEGED_DATA static volatile BaseType_t xYieldPending[portNUM_PROCESSORS] = {pdFALSE}; PRIVILEGED_DATA static volatile BaseType_t xNumOfOverflows = ( BaseType_t ) 0; PRIVILEGED_DATA static UBaseType_t uxTaskNumber = ( UBaseType_t ) 0U; PRIVILEGED_DATA static volatile TickType_t xNextTaskUnblockTime = portMAX_DELAY; @@ -417,6 +442,9 @@ count overflows. */ vListInsertEnd( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xGenericListItem ) ) /*-----------------------------------------------------------*/ + +#define tskCAN_RUN_HERE( cpuid ) ( cpuid==xPortGetCoreID() || cpuid==tskNO_AFFINITY ) + /* * Several functions take an TaskHandle_t parameter that can optionally be NULL, * where NULL is used to indicate that the handle of the currently executing @@ -456,12 +484,6 @@ to its original value when it is released. */ /* File private functions. --------------------------------*/ -/* - * Utility to ready a TCB for a given task. Mainly just copies the parameters - * into the TCB structure. - */ -static void prvInitialiseTCBVariables( TCB_t * const pxTCB, const char * const pcName, UBaseType_t uxPriority, const MemoryRegion_t * const xRegions, const uint16_t usStackDepth, const BaseType_t xCoreID ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ - /** * Utility task that simply returns pdTRUE if the task referenced by xTask is * currently in the Suspended state, or pdFALSE if the task referenced by xTask @@ -516,12 +538,6 @@ static void prvCheckTasksWaitingTermination( void ) PRIVILEGED_FUNCTION; */ static void prvAddCurrentTaskToDelayedList( const portBASE_TYPE xCoreID, const TickType_t xTimeToWake ) PRIVILEGED_FUNCTION; -/* - * Allocates memory from the heap for a TCB and associated stack. Checks the - * allocation was successful. - */ -static TCB_t *prvAllocateTCBAndStack( const uint16_t usStackDepth, StackType_t * const puxStackBuffer ) PRIVILEGED_FUNCTION; - /* * Fills an TaskStatus_t structure with information on each task that is * referenced from the pxList list (which may be a ready list, a delayed list, @@ -578,120 +594,481 @@ static void prvResetNextTaskUnblockTime( void ); #endif +/* + * Called after a Task_t structure has been allocated either statically or + * dynamically to fill in the structure's members. + */ +static void prvInitialiseNewTask( TaskFunction_t pxTaskCode, + const char * const pcName, + const uint32_t ulStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + TaskHandle_t * const pxCreatedTask, + TCB_t *pxNewTCB, + const MemoryRegion_t * const xRegions, const BaseType_t xCoreID) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + +/* + * Called after a new task has been created and initialised to place the task + * under the control of the scheduler. + */ +static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode, const BaseType_t xCoreID ) PRIVILEGED_FUNCTION; + + /*-----------------------------------------------------------*/ -BaseType_t xTaskGenericCreate( TaskFunction_t pxTaskCode, const char * const pcName, const uint16_t usStackDepth, void * const pvParameters, UBaseType_t uxPriority, TaskHandle_t * const pxCreatedTask, StackType_t * const puxStackBuffer, const MemoryRegion_t * const xRegions, const BaseType_t xCoreID) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ +/* + * This routine tries to send an interrupt to another core if needed to make it execute a task + * of higher priority. We try to figure out if needed first by inspecting the pxTCB of the + * other CPU first. Specifically for Xtensa, we can do this because pxTCB is an atomic pointer. It + * is possible that it is inaccurate because the other CPU just did a task switch, but in that case + * at most a superfluous interrupt is generated. +*/ +void taskYIELD_OTHER_CORE( BaseType_t xCoreID, UBaseType_t uxPriority ) { -BaseType_t xReturn; -TCB_t * pxNewTCB; -StackType_t *pxTopOfStack; -BaseType_t i; - configASSERT( pxTaskCode ); - configASSERT( ( ( uxPriority & ( ~portPRIVILEGE_BIT ) ) < configMAX_PRIORITIES ) ); - configASSERT( (xCoreID>=0 && xCoreIDuxPriority < uxPriority ) { + vPortYieldOtherCore( xCoreID ); + } + } + else { - #if( portUSING_MPU_WRAPPERS == 1 ) - /* Should the task be created in privileged mode? */ - BaseType_t xRunPrivileged; - if( ( uxPriority & portPRIVILEGE_BIT ) != 0U ) + /* The task has no affinity. See if we can find a CPU to put it on.*/ + for (i=0; iuxPriority < uxPriority) { - xRunPrivileged = pdTRUE; + vPortYieldOtherCore( i ); + break; } - else - { - xRunPrivileged = pdFALSE; - } - uxPriority &= ~portPRIVILEGE_BIT; + } + } +} - if( puxStackBuffer != NULL ) - { - /* The application provided its own stack. Note this so no - attempt is made to delete the stack should that task be - deleted. */ - pxNewTCB->xUsingStaticallyAllocatedStack = pdTRUE; - } - else - { - /* The stack was allocated dynamically. Note this so it can be - deleted again if the task is deleted. */ - pxNewTCB->xUsingStaticallyAllocatedStack = pdFALSE; - } - #endif /* portUSING_MPU_WRAPPERS == 1 */ +#if( configSUPPORT_STATIC_ALLOCATION == 1 ) - /* Calculate the top of stack address. This depends on whether the - stack grows from high memory to low (as per the 80x86) or vice versa. - portSTACK_GROWTH is used to make the result positive or negative as - required by the port. */ - #if( portSTACK_GROWTH < 0 ) + TaskHandle_t xTaskCreateStaticPinnedToCore( TaskFunction_t pxTaskCode, + const char * const pcName, + const uint32_t ulStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + StackType_t * const puxStackBuffer, + StaticTask_t * const pxTaskBuffer, + const BaseType_t xCoreID ) + { + TCB_t *pxNewTCB; + TaskHandle_t xReturn; + + configASSERT( puxStackBuffer != NULL ); + configASSERT( pxTaskBuffer != NULL ); + configASSERT( (xCoreID>=0 && xCoreIDpxStack + ( usStackDepth - ( uint16_t ) 1 ); - pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ( portPOINTER_SIZE_TYPE ) ~portBYTE_ALIGNMENT_MASK ) ); /*lint !e923 MISRA exception. Avoiding casts between pointers and integers is not practical. Size differences accounted for using portPOINTER_SIZE_TYPE type. */ + /* The memory used for the task's TCB and stack are passed into this + function - use them. */ + pxNewTCB = ( TCB_t * ) pxTaskBuffer; /*lint !e740 Unusual cast is ok as the structures are designed to have the same alignment, and the size is checked by an assert. */ + pxNewTCB->pxStack = ( StackType_t * ) puxStackBuffer; - /* Check the alignment of the calculated top of stack is correct. */ - configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) ); + #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) + { + /* Tasks can be created statically or dynamically, so note this + task was created statically in case the task is later deleted. */ + pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_AND_TCB; + } + #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ + + prvInitialiseNewTask( pxTaskCode, pcName, ulStackDepth, pvParameters, uxPriority, &xReturn, pxNewTCB, NULL, xCoreID ); + prvAddNewTaskToReadyList( pxNewTCB, pxTaskCode, xCoreID ); + } + else + { + xReturn = NULL; + } + + return xReturn; + } + +#endif /* SUPPORT_STATIC_ALLOCATION */ +/*-----------------------------------------------------------*/ + +#if( portUSING_MPU_WRAPPERS == 1 ) + + BaseType_t xTaskCreateRestricted( const TaskParameters_t * const pxTaskDefinition, TaskHandle_t *pxCreatedTask ) + { + TCB_t *pxNewTCB; + BaseType_t xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; + + configASSERT( pxTaskDefinition->puxStackBuffer ); + + if( pxTaskDefinition->puxStackBuffer != NULL ) + { + /* Allocate space for the TCB. Where the memory comes from depends + on the implementation of the port malloc function and whether or + not static allocation is being used. */ + pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); + + if( pxNewTCB != NULL ) + { + /* Store the stack location in the TCB. */ + pxNewTCB->pxStack = pxTaskDefinition->puxStackBuffer; + + /* Tasks can be created statically or dynamically, so note + this task had a statically allocated stack in case it is + later deleted. The TCB was allocated dynamically. */ + pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_ONLY; + + prvInitialiseNewTask( pxTaskDefinition->pvTaskCode, + pxTaskDefinition->pcName, + ( uint32_t ) pxTaskDefinition->usStackDepth, + pxTaskDefinition->pvParameters, + pxTaskDefinition->uxPriority, + pxCreatedTask, pxNewTCB, + pxTaskDefinition->xRegions, + tskNO_AFFINITY ); + + prvAddNewTaskToReadyList( pxNewTCB, pxTaskDefinition->pvTaskCode, tskNO_AFFINITY ); + xReturn = pdPASS; + } + } + + return xReturn; + } + +#endif /* portUSING_MPU_WRAPPERS */ +/*-----------------------------------------------------------*/ + +#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + + BaseType_t xTaskCreatePinnedToCore( TaskFunction_t pxTaskCode, + const char * const pcName, + const uint16_t usStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + TaskHandle_t * const pxCreatedTask, + const BaseType_t xCoreID ) + { + TCB_t *pxNewTCB; + BaseType_t xReturn; + + /* If the stack grows down then allocate the stack then the TCB so the stack + does not grow into the TCB. Likewise if the stack grows up then allocate + the TCB then the stack. */ + #if( portSTACK_GROWTH > 0 ) + { + /* Allocate space for the TCB. Where the memory comes from depends on + the implementation of the port malloc function and whether or not static + allocation is being used. */ + pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); + + if( pxNewTCB != NULL ) + { + /* Allocate space for the stack used by the task being created. + The base of the stack memory stored in the TCB so the task can + be deleted later if required. */ + pxNewTCB->pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + + if( pxNewTCB->pxStack == NULL ) + { + /* Could not allocate the stack. Delete the allocated TCB. */ + vPortFree( pxNewTCB ); + pxNewTCB = NULL; + } + } } #else /* portSTACK_GROWTH */ { - pxTopOfStack = pxNewTCB->pxStack; + StackType_t *pxStack; - /* Check the alignment of the stack buffer is correct. */ - configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxNewTCB->pxStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) ); + /* Allocate space for the stack used by the task being created. */ + pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ - /* If we want to use stack checking on architectures that use - a positive stack growth direction then we also need to store the - other extreme of the stack space. */ - pxNewTCB->pxEndOfStack = pxNewTCB->pxStack + ( usStackDepth - 1 ); + if( pxStack != NULL ) + { + /* Allocate space for the TCB. */ + pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); /*lint !e961 MISRA exception as the casts are only redundant for some paths. */ + + if( pxNewTCB != NULL ) + { + /* Store the stack location in the TCB. */ + pxNewTCB->pxStack = pxStack; + } + else + { + /* The stack cannot be used as the TCB was not created. Free + it again. */ + vPortFree( pxStack ); + } + } + else + { + pxNewTCB = NULL; + } } #endif /* portSTACK_GROWTH */ - /* Setup the newly allocated TCB with the initial state of the task. */ - prvInitialiseTCBVariables( pxNewTCB, pcName, uxPriority, xRegions, usStackDepth, xCoreID ); + if( pxNewTCB != NULL ) + { + #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) + { + /* Tasks can be created statically or dynamically, so note this + task was created dynamically in case it is later deleted. */ + pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB; + } + #endif /* configSUPPORT_STATIC_ALLOCATION */ - /* Initialize the TCB stack to look as if the task was already running, - but had been interrupted by the scheduler. The return address is set - to the start of the task function. Once the stack has been initialised - the top of stack variable is updated. */ - #if( portUSING_MPU_WRAPPERS == 1 ) - { - pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters, xRunPrivileged ); + prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL, xCoreID ); + prvAddNewTaskToReadyList( pxNewTCB, pxTaskCode, xCoreID ); + xReturn = pdPASS; } - #else /* portUSING_MPU_WRAPPERS */ + else { - pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters ); + xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; } - #endif /* portUSING_MPU_WRAPPERS */ - if( ( void * ) pxCreatedTask != NULL ) + return xReturn; + } + +#endif /* configSUPPORT_DYNAMIC_ALLOCATION */ +/*-----------------------------------------------------------*/ + +static void prvInitialiseNewTask( TaskFunction_t pxTaskCode, + const char * const pcName, + const uint32_t ulStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + TaskHandle_t * const pxCreatedTask, + TCB_t *pxNewTCB, + const MemoryRegion_t * const xRegions, const BaseType_t xCoreID ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ +{ +StackType_t *pxTopOfStack; +UBaseType_t x; + + #if( portUSING_MPU_WRAPPERS == 1 ) + /* Should the task be created in privileged mode? */ + BaseType_t xRunPrivileged; + if( ( uxPriority & portPRIVILEGE_BIT ) != 0U ) { - /* Pass the TCB out - in an anonymous way. The calling function/ - task can use this as a handle to delete the task later if - required.*/ - *pxCreatedTask = ( TaskHandle_t ) pxNewTCB; + xRunPrivileged = pdTRUE; + } + else + { + xRunPrivileged = pdFALSE; + } + uxPriority &= ~portPRIVILEGE_BIT; + #endif /* portUSING_MPU_WRAPPERS == 1 */ + + /* Avoid dependency on memset() if it is not required. */ + #if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ) + { + /* Fill the stack with a known value to assist debugging. */ + ( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) ulStackDepth * sizeof( StackType_t ) ); + } + #endif /* ( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ) ) */ + + /* Calculate the top of stack address. This depends on whether the stack + grows from high memory to low (as per the 80x86) or vice versa. + portSTACK_GROWTH is used to make the result positive or negative as required + by the port. */ + #if( portSTACK_GROWTH < 0 ) + { + pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 ); + pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); /*lint !e923 MISRA exception. Avoiding casts between pointers and integers is not practical. Size differences accounted for using portPOINTER_SIZE_TYPE type. */ + + /* Check the alignment of the calculated top of stack is correct. */ + configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) ); + } + #else /* portSTACK_GROWTH */ + { + pxTopOfStack = pxNewTCB->pxStack; + + /* Check the alignment of the stack buffer is correct. */ + configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxNewTCB->pxStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) ); + + /* The other extreme of the stack space is required if stack checking is + performed. */ + pxNewTCB->pxEndOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 ); + } + #endif /* portSTACK_GROWTH */ + + /* Store the task name in the TCB. */ + for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ ) + { + pxNewTCB->pcTaskName[ x ] = pcName[ x ]; + + /* Don't copy all configMAX_TASK_NAME_LEN if the string is shorter than + configMAX_TASK_NAME_LEN characters just in case the memory after the + string is not accessible (extremely unlikely). */ + if( pcName[ x ] == 0x00 ) + { + break; } else { mtCOVERAGE_TEST_MARKER(); } + } - /* Ensure interrupts don't access the task lists while they are being - updated. */ - taskENTER_CRITICAL(&xTaskQueueMutex); + /* Ensure the name string is terminated in the case that the string length + was greater or equal to configMAX_TASK_NAME_LEN. */ + pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0'; + + /* This is used as an array index so must ensure it's not too large. First + remove the privilege bit if one is present. */ + if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES ) + { + uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + pxNewTCB->uxPriority = uxPriority; + pxNewTCB->xCoreID = xCoreID; + #if ( configUSE_MUTEXES == 1 ) + { + pxNewTCB->uxBasePriority = uxPriority; + pxNewTCB->uxMutexesHeld = 0; + } + #endif /* configUSE_MUTEXES */ + + vListInitialiseItem( &( pxNewTCB->xGenericListItem ) ); + vListInitialiseItem( &( pxNewTCB->xEventListItem ) ); + + /* Set the pxNewTCB as a link back from the ListItem_t. This is so we can get + back to the containing TCB from a generic item in a list. */ + listSET_LIST_ITEM_OWNER( &( pxNewTCB->xGenericListItem ), pxNewTCB ); + + /* Event lists are always in priority order. */ + listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB ); + + #if ( portCRITICAL_NESTING_IN_TCB == 1 ) + { + pxNewTCB->uxCriticalNesting = ( UBaseType_t ) 0U; + } + #endif /* portCRITICAL_NESTING_IN_TCB */ + + #if ( configUSE_APPLICATION_TASK_TAG == 1 ) + { + pxNewTCB->pxTaskTag = NULL; + } + #endif /* configUSE_APPLICATION_TASK_TAG */ + + #if ( configGENERATE_RUN_TIME_STATS == 1 ) + { + pxNewTCB->ulRunTimeCounter = 0UL; + } + #endif /* configGENERATE_RUN_TIME_STATS */ + + #if ( portUSING_MPU_WRAPPERS == 1 ) + { + vPortStoreTaskMPUSettings( &( pxNewTCB->xMPUSettings ), xRegions, pxNewTCB->pxStack, ulStackDepth ); + } + #else + { + /* Avoid compiler warning about unreferenced parameter. */ + ( void ) xRegions; + } + #endif + + #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 ) + { + for( x = 0; x < ( UBaseType_t ) configNUM_THREAD_LOCAL_STORAGE_POINTERS; x++ ) { - uxCurrentNumberOfTasks++; + pxNewTCB->pvThreadLocalStoragePointers[ x ] = NULL; + #if ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS == 1) + pxNewTCB->pvThreadLocalStoragePointersDelCallback[ x ] = NULL; + #endif + } + } + #endif + + #if ( configUSE_TASK_NOTIFICATIONS == 1 ) + { + pxNewTCB->ulNotifiedValue = 0; + pxNewTCB->eNotifyState = eNotWaitingNotification; + } + #endif + + #if ( configUSE_NEWLIB_REENTRANT == 1 ) + { + /* Initialise this task's Newlib reent structure. */ + esp_reent_init(&pxNewTCB->xNewLib_reent); + } + #endif + + #if( INCLUDE_xTaskAbortDelay == 1 ) + { + pxNewTCB->ucDelayAborted = pdFALSE; + } + #endif + + /* Initialize the TCB stack to look as if the task was already running, + but had been interrupted by the scheduler. The return address is set + to the start of the task function. Once the stack has been initialised + the top of stack variable is updated. */ + #if( portUSING_MPU_WRAPPERS == 1 ) + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters, xRunPrivileged ); + } + #else /* portUSING_MPU_WRAPPERS */ + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters ); + } + #endif /* portUSING_MPU_WRAPPERS */ + + if( ( void * ) pxCreatedTask != NULL ) + { + /* Pass the handle out in an anonymous way. The handle can be used to + change the created task's priority, delete the created task, etc.*/ + *pxCreatedTask = ( TaskHandle_t ) pxNewTCB; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } +} +/*-----------------------------------------------------------*/ + +static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode, const BaseType_t xCoreID ) +{ + BaseType_t i; + + /* Ensure interrupts don't access the task lists while the lists are being + updated. */ + taskENTER_CRITICAL(&xTaskQueueMutex); + { + uxCurrentNumberOfTasks++; + if( pxCurrentTCB[ xPortGetCoreID() ] == NULL ) + { + /* There are no other tasks, or all the other tasks are in + the suspended state - make this the current task. */ + pxCurrentTCB[ xPortGetCoreID() ] = pxNewTCB; + if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 ) { +#if portFIRST_TASK_HOOK + if ( xPortGetCoreID() == 0 ) { + vPortFirstTaskHook(pxTaskCode); + } +#endif /* configFIRST_TASK_HOOK */ /* This is the first task to be created so do the preliminary initialisation required. We will not recover if this call fails, but we will report the failure. */ prvInitialiseTaskLists(); } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* If the scheduler is not already running, make this task the + current task if it is the highest priority task to be created + so far. */ if( xSchedulerRunning == pdFALSE ) { /* Scheduler isn't running yet. We need to determine on which CPU to run this task. */ @@ -701,14 +1078,8 @@ BaseType_t i; if (xCoreID == tskNO_AFFINITY || xCoreID == i) { /* Schedule if nothing is scheduled yet, or overwrite a task of lower prio. */ - if ( pxCurrentTCB[i] == NULL || pxCurrentTCB[i]->uxPriority <= uxPriority ) + if ( pxCurrentTCB[i] == NULL || pxCurrentTCB[i]->uxPriority <= pxNewTCB->uxPriority ) { -#if portFIRST_TASK_HOOK - if ( i == 0) { - vPortFirstTaskHook(pxTaskCode); - } -#endif /* configFIRST_TASK_HOOK */ - pxCurrentTCB[i] = pxNewTCB; break; } @@ -719,44 +1090,45 @@ BaseType_t i; { mtCOVERAGE_TEST_MARKER(); } - - uxTaskNumber++; - - #if ( configUSE_TRACE_FACILITY == 1 ) - { - /* Add a counter into the TCB for tracing only. */ - pxNewTCB->uxTCBNumber = uxTaskNumber; - } - #endif /* configUSE_TRACE_FACILITY */ - traceTASK_CREATE( pxNewTCB ); - - prvAddTaskToReadyList( pxNewTCB ); - - xReturn = pdPASS; - portSETUP_TCB( pxNewTCB ); } - taskEXIT_CRITICAL(&xTaskQueueMutex); - } - else - { - xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; - traceTASK_CREATE_FAILED(); - } - if( xReturn == pdPASS ) + uxTaskNumber++; + + #if ( configUSE_TRACE_FACILITY == 1 ) + { + /* Add a counter into the TCB for tracing only. */ + pxNewTCB->uxTCBNumber = uxTaskNumber; + } + #endif /* configUSE_TRACE_FACILITY */ + traceTASK_CREATE( pxNewTCB ); + + prvAddTaskToReadyList( pxNewTCB ); + + portSETUP_TCB( pxNewTCB ); + } + taskEXIT_CRITICAL(&xTaskQueueMutex); + + if( xSchedulerRunning != pdFALSE ) { - if( xSchedulerRunning != pdFALSE ) + /* Scheduler is running. If the created task is of a higher priority than an executing task + then it should run now. + ToDo: This only works for the current core. If a task is scheduled on an other processor, + the other processor will keep running the task it's working on, and only switch to the newer + task on a timer interrupt. */ + //No mux here, uxPriority is mostly atomic and there's not really any harm if this check misfires. + if( pxCurrentTCB[ xPortGetCoreID() ]->uxPriority < pxNewTCB->uxPriority ) { /* Scheduler is running. If the created task is of a higher priority than an executing task then it should run now. - ToDo: This only works for the current core. If a task is scheduled on an other processor, - the other processor will keep running the task it's working on, and only switch to the newer - task on a timer interrupt. */ - //No mux here, uxPriority is mostly atomic and there's not really any harm if this check misfires. - if( pxCurrentTCB[ xPortGetCoreID() ]->uxPriority < uxPriority ) + No mux here, uxPriority is mostly atomic and there's not really any harm if this check misfires. + */ + if( tskCAN_RUN_HERE( xCoreID ) && pxCurrentTCB[ xPortGetCoreID() ]->uxPriority < pxNewTCB->uxPriority ) { taskYIELD_IF_USING_PREEMPTION(); } + else if( xCoreID != xPortGetCoreID() ) { + taskYIELD_OTHER_CORE(xCoreID, pxNewTCB->uxPriority); + } else { mtCOVERAGE_TEST_MARKER(); @@ -767,8 +1139,10 @@ BaseType_t i; mtCOVERAGE_TEST_MARKER(); } } - - return xReturn; + else + { + mtCOVERAGE_TEST_MARKER(); + } } /*-----------------------------------------------------------*/ @@ -834,7 +1208,7 @@ BaseType_t i; after which it is not possible to yield away from this task - hence xYieldPending is used to latch that a context switch is required. */ - portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending ); + portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending[xPortGetCoreID()] ); portYIELD_WITHIN_API(); } else @@ -1188,10 +1562,14 @@ BaseType_t i; /* The priority of a task other than the currently running task is being raised. Is the priority being raised above that of the running task? */ - if( uxNewPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) + if ( tskCAN_RUN_HERE(pxTCB->xCoreID) && uxNewPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) { xYieldRequired = pdTRUE; } + else if ( pxTCB->xCoreID != xPortGetCoreID() ) + { + taskYIELD_OTHER_CORE( pxTCB->xCoreID, uxNewPriority ); + } else { mtCOVERAGE_TEST_MARKER(); @@ -1413,7 +1791,7 @@ BaseType_t i; if( listIS_CONTAINED_WITHIN( &xSuspendedTaskList, &( pxTCB->xGenericListItem ) ) != pdFALSE ) { /* Has the task already been resumed from within an ISR? */ - if( listIS_CONTAINED_WITHIN( &xPendingReadyList, &( pxTCB->xEventListItem ) ) == pdFALSE ) + if( listIS_CONTAINED_WITHIN( &xPendingReadyList[ xPortGetCoreID() ], &( pxTCB->xEventListItem ) ) == pdFALSE ) { /* Is it in the suspended list because it is in the Suspended state, or because is is blocked with no timeout? */ @@ -1470,13 +1848,17 @@ BaseType_t i; prvAddTaskToReadyList( pxTCB ); /* We may have just resumed a higher priority task. */ - if( pxTCB->uxPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) + if( tskCAN_RUN_HERE(pxTCB->xCoreID) && pxTCB->uxPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) { /* This yield may not cause the task just resumed to run, but will leave the lists in the correct state for the next yield. */ taskYIELD_IF_USING_PREEMPTION_MUX(&xTaskQueueMutex); } + else if( pxTCB->xCoreID != xPortGetCoreID() ) + { + taskYIELD_OTHER_CORE( pxTCB->xCoreID, pxTCB->uxPriority ); + } else { mtCOVERAGE_TEST_MARKER(); @@ -1501,7 +1883,6 @@ BaseType_t i; #if ( ( INCLUDE_xTaskResumeFromISR == 1 ) && ( INCLUDE_vTaskSuspend == 1 ) ) -/* ToDo: Make this multicore-compatible. */ BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume ) { BaseType_t xYieldRequired = pdFALSE; @@ -1521,24 +1902,28 @@ BaseType_t i; { /* Ready lists can be accessed so move the task from the suspended list to the ready list directly. */ - if( pxTCB->uxPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) + ( void ) uxListRemove( &( pxTCB->xGenericListItem ) ); + prvAddTaskToReadyList( pxTCB ); + + if( tskCAN_RUN_HERE( pxTCB->xCoreID ) && pxTCB->uxPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) { xYieldRequired = pdTRUE; } + else if ( pxTCB->xCoreID != xPortGetCoreID() ) + { + taskYIELD_OTHER_CORE( pxTCB->xCoreID, pxTCB->uxPriority); + } else { mtCOVERAGE_TEST_MARKER(); } - - ( void ) uxListRemove( &( pxTCB->xGenericListItem ) ); - prvAddTaskToReadyList( pxTCB ); } else { /* The delayed or ready lists cannot be accessed so the task is held in the pending ready list until the scheduler is unsuspended. */ - vListInsertEnd( &( xPendingReadyList ), &( pxTCB->xEventListItem ) ); + vListInsertEnd( &( xPendingReadyList[ xPortGetCoreID() ] ), &( pxTCB->xEventListItem ) ); } } else @@ -1723,18 +2108,19 @@ BaseType_t xAlreadyYielded = pdFALSE; { /* Move any readied tasks from the pending list into the appropriate ready list. */ - while( listLIST_IS_EMPTY( &xPendingReadyList ) == pdFALSE ) + while( listLIST_IS_EMPTY( &xPendingReadyList[ xPortGetCoreID() ] ) == pdFALSE ) { - pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyList ) ); + pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyList[ xPortGetCoreID() ] ) ); ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); ( void ) uxListRemove( &( pxTCB->xGenericListItem ) ); prvAddTaskToReadyList( pxTCB ); /* If the moved task has a priority higher than the current task then a yield must be performed. */ - if( pxTCB->uxPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) + if ( tskCAN_RUN_HERE(pxTCB->xCoreID) && pxTCB->uxPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) { - xYieldPending = pdTRUE; + /* We can schedule the awoken task on this CPU. */ + xYieldPending[xPortGetCoreID()] = pdTRUE; break; } else @@ -1753,7 +2139,7 @@ BaseType_t xAlreadyYielded = pdFALSE; { if( xTaskIncrementTick() != pdFALSE ) { - xYieldPending = pdTRUE; + xYieldPending[ xPortGetCoreID() ] = pdTRUE; } else { @@ -1767,7 +2153,7 @@ BaseType_t xAlreadyYielded = pdFALSE; mtCOVERAGE_TEST_MARKER(); } - if( xYieldPending == pdTRUE ) + if( xYieldPending[ xPortGetCoreID() ] == pdTRUE ) { #if( configUSE_PREEMPTION != 0 ) { @@ -1832,7 +2218,6 @@ UBaseType_t uxTaskGetNumberOfTasks( void ) /*-----------------------------------------------------------*/ #if ( INCLUDE_pcTaskGetTaskName == 1 ) - char *pcTaskGetTaskName( TaskHandle_t xTaskToQuery ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ { TCB_t *pxTCB; @@ -1975,6 +2360,23 @@ BaseType_t xSwitchRequired = pdFALSE; We can't really calculate what we need, that's done on core 0... just assume we need a switch. ToDo: Make this more intelligent? -- JD */ + //We do need the tick hook to satisfy the int watchdog. + #if ( configUSE_TICK_HOOK == 1 ) + { + /* Guard against the tick hook being called when the pended tick + count is being unwound (when the scheduler is being unlocked). */ + if( ( uxSchedulerSuspended[ xPortGetCoreID() ] != ( UBaseType_t ) pdFALSE ) || uxPendedTicks == ( UBaseType_t ) 0U ) + { + vApplicationTickHook(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_TICK_HOOK */ + + return pdTRUE; } @@ -2135,7 +2537,7 @@ BaseType_t xSwitchRequired = pdFALSE; #if ( configUSE_PREEMPTION == 1 ) { - if( xYieldPending != pdFALSE ) + if( xYieldPending [ xPortGetCoreID() ] != pdFALSE ) { xSwitchRequired = pdTRUE; } @@ -2251,11 +2653,11 @@ void vTaskSwitchContext( void ) { /* The scheduler is currently suspended - do not allow a context switch. */ - xYieldPending = pdTRUE; + xYieldPending[ xPortGetCoreID() ] = pdTRUE; } else { - xYieldPending = pdFALSE; + xYieldPending[ xPortGetCoreID() ] = pdFALSE; traceTASK_SWITCHED_OUT(); #if ( configGENERATE_RUN_TIME_STATS == 1 ) @@ -2606,11 +3008,11 @@ BaseType_t xReturn; /* The delayed and ready lists cannot be accessed, so hold this task pending until the scheduler is resumed. */ taskENTER_CRITICAL(&xTaskQueueMutex); - vListInsertEnd( &( xPendingReadyList ), &( pxUnblockedTCB->xEventListItem ) ); + vListInsertEnd( &( xPendingReadyList[ xPortGetCoreID() ] ), &( pxUnblockedTCB->xEventListItem ) ); taskEXIT_CRITICAL(&xTaskQueueMutex); } - if( pxUnblockedTCB->uxPriority > pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) + if ( tskCAN_RUN_HERE(pxUnblockedTCB->xCoreID) && pxUnblockedTCB->uxPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) { /* Return true if the task removed from the event list has a higher priority than the calling task. This allows the calling task to know if @@ -2619,7 +3021,12 @@ BaseType_t xReturn; /* Mark that a yield is pending in case the user is not using the "xHigherPriorityTaskWoken" parameter to an ISR safe FreeRTOS function. */ - xYieldPending = pdTRUE; + xYieldPending[ xPortGetCoreID() ] = pdTRUE; + } + else if ( pxUnblockedTCB->xCoreID != xPortGetCoreID() ) + { + taskYIELD_OTHER_CORE( pxUnblockedTCB->xCoreID, pxUnblockedTCB->uxPriority ); + xReturn = pdFALSE; } else { @@ -2670,7 +3077,7 @@ BaseType_t xReturn; ( void ) uxListRemove( &( pxUnblockedTCB->xGenericListItem ) ); prvAddTaskToReadyList( pxUnblockedTCB ); - if( pxUnblockedTCB->uxPriority > pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) + if ( tskCAN_RUN_HERE(pxUnblockedTCB->xCoreID) && pxUnblockedTCB->uxPriority >= pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) { /* Return true if the task removed from the event list has a higher priority than the calling task. This allows @@ -2680,7 +3087,12 @@ BaseType_t xReturn; /* Mark that a yield is pending in case the user is not using the "xHigherPriorityTaskWoken" parameter to an ISR safe FreeRTOS function. */ - xYieldPending = pdTRUE; + xYieldPending[ xPortGetCoreID() ] = pdTRUE; + } + else if ( pxUnblockedTCB->xCoreID != xPortGetCoreID() ) + { + taskYIELD_OTHER_CORE( pxUnblockedTCB->xCoreID, pxUnblockedTCB->uxPriority ); + xReturn = pdFALSE; } else { @@ -2751,7 +3163,7 @@ BaseType_t xReturn; void vTaskMissedYield( void ) { - xYieldPending = pdTRUE; + xYieldPending[ xPortGetCoreID() ] = pdTRUE; } /*-----------------------------------------------------------*/ @@ -2916,12 +3328,12 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters ) eSleepModeStatus eReturn = eStandardSleep; taskENTER_CRITICAL(&xTaskQueueMutex); - if( listCURRENT_LIST_LENGTH( &xPendingReadyList ) != 0 ) + if( listCURRENT_LIST_LENGTH( &xPendingReadyList[ xPortGetCoreID() ] ) != 0 ) { /* A task was made ready while the scheduler was suspended. */ eReturn = eAbortSleep; } - else if( xYieldPending != pdFALSE ) + else if( xYieldPending[ xPortGetCoreID() ] != pdFALSE ) { /* A yield was pended while the scheduler was suspended. */ eReturn = eAbortSleep; @@ -2955,120 +3367,6 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters ) #endif /* configUSE_TICKLESS_IDLE */ /*-----------------------------------------------------------*/ -static void prvInitialiseTCBVariables( TCB_t * const pxTCB, const char * const pcName, UBaseType_t uxPriority, const MemoryRegion_t * const xRegions, const uint16_t usStackDepth, const BaseType_t xCoreID ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ -{ -UBaseType_t x; - - /* Store the task name in the TCB. */ - for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ ) - { - pxTCB->pcTaskName[ x ] = pcName[ x ]; - - /* Don't copy all configMAX_TASK_NAME_LEN if the string is shorter than - configMAX_TASK_NAME_LEN characters just in case the memory after the - string is not accessible (extremely unlikely). */ - if( pcName[ x ] == 0x00 ) - { - break; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - - /* Ensure the name string is terminated in the case that the string length - was greater or equal to configMAX_TASK_NAME_LEN. */ - pxTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0'; - - /* This is used as an array index so must ensure it's not too large. First - remove the privilege bit if one is present. */ - if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES ) - { - uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - pxTCB->uxPriority = uxPriority; - pxTCB->xCoreID = xCoreID; - #if ( configUSE_MUTEXES == 1 ) - { - pxTCB->uxBasePriority = uxPriority; - pxTCB->uxMutexesHeld = 0; - } - #endif /* configUSE_MUTEXES */ - - vListInitialiseItem( &( pxTCB->xGenericListItem ) ); - vListInitialiseItem( &( pxTCB->xEventListItem ) ); - - /* Set the pxTCB as a link back from the ListItem_t. This is so we can get - back to the containing TCB from a generic item in a list. */ - listSET_LIST_ITEM_OWNER( &( pxTCB->xGenericListItem ), pxTCB ); - - /* Event lists are always in priority order. */ - listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ - listSET_LIST_ITEM_OWNER( &( pxTCB->xEventListItem ), pxTCB ); - - #if ( portCRITICAL_NESTING_IN_TCB == 1 ) - { - pxTCB->uxCriticalNesting = ( UBaseType_t ) 0U; - } - #endif /* portCRITICAL_NESTING_IN_TCB */ - - #if ( configUSE_APPLICATION_TASK_TAG == 1 ) - { - pxTCB->pxTaskTag = NULL; - } - #endif /* configUSE_APPLICATION_TASK_TAG */ - - #if ( configGENERATE_RUN_TIME_STATS == 1 ) - { - pxTCB->ulRunTimeCounter = 0UL; - } - #endif /* configGENERATE_RUN_TIME_STATS */ - - #if ( portUSING_MPU_WRAPPERS == 1 ) - { - vPortStoreTaskMPUSettings( &( pxTCB->xMPUSettings ), xRegions, pxTCB->pxStack, usStackDepth ); - } - #else /* portUSING_MPU_WRAPPERS */ - { - ( void ) xRegions; - ( void ) usStackDepth; - } - #endif /* portUSING_MPU_WRAPPERS */ - - #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 ) - { - for( x = 0; x < ( UBaseType_t ) configNUM_THREAD_LOCAL_STORAGE_POINTERS; x++ ) - { - pxTCB->pvThreadLocalStoragePointers[ x ] = NULL; - #if ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS ) - pxTCB->pvThreadLocalStoragePointersDelCallback[ x ] = (TlsDeleteCallbackFunction_t)NULL; - #endif - } - } - #endif - - - #if ( configUSE_TASK_NOTIFICATIONS == 1 ) - { - pxTCB->ulNotifiedValue = 0; - pxTCB->eNotifyState = eNotWaitingNotification; - } - #endif - - #if ( configUSE_NEWLIB_REENTRANT == 1 ) - { - /* Initialise this task's Newlib reent structure. */ - _REENT_INIT_PTR( ( &( pxTCB->xNewLib_reent ) ) ); - } - #endif /* configUSE_NEWLIB_REENTRANT */ -} -/*-----------------------------------------------------------*/ #if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 ) #if ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS ) @@ -3159,7 +3457,7 @@ UBaseType_t uxPriority; vListInitialise( &xDelayedTaskList1 ); vListInitialise( &xDelayedTaskList2 ); - vListInitialise( &xPendingReadyList ); + vListInitialise( &xPendingReadyList[ xPortGetCoreID() ] ); #if ( INCLUDE_vTaskDelete == 1 ) { @@ -3264,81 +3562,6 @@ static void prvAddCurrentTaskToDelayedList( const BaseType_t xCoreID, const Tick } /*-----------------------------------------------------------*/ -static TCB_t *prvAllocateTCBAndStack( const uint16_t usStackDepth, StackType_t * const puxStackBuffer ) -{ -TCB_t *pxNewTCB; - - /* If the stack grows down then allocate the stack then the TCB so the stack - does not grow into the TCB. Likewise if the stack grows up then allocate - the TCB then the stack. */ - #if( portSTACK_GROWTH > 0 ) - { - /* Allocate space for the TCB. Where the memory comes from depends on - the implementation of the port malloc function. */ - pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); - - if( pxNewTCB != NULL ) - { - /* Allocate space for the stack used by the task being created. - The base of the stack memory stored in the TCB so the task can - be deleted later if required. */ - pxNewTCB->pxStack = ( StackType_t * ) pvPortMallocAligned( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ), puxStackBuffer ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ - - if( pxNewTCB->pxStack == NULL ) - { - /* Could not allocate the stack. Delete the allocated TCB. */ - vPortFree( pxNewTCB ); - pxNewTCB = NULL; - } - } - } - #else /* portSTACK_GROWTH */ - { - StackType_t *pxStack; - - /* Allocate space for the stack used by the task being created. */ - pxStack = ( StackType_t * ) pvPortMallocAligned( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ), puxStackBuffer ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ - - if( pxStack != NULL ) - { - /* Allocate space for the TCB. Where the memory comes from depends - on the implementation of the port malloc function. */ - pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); - - if( pxNewTCB != NULL ) - { - /* Store the stack location in the TCB. */ - pxNewTCB->pxStack = pxStack; - } - else - { - /* The stack cannot be used as the TCB was not created. Free it - again. */ - vPortFree( pxStack ); - } - } - else - { - pxNewTCB = NULL; - } - } - #endif /* portSTACK_GROWTH */ - - if( pxNewTCB != NULL ) - { - /* Avoid dependency on memset() if it is not required. */ - #if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ) - { - /* Just to help debugging. */ - ( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) usStackDepth * sizeof( StackType_t ) ); - } - #endif /* ( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ) ) */ - } - - return pxNewTCB; -} -/*-----------------------------------------------------------*/ - BaseType_t xTaskGetAffinity( TaskHandle_t xTask ) { TCB_t *pxTCB; @@ -3487,8 +3710,6 @@ BaseType_t xTaskGetAffinity( TaskHandle_t xTask ) #if ( INCLUDE_vTaskDelete == 1 ) - // TODO: move this to newlib component and provide a header file - extern void _extra_cleanup_r(struct _reent* r); static void prvDeleteTCB( TCB_t *pxTCB ) { @@ -3501,27 +3722,44 @@ BaseType_t xTaskGetAffinity( TaskHandle_t xTask ) to the task to free any memory allocated at the application level. */ #if ( configUSE_NEWLIB_REENTRANT == 1 ) { - pxTCB->xNewLib_reent.__cleanup = &_extra_cleanup_r; _reclaim_reent( &( pxTCB->xNewLib_reent ) ); } #endif /* configUSE_NEWLIB_REENTRANT */ - #if( portUSING_MPU_WRAPPERS == 1 ) + #if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) && ( portUSING_MPU_WRAPPERS == 0 ) ) { - /* Only free the stack if it was allocated dynamically in the first - place. */ - if( pxTCB->xUsingStaticallyAllocatedStack == pdFALSE ) + /* The task can only have been allocated dynamically - free both + the stack and TCB. */ + vPortFreeAligned( pxTCB->pxStack ); + vPortFree( pxTCB ); + } + #elif( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE == 1 ) + { + /* The task could have been allocated statically or dynamically, so + check what was statically allocated before trying to free the + memory. */ + if( pxTCB->ucStaticallyAllocated == tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB ) { + /* Both the stack and TCB were allocated dynamically, so both + must be freed. */ vPortFreeAligned( pxTCB->pxStack ); + vPortFree( pxTCB ); + } + else if( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_ONLY ) + { + /* Only the stack was statically allocated, so the TCB is the + only memory that must be freed. */ + vPortFree( pxTCB ); + } + else + { + /* Neither the stack nor the TCB were allocated dynamically, so + nothing needs to be freed. */ + configASSERT( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_AND_TCB ) + mtCOVERAGE_TEST_MARKER(); } } - #else - { - vPortFreeAligned( pxTCB->pxStack ); - } - #endif - - vPortFree( pxTCB ); + #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ } #endif /* INCLUDE_vTaskDelete */ @@ -3566,6 +3804,19 @@ TCB_t *pxTCB; return xReturn; } + TaskHandle_t xTaskGetCurrentTaskHandleForCPU( BaseType_t cpuid ) + { + TaskHandle_t xReturn=NULL; + + //Xtensa-specific: the pxCurrentPCB pointer is atomic so we shouldn't need a lock. + if (cpuid < portNUM_PROCESSORS) { + xReturn = pxCurrentTCB[ cpuid ]; + } + + return xReturn; + } + + #endif /* ( ( INCLUDE_xTaskGetCurrentTaskHandle == 1 ) || ( configUSE_MUTEXES == 1 ) ) */ /*-----------------------------------------------------------*/ @@ -3597,12 +3848,6 @@ TCB_t *pxTCB; #endif /* ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) */ /*-----------------------------------------------------------*/ -/* -ToDo: Mutexes haven't been tested or adapted to multicore at all. - -In fact, nothing below this line has/is. -*/ - #if ( configUSE_MUTEXES == 1 ) void vTaskPriorityInherit( TaskHandle_t const pxMutexHolder ) @@ -3929,7 +4174,9 @@ is not running. Re-enabling the scheduler will re-enable the interrupts instead function is executing. */ uxArraySize = uxCurrentNumberOfTasks; - /* Allocate an array index for each task. */ + /* Allocate an array index for each task. NOTE! if + configSUPPORT_DYNAMIC_ALLOCATION is set to 0 then pvPortMalloc() will + equate to NULL. */ pxTaskStatusArray = pvPortMalloc( uxCurrentNumberOfTasks * sizeof( TaskStatus_t ) ); if( pxTaskStatusArray != NULL ) @@ -3969,7 +4216,8 @@ is not running. Re-enabling the scheduler will re-enable the interrupts instead pcWriteBuffer += strlen( pcWriteBuffer ); } - /* Free the array again. */ + /* Free the array again. NOTE! If configSUPPORT_DYNAMIC_ALLOCATION + is 0 then vPortFree() will be #defined to nothing. */ vPortFree( pxTaskStatusArray ); } else @@ -4028,7 +4276,9 @@ is not running. Re-enabling the scheduler will re-enable the interrupts instead function is executing. */ uxArraySize = uxCurrentNumberOfTasks; - /* Allocate an array index for each task. */ + /* Allocate an array index for each task. NOTE! If + configSUPPORT_DYNAMIC_ALLOCATION is set to 0 then pvPortMalloc() will + equate to NULL. */ pxTaskStatusArray = pvPortMalloc( uxCurrentNumberOfTasks * sizeof( TaskStatus_t ) ); if( pxTaskStatusArray != NULL ) @@ -4094,7 +4344,8 @@ is not running. Re-enabling the scheduler will re-enable the interrupts instead mtCOVERAGE_TEST_MARKER(); } - /* Free the array again. */ + /* Free the array again. NOTE! If configSUPPORT_DYNAMIC_ALLOCATION + is 0 then vPortFree() will be #defined to nothing. */ vPortFree( pxTaskStatusArray ); } else @@ -4434,12 +4685,16 @@ TickType_t uxReturn; /* The task should not have been on an event list. */ configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL ); - if( pxTCB->uxPriority > pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) + if( tskCAN_RUN_HERE(pxTCB->xCoreID) && pxTCB->uxPriority > pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) { /* The notified task has a priority above the currently executing task so a yield is required. */ portYIELD_WITHIN_API(); } + else if ( pxTCB->xCoreID != xPortGetCoreID() ) + { + taskYIELD_OTHER_CORE(pxTCB->xCoreID, pxTCB->uxPriority); + } else { mtCOVERAGE_TEST_MARKER(); @@ -4527,10 +4782,10 @@ TickType_t uxReturn; { /* The delayed and ready lists cannot be accessed, so hold this task pending until the scheduler is resumed. */ - vListInsertEnd( &( xPendingReadyList ), &( pxTCB->xEventListItem ) ); + vListInsertEnd( &( xPendingReadyList[ xPortGetCoreID() ] ), &( pxTCB->xEventListItem ) ); } - if( pxTCB->uxPriority > pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) + if( tskCAN_RUN_HERE(pxTCB->xCoreID) && pxTCB->uxPriority > pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) { /* The notified task has a priority above the currently executing task so a yield is required. */ @@ -4539,6 +4794,10 @@ TickType_t uxReturn; *pxHigherPriorityTaskWoken = pdTRUE; } } + else if ( pxTCB->xCoreID != xPortGetCoreID() ) + { + taskYIELD_OTHER_CORE( pxTCB->xCoreID, pxTCB->uxPriority ); + } else { mtCOVERAGE_TEST_MARKER(); @@ -4591,10 +4850,10 @@ TickType_t uxReturn; { /* The delayed and ready lists cannot be accessed, so hold this task pending until the scheduler is resumed. */ - vListInsertEnd( &( xPendingReadyList ), &( pxTCB->xEventListItem ) ); + vListInsertEnd( &( xPendingReadyList[ xPortGetCoreID() ] ), &( pxTCB->xEventListItem ) ); } - - if( pxTCB->uxPriority > pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) + + if( tskCAN_RUN_HERE(pxTCB->xCoreID) && pxTCB->uxPriority > pxCurrentTCB[ xPortGetCoreID() ]->uxPriority ) { /* The notified task has a priority above the currently executing task so a yield is required. */ @@ -4603,6 +4862,10 @@ TickType_t uxReturn; *pxHigherPriorityTaskWoken = pdTRUE; } } + else if ( pxTCB->xCoreID != xPortGetCoreID() ) + { + taskYIELD_OTHER_CORE( pxTCB->xCoreID, pxTCB->uxPriority ); + } else { mtCOVERAGE_TEST_MARKER(); diff --git a/components/freertos/xtensa_vectors.S b/components/freertos/xtensa_vectors.S index 7c2fc29607..7cf70f0032 100644 --- a/components/freertos/xtensa_vectors.S +++ b/components/freertos/xtensa_vectors.S @@ -91,12 +91,13 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *******************************************************************************/ #include "xtensa_rtos.h" - +#include "esp_panic.h" +#include "sdkconfig.h" /* Define for workaround: pin no-cpu-affinity tasks to a cpu when fpu is used. Please change this when the tcb structure is changed */ -#define TASKTCB_XCOREID_OFFSET (0x3C+configMAX_TASK_NAME_LEN+3)&~3 +#define TASKTCB_XCOREID_OFFSET (0x38+configMAX_TASK_NAME_LEN+3)&~3 .extern pxCurrentTCB /* Enable stack backtrace across exception/interrupt - see below */ @@ -302,12 +303,12 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ .section .iram1,"ax" - .global panicHandler + .global panicHandler .global _xt_panic .type _xt_panic,@function .align 4 - .literal_position + .literal_position .align 4 _xt_panic: @@ -339,45 +340,45 @@ _xt_panic: rsr a0, EXCSAVE_1 /* save interruptee's a0 */ s32i a0, sp, XT_STK_A0 - /* Set up PS for C, reenable hi-pri interrupts, and clear EXCM. */ - movi a0, PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM | PS_WOE + /* Set up PS for C, disable all interrupts, and clear EXCM. */ + movi a0, PS_INTLEVEL(7) | PS_UM | PS_WOE wsr a0, PS - //Call panic handler - mov a2,sp - call4 panicHandler + //Call panic handler + mov a6,sp + call4 panicHandler 1: j 1b /* loop infinitely */ - retw + retw - .align 4 + .align 4 //Call using call0. Prints the hex char in a2. Kills a3, a4, a5 panic_print_hex: - movi a3,0x60000000 - movi a4,8 + movi a3,0x60000000 + movi a4,8 panic_print_hex_loop: - l32i a5, a3, 0x1c - extui a5, a5, 16, 8 - bgei a5,64,panic_print_hex_loop + l32i a5, a3, 0x1c + extui a5, a5, 16, 8 + bgei a5,64,panic_print_hex_loop - srli a5,a2,28 - bgei a5,10,panic_print_hex_a - addi a5,a5,'0' - j panic_print_hex_ok + srli a5,a2,28 + bgei a5,10,panic_print_hex_a + addi a5,a5,'0' + j panic_print_hex_ok panic_print_hex_a: - addi a5,a5,'A'-10 + addi a5,a5,'A'-10 panic_print_hex_ok: - s32i a5,a3,0 - slli a2,a2,4 - - addi a4,a4,-1 - bnei a4,0,panic_print_hex_loop - movi a5,' ' - s32i a5,a3,0 + s32i a5,a3,0 + slli a2,a2,4 + + addi a4,a4,-1 + bnei a4,0,panic_print_hex_loop + movi a5,' ' + s32i a5,a3,0 - ret + ret @@ -462,6 +463,8 @@ _DebugExceptionVector: jx a3 #else wsr a0, EXCSAVE+XCHAL_DEBUGLEVEL /* save original a0 somewhere */ + movi a0,PANIC_RSN_DEBUGEXCEPTION + wsr a0,EXCCAUSE call0 _xt_panic /* does not return */ rfi XCHAL_DEBUGLEVEL /* make a0 point here not later */ #endif @@ -489,6 +492,8 @@ _DoubleExceptionVector: #if XCHAL_HAVE_DEBUG break 1, 4 /* unhandled double exception */ #endif + movi a0,PANIC_RSN_DOUBLEEXCEPTION + wsr a0,EXCCAUSE call0 _xt_panic /* does not return */ rfde /* make a0 point here not later */ @@ -522,6 +527,8 @@ _xt_kernel_exc: #if XCHAL_HAVE_DEBUG break 1, 0 /* unhandled kernel exception */ #endif + movi a0,PANIC_RSN_KERNELEXCEPTION + wsr a0,EXCCAUSE call0 _xt_panic /* does not return */ rfe /* make a0 point here not there */ @@ -909,11 +916,11 @@ _xt_coproc_exc: addi a2, a2, TASKTCB_XCOREID_OFFSET /* offset to xCoreID in tcb struct */ s32i a3, a2, 0 /* store current cpuid */ - /* Grab correct xt_coproc_owner_sa for this core */ - movi a2, XCHAL_CP_MAX << 2 - mull a2, a2, a3 + /* Grab correct xt_coproc_owner_sa for this core */ + movi a2, XCHAL_CP_MAX << 2 + mull a2, a2, a3 movi a3, _xt_coproc_owner_sa /* a3 = base of owner array */ - add a3, a3, a2 + add a3, a3, a2 extui a2, a0, 0, 16 /* coprocessor bitmask portion */ or a4, a4, a2 /* a4 = CPENABLE | (1 << n) */ @@ -1024,6 +1031,8 @@ _xt_coproc_exc: #if XCHAL_HAVE_DEBUG break 1, 1 /* unhandled user exception */ #endif + movi a0,PANIC_RSN_COPROCEXCEPTION + wsr a0,EXCCAUSE call0 _xt_panic /* not in a thread (invalid) */ /* never returns */ @@ -1607,6 +1616,28 @@ _xt_highint4: ADD HIGH PRIORITY LEVEL 4 INTERRUPT HANDLER CODE HERE. */ + + + /* On the ESP32, this level is used for the INT_WDT handler. If that triggers, the program is stuck with interrupts + off and the CPU should panic. */ + rsr a0, EXCSAVE_4 + wsr a0, EXCSAVE_1 /* panic handler reads this register */ + /* Set EXCCAUSE to reflect cause of the wdt int trigger */ + movi a0,PANIC_RSN_INTWDT_CPU0 + wsr a0,EXCCAUSE +#if CONFIG_INT_WDT_CHECK_CPU1 + /* Check if the cause is the app cpu failing to tick.*/ + movi a0, int_wdt_app_cpu_ticked + l32i a0, a0, 0 + bnez a0, 1f + /* It is. Modify cause. */ + movi a0,PANIC_RSN_INTWDT_CPU1 + wsr a0,EXCCAUSE +1: +#endif + call0 _xt_panic + + .align 4 .L_xt_highint4_exit: rsr a0, EXCSAVE_4 /* restore a0 */ diff --git a/components/freertos/xtensa_vectors.S-new b/components/freertos/xtensa_vectors.S-new deleted file mode 100644 index 88349eee9f..0000000000 --- a/components/freertos/xtensa_vectors.S-new +++ /dev/null @@ -1,1915 +0,0 @@ -/******************************************************************************* -Copyright (c) 2006-2015 Cadence Design Systems Inc. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------------------------------- - - XTENSA VECTORS AND LOW LEVEL HANDLERS FOR AN RTOS - - Xtensa low level exception and interrupt vectors and handlers for an RTOS. - - Interrupt handlers and user exception handlers support interaction with - the RTOS by calling XT_RTOS_INT_ENTER and XT_RTOS_INT_EXIT before and - after user's specific interrupt handlers. These macros are defined in - xtensa_.h to call suitable functions in a specific RTOS. - - Users can install application-specific interrupt handlers for low and - medium level interrupts, by calling xt_set_interrupt_handler(). These - handlers can be written in C, and must obey C calling convention. The - handler table is indexed by the interrupt number. Each handler may be - provided with an argument. - - Note that the system timer interrupt is handled specially, and is - dispatched to the RTOS-specific handler. This timer cannot be hooked - by application code. - - Optional hooks are also provided to install a handler per level at - run-time, made available by compiling this source file with - '-DXT_INTEXC_HOOKS' (useful for automated testing). - -!! This file is a template that usually needs to be modified to handle !! -!! application specific interrupts. Search USER_EDIT for helpful comments !! -!! on where to insert handlers and how to write them. !! - - Users can also install application-specific exception handlers in the - same way, by calling xt_set_exception_handler(). One handler slot is - provided for each exception type. Note that some exceptions are handled - by the porting layer itself, and cannot be taken over by application - code in this manner. These are the alloca, syscall, and coprocessor - exceptions. - - The exception handlers can be written in C, and must follow C calling - convention. Each handler is passed a pointer to an exception frame as - its single argument. The exception frame is created on the stack, and - holds the saved context of the thread that took the exception. If the - handler returns, the context will be restored and the instruction that - caused the exception will be retried. If the handler makes any changes - to the saved state in the exception frame, the changes will be applied - when restoring the context. - - Because Xtensa is a configurable architecture, this port supports all user - generated configurations (except restrictions stated in the release notes). - This is accomplished by conditional compilation using macros and functions - defined in the Xtensa HAL (hardware adaptation layer) for your configuration. - Only the relevant parts of this file will be included in your RTOS build. - For example, this file provides interrupt vector templates for all types and - all priority levels, but only the ones in your configuration are built. - - NOTES on the use of 'call0' for long jumps instead of 'j': - 1. This file should be assembled with the -mlongcalls option to xt-xcc. - 2. The -mlongcalls compiler option causes 'call0 dest' to be expanded to - a sequence 'l32r a0, dest' 'callx0 a0' which works regardless of the - distance from the call to the destination. The linker then relaxes - it back to 'call0 dest' if it determines that dest is within range. - This allows more flexibility in locating code without the performance - overhead of the 'l32r' literal data load in cases where the destination - is in range of 'call0'. There is an additional benefit in that 'call0' - has a longer range than 'j' due to the target being word-aligned, so - the 'l32r' sequence is less likely needed. - 3. The use of 'call0' with -mlongcalls requires that register a0 not be - live at the time of the call, which is always the case for a function - call but needs to be ensured if 'call0' is used as a jump in lieu of 'j'. - 4. This use of 'call0' is independent of the C function call ABI. - -*******************************************************************************/ - -#include "xtensa_rtos.h" - - -/* Enable stack backtrace across exception/interrupt - see below */ -#define XT_DEBUG_BACKTRACE 1 - - -/* --------------------------------------------------------------------------------- - Defines used to access _xtos_interrupt_table. --------------------------------------------------------------------------------- -*/ -#define XIE_HANDLER 0 -#define XIE_ARG 4 -#define XIE_SIZE 8 - -/* --------------------------------------------------------------------------------- - Macro extract_msb - return the input with only the highest bit set. - - Input : "ain" - Input value, clobbered. - Output : "aout" - Output value, has only one bit set, MSB of "ain". - The two arguments must be different AR registers. --------------------------------------------------------------------------------- -*/ - - .macro extract_msb aout ain -1: - addi \aout, \ain, -1 /* aout = ain - 1 */ - and \ain, \ain, \aout /* ain = ain & aout */ - bnez \ain, 1b /* repeat until ain == 0 */ - addi \aout, \aout, 1 /* return aout + 1 */ - .endm - -/* --------------------------------------------------------------------------------- - Macro dispatch_c_isr - dispatch interrupts to user ISRs. - This will dispatch to user handlers (if any) that are registered in the - XTOS dispatch table (_xtos_interrupt_table). These handlers would have - been registered by calling _xtos_set_interrupt_handler(). There is one - exception - the timer interrupt used by the OS will not be dispatched - to a user handler - this must be handled by the caller of this macro. - - Level triggered and software interrupts are automatically deasserted by - this code. - - ASSUMPTIONS: - -- PS.INTLEVEL is set to "level" at entry - -- PS.EXCM = 0, C calling enabled - - NOTE: For CALL0 ABI, a12-a15 have not yet been saved. - - NOTE: This macro will use registers a0 and a2-a6. The arguments are: - level -- interrupt level - mask -- interrupt bitmask for this level --------------------------------------------------------------------------------- -*/ - - .macro dispatch_c_isr level mask - - /* Get mask of pending, enabled interrupts at this level into a2. */ - -.L_xt_user_int_&level&: - rsr a2, INTENABLE - rsr a3, INTERRUPT - movi a4, \mask - and a2, a2, a3 - and a2, a2, a4 - beqz a2, 9f /* nothing to do */ - - /* This bit of code provides a nice debug backtrace in the debugger. - It does take a few more instructions, so undef XT_DEBUG_BACKTRACE - if you want to save the cycles. - */ - #if XT_DEBUG_BACKTRACE - #ifndef __XTENSA_CALL0_ABI__ - rsr a0, EPC_1 + \level - 1 /* return address */ - movi a4, 0xC0000000 /* constant with top 2 bits set (call size) */ - or a0, a0, a4 /* set top 2 bits */ - addx2 a0, a4, a0 /* clear top bit -- simulating call4 size */ - #endif - #endif - - #ifdef XT_INTEXC_HOOKS - /* Call interrupt hook if present to (pre)handle interrupts. */ - movi a4, _xt_intexc_hooks - l32i a4, a4, \level << 2 - beqz a4, 2f - #ifdef __XTENSA_CALL0_ABI__ - callx0 a4 - beqz a2, 9f - #else - mov a6, a2 - callx4 a4 - beqz a6, 9f - mov a2, a6 - #endif -2: - #endif - - /* Now look up in the dispatch table and call user ISR if any. */ - /* If multiple bits are set then MSB has highest priority. */ - - extract_msb a4, a2 /* a4 = MSB of a2, a2 trashed */ - - #ifdef XT_USE_SWPRI - /* Enable all interrupts at this level that are numerically higher - than the one we just selected, since they are treated as higher - priority. - */ - movi a3, \mask /* a3 = all interrupts at this level */ - add a2, a4, a4 /* a2 = a4 << 1 */ - addi a2, a2, -1 /* a2 = mask of 1's <= a4 bit */ - and a2, a2, a3 /* a2 = mask of all bits <= a4 at this level */ - movi a3, _xt_intdata - l32i a6, a3, 4 /* a6 = _xt_vpri_mask */ - neg a2, a2 - addi a2, a2, -1 /* a2 = mask to apply */ - and a5, a6, a2 /* mask off all bits <= a4 bit */ - s32i a5, a3, 4 /* update _xt_vpri_mask */ - rsr a3, INTENABLE - and a3, a3, a2 /* mask off all bits <= a4 bit */ - wsr a3, INTENABLE - rsil a3, \level - 1 /* lower interrupt level by 1 */ - #endif - - movi a3, XT_TIMER_INTEN /* a3 = timer interrupt bit */ - wsr a4, INTCLEAR /* clear sw or edge-triggered interrupt */ - beq a3, a4, 7f /* if timer interrupt then skip table */ - - find_ms_setbit a3, a4, a3, 0 /* a3 = interrupt number */ - - movi a4, _xt_interrupt_table - addx8 a3, a3, a4 /* a3 = address of interrupt table entry */ - l32i a4, a3, XIE_HANDLER /* a4 = handler address */ - #ifdef __XTENSA_CALL0_ABI__ - mov a12, a6 /* save in callee-saved reg */ - l32i a2, a3, XIE_ARG /* a2 = handler arg */ - callx0 a4 /* call handler */ - mov a2, a12 - #else - mov a2, a6 /* save in windowed reg */ - l32i a6, a3, XIE_ARG /* a6 = handler arg */ - callx4 a4 /* call handler */ - #endif - - #ifdef XT_USE_SWPRI - j 8f - #else - j .L_xt_user_int_&level& /* check for more interrupts */ - #endif - -7: - - .ifeq XT_TIMER_INTPRI - \level -.L_xt_user_int_timer_&level&: - /* - Interrupt handler for the RTOS tick timer if at this level. - We'll be reading the interrupt state again after this call - so no need to preserve any registers except a6 (vpri_mask). - */ - - #ifdef __XTENSA_CALL0_ABI__ - mov a12, a6 - call0 XT_RTOS_TIMER_INT - mov a2, a12 - #else - mov a2, a6 - call4 XT_RTOS_TIMER_INT - #endif - .endif - - #ifdef XT_USE_SWPRI - j 8f - #else - j .L_xt_user_int_&level& /* check for more interrupts */ - #endif - - #ifdef XT_USE_SWPRI -8: - /* Restore old value of _xt_vpri_mask from a2. Also update INTENABLE from - virtual _xt_intenable which _could_ have changed during interrupt - processing. */ - - movi a3, _xt_intdata - l32i a4, a3, 0 /* a4 = _xt_intenable */ - s32i a2, a3, 4 /* update _xt_vpri_mask */ - and a4, a4, a2 /* a4 = masked intenable */ - wsr a4, INTENABLE /* update INTENABLE */ - #endif - -9: - /* done */ - - .endm - - -/* --------------------------------------------------------------------------------- - Panic handler. - Should be reached by call0 (preferable) or jump only. If call0, a0 says where - from. If on simulator, display panic message and abort, else loop indefinitely. --------------------------------------------------------------------------------- -*/ - - .text - .global _xt_panic - .type _xt_panic,@function - .align 4 - -_xt_panic: - #ifdef XT_SIMULATOR - addi a4, a0, -3 /* point to call0 */ - movi a3, _xt_panic_message - movi a2, SYS_log_msg - simcall - movi a2, SYS_gdb_abort - simcall - #else - rsil a2, XCHAL_EXCM_LEVEL /* disable all low & med ints */ -1: j 1b /* loop infinitely */ - #endif - - .section .rodata, "a" - .align 4 - -_xt_panic_message: - .string "\n*** _xt_panic() was called from 0x%08x or jumped to. ***\n" - - -/* --------------------------------------------------------------------------------- - Hooks to dynamically install handlers for exceptions and interrupts. - Allows automated regression frameworks to install handlers per test. - Consists of an array of function pointers indexed by interrupt level, - with index 0 containing the entry for user exceptions. - Initialized with all 0s, meaning no handler is installed at each level. - See comment in xtensa_rtos.h for more details. --------------------------------------------------------------------------------- -*/ - - #ifdef XT_INTEXC_HOOKS - .data - .global _xt_intexc_hooks - .type _xt_intexc_hooks,@object - .align 4 - -_xt_intexc_hooks: - .fill XT_INTEXC_HOOK_NUM, 4, 0 - #endif - - -/* --------------------------------------------------------------------------------- - EXCEPTION AND LEVEL 1 INTERRUPT VECTORS AND LOW LEVEL HANDLERS - (except window exception vectors). - - Each vector goes at a predetermined location according to the Xtensa - hardware configuration, which is ensured by its placement in a special - section known to the Xtensa linker support package (LSP). It performs - the minimum necessary before jumping to the handler in the .text section. - - The corresponding handler goes in the normal .text section. It sets up - the appropriate stack frame, saves a few vector-specific registers and - calls XT_RTOS_INT_ENTER to save the rest of the interrupted context - and enter the RTOS, then sets up a C environment. It then calls the - user's interrupt handler code (which may be coded in C) and finally - calls XT_RTOS_INT_EXIT to transfer control to the RTOS for scheduling. - - While XT_RTOS_INT_EXIT does not return directly to the interruptee, - eventually the RTOS scheduler will want to dispatch the interrupted - task or handler. The scheduler will return to the exit point that was - saved in the interrupt stack frame at XT_STK_EXIT. --------------------------------------------------------------------------------- -*/ - - -/* --------------------------------------------------------------------------------- -Debug Exception. --------------------------------------------------------------------------------- -*/ - -#if XCHAL_HAVE_DEBUG - - .begin literal_prefix .DebugExceptionVector - .section .DebugExceptionVector.text, "ax" - .global _DebugExceptionVector - .align 4 - -_DebugExceptionVector: - - #ifdef XT_SIMULATOR - /* - In the simulator, let the debugger (if any) handle the debug exception, - or simply stop the simulation: - */ - wsr a2, EXCSAVE+XCHAL_DEBUGLEVEL /* save a2 where sim expects it */ - movi a2, SYS_gdb_enter_sktloop - simcall /* have ISS handle debug exc. */ - #elif 0 /* change condition to 1 to use the HAL minimal debug handler */ - wsr a3, EXCSAVE+XCHAL_DEBUGLEVEL - movi a3, xthal_debugexc_defhndlr_nw /* use default debug handler */ - jx a3 - #else - wsr a0, EXCSAVE+XCHAL_DEBUGLEVEL /* save original a0 somewhere */ - call0 _xt_panic /* does not return */ - rfi XCHAL_DEBUGLEVEL /* make a0 point here not later */ - #endif - - .end literal_prefix - -#endif - -/* --------------------------------------------------------------------------------- -Double Exception. -Double exceptions are not a normal occurrence. They indicate a bug of some kind. --------------------------------------------------------------------------------- -*/ - -#ifdef XCHAL_DOUBLEEXC_VECTOR_VADDR - - .begin literal_prefix .DoubleExceptionVector - .section .DoubleExceptionVector.text, "ax" - .global _DoubleExceptionVector - .align 4 - -_DoubleExceptionVector: - - #if XCHAL_HAVE_DEBUG - break 1, 4 /* unhandled double exception */ - #endif - call0 _xt_panic /* does not return */ - rfde /* make a0 point here not later */ - - .end literal_prefix - -#endif /* XCHAL_DOUBLEEXC_VECTOR_VADDR */ - -/* --------------------------------------------------------------------------------- -Kernel Exception (including Level 1 Interrupt from kernel mode). --------------------------------------------------------------------------------- -*/ - - .begin literal_prefix .KernelExceptionVector - .section .KernelExceptionVector.text, "ax" - .global _KernelExceptionVector - .align 4 - -_KernelExceptionVector: - - wsr a0, EXCSAVE_1 /* preserve a0 */ - call0 _xt_kernel_exc /* kernel exception handler */ - /* never returns here - call0 is used as a jump (see note at top) */ - - .end literal_prefix - - .text - .align 4 - -_xt_kernel_exc: - #if XCHAL_HAVE_DEBUG - break 1, 0 /* unhandled kernel exception */ - #endif - call0 _xt_panic /* does not return */ - rfe /* make a0 point here not there */ - - -/* --------------------------------------------------------------------------------- -User Exception (including Level 1 Interrupt from user mode). --------------------------------------------------------------------------------- -*/ - - .begin literal_prefix .UserExceptionVector - .section .UserExceptionVector.text, "ax" - .global _UserExceptionVector - .type _UserExceptionVector,@function - .align 4 - -_UserExceptionVector: - - wsr a0, EXCSAVE_1 /* preserve a0 */ - call0 _xt_user_exc /* user exception handler */ - /* never returns here - call0 is used as a jump (see note at top) */ - - .end literal_prefix - -/* --------------------------------------------------------------------------------- - Insert some waypoints for jumping beyond the signed 8-bit range of - conditional branch instructions, so the conditional branchces to specific - exception handlers are not taken in the mainline. Saves some cycles in the - mainline. --------------------------------------------------------------------------------- -*/ - - .text - - #if XCHAL_HAVE_WINDOWED - .align 4 -_xt_to_alloca_exc: - call0 _xt_alloca_exc /* in window vectors section */ - /* never returns here - call0 is used as a jump (see note at top) */ - #endif - - .align 4 -_xt_to_syscall_exc: - call0 _xt_syscall_exc - /* never returns here - call0 is used as a jump (see note at top) */ - - #if XCHAL_CP_NUM > 0 - .align 4 -_xt_to_coproc_exc: - call0 _xt_coproc_exc - /* never returns here - call0 is used as a jump (see note at top) */ - #endif - - -/* --------------------------------------------------------------------------------- - User exception handler. --------------------------------------------------------------------------------- -*/ - - .type _xt_user_exc,@function - .align 4 - -_xt_user_exc: - - /* If level 1 interrupt then jump to the dispatcher */ - rsr a0, EXCCAUSE - beqi a0, EXCCAUSE_LEVEL1INTERRUPT, _xt_lowint1 - - /* Handle any coprocessor exceptions. Rely on the fact that exception - numbers above EXCCAUSE_CP0_DISABLED all relate to the coprocessors. - */ - #if XCHAL_CP_NUM > 0 - bgeui a0, EXCCAUSE_CP0_DISABLED, _xt_to_coproc_exc - #endif - - /* Handle alloca and syscall exceptions */ - #if XCHAL_HAVE_WINDOWED - beqi a0, EXCCAUSE_ALLOCA, _xt_to_alloca_exc - #endif - beqi a0, EXCCAUSE_SYSCALL, _xt_to_syscall_exc - - /* Handle all other exceptions. All can have user-defined handlers. */ - /* NOTE: we'll stay on the user stack for exception handling. */ - - /* Allocate exception frame and save minimal context. */ - mov a0, sp - addi sp, sp, -XT_STK_FRMSZ - s32i a0, sp, XT_STK_A1 - #if XCHAL_HAVE_WINDOWED - s32e a0, sp, -12 /* for debug backtrace */ - #endif - rsr a0, PS /* save interruptee's PS */ - s32i a0, sp, XT_STK_PS - rsr a0, EPC_1 /* save interruptee's PC */ - s32i a0, sp, XT_STK_PC - rsr a0, EXCSAVE_1 /* save interruptee's a0 */ - s32i a0, sp, XT_STK_A0 - #if XCHAL_HAVE_WINDOWED - s32e a0, sp, -16 /* for debug backtrace */ - #endif - s32i a12, sp, XT_STK_A12 /* _xt_context_save requires A12- */ - s32i a13, sp, XT_STK_A13 /* A13 to have already been saved */ - call0 _xt_context_save - - /* Save exc cause and vaddr into exception frame */ - rsr a0, EXCCAUSE - s32i a0, sp, XT_STK_EXCCAUSE - rsr a0, EXCVADDR - s32i a0, sp, XT_STK_EXCVADDR - - /* Set up PS for C, reenable hi-pri interrupts, and clear EXCM. */ - #ifdef __XTENSA_CALL0_ABI__ - movi a0, PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM - #else - movi a0, PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM | PS_WOE - #endif - wsr a0, PS - - #ifdef XT_DEBUG_BACKTRACE - #ifndef __XTENSA_CALL0_ABI__ - rsr a0, EPC_1 /* return address for debug backtrace */ - movi a5, 0xC0000000 /* constant with top 2 bits set (call size) */ - rsync /* wait for WSR.PS to complete */ - or a0, a0, a5 /* set top 2 bits */ - addx2 a0, a5, a0 /* clear top bit -- thus simulating call4 size */ - #else - rsync /* wait for WSR.PS to complete */ - #endif - #endif - - rsr a2, EXCCAUSE /* recover exc cause */ - - #ifdef XT_INTEXC_HOOKS - /* - Call exception hook to pre-handle exceptions (if installed). - Pass EXCCAUSE in a2, and check result in a2 (if -1, skip default handling). - */ - movi a4, _xt_intexc_hooks - l32i a4, a4, 0 /* user exception hook index 0 */ - beqz a4, 1f -.Ln_xt_user_exc_call_hook: - #ifdef __XTENSA_CALL0_ABI__ - callx0 a4 - beqi a2, -1, .L_xt_user_done - #else - mov a6, a2 - callx4 a4 - beqi a6, -1, .L_xt_user_done - mov a2, a6 - #endif -1: - #endif - - rsr a2, EXCCAUSE /* recover exc cause */ - movi a3, _xt_exception_table - addx4 a4, a2, a3 /* a4 = address of exception table entry */ - l32i a4, a4, 0 /* a4 = handler address */ - #ifdef __XTENSA_CALL0_ABI__ - mov a2, sp /* a2 = pointer to exc frame */ - callx0 a4 /* call handler */ - #else - mov a6, sp /* a6 = pointer to exc frame */ - callx4 a4 /* call handler */ - #endif - -.L_xt_user_done: - - /* Restore context and return */ - call0 _xt_context_restore - l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */ - wsr a0, PS - l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */ - wsr a0, EPC_1 - l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */ - l32i sp, sp, XT_STK_A1 /* remove exception frame */ - rsync /* ensure PS and EPC written */ - rfe /* PS.EXCM is cleared */ - - -/* --------------------------------------------------------------------------------- - Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT - on entry and used to return to a thread or interrupted interrupt handler. --------------------------------------------------------------------------------- -*/ - - .global _xt_user_exit - .type _xt_user_exit,@function - .align 4 -_xt_user_exit: - l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */ - wsr a0, PS - l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */ - wsr a0, EPC_1 - l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */ - l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */ - rsync /* ensure PS and EPC written */ - rfe /* PS.EXCM is cleared */ - - -/* --------------------------------------------------------------------------------- -Syscall Exception Handler (jumped to from User Exception Handler). -Syscall 0 is required to spill the register windows (no-op in Call 0 ABI). -Only syscall 0 is handled here. Other syscalls return -1 to caller in a2. --------------------------------------------------------------------------------- -*/ - - .text - .type _xt_syscall_exc,@function - .align 4 -_xt_syscall_exc: - - #ifdef __XTENSA_CALL0_ABI__ - /* - Save minimal regs for scratch. Syscall 0 does nothing in Call0 ABI. - Use a minimal stack frame (16B) to save A2 & A3 for scratch. - PS.EXCM could be cleared here, but unlikely to improve worst-case latency. - rsr a0, PS - addi a0, a0, -PS_EXCM_MASK - wsr a0, PS - */ - addi sp, sp, -16 - s32i a2, sp, 8 - s32i a3, sp, 12 - #else /* Windowed ABI */ - /* - Save necessary context and spill the register windows. - PS.EXCM is still set and must remain set until after the spill. - Reuse context save function though it saves more than necessary. - For this reason, a full interrupt stack frame is allocated. - */ - addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */ - s32i a12, sp, XT_STK_A12 /* _xt_context_save requires A12- */ - s32i a13, sp, XT_STK_A13 /* A13 to have already been saved */ - call0 _xt_context_save - #endif - - /* - Grab the interruptee's PC and skip over the 'syscall' instruction. - If it's at the end of a zero-overhead loop and it's not on the last - iteration, decrement loop counter and skip to beginning of loop. - */ - rsr a2, EPC_1 /* a2 = PC of 'syscall' */ - addi a3, a2, 3 /* ++PC */ - #if XCHAL_HAVE_LOOPS - rsr a0, LEND /* if (PC == LEND */ - bne a3, a0, 1f - rsr a0, LCOUNT /* && LCOUNT != 0) */ - beqz a0, 1f /* { */ - addi a0, a0, -1 /* --LCOUNT */ - rsr a3, LBEG /* PC = LBEG */ - wsr a0, LCOUNT /* } */ - #endif -1: wsr a3, EPC_1 /* update PC */ - - /* Restore interruptee's context and return from exception. */ - #ifdef __XTENSA_CALL0_ABI__ - l32i a2, sp, 8 - l32i a3, sp, 12 - addi sp, sp, 16 - #else - call0 _xt_context_restore - addi sp, sp, XT_STK_FRMSZ - #endif - movi a0, -1 - movnez a2, a0, a2 /* return -1 if not syscall 0 */ - rsr a0, EXCSAVE_1 - rfe - -/* --------------------------------------------------------------------------------- -Co-Processor Exception Handler (jumped to from User Exception Handler). -These exceptions are generated by co-processor instructions, which are only -allowed in thread code (not in interrupts or kernel code). This restriction is -deliberately imposed to reduce the burden of state-save/restore in interrupts. --------------------------------------------------------------------------------- -*/ -#if XCHAL_CP_NUM > 0 - - .section .rodata, "a" - -/* Offset to CP n save area in thread's CP save area. */ - .global _xt_coproc_sa_offset - .type _xt_coproc_sa_offset,@object - .align 16 /* minimize crossing cache boundaries */ -_xt_coproc_sa_offset: - .word XT_CP0_SA, XT_CP1_SA, XT_CP2_SA, XT_CP3_SA - .word XT_CP4_SA, XT_CP5_SA, XT_CP6_SA, XT_CP7_SA - -/* Bitmask for CP n's CPENABLE bit. */ - .type _xt_coproc_mask,@object - .align 16,,8 /* try to keep it all in one cache line */ - .set i, 0 -_xt_coproc_mask: - .rept XCHAL_CP_MAX - .long (i<<16) | (1<= 2 - - .begin literal_prefix .Level2InterruptVector - .section .Level2InterruptVector.text, "ax" - .global _Level2Vector - .type _Level2Vector,@function - .align 4 -_Level2Vector: - wsr a0, EXCSAVE_2 /* preserve a0 */ - call0 _xt_medint2 /* load interrupt handler */ - /* never returns here - call0 is used as a jump (see note at top) */ - - .end literal_prefix - - .text - .type _xt_medint2,@function - .align 4 -_xt_medint2: - mov a0, sp /* sp == a1 */ - addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */ - s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */ - rsr a0, EPS_2 /* save interruptee's PS */ - s32i a0, sp, XT_STK_PS - rsr a0, EPC_2 /* save interruptee's PC */ - s32i a0, sp, XT_STK_PC - rsr a0, EXCSAVE_2 /* save interruptee's a0 */ - s32i a0, sp, XT_STK_A0 - movi a0, _xt_medint2_exit /* save exit point for dispatch */ - s32i a0, sp, XT_STK_EXIT - - /* Save rest of interrupt context and enter RTOS. */ - call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */ - - /* !! We are now on the RTOS system stack !! */ - - /* Set up PS for C, enable interrupts above this level and clear EXCM. */ - #ifdef __XTENSA_CALL0_ABI__ - movi a0, PS_INTLEVEL(2) | PS_UM - #else - movi a0, PS_INTLEVEL(2) | PS_UM | PS_WOE - #endif - wsr a0, PS - rsync - - /* OK to call C code at this point, dispatch user ISRs */ - - dispatch_c_isr 2 XCHAL_INTLEVEL2_MASK - - /* Done handling interrupts, transfer control to OS */ - call0 XT_RTOS_INT_EXIT /* does not return directly here */ - - /* - Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT - on entry and used to return to a thread or interrupted interrupt handler. - */ - .global _xt_medint2_exit - .type _xt_medint2_exit,@function - .align 4 -_xt_medint2_exit: - /* Restore only level-specific regs (the rest were already restored) */ - l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */ - wsr a0, EPS_2 - l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */ - wsr a0, EPC_2 - l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */ - l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */ - rsync /* ensure EPS and EPC written */ - rfi 2 - -#endif /* Level 2 */ - -#if XCHAL_EXCM_LEVEL >= 3 - - .begin literal_prefix .Level3InterruptVector - .section .Level3InterruptVector.text, "ax" - .global _Level3Vector - .type _Level3Vector,@function - .align 4 -_Level3Vector: - wsr a0, EXCSAVE_3 /* preserve a0 */ - call0 _xt_medint3 /* load interrupt handler */ - /* never returns here - call0 is used as a jump (see note at top) */ - - .end literal_prefix - - .text - .type _xt_medint3,@function - .align 4 -_xt_medint3: - mov a0, sp /* sp == a1 */ - addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */ - s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */ - rsr a0, EPS_3 /* save interruptee's PS */ - s32i a0, sp, XT_STK_PS - rsr a0, EPC_3 /* save interruptee's PC */ - s32i a0, sp, XT_STK_PC - rsr a0, EXCSAVE_3 /* save interruptee's a0 */ - s32i a0, sp, XT_STK_A0 - movi a0, _xt_medint3_exit /* save exit point for dispatch */ - s32i a0, sp, XT_STK_EXIT - - /* Save rest of interrupt context and enter RTOS. */ - call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */ - - /* !! We are now on the RTOS system stack !! */ - - /* Set up PS for C, enable interrupts above this level and clear EXCM. */ - #ifdef __XTENSA_CALL0_ABI__ - movi a0, PS_INTLEVEL(3) | PS_UM - #else - movi a0, PS_INTLEVEL(3) | PS_UM | PS_WOE - #endif - wsr a0, PS - rsync - - /* OK to call C code at this point, dispatch user ISRs */ - - dispatch_c_isr 3 XCHAL_INTLEVEL3_MASK - - /* Done handling interrupts, transfer control to OS */ - call0 XT_RTOS_INT_EXIT /* does not return directly here */ - - /* - Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT - on entry and used to return to a thread or interrupted interrupt handler. - */ - .global _xt_medint3_exit - .type _xt_medint3_exit,@function - .align 4 -_xt_medint3_exit: - /* Restore only level-specific regs (the rest were already restored) */ - l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */ - wsr a0, EPS_3 - l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */ - wsr a0, EPC_3 - l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */ - l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */ - rsync /* ensure EPS and EPC written */ - rfi 3 - -#endif /* Level 3 */ - -#if XCHAL_EXCM_LEVEL >= 4 - - .begin literal_prefix .Level4InterruptVector - .section .Level4InterruptVector.text, "ax" - .global _Level4Vector - .type _Level4Vector,@function - .align 4 -_Level4Vector: - wsr a0, EXCSAVE_4 /* preserve a0 */ - call0 _xt_medint4 /* load interrupt handler */ - - .end literal_prefix - - .text - .type _xt_medint4,@function - .align 4 -_xt_medint4: - mov a0, sp /* sp == a1 */ - addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */ - s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */ - rsr a0, EPS_4 /* save interruptee's PS */ - s32i a0, sp, XT_STK_PS - rsr a0, EPC_4 /* save interruptee's PC */ - s32i a0, sp, XT_STK_PC - rsr a0, EXCSAVE_4 /* save interruptee's a0 */ - s32i a0, sp, XT_STK_A0 - movi a0, _xt_medint4_exit /* save exit point for dispatch */ - s32i a0, sp, XT_STK_EXIT - - /* Save rest of interrupt context and enter RTOS. */ - call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */ - - /* !! We are now on the RTOS system stack !! */ - - /* Set up PS for C, enable interrupts above this level and clear EXCM. */ - #ifdef __XTENSA_CALL0_ABI__ - movi a0, PS_INTLEVEL(4) | PS_UM - #else - movi a0, PS_INTLEVEL(4) | PS_UM | PS_WOE - #endif - wsr a0, PS - rsync - - /* OK to call C code at this point, dispatch user ISRs */ - - dispatch_c_isr 4 XCHAL_INTLEVEL4_MASK - - /* Done handling interrupts, transfer control to OS */ - call0 XT_RTOS_INT_EXIT /* does not return directly here */ - - /* - Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT - on entry and used to return to a thread or interrupted interrupt handler. - */ - .global _xt_medint4_exit - .type _xt_medint4_exit,@function - .align 4 -_xt_medint4_exit: - /* Restore only level-specific regs (the rest were already restored) */ - l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */ - wsr a0, EPS_4 - l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */ - wsr a0, EPC_4 - l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */ - l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */ - rsync /* ensure EPS and EPC written */ - rfi 4 - -#endif /* Level 4 */ - -#if XCHAL_EXCM_LEVEL >= 5 - - .begin literal_prefix .Level5InterruptVector - .section .Level5InterruptVector.text, "ax" - .global _Level5Vector - .type _Level5Vector,@function - .align 4 -_Level5Vector: - wsr a0, EXCSAVE_5 /* preserve a0 */ - call0 _xt_medint5 /* load interrupt handler */ - - .end literal_prefix - - .text - .type _xt_medint5,@function - .align 4 -_xt_medint5: - mov a0, sp /* sp == a1 */ - addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */ - s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */ - rsr a0, EPS_5 /* save interruptee's PS */ - s32i a0, sp, XT_STK_PS - rsr a0, EPC_5 /* save interruptee's PC */ - s32i a0, sp, XT_STK_PC - rsr a0, EXCSAVE_5 /* save interruptee's a0 */ - s32i a0, sp, XT_STK_A0 - movi a0, _xt_medint5_exit /* save exit point for dispatch */ - s32i a0, sp, XT_STK_EXIT - - /* Save rest of interrupt context and enter RTOS. */ - call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */ - - /* !! We are now on the RTOS system stack !! */ - - /* Set up PS for C, enable interrupts above this level and clear EXCM. */ - #ifdef __XTENSA_CALL0_ABI__ - movi a0, PS_INTLEVEL(5) | PS_UM - #else - movi a0, PS_INTLEVEL(5) | PS_UM | PS_WOE - #endif - wsr a0, PS - rsync - - /* OK to call C code at this point, dispatch user ISRs */ - - dispatch_c_isr 5 XCHAL_INTLEVEL5_MASK - - /* Done handling interrupts, transfer control to OS */ - call0 XT_RTOS_INT_EXIT /* does not return directly here */ - - /* - Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT - on entry and used to return to a thread or interrupted interrupt handler. - */ - .global _xt_medint5_exit - .type _xt_medint5_exit,@function - .align 4 -_xt_medint5_exit: - /* Restore only level-specific regs (the rest were already restored) */ - l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */ - wsr a0, EPS_5 - l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */ - wsr a0, EPC_5 - l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */ - l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */ - rsync /* ensure EPS and EPC written */ - rfi 5 - -#endif /* Level 5 */ - -#if XCHAL_EXCM_LEVEL >= 6 - - .begin literal_prefix .Level6InterruptVector - .section .Level6InterruptVector.text, "ax" - .global _Level6Vector - .type _Level6Vector,@function - .align 4 -_Level6Vector: - wsr a0, EXCSAVE_6 /* preserve a0 */ - call0 _xt_medint6 /* load interrupt handler */ - - .end literal_prefix - - .text - .type _xt_medint6,@function - .align 4 -_xt_medint6: - mov a0, sp /* sp == a1 */ - addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */ - s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */ - rsr a0, EPS_6 /* save interruptee's PS */ - s32i a0, sp, XT_STK_PS - rsr a0, EPC_6 /* save interruptee's PC */ - s32i a0, sp, XT_STK_PC - rsr a0, EXCSAVE_6 /* save interruptee's a0 */ - s32i a0, sp, XT_STK_A0 - movi a0, _xt_medint6_exit /* save exit point for dispatch */ - s32i a0, sp, XT_STK_EXIT - - /* Save rest of interrupt context and enter RTOS. */ - call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */ - - /* !! We are now on the RTOS system stack !! */ - - /* Set up PS for C, enable interrupts above this level and clear EXCM. */ - #ifdef __XTENSA_CALL0_ABI__ - movi a0, PS_INTLEVEL(6) | PS_UM - #else - movi a0, PS_INTLEVEL(6) | PS_UM | PS_WOE - #endif - wsr a0, PS - rsync - - /* OK to call C code at this point, dispatch user ISRs */ - - dispatch_c_isr 6 XCHAL_INTLEVEL6_MASK - - /* Done handling interrupts, transfer control to OS */ - call0 XT_RTOS_INT_EXIT /* does not return directly here */ - - /* - Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT - on entry and used to return to a thread or interrupted interrupt handler. - */ - .global _xt_medint6_exit - .type _xt_medint6_exit,@function - .align 4 -_xt_medint6_exit: - /* Restore only level-specific regs (the rest were already restored) */ - l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */ - wsr a0, EPS_6 - l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */ - wsr a0, EPC_6 - l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */ - l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */ - rsync /* ensure EPS and EPC written */ - rfi 6 - -#endif /* Level 6 */ - - -/******************************************************************************* - -HIGH PRIORITY (LEVEL > XCHAL_EXCM_LEVEL) INTERRUPT VECTORS AND HANDLERS - -High priority interrupts are by definition those with priorities greater -than XCHAL_EXCM_LEVEL. This includes non-maskable (NMI). High priority -interrupts cannot interact with the RTOS, that is they must save all regs -they use and not call any RTOS function. - -A further restriction imposed by the Xtensa windowed architecture is that -high priority interrupts must not modify the stack area even logically -"above" the top of the interrupted stack (they need to provide their -own stack or static save area). - -Cadence Design Systems recommends high priority interrupt handlers be coded in assembly -and used for purposes requiring very short service times. - -Here are templates for high priority (level 2+) interrupt vectors. -They assume only one interrupt per level to avoid the burden of identifying -which interrupts at this level are pending and enabled. This allows for -minimum latency and avoids having to save/restore a2 in addition to a0. -If more than one interrupt per high priority level is configured, this burden -is on the handler which in any case must provide a way to save and restore -registers it uses without touching the interrupted stack. - -Each vector goes at a predetermined location according to the Xtensa -hardware configuration, which is ensured by its placement in a special -section known to the Xtensa linker support package (LSP). It performs -the minimum necessary before jumping to the handler in the .text section. - -*******************************************************************************/ - -/* -Currently only shells for high priority interrupt handlers are provided -here. However a template and example can be found in the Cadence Design Systems tools -documentation: "Microprocessor Programmer's Guide". -*/ - -#if XCHAL_NUM_INTLEVELS >=2 && XCHAL_EXCM_LEVEL <2 && XCHAL_DEBUGLEVEL !=2 - - .begin literal_prefix .Level2InterruptVector - .section .Level2InterruptVector.text, "ax" - .global _Level2Vector - .type _Level2Vector,@function - .align 4 -_Level2Vector: - wsr a0, EXCSAVE_2 /* preserve a0 */ - call0 _xt_highint2 /* load interrupt handler */ - - .end literal_prefix - - .text - .type _xt_highint2,@function - .align 4 -_xt_highint2: - - #ifdef XT_INTEXC_HOOKS - /* Call interrupt hook if present to (pre)handle interrupts. */ - movi a0, _xt_intexc_hooks - l32i a0, a0, 2<<2 - beqz a0, 1f -.Ln_xt_highint2_call_hook: - callx0 a0 /* must NOT disturb stack! */ -1: - #endif - - /* USER_EDIT: - ADD HIGH PRIORITY LEVEL 2 INTERRUPT HANDLER CODE HERE. - */ - - .align 4 -.L_xt_highint2_exit: - rsr a0, EXCSAVE_2 /* restore a0 */ - rfi 2 - -#endif /* Level 2 */ - -#if XCHAL_NUM_INTLEVELS >=3 && XCHAL_EXCM_LEVEL <3 && XCHAL_DEBUGLEVEL !=3 - - .begin literal_prefix .Level3InterruptVector - .section .Level3InterruptVector.text, "ax" - .global _Level3Vector - .type _Level3Vector,@function - .align 4 -_Level3Vector: - wsr a0, EXCSAVE_3 /* preserve a0 */ - call0 _xt_highint3 /* load interrupt handler */ - /* never returns here - call0 is used as a jump (see note at top) */ - - .end literal_prefix - - .text - .type _xt_highint3,@function - .align 4 -_xt_highint3: - - #ifdef XT_INTEXC_HOOKS - /* Call interrupt hook if present to (pre)handle interrupts. */ - movi a0, _xt_intexc_hooks - l32i a0, a0, 3<<2 - beqz a0, 1f -.Ln_xt_highint3_call_hook: - callx0 a0 /* must NOT disturb stack! */ -1: - #endif - - /* USER_EDIT: - ADD HIGH PRIORITY LEVEL 3 INTERRUPT HANDLER CODE HERE. - */ - - .align 4 -.L_xt_highint3_exit: - rsr a0, EXCSAVE_3 /* restore a0 */ - rfi 3 - -#endif /* Level 3 */ - -#if XCHAL_NUM_INTLEVELS >=4 && XCHAL_EXCM_LEVEL <4 && XCHAL_DEBUGLEVEL !=4 - - .begin literal_prefix .Level4InterruptVector - .section .Level4InterruptVector.text, "ax" - .global _Level4Vector - .type _Level4Vector,@function - .align 4 -_Level4Vector: - wsr a0, EXCSAVE_4 /* preserve a0 */ - call0 _xt_highint4 /* load interrupt handler */ - /* never returns here - call0 is used as a jump (see note at top) */ - - .end literal_prefix - - .text - .type _xt_highint4,@function - .align 4 -_xt_highint4: - - #ifdef XT_INTEXC_HOOKS - /* Call interrupt hook if present to (pre)handle interrupts. */ - movi a0, _xt_intexc_hooks - l32i a0, a0, 4<<2 - beqz a0, 1f -.Ln_xt_highint4_call_hook: - callx0 a0 /* must NOT disturb stack! */ -1: - #endif - - /* USER_EDIT: - ADD HIGH PRIORITY LEVEL 4 INTERRUPT HANDLER CODE HERE. - */ - - .align 4 -.L_xt_highint4_exit: - rsr a0, EXCSAVE_4 /* restore a0 */ - rfi 4 - -#endif /* Level 4 */ - -#if XCHAL_NUM_INTLEVELS >=5 && XCHAL_EXCM_LEVEL <5 && XCHAL_DEBUGLEVEL !=5 - - .begin literal_prefix .Level5InterruptVector - .section .Level5InterruptVector.text, "ax" - .global _Level5Vector - .type _Level5Vector,@function - .align 4 -_Level5Vector: - wsr a0, EXCSAVE_5 /* preserve a0 */ - call0 _xt_highint5 /* load interrupt handler */ - /* never returns here - call0 is used as a jump (see note at top) */ - - .end literal_prefix - - .text - .type _xt_highint5,@function - .align 4 -_xt_highint5: - - #ifdef XT_INTEXC_HOOKS - /* Call interrupt hook if present to (pre)handle interrupts. */ - movi a0, _xt_intexc_hooks - l32i a0, a0, 5<<2 - beqz a0, 1f -.Ln_xt_highint5_call_hook: - callx0 a0 /* must NOT disturb stack! */ -1: - #endif - - /* USER_EDIT: - ADD HIGH PRIORITY LEVEL 5 INTERRUPT HANDLER CODE HERE. - */ - - .align 4 -.L_xt_highint5_exit: - rsr a0, EXCSAVE_5 /* restore a0 */ - rfi 5 - -#endif /* Level 5 */ - -#if XCHAL_NUM_INTLEVELS >=6 && XCHAL_EXCM_LEVEL <6 && XCHAL_DEBUGLEVEL !=6 - - .begin literal_prefix .Level6InterruptVector - .section .Level6InterruptVector.text, "ax" - .global _Level6Vector - .type _Level6Vector,@function - .align 4 -_Level6Vector: - wsr a0, EXCSAVE_6 /* preserve a0 */ - call0 _xt_highint6 /* load interrupt handler */ - /* never returns here - call0 is used as a jump (see note at top) */ - - .end literal_prefix - - .text - .type _xt_highint6,@function - .align 4 -_xt_highint6: - - #ifdef XT_INTEXC_HOOKS - /* Call interrupt hook if present to (pre)handle interrupts. */ - movi a0, _xt_intexc_hooks - l32i a0, a0, 6<<2 - beqz a0, 1f -.Ln_xt_highint6_call_hook: - callx0 a0 /* must NOT disturb stack! */ -1: - #endif - - /* USER_EDIT: - ADD HIGH PRIORITY LEVEL 6 INTERRUPT HANDLER CODE HERE. - */ - - .align 4 -.L_xt_highint6_exit: - rsr a0, EXCSAVE_6 /* restore a0 */ - rfi 6 - -#endif /* Level 6 */ - -#if XCHAL_HAVE_NMI - - .begin literal_prefix .NMIExceptionVector - .section .NMIExceptionVector.text, "ax" - .global _NMIExceptionVector - .type _NMIExceptionVector,@function - .align 4 -_NMIExceptionVector: - wsr a0, EXCSAVE + XCHAL_NMILEVEL _ /* preserve a0 */ - call0 _xt_nmi /* load interrupt handler */ - /* never returns here - call0 is used as a jump (see note at top) */ - - .end literal_prefix - - .text - .type _xt_nmi,@function - .align 4 -_xt_nmi: - - #ifdef XT_INTEXC_HOOKS - /* Call interrupt hook if present to (pre)handle interrupts. */ - movi a0, _xt_intexc_hooks - l32i a0, a0, XCHAL_NMILEVEL<<2 - beqz a0, 1f -.Ln_xt_nmi_call_hook: - callx0 a0 /* must NOT disturb stack! */ -1: - #endif - - /* USER_EDIT: - ADD HIGH PRIORITY NON-MASKABLE INTERRUPT (NMI) HANDLER CODE HERE. - */ - - .align 4 -.L_xt_nmi_exit: - rsr a0, EXCSAVE + XCHAL_NMILEVEL /* restore a0 */ - rfi XCHAL_NMILEVEL - -#endif /* NMI */ - - -/******************************************************************************* - -WINDOW OVERFLOW AND UNDERFLOW EXCEPTION VECTORS AND ALLOCA EXCEPTION HANDLER - -Here is the code for each window overflow/underflow exception vector and -(interspersed) efficient code for handling the alloca exception cause. -Window exceptions are handled entirely in the vector area and are very -tight for performance. The alloca exception is also handled entirely in -the window vector area so comes at essentially no cost in code size. -Users should never need to modify them and Cadence Design Systems recommends -they do not. - -Window handlers go at predetermined vector locations according to the -Xtensa hardware configuration, which is ensured by their placement in a -special section known to the Xtensa linker support package (LSP). Since -their offsets in that section are always the same, the LSPs do not define -a section per vector. - -These things are coded for XEA2 only (XEA1 is not supported). - -Note on Underflow Handlers: -The underflow handler for returning from call[i+1] to call[i] -must preserve all the registers from call[i+1]'s window. -In particular, a0 and a1 must be preserved because the RETW instruction -will be reexecuted (and may even underflow if an intervening exception -has flushed call[i]'s registers). -Registers a2 and up may contain return values. - -*******************************************************************************/ - -#if XCHAL_HAVE_WINDOWED - - .section .WindowVectors.text, "ax" - -/* --------------------------------------------------------------------------------- -Window Overflow Exception for Call4. - -Invoked if a call[i] referenced a register (a4-a15) -that contains data from ancestor call[j]; -call[j] had done a call4 to call[j+1]. -On entry here: - window rotated to call[j] start point; - a0-a3 are registers to be saved; - a4-a15 must be preserved; - a5 is call[j+1]'s stack pointer. --------------------------------------------------------------------------------- -*/ - - .org 0x0 - .global _WindowOverflow4 -_WindowOverflow4: - - s32e a0, a5, -16 /* save a0 to call[j+1]'s stack frame */ - s32e a1, a5, -12 /* save a1 to call[j+1]'s stack frame */ - s32e a2, a5, -8 /* save a2 to call[j+1]'s stack frame */ - s32e a3, a5, -4 /* save a3 to call[j+1]'s stack frame */ - rfwo /* rotates back to call[i] position */ - -/* --------------------------------------------------------------------------------- -Window Underflow Exception for Call4 - -Invoked by RETW returning from call[i+1] to call[i] -where call[i]'s registers must be reloaded (not live in ARs); -where call[i] had done a call4 to call[i+1]. -On entry here: - window rotated to call[i] start point; - a0-a3 are undefined, must be reloaded with call[i].reg[0..3]; - a4-a15 must be preserved (they are call[i+1].reg[0..11]); - a5 is call[i+1]'s stack pointer. --------------------------------------------------------------------------------- -*/ - - .org 0x40 - .global _WindowUnderflow4 -_WindowUnderflow4: - - l32e a0, a5, -16 /* restore a0 from call[i+1]'s stack frame */ - l32e a1, a5, -12 /* restore a1 from call[i+1]'s stack frame */ - l32e a2, a5, -8 /* restore a2 from call[i+1]'s stack frame */ - l32e a3, a5, -4 /* restore a3 from call[i+1]'s stack frame */ - rfwu - -/* --------------------------------------------------------------------------------- -Handle alloca exception generated by interruptee executing 'movsp'. -This uses space between the window vectors, so is essentially "free". -All interruptee's regs are intact except a0 which is saved in EXCSAVE_1, -and PS.EXCM has been set by the exception hardware (can't be interrupted). -The fact the alloca exception was taken means the registers associated with -the base-save area have been spilled and will be restored by the underflow -handler, so those 4 registers are available for scratch. -The code is optimized to avoid unaligned branches and minimize cache misses. --------------------------------------------------------------------------------- -*/ - - .align 4 - .global _xt_alloca_exc -_xt_alloca_exc: - - rsr a0, WINDOWBASE /* grab WINDOWBASE before rotw changes it */ - rotw -1 /* WINDOWBASE goes to a4, new a0-a3 are scratch */ - rsr a2, PS - extui a3, a2, XCHAL_PS_OWB_SHIFT, XCHAL_PS_OWB_BITS - xor a3, a3, a4 /* bits changed from old to current windowbase */ - rsr a4, EXCSAVE_1 /* restore original a0 (now in a4) */ - slli a3, a3, XCHAL_PS_OWB_SHIFT - xor a2, a2, a3 /* flip changed bits in old window base */ - wsr a2, PS /* update PS.OWB to new window base */ - rsync - - _bbci.l a4, 31, _WindowUnderflow4 - rotw -1 /* original a0 goes to a8 */ - _bbci.l a8, 30, _WindowUnderflow8 - rotw -1 - j _WindowUnderflow12 - -/* --------------------------------------------------------------------------------- -Window Overflow Exception for Call8 - -Invoked if a call[i] referenced a register (a4-a15) -that contains data from ancestor call[j]; -call[j] had done a call8 to call[j+1]. -On entry here: - window rotated to call[j] start point; - a0-a7 are registers to be saved; - a8-a15 must be preserved; - a9 is call[j+1]'s stack pointer. --------------------------------------------------------------------------------- -*/ - - .org 0x80 - .global _WindowOverflow8 -_WindowOverflow8: - - s32e a0, a9, -16 /* save a0 to call[j+1]'s stack frame */ - l32e a0, a1, -12 /* a0 <- call[j-1]'s sp - (used to find end of call[j]'s frame) */ - s32e a1, a9, -12 /* save a1 to call[j+1]'s stack frame */ - s32e a2, a9, -8 /* save a2 to call[j+1]'s stack frame */ - s32e a3, a9, -4 /* save a3 to call[j+1]'s stack frame */ - s32e a4, a0, -32 /* save a4 to call[j]'s stack frame */ - s32e a5, a0, -28 /* save a5 to call[j]'s stack frame */ - s32e a6, a0, -24 /* save a6 to call[j]'s stack frame */ - s32e a7, a0, -20 /* save a7 to call[j]'s stack frame */ - rfwo /* rotates back to call[i] position */ - -/* --------------------------------------------------------------------------------- -Window Underflow Exception for Call8 - -Invoked by RETW returning from call[i+1] to call[i] -where call[i]'s registers must be reloaded (not live in ARs); -where call[i] had done a call8 to call[i+1]. -On entry here: - window rotated to call[i] start point; - a0-a7 are undefined, must be reloaded with call[i].reg[0..7]; - a8-a15 must be preserved (they are call[i+1].reg[0..7]); - a9 is call[i+1]'s stack pointer. --------------------------------------------------------------------------------- -*/ - - .org 0xC0 - .global _WindowUnderflow8 -_WindowUnderflow8: - - l32e a0, a9, -16 /* restore a0 from call[i+1]'s stack frame */ - l32e a1, a9, -12 /* restore a1 from call[i+1]'s stack frame */ - l32e a2, a9, -8 /* restore a2 from call[i+1]'s stack frame */ - l32e a7, a1, -12 /* a7 <- call[i-1]'s sp - (used to find end of call[i]'s frame) */ - l32e a3, a9, -4 /* restore a3 from call[i+1]'s stack frame */ - l32e a4, a7, -32 /* restore a4 from call[i]'s stack frame */ - l32e a5, a7, -28 /* restore a5 from call[i]'s stack frame */ - l32e a6, a7, -24 /* restore a6 from call[i]'s stack frame */ - l32e a7, a7, -20 /* restore a7 from call[i]'s stack frame */ - rfwu - -/* --------------------------------------------------------------------------------- -Window Overflow Exception for Call12 - -Invoked if a call[i] referenced a register (a4-a15) -that contains data from ancestor call[j]; -call[j] had done a call12 to call[j+1]. -On entry here: - window rotated to call[j] start point; - a0-a11 are registers to be saved; - a12-a15 must be preserved; - a13 is call[j+1]'s stack pointer. --------------------------------------------------------------------------------- -*/ - - .org 0x100 - .global _WindowOverflow12 -_WindowOverflow12: - - s32e a0, a13, -16 /* save a0 to call[j+1]'s stack frame */ - l32e a0, a1, -12 /* a0 <- call[j-1]'s sp - (used to find end of call[j]'s frame) */ - s32e a1, a13, -12 /* save a1 to call[j+1]'s stack frame */ - s32e a2, a13, -8 /* save a2 to call[j+1]'s stack frame */ - s32e a3, a13, -4 /* save a3 to call[j+1]'s stack frame */ - s32e a4, a0, -48 /* save a4 to end of call[j]'s stack frame */ - s32e a5, a0, -44 /* save a5 to end of call[j]'s stack frame */ - s32e a6, a0, -40 /* save a6 to end of call[j]'s stack frame */ - s32e a7, a0, -36 /* save a7 to end of call[j]'s stack frame */ - s32e a8, a0, -32 /* save a8 to end of call[j]'s stack frame */ - s32e a9, a0, -28 /* save a9 to end of call[j]'s stack frame */ - s32e a10, a0, -24 /* save a10 to end of call[j]'s stack frame */ - s32e a11, a0, -20 /* save a11 to end of call[j]'s stack frame */ - rfwo /* rotates back to call[i] position */ - -/* --------------------------------------------------------------------------------- -Window Underflow Exception for Call12 - -Invoked by RETW returning from call[i+1] to call[i] -where call[i]'s registers must be reloaded (not live in ARs); -where call[i] had done a call12 to call[i+1]. -On entry here: - window rotated to call[i] start point; - a0-a11 are undefined, must be reloaded with call[i].reg[0..11]; - a12-a15 must be preserved (they are call[i+1].reg[0..3]); - a13 is call[i+1]'s stack pointer. --------------------------------------------------------------------------------- -*/ - - .org 0x140 - .global _WindowUnderflow12 -_WindowUnderflow12: - - l32e a0, a13, -16 /* restore a0 from call[i+1]'s stack frame */ - l32e a1, a13, -12 /* restore a1 from call[i+1]'s stack frame */ - l32e a2, a13, -8 /* restore a2 from call[i+1]'s stack frame */ - l32e a11, a1, -12 /* a11 <- call[i-1]'s sp - (used to find end of call[i]'s frame) */ - l32e a3, a13, -4 /* restore a3 from call[i+1]'s stack frame */ - l32e a4, a11, -48 /* restore a4 from end of call[i]'s stack frame */ - l32e a5, a11, -44 /* restore a5 from end of call[i]'s stack frame */ - l32e a6, a11, -40 /* restore a6 from end of call[i]'s stack frame */ - l32e a7, a11, -36 /* restore a7 from end of call[i]'s stack frame */ - l32e a8, a11, -32 /* restore a8 from end of call[i]'s stack frame */ - l32e a9, a11, -28 /* restore a9 from end of call[i]'s stack frame */ - l32e a10, a11, -24 /* restore a10 from end of call[i]'s stack frame */ - l32e a11, a11, -20 /* restore a11 from end of call[i]'s stack frame */ - rfwu - -#endif /* XCHAL_HAVE_WINDOWED */ - - .section .UserEnter.text, "ax" - .global call_user_start - .type call_user_start,@function - .align 4 - .literal_position - - -call_user_start: - - movi a2, 0x40040000 /* note: absolute symbol, not a ptr */ - wsr a2, vecbase - call0 user_start /* user exception handler */ diff --git a/components/freertos/xtensa_vectors.S-old b/components/freertos/xtensa_vectors.S-old deleted file mode 100644 index 2d0f7a99d2..0000000000 --- a/components/freertos/xtensa_vectors.S-old +++ /dev/null @@ -1,2064 +0,0 @@ -/******************************************************************************* -Copyright (c) 2006-2015 Cadence Design Systems Inc. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------------------------------- - - XTENSA VECTORS AND LOW LEVEL HANDLERS FOR AN RTOS - - Xtensa low level exception and interrupt vectors and handlers for an RTOS. - - Interrupt handlers and user exception handlers support interaction with - the RTOS by calling XT_RTOS_INT_ENTER and XT_RTOS_INT_EXIT before and - after user's specific interrupt handlers. These macros are defined in - xtensa_.h to call suitable functions in a specific RTOS. - - Users can install application-specific interrupt handlers for low and - medium level interrupts, by calling xt_set_interrupt_handler(). These - handlers can be written in C, and must obey C calling convention. The - handler table is indexed by the interrupt number. Each handler may be - provided with an argument. - - Note that the system timer interrupt is handled specially, and is - dispatched to the RTOS-specific handler. This timer cannot be hooked - by application code. - - Optional hooks are also provided to install a handler per level at - run-time, made available by compiling this source file with - '-DXT_INTEXC_HOOKS' (useful for automated testing). - -!! This file is a template that usually needs to be modified to handle !! -!! application specific interrupts. Search USER_EDIT for helpful comments !! -!! on where to insert handlers and how to write them. !! - - Users can also install application-specific exception handlers in the - same way, by calling xt_set_exception_handler(). One handler slot is - provided for each exception type. Note that some exceptions are handled - by the porting layer itself, and cannot be taken over by application - code in this manner. These are the alloca, syscall, and coprocessor - exceptions. - - The exception handlers can be written in C, and must follow C calling - convention. Each handler is passed a pointer to an exception frame as - its single argument. The exception frame is created on the stack, and - holds the saved context of the thread that took the exception. If the - handler returns, the context will be restored and the instruction that - caused the exception will be retried. If the handler makes any changes - to the saved state in the exception frame, the changes will be applied - when restoring the context. - - Because Xtensa is a configurable architecture, this port supports all user - generated configurations (except restrictions stated in the release notes). - This is accomplished by conditional compilation using macros and functions - defined in the Xtensa HAL (hardware adaptation layer) for your configuration. - Only the relevant parts of this file will be included in your RTOS build. - For example, this file provides interrupt vector templates for all types and - all priority levels, but only the ones in your configuration are built. - - NOTES on the use of 'call0' for long jumps instead of 'j': - 1. This file should be assembled with the -mlongcalls option to xt-xcc. - 2. The -mlongcalls compiler option causes 'call0 dest' to be expanded to - a sequence 'l32r a0, dest' 'callx0 a0' which works regardless of the - distance from the call to the destination. The linker then relaxes - it back to 'call0 dest' if it determines that dest is within range. - This allows more flexibility in locating code without the performance - overhead of the 'l32r' literal data load in cases where the destination - is in range of 'call0'. There is an additional benefit in that 'call0' - has a longer range than 'j' due to the target being word-aligned, so - the 'l32r' sequence is less likely needed. - 3. The use of 'call0' with -mlongcalls requires that register a0 not be - live at the time of the call, which is always the case for a function - call but needs to be ensured if 'call0' is used as a jump in lieu of 'j'. - 4. This use of 'call0' is independent of the C function call ABI. - -*******************************************************************************/ - -#include "xtensa_rtos.h" - - -/* Enable stack backtrace across exception/interrupt - see below */ -#define XT_DEBUG_BACKTRACE 0 - - -/* --------------------------------------------------------------------------------- - Defines used to access _xtos_interrupt_table. --------------------------------------------------------------------------------- -*/ -#define XIE_HANDLER 0 -#define XIE_ARG 4 -#define XIE_SIZE 8 - -/* --------------------------------------------------------------------------------- - Macro extract_msb - return the input with only the highest bit set. - - Input : "ain" - Input value, clobbered. - Output : "aout" - Output value, has only one bit set, MSB of "ain". - The two arguments must be different AR registers. --------------------------------------------------------------------------------- -*/ - - .macro extract_msb aout ain -1: - addi \aout, \ain, -1 /* aout = ain - 1 */ - and \ain, \ain, \aout /* ain = ain & aout */ - bnez \ain, 1b /* repeat until ain == 0 */ - addi \aout, \aout, 1 /* return aout + 1 */ - .endm - -/* --------------------------------------------------------------------------------- - Macro dispatch_c_isr - dispatch interrupts to user ISRs. - This will dispatch to user handlers (if any) that are registered in the - XTOS dispatch table (_xtos_interrupt_table). These handlers would have - been registered by calling _xtos_set_interrupt_handler(). There is one - exception - the timer interrupt used by the OS will not be dispatched - to a user handler - this must be handled by the caller of this macro. - - Level triggered and software interrupts are automatically deasserted by - this code. - - ASSUMPTIONS: - -- PS.INTLEVEL is set to "level" at entry - -- PS.EXCM = 0, C calling enabled - - NOTE: For CALL0 ABI, a12-a15 have not yet been saved. - - NOTE: This macro will use registers a0 and a2-a6. The arguments are: - level -- interrupt level - mask -- interrupt bitmask for this level --------------------------------------------------------------------------------- -*/ - - .macro dispatch_c_isr level mask - - /* Get mask of pending, enabled interrupts at this level into a2. */ - -.L_xt_user_int_&level&: - rsr a2, INTENABLE - rsr a3, INTERRUPT - movi a4, \mask - and a2, a2, a3 - and a2, a2, a4 - beqz a2, 9f /* nothing to do */ - - /* This bit of code provides a nice debug backtrace in the debugger. - It does take a few more instructions, so undef XT_DEBUG_BACKTRACE - if you want to save the cycles. - */ - #if XT_DEBUG_BACKTRACE - #ifndef __XTENSA_CALL0_ABI__ - rsr a0, EPC_1 + \level - 1 /* return address */ - movi a4, 0xC0000000 /* constant with top 2 bits set (call size) */ - or a0, a0, a4 /* set top 2 bits */ - addx2 a0, a4, a0 /* clear top bit -- simulating call4 size */ - #endif - #endif - - #ifdef XT_INTEXC_HOOKS - /* Call interrupt hook if present to (pre)handle interrupts. */ - movi a4, _xt_intexc_hooks - l32i a4, a4, \level << 2 - beqz a4, 2f - #ifdef __XTENSA_CALL0_ABI__ - callx0 a4 - beqz a2, 9f - #else - mov a6, a2 - callx4 a4 - beqz a6, 9f - mov a2, a6 - #endif -2: - #endif - - /* Now look up in the dispatch table and call user ISR if any. */ - /* If multiple bits are set then MSB has highest priority. */ - - extract_msb a4, a2 /* a4 = MSB of a2, a2 trashed */ - - #ifdef XT_USE_SWPRI - /* Enable all interrupts at this level that are numerically higher - than the one we just selected, since they are treated as higher - priority. - */ - movi a3, \mask /* a3 = all interrupts at this level */ - add a2, a4, a4 /* a2 = a4 << 1 */ - addi a2, a2, -1 /* a2 = mask of 1's <= a4 bit */ - and a2, a2, a3 /* a2 = mask of all bits <= a4 at this level */ - movi a3, _xt_intdata - l32i a6, a3, 4 /* a6 = _xt_vpri_mask */ - neg a2, a2 - addi a2, a2, -1 /* a2 = mask to apply */ - and a5, a6, a2 /* mask off all bits <= a4 bit */ - s32i a5, a3, 4 /* update _xt_vpri_mask */ - rsr a3, INTENABLE - and a3, a3, a2 /* mask off all bits <= a4 bit */ - wsr a3, INTENABLE - rsil a3, \level - 1 /* lower interrupt level by 1 */ - #endif - - movi a3, XT_TIMER_INTEN /* a3 = timer interrupt bit */ - wsr a4, INTCLEAR /* clear sw or edge-triggered interrupt */ - beq a3, a4, 7f /* if timer interrupt then skip table */ - - find_ms_setbit a3, a4, a3, 0 /* a3 = interrupt number */ - - movi a4, _xt_interrupt_table - addx8 a3, a3, a4 /* a3 = address of interrupt table entry */ - l32i a4, a3, XIE_HANDLER /* a4 = handler address */ - #ifdef __XTENSA_CALL0_ABI__ - mov a12, a6 /* save in callee-saved reg */ - l32i a2, a3, XIE_ARG /* a2 = handler arg */ - callx0 a4 /* call handler */ - mov a2, a12 - #else - mov a2, a6 /* save in windowed reg */ - l32i a6, a3, XIE_ARG /* a6 = handler arg */ - callx4 a4 /* call handler */ - #endif - - #ifdef XT_USE_SWPRI - j 8f - #else - j .L_xt_user_int_&level& /* check for more interrupts */ - #endif - -7: - - .ifeq XT_TIMER_INTPRI - \level -.L_xt_user_int_timer_&level&: - /* - Interrupt handler for the RTOS tick timer if at this level. - We'll be reading the interrupt state again after this call - so no need to preserve any registers except a6 (vpri_mask). - */ - - #ifdef __XTENSA_CALL0_ABI__ - mov a12, a6 - call0 XT_RTOS_TIMER_INT - mov a2, a12 - #else - mov a2, a6 - call4 XT_RTOS_TIMER_INT - #endif - .endif - - #ifdef XT_USE_SWPRI - j 8f - #else - j .L_xt_user_int_&level& /* check for more interrupts */ - #endif - - #ifdef XT_USE_SWPRI -8: - /* Restore old value of _xt_vpri_mask from a2. Also update INTENABLE from - virtual _xt_intenable which _could_ have changed during interrupt - processing. */ - - movi a3, _xt_intdata - l32i a4, a3, 0 /* a4 = _xt_intenable */ - s32i a2, a3, 4 /* update _xt_vpri_mask */ - and a4, a4, a2 /* a4 = masked intenable */ - wsr a4, INTENABLE /* update INTENABLE */ - #endif - -9: - /* done */ - - .endm - - -/* --------------------------------------------------------------------------------- - Panic handler. - Should be reached by call0 (preferable) or jump only. If call0, a0 says where - from. If on simulator, display panic message and abort, else loop indefinitely. --------------------------------------------------------------------------------- -*/ - - .text - .global panicHandlerRegs - .global panicHandler - .global panicStack - - .global _xt_panic - .type _xt_panic,@function - .align 4 - -_xt_panic: - //ToDo: save registers - movi a2, panicHandlerRegs - s32i a0,a2,0 - s32i a1,a2,4 - s32i a2,a2,8 - s32i a3,a2,12 - s32i a4,a2,16 - s32i a5,a2,20 - s32i a6,a2,24 - s32i a7,a2,28 - s32i a8,a2,32 - s32i a9,a2,36 - s32i a10,a2,40 - s32i a11,a2,44 - s32i a12,a2,48 - s32i a13,a2,52 - s32i a14,a2,56 - s32i a15,a2,60 - - rsr a3, EXCSAVE - s32i a3,a2,64 - rsr a3, EXCSAVE+1 - s32i a3,a2,68 - rsr a3, EXCSAVE+2 - s32i a3,a2,72 - rsr a3, EXCSAVE+3 - s32i a3,a2,76 - rsr a3, EXCSAVE+4 - s32i a3,a2,80 - rsr a3, EXCSAVE+5 - s32i a3,a2,84 - rsr a3, EXCSAVE+6 - s32i a3,a2,88 - rsr a3, EXCSAVE+7 - s32i a3,a2,92 - - rsr a3, EPC - s32i a3,a2,96 - rsr a3, EPC+1 - s32i a3,a2,100 - rsr a3, EPC+2 - s32i a3,a2,104 - rsr a3, EPC+3 - s32i a3,a2,108 - rsr a3, EPC+4 - s32i a3,a2,112 - rsr a3, EPC+5 - s32i a3,a2,116 - rsr a3, EPC+6 - s32i a3,a2,120 - rsr a3, EPC+7 - s32i a3,a2,124 - - rsr a3, DEPC - s32i a3,a2,128 - rsr a3, EXCVADDR - s32i a3,a2,132 - - //Reset window start / base to 0 - movi a2, 1 - wsr a2, windowstart - movi a2, 0 - wsr a2, windowbase - rsync - - //Clear EXCM, set WOE flags - rsr a2, ps - movi a3, ~(PS_EXCM_MASK) - and a2, a2, a3 - movi a3, PS_WOE_MASK - or a2, a2, a3 - wsr a2, ps - rsync - - //Switch to private stack - movi a1,panicStack+(255*4) - rsil a2, XCHAL_EXCM_LEVEL /* disable all low & med ints */ - - - // Debug: output '!' on serport - movi a2,0x60000000 - movi a3,'!' - s32i a3,a2,0 - - movi a3, panicHandlerRegs - l32i a2,a3,0 - call0 panic_print_hex - rsr a2, EXCCAUSE - call0 panic_print_hex - rsr a2, EXCVADDR - call0 panic_print_hex - rsr a2,EPC+1 - call0 panic_print_hex - rsr a2,EPC+2 - call0 panic_print_hex - rsr a2,EPC+3 - call0 panic_print_hex - rsr a2,EPC+6 - call0 panic_print_hex - rsr a2,EXCSAVE_1 - call0 panic_print_hex - rsr a2,DEPC - call0 panic_print_hex - - //Call panic handler -// movi a4, panicHandler -// callx4 a4 - call4 panicHandler - - movi a2,0x60000000 - movi a3,'d' - s32i a3,a2,0 - - -1: j 1b /* loop infinitely */ - - .align 4 -panicHandlerz: - entry a1,32 - movi a2,0x60000000 - movi a3,'h' - memw - s32i a3,a2,0 - - retw - - - .align 4 -//Call using call0. Prints the hex char in a2. Kills a3, a4, a5 -panic_print_hex: - movi a3,0x60000000 - movi a4,8 -panic_print_hex_loop: - l32i a5, a3, 0x1c - extui a5, a5, 16, 8 - bgei a5,64,panic_print_hex_loop - - srli a5,a2,28 - bgei a5,10,panic_print_hex_a - addi a5,a5,'0' - j panic_print_hex_ok -panic_print_hex_a: - addi a5,a5,'A'-10 -panic_print_hex_ok: - s32i a5,a3,0 - slli a2,a2,4 - - addi a4,a4,-1 - bnei a4,0,panic_print_hex_loop - movi a5,' ' - s32i a5,a3,0 - - ret - - - - .section .rodata, "a" - .align 4 - - - -/* --------------------------------------------------------------------------------- - Hooks to dynamically install handlers for exceptions and interrupts. - Allows automated regression frameworks to install handlers per test. - Consists of an array of function pointers indexed by interrupt level, - with index 0 containing the entry for user exceptions. - Initialized with all 0s, meaning no handler is installed at each level. - See comment in xtensa_rtos.h for more details. --------------------------------------------------------------------------------- -*/ - - #ifdef XT_INTEXC_HOOKS - .data - .global _xt_intexc_hooks - .type _xt_intexc_hooks,@object - .align 4 - -_xt_intexc_hooks: - .fill XT_INTEXC_HOOK_NUM, 4, 0 - #endif - - -/* --------------------------------------------------------------------------------- - EXCEPTION AND LEVEL 1 INTERRUPT VECTORS AND LOW LEVEL HANDLERS - (except window exception vectors). - - Each vector goes at a predetermined location according to the Xtensa - hardware configuration, which is ensured by its placement in a special - section known to the Xtensa linker support package (LSP). It performs - the minimum necessary before jumping to the handler in the .text section. - - The corresponding handler goes in the normal .text section. It sets up - the appropriate stack frame, saves a few vector-specific registers and - calls XT_RTOS_INT_ENTER to save the rest of the interrupted context - and enter the RTOS, then sets up a C environment. It then calls the - user's interrupt handler code (which may be coded in C) and finally - calls XT_RTOS_INT_EXIT to transfer control to the RTOS for scheduling. - - While XT_RTOS_INT_EXIT does not return directly to the interruptee, - eventually the RTOS scheduler will want to dispatch the interrupted - task or handler. The scheduler will return to the exit point that was - saved in the interrupt stack frame at XT_STK_EXIT. --------------------------------------------------------------------------------- -*/ - - -/* --------------------------------------------------------------------------------- -Debug Exception. --------------------------------------------------------------------------------- -*/ - -#if XCHAL_HAVE_DEBUG - - .begin literal_prefix .DebugExceptionVector - .section .DebugExceptionVector.text, "ax" - .global _DebugExceptionVector - .align 4 - -_DebugExceptionVector: - - #ifdef XT_SIMULATOR - /* - In the simulator, let the debugger (if any) handle the debug exception, - or simply stop the simulation: - */ - wsr a2, EXCSAVE+XCHAL_DEBUGLEVEL /* save a2 where sim expects it */ - movi a2, SYS_gdb_enter_sktloop - simcall /* have ISS handle debug exc. */ - #elif 0 /* change condition to 1 to use the HAL minimal debug handler */ - wsr a3, EXCSAVE+XCHAL_DEBUGLEVEL - movi a3, xthal_debugexc_defhndlr_nw /* use default debug handler */ - jx a3 - #else - wsr a0, EXCSAVE+XCHAL_DEBUGLEVEL /* save original a0 somewhere */ - call0 _xt_panic /* does not return */ - rfi XCHAL_DEBUGLEVEL /* make a0 point here not later */ - #endif - - .end literal_prefix - -#endif - -/* --------------------------------------------------------------------------------- -Double Exception. -Double exceptions are not a normal occurrence. They indicate a bug of some kind. --------------------------------------------------------------------------------- -*/ - -#ifdef XCHAL_DOUBLEEXC_VECTOR_VADDR - - .begin literal_prefix .DoubleExceptionVector - .section .DoubleExceptionVector.text, "ax" - .global _DoubleExceptionVector - .align 4 - -_DoubleExceptionVector: - - #if XCHAL_HAVE_DEBUG - break 1, 4 /* unhandled double exception */ - #endif - call0 _xt_panic /* does not return */ - rfde /* make a0 point here not later */ - - .end literal_prefix - -#endif /* XCHAL_DOUBLEEXC_VECTOR_VADDR */ - -/* --------------------------------------------------------------------------------- -Kernel Exception (including Level 1 Interrupt from kernel mode). --------------------------------------------------------------------------------- -*/ - - .begin literal_prefix .KernelExceptionVector - .section .KernelExceptionVector.text, "ax" - .global _KernelExceptionVector - .align 4 - -_KernelExceptionVector: - - wsr a0, EXCSAVE_1 /* preserve a0 */ - call0 _xt_kernel_exc /* kernel exception handler */ - /* never returns here - call0 is used as a jump (see note at top) */ - - .end literal_prefix - - .text - .align 4 - -_xt_kernel_exc: - #if XCHAL_HAVE_DEBUG - break 1, 0 /* unhandled kernel exception */ - #endif - call0 _xt_panic /* does not return */ - rfe /* make a0 point here not there */ - - -/* --------------------------------------------------------------------------------- -User Exception (including Level 1 Interrupt from user mode). --------------------------------------------------------------------------------- -*/ - - .begin literal_prefix .UserExceptionVector - .section .UserExceptionVector.text, "ax" - .global _UserExceptionVector - .type _UserExceptionVector,@function - .align 4 - -_UserExceptionVector: - - wsr a0, EXCSAVE_1 /* preserve a0 */ - call0 _xt_user_exc /* user exception handler */ - /* never returns here - call0 is used as a jump (see note at top) */ - - .end literal_prefix - -/* --------------------------------------------------------------------------------- - Insert some waypoints for jumping beyond the signed 8-bit range of - conditional branch instructions, so the conditional branchces to specific - exception handlers are not taken in the mainline. Saves some cycles in the - mainline. --------------------------------------------------------------------------------- -*/ - - .text - - #if XCHAL_HAVE_WINDOWED - .align 4 -_xt_to_alloca_exc: - call0 _xt_alloca_exc /* in window vectors section */ - /* never returns here - call0 is used as a jump (see note at top) */ - #endif - - .align 4 -_xt_to_syscall_exc: - call0 _xt_syscall_exc - /* never returns here - call0 is used as a jump (see note at top) */ - - #if XCHAL_CP_NUM > 0 - .align 4 -_xt_to_coproc_exc: - call0 _xt_coproc_exc - /* never returns here - call0 is used as a jump (see note at top) */ - #endif - - -/* --------------------------------------------------------------------------------- - User exception handler. --------------------------------------------------------------------------------- -*/ - - .type _xt_user_exc,@function - .align 4 - -_xt_user_exc: - - /* If level 1 interrupt then jump to the dispatcher */ - rsr a0, EXCCAUSE - beqi a0, EXCCAUSE_LEVEL1INTERRUPT, _xt_lowint1 - - /* Handle any coprocessor exceptions. Rely on the fact that exception - numbers above EXCCAUSE_CP0_DISABLED all relate to the coprocessors. - */ - #if XCHAL_CP_NUM > 0 - bgeui a0, EXCCAUSE_CP0_DISABLED, _xt_to_coproc_exc - #endif - - /* Handle alloca and syscall exceptions */ - #if XCHAL_HAVE_WINDOWED - beqi a0, EXCCAUSE_ALLOCA, _xt_to_alloca_exc - #endif - beqi a0, EXCCAUSE_SYSCALL, _xt_to_syscall_exc - - //HACK! No custom vector stuff, just call panic handler directly. ToDo: remove this when possible. -JD - call0 _xt_panic - - /* Handle all other exceptions. All can have user-defined handlers. */ - /* NOTE: we'll stay on the user stack for exception handling. */ - - /* Allocate exception frame and save minimal context. */ - mov a0, sp - addi sp, sp, -XT_STK_FRMSZ - s32i a0, sp, XT_STK_A1 - #if XCHAL_HAVE_WINDOWED - s32e a0, sp, -12 /* for debug backtrace */ - #endif - rsr a0, PS /* save interruptee's PS */ - s32i a0, sp, XT_STK_PS - rsr a0, EPC_1 /* save interruptee's PC */ - s32i a0, sp, XT_STK_PC - rsr a0, EXCSAVE_1 /* save interruptee's a0 */ - s32i a0, sp, XT_STK_A0 - #if XCHAL_HAVE_WINDOWED - s32e a0, sp, -16 /* for debug backtrace */ - #endif - s32i a12, sp, XT_STK_A12 /* _xt_context_save requires A12- */ - s32i a13, sp, XT_STK_A13 /* A13 to have already been saved */ - call0 _xt_context_save - - /* Save exc cause and vaddr into exception frame */ - rsr a0, EXCCAUSE - s32i a0, sp, XT_STK_EXCCAUSE - rsr a0, EXCVADDR - s32i a0, sp, XT_STK_EXCVADDR - - /* Set up PS for C, reenable hi-pri interrupts, and clear EXCM. */ - #ifdef __XTENSA_CALL0_ABI__ - movi a0, PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM - #else - movi a0, PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM | PS_WOE - #endif - wsr a0, PS - - #ifdef XT_DEBUG_BACKTRACE - #ifndef __XTENSA_CALL0_ABI__ - rsr a0, EPC_1 /* return address for debug backtrace */ - movi a5, 0xC0000000 /* constant with top 2 bits set (call size) */ - rsync /* wait for WSR.PS to complete */ - or a0, a0, a5 /* set top 2 bits */ - addx2 a0, a5, a0 /* clear top bit -- thus simulating call4 size */ - #else - rsync /* wait for WSR.PS to complete */ - #endif - #endif - - rsr a2, EXCCAUSE /* recover exc cause */ - - #ifdef XT_INTEXC_HOOKS - /* - Call exception hook to pre-handle exceptions (if installed). - Pass EXCCAUSE in a2, and check result in a2 (if -1, skip default handling). - */ - movi a4, _xt_intexc_hooks - l32i a4, a4, 0 /* user exception hook index 0 */ - beqz a4, 1f -.Ln_xt_user_exc_call_hook: - #ifdef __XTENSA_CALL0_ABI__ - callx0 a4 - beqi a2, -1, .L_xt_user_done - #else - mov a6, a2 - callx4 a4 - beqi a6, -1, .L_xt_user_done - mov a2, a6 - #endif -1: - #endif - - rsr a2, EXCCAUSE /* recover exc cause */ - movi a3, _xt_exception_table - addx4 a4, a2, a3 /* a4 = address of exception table entry */ - l32i a4, a4, 0 /* a4 = handler address */ - #ifdef __XTENSA_CALL0_ABI__ - mov a2, sp /* a2 = pointer to exc frame */ - callx0 a4 /* call handler */ - #else - mov a6, sp /* a6 = pointer to exc frame */ - callx4 a4 /* call handler */ - #endif - -.L_xt_user_done: - - /* Restore context and return */ - call0 _xt_context_restore - l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */ - wsr a0, PS - l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */ - wsr a0, EPC_1 - l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */ - l32i sp, sp, XT_STK_A1 /* remove exception frame */ - rsync /* ensure PS and EPC written */ - rfe /* PS.EXCM is cleared */ - - -/* --------------------------------------------------------------------------------- - Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT - on entry and used to return to a thread or interrupted interrupt handler. --------------------------------------------------------------------------------- -*/ - - .global _xt_user_exit - .type _xt_user_exit,@function - .align 4 -_xt_user_exit: - l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */ - wsr a0, PS - l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */ - wsr a0, EPC_1 - l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */ - l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */ - rsync /* ensure PS and EPC written */ - rfe /* PS.EXCM is cleared */ - - -/* --------------------------------------------------------------------------------- -Syscall Exception Handler (jumped to from User Exception Handler). -Syscall 0 is required to spill the register windows (no-op in Call 0 ABI). -Only syscall 0 is handled here. Other syscalls return -1 to caller in a2. --------------------------------------------------------------------------------- -*/ - - .text - .type _xt_syscall_exc,@function - .align 4 -_xt_syscall_exc: - - #ifdef __XTENSA_CALL0_ABI__ - /* - Save minimal regs for scratch. Syscall 0 does nothing in Call0 ABI. - Use a minimal stack frame (16B) to save A2 & A3 for scratch. - PS.EXCM could be cleared here, but unlikely to improve worst-case latency. - rsr a0, PS - addi a0, a0, -PS_EXCM_MASK - wsr a0, PS - */ - addi sp, sp, -16 - s32i a2, sp, 8 - s32i a3, sp, 12 - #else /* Windowed ABI */ - /* - Save necessary context and spill the register windows. - PS.EXCM is still set and must remain set until after the spill. - Reuse context save function though it saves more than necessary. - For this reason, a full interrupt stack frame is allocated. - */ - addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */ - s32i a12, sp, XT_STK_A12 /* _xt_context_save requires A12- */ - s32i a13, sp, XT_STK_A13 /* A13 to have already been saved */ - call0 _xt_context_save - #endif - - /* - Grab the interruptee's PC and skip over the 'syscall' instruction. - If it's at the end of a zero-overhead loop and it's not on the last - iteration, decrement loop counter and skip to beginning of loop. - */ - rsr a2, EPC_1 /* a2 = PC of 'syscall' */ - addi a3, a2, 3 /* ++PC */ - #if XCHAL_HAVE_LOOPS - rsr a0, LEND /* if (PC == LEND */ - bne a3, a0, 1f - rsr a0, LCOUNT /* && LCOUNT != 0) */ - beqz a0, 1f /* { */ - addi a0, a0, -1 /* --LCOUNT */ - rsr a3, LBEG /* PC = LBEG */ - wsr a0, LCOUNT /* } */ - #endif -1: wsr a3, EPC_1 /* update PC */ - - /* Restore interruptee's context and return from exception. */ - #ifdef __XTENSA_CALL0_ABI__ - l32i a2, sp, 8 - l32i a3, sp, 12 - addi sp, sp, 16 - #else - call0 _xt_context_restore - addi sp, sp, XT_STK_FRMSZ - #endif - movi a0, -1 - movnez a2, a0, a2 /* return -1 if not syscall 0 */ - rsr a0, EXCSAVE_1 - rfe - -/* --------------------------------------------------------------------------------- -Co-Processor Exception Handler (jumped to from User Exception Handler). -These exceptions are generated by co-processor instructions, which are only -allowed in thread code (not in interrupts or kernel code). This restriction is -deliberately imposed to reduce the burden of state-save/restore in interrupts. --------------------------------------------------------------------------------- -*/ -#if XCHAL_CP_NUM > 0 - - .section .rodata, "a" - -/* Offset to CP n save area in thread's CP save area. */ - .global _xt_coproc_sa_offset - .type _xt_coproc_sa_offset,@object - .align 16 /* minimize crossing cache boundaries */ -_xt_coproc_sa_offset: - .word XT_CP0_SA, XT_CP1_SA, XT_CP2_SA, XT_CP3_SA - .word XT_CP4_SA, XT_CP5_SA, XT_CP6_SA, XT_CP7_SA - -/* Bitmask for CP n's CPENABLE bit. */ - .type _xt_coproc_mask,@object - .align 16,,8 /* try to keep it all in one cache line */ - .set i, 0 -_xt_coproc_mask: - .rept XCHAL_CP_MAX - .long (i<<16) | (1<= 2 - - .begin literal_prefix .Level2InterruptVector - .section .Level2InterruptVector.text, "ax" - .global _Level2Vector - .type _Level2Vector,@function - .align 4 -_Level2Vector: - wsr a0, EXCSAVE_2 /* preserve a0 */ - call0 _xt_medint2 /* load interrupt handler */ - /* never returns here - call0 is used as a jump (see note at top) */ - - .end literal_prefix - - .text - .type _xt_medint2,@function - .align 4 -_xt_medint2: - mov a0, sp /* sp == a1 */ - addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */ - s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */ - rsr a0, EPS_2 /* save interruptee's PS */ - s32i a0, sp, XT_STK_PS - rsr a0, EPC_2 /* save interruptee's PC */ - s32i a0, sp, XT_STK_PC - rsr a0, EXCSAVE_2 /* save interruptee's a0 */ - s32i a0, sp, XT_STK_A0 - movi a0, _xt_medint2_exit /* save exit point for dispatch */ - s32i a0, sp, XT_STK_EXIT - - /* Save rest of interrupt context and enter RTOS. */ - call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */ - - /* !! We are now on the RTOS system stack !! */ - - /* Set up PS for C, enable interrupts above this level and clear EXCM. */ - #ifdef __XTENSA_CALL0_ABI__ - movi a0, PS_INTLEVEL(2) | PS_UM - #else - movi a0, PS_INTLEVEL(2) | PS_UM | PS_WOE - #endif - wsr a0, PS - rsync - - /* OK to call C code at this point, dispatch user ISRs */ - - dispatch_c_isr 2 XCHAL_INTLEVEL2_MASK - - /* Done handling interrupts, transfer control to OS */ - call0 XT_RTOS_INT_EXIT /* does not return directly here */ - - /* - Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT - on entry and used to return to a thread or interrupted interrupt handler. - */ - .global _xt_medint2_exit - .type _xt_medint2_exit,@function - .align 4 -_xt_medint2_exit: - /* Restore only level-specific regs (the rest were already restored) */ - l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */ - wsr a0, EPS_2 - l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */ - wsr a0, EPC_2 - l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */ - l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */ - rsync /* ensure EPS and EPC written */ - rfi 2 - -#endif /* Level 2 */ - -#if XCHAL_EXCM_LEVEL >= 3 - - .begin literal_prefix .Level3InterruptVector - .section .Level3InterruptVector.text, "ax" - .global _Level3Vector - .type _Level3Vector,@function - .align 4 -_Level3Vector: - wsr a0, EXCSAVE_3 /* preserve a0 */ - call0 _xt_medint3 /* load interrupt handler */ - /* never returns here - call0 is used as a jump (see note at top) */ - - .end literal_prefix - - .text - .type _xt_medint3,@function - .align 4 -_xt_medint3: - mov a0, sp /* sp == a1 */ - addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */ - s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */ - rsr a0, EPS_3 /* save interruptee's PS */ - s32i a0, sp, XT_STK_PS - rsr a0, EPC_3 /* save interruptee's PC */ - s32i a0, sp, XT_STK_PC - rsr a0, EXCSAVE_3 /* save interruptee's a0 */ - s32i a0, sp, XT_STK_A0 - movi a0, _xt_medint3_exit /* save exit point for dispatch */ - s32i a0, sp, XT_STK_EXIT - - /* Save rest of interrupt context and enter RTOS. */ - call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */ - - /* !! We are now on the RTOS system stack !! */ - - /* Set up PS for C, enable interrupts above this level and clear EXCM. */ - #ifdef __XTENSA_CALL0_ABI__ - movi a0, PS_INTLEVEL(3) | PS_UM - #else - movi a0, PS_INTLEVEL(3) | PS_UM | PS_WOE - #endif - wsr a0, PS - rsync - - /* OK to call C code at this point, dispatch user ISRs */ - - dispatch_c_isr 3 XCHAL_INTLEVEL3_MASK - - /* Done handling interrupts, transfer control to OS */ - call0 XT_RTOS_INT_EXIT /* does not return directly here */ - - /* - Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT - on entry and used to return to a thread or interrupted interrupt handler. - */ - .global _xt_medint3_exit - .type _xt_medint3_exit,@function - .align 4 -_xt_medint3_exit: - /* Restore only level-specific regs (the rest were already restored) */ - l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */ - wsr a0, EPS_3 - l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */ - wsr a0, EPC_3 - l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */ - l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */ - rsync /* ensure EPS and EPC written */ - rfi 3 - -#endif /* Level 3 */ - -#if XCHAL_EXCM_LEVEL >= 4 - - .begin literal_prefix .Level4InterruptVector - .section .Level4InterruptVector.text, "ax" - .global _Level4Vector - .type _Level4Vector,@function - .align 4 -_Level4Vector: - wsr a0, EXCSAVE_4 /* preserve a0 */ - call0 _xt_medint4 /* load interrupt handler */ - - .end literal_prefix - - .text - .type _xt_medint4,@function - .align 4 -_xt_medint4: - mov a0, sp /* sp == a1 */ - addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */ - s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */ - rsr a0, EPS_4 /* save interruptee's PS */ - s32i a0, sp, XT_STK_PS - rsr a0, EPC_4 /* save interruptee's PC */ - s32i a0, sp, XT_STK_PC - rsr a0, EXCSAVE_4 /* save interruptee's a0 */ - s32i a0, sp, XT_STK_A0 - movi a0, _xt_medint4_exit /* save exit point for dispatch */ - s32i a0, sp, XT_STK_EXIT - - /* Save rest of interrupt context and enter RTOS. */ - call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */ - - /* !! We are now on the RTOS system stack !! */ - - /* Set up PS for C, enable interrupts above this level and clear EXCM. */ - #ifdef __XTENSA_CALL0_ABI__ - movi a0, PS_INTLEVEL(4) | PS_UM - #else - movi a0, PS_INTLEVEL(4) | PS_UM | PS_WOE - #endif - wsr a0, PS - rsync - - /* OK to call C code at this point, dispatch user ISRs */ - - dispatch_c_isr 4 XCHAL_INTLEVEL4_MASK - - /* Done handling interrupts, transfer control to OS */ - call0 XT_RTOS_INT_EXIT /* does not return directly here */ - - /* - Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT - on entry and used to return to a thread or interrupted interrupt handler. - */ - .global _xt_medint4_exit - .type _xt_medint4_exit,@function - .align 4 -_xt_medint4_exit: - /* Restore only level-specific regs (the rest were already restored) */ - l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */ - wsr a0, EPS_4 - l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */ - wsr a0, EPC_4 - l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */ - l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */ - rsync /* ensure EPS and EPC written */ - rfi 4 - -#endif /* Level 4 */ - -#if XCHAL_EXCM_LEVEL >= 5 - - .begin literal_prefix .Level5InterruptVector - .section .Level5InterruptVector.text, "ax" - .global _Level5Vector - .type _Level5Vector,@function - .align 4 -_Level5Vector: - wsr a0, EXCSAVE_5 /* preserve a0 */ - call0 _xt_medint5 /* load interrupt handler */ - - .end literal_prefix - - .text - .type _xt_medint5,@function - .align 4 -_xt_medint5: - mov a0, sp /* sp == a1 */ - addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */ - s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */ - rsr a0, EPS_5 /* save interruptee's PS */ - s32i a0, sp, XT_STK_PS - rsr a0, EPC_5 /* save interruptee's PC */ - s32i a0, sp, XT_STK_PC - rsr a0, EXCSAVE_5 /* save interruptee's a0 */ - s32i a0, sp, XT_STK_A0 - movi a0, _xt_medint5_exit /* save exit point for dispatch */ - s32i a0, sp, XT_STK_EXIT - - /* Save rest of interrupt context and enter RTOS. */ - call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */ - - /* !! We are now on the RTOS system stack !! */ - - /* Set up PS for C, enable interrupts above this level and clear EXCM. */ - #ifdef __XTENSA_CALL0_ABI__ - movi a0, PS_INTLEVEL(5) | PS_UM - #else - movi a0, PS_INTLEVEL(5) | PS_UM | PS_WOE - #endif - wsr a0, PS - rsync - - /* OK to call C code at this point, dispatch user ISRs */ - - dispatch_c_isr 5 XCHAL_INTLEVEL5_MASK - - /* Done handling interrupts, transfer control to OS */ - call0 XT_RTOS_INT_EXIT /* does not return directly here */ - - /* - Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT - on entry and used to return to a thread or interrupted interrupt handler. - */ - .global _xt_medint5_exit - .type _xt_medint5_exit,@function - .align 4 -_xt_medint5_exit: - /* Restore only level-specific regs (the rest were already restored) */ - l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */ - wsr a0, EPS_5 - l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */ - wsr a0, EPC_5 - l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */ - l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */ - rsync /* ensure EPS and EPC written */ - rfi 5 - -#endif /* Level 5 */ - -#if XCHAL_EXCM_LEVEL >= 6 - - .begin literal_prefix .Level6InterruptVector - .section .Level6InterruptVector.text, "ax" - .global _Level6Vector - .type _Level6Vector,@function - .align 4 -_Level6Vector: - wsr a0, EXCSAVE_6 /* preserve a0 */ - call0 _xt_medint6 /* load interrupt handler */ - - .end literal_prefix - - .text - .type _xt_medint6,@function - .align 4 -_xt_medint6: - mov a0, sp /* sp == a1 */ - addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */ - s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */ - rsr a0, EPS_6 /* save interruptee's PS */ - s32i a0, sp, XT_STK_PS - rsr a0, EPC_6 /* save interruptee's PC */ - s32i a0, sp, XT_STK_PC - rsr a0, EXCSAVE_6 /* save interruptee's a0 */ - s32i a0, sp, XT_STK_A0 - movi a0, _xt_medint6_exit /* save exit point for dispatch */ - s32i a0, sp, XT_STK_EXIT - - /* Save rest of interrupt context and enter RTOS. */ - call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */ - - /* !! We are now on the RTOS system stack !! */ - - /* Set up PS for C, enable interrupts above this level and clear EXCM. */ - #ifdef __XTENSA_CALL0_ABI__ - movi a0, PS_INTLEVEL(6) | PS_UM - #else - movi a0, PS_INTLEVEL(6) | PS_UM | PS_WOE - #endif - wsr a0, PS - rsync - - /* OK to call C code at this point, dispatch user ISRs */ - - dispatch_c_isr 6 XCHAL_INTLEVEL6_MASK - - /* Done handling interrupts, transfer control to OS */ - call0 XT_RTOS_INT_EXIT /* does not return directly here */ - - /* - Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT - on entry and used to return to a thread or interrupted interrupt handler. - */ - .global _xt_medint6_exit - .type _xt_medint6_exit,@function - .align 4 -_xt_medint6_exit: - /* Restore only level-specific regs (the rest were already restored) */ - l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */ - wsr a0, EPS_6 - l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */ - wsr a0, EPC_6 - l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */ - l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */ - rsync /* ensure EPS and EPC written */ - rfi 6 - -#endif /* Level 6 */ - - -/******************************************************************************* - -HIGH PRIORITY (LEVEL > XCHAL_EXCM_LEVEL) INTERRUPT VECTORS AND HANDLERS - -High priority interrupts are by definition those with priorities greater -than XCHAL_EXCM_LEVEL. This includes non-maskable (NMI). High priority -interrupts cannot interact with the RTOS, that is they must save all regs -they use and not call any RTOS function. - -A further restriction imposed by the Xtensa windowed architecture is that -high priority interrupts must not modify the stack area even logically -"above" the top of the interrupted stack (they need to provide their -own stack or static save area). - -Cadence Design Systems recommends high priority interrupt handlers be coded in assembly -and used for purposes requiring very short service times. - -Here are templates for high priority (level 2+) interrupt vectors. -They assume only one interrupt per level to avoid the burden of identifying -which interrupts at this level are pending and enabled. This allows for -minimum latency and avoids having to save/restore a2 in addition to a0. -If more than one interrupt per high priority level is configured, this burden -is on the handler which in any case must provide a way to save and restore -registers it uses without touching the interrupted stack. - -Each vector goes at a predetermined location according to the Xtensa -hardware configuration, which is ensured by its placement in a special -section known to the Xtensa linker support package (LSP). It performs -the minimum necessary before jumping to the handler in the .text section. - -*******************************************************************************/ - -/* -Currently only shells for high priority interrupt handlers are provided -here. However a template and example can be found in the Cadence Design Systems tools -documentation: "Microprocessor Programmer's Guide". -*/ - -#if XCHAL_NUM_INTLEVELS >=2 && XCHAL_EXCM_LEVEL <2 && XCHAL_DEBUGLEVEL !=2 - - .begin literal_prefix .Level2InterruptVector - .section .Level2InterruptVector.text, "ax" - .global _Level2Vector - .type _Level2Vector,@function - .align 4 -_Level2Vector: - wsr a0, EXCSAVE_2 /* preserve a0 */ - call0 _xt_highint2 /* load interrupt handler */ - - .end literal_prefix - - .text - .type _xt_highint2,@function - .align 4 -_xt_highint2: - - #ifdef XT_INTEXC_HOOKS - /* Call interrupt hook if present to (pre)handle interrupts. */ - movi a0, _xt_intexc_hooks - l32i a0, a0, 2<<2 - beqz a0, 1f -.Ln_xt_highint2_call_hook: - callx0 a0 /* must NOT disturb stack! */ -1: - #endif - - /* USER_EDIT: - ADD HIGH PRIORITY LEVEL 2 INTERRUPT HANDLER CODE HERE. - */ - - .align 4 -.L_xt_highint2_exit: - rsr a0, EXCSAVE_2 /* restore a0 */ - rfi 2 - -#endif /* Level 2 */ - -#if XCHAL_NUM_INTLEVELS >=3 && XCHAL_EXCM_LEVEL <3 && XCHAL_DEBUGLEVEL !=3 - - .begin literal_prefix .Level3InterruptVector - .section .Level3InterruptVector.text, "ax" - .global _Level3Vector - .type _Level3Vector,@function - .align 4 -_Level3Vector: - wsr a0, EXCSAVE_3 /* preserve a0 */ - call0 _xt_highint3 /* load interrupt handler */ - /* never returns here - call0 is used as a jump (see note at top) */ - - .end literal_prefix - - .text - .type _xt_highint3,@function - .align 4 -_xt_highint3: - - #ifdef XT_INTEXC_HOOKS - /* Call interrupt hook if present to (pre)handle interrupts. */ - movi a0, _xt_intexc_hooks - l32i a0, a0, 3<<2 - beqz a0, 1f -.Ln_xt_highint3_call_hook: - callx0 a0 /* must NOT disturb stack! */ -1: - #endif - - /* USER_EDIT: - ADD HIGH PRIORITY LEVEL 3 INTERRUPT HANDLER CODE HERE. - */ - - .align 4 -.L_xt_highint3_exit: - rsr a0, EXCSAVE_3 /* restore a0 */ - rfi 3 - -#endif /* Level 3 */ - -#if XCHAL_NUM_INTLEVELS >=4 && XCHAL_EXCM_LEVEL <4 && XCHAL_DEBUGLEVEL !=4 - - .begin literal_prefix .Level4InterruptVector - .section .Level4InterruptVector.text, "ax" - .global _Level4Vector - .type _Level4Vector,@function - .align 4 -_Level4Vector: - wsr a0, EXCSAVE_4 /* preserve a0 */ - call0 _xt_highint4 /* load interrupt handler */ - /* never returns here - call0 is used as a jump (see note at top) */ - - .end literal_prefix - - .text - .type _xt_highint4,@function - .align 4 -_xt_highint4: - - #ifdef XT_INTEXC_HOOKS - /* Call interrupt hook if present to (pre)handle interrupts. */ - movi a0, _xt_intexc_hooks - l32i a0, a0, 4<<2 - beqz a0, 1f -.Ln_xt_highint4_call_hook: - callx0 a0 /* must NOT disturb stack! */ -1: - #endif - - /* USER_EDIT: - ADD HIGH PRIORITY LEVEL 4 INTERRUPT HANDLER CODE HERE. - */ - - .align 4 -.L_xt_highint4_exit: - rsr a0, EXCSAVE_4 /* restore a0 */ - rfi 4 - -#endif /* Level 4 */ - -#if XCHAL_NUM_INTLEVELS >=5 && XCHAL_EXCM_LEVEL <5 && XCHAL_DEBUGLEVEL !=5 - - .begin literal_prefix .Level5InterruptVector - .section .Level5InterruptVector.text, "ax" - .global _Level5Vector - .type _Level5Vector,@function - .align 4 -_Level5Vector: - wsr a0, EXCSAVE_5 /* preserve a0 */ - call0 _xt_highint5 /* load interrupt handler */ - /* never returns here - call0 is used as a jump (see note at top) */ - - .end literal_prefix - - .text - .type _xt_highint5,@function - .align 4 -_xt_highint5: - - #ifdef XT_INTEXC_HOOKS - /* Call interrupt hook if present to (pre)handle interrupts. */ - movi a0, _xt_intexc_hooks - l32i a0, a0, 5<<2 - beqz a0, 1f -.Ln_xt_highint5_call_hook: - callx0 a0 /* must NOT disturb stack! */ -1: - #endif - - /* USER_EDIT: - ADD HIGH PRIORITY LEVEL 5 INTERRUPT HANDLER CODE HERE. - */ - - .align 4 -.L_xt_highint5_exit: - rsr a0, EXCSAVE_5 /* restore a0 */ - rfi 5 - -#endif /* Level 5 */ - -#if XCHAL_NUM_INTLEVELS >=6 && XCHAL_EXCM_LEVEL <6 && XCHAL_DEBUGLEVEL !=6 - - .begin literal_prefix .Level6InterruptVector - .section .Level6InterruptVector.text, "ax" - .global _Level6Vector - .type _Level6Vector,@function - .align 4 -_Level6Vector: - wsr a0, EXCSAVE_6 /* preserve a0 */ - call0 _xt_highint6 /* load interrupt handler */ - /* never returns here - call0 is used as a jump (see note at top) */ - - .end literal_prefix - - .text - .type _xt_highint6,@function - .align 4 -_xt_highint6: - - #ifdef XT_INTEXC_HOOKS - /* Call interrupt hook if present to (pre)handle interrupts. */ - movi a0, _xt_intexc_hooks - l32i a0, a0, 6<<2 - beqz a0, 1f -.Ln_xt_highint6_call_hook: - callx0 a0 /* must NOT disturb stack! */ -1: - #endif - - /* USER_EDIT: - ADD HIGH PRIORITY LEVEL 6 INTERRUPT HANDLER CODE HERE. - */ - - .align 4 -.L_xt_highint6_exit: - rsr a0, EXCSAVE_6 /* restore a0 */ - rfi 6 - -#endif /* Level 6 */ - -#if XCHAL_HAVE_NMI - - .begin literal_prefix .NMIExceptionVector - .section .NMIExceptionVector.text, "ax" - .global _NMIExceptionVector - .type _NMIExceptionVector,@function - .align 4 -_NMIExceptionVector: - wsr a0, EXCSAVE + XCHAL_NMILEVEL _ /* preserve a0 */ - call0 _xt_nmi /* load interrupt handler */ - /* never returns here - call0 is used as a jump (see note at top) */ - - .end literal_prefix - - .text - .type _xt_nmi,@function - .align 4 -_xt_nmi: - - #ifdef XT_INTEXC_HOOKS - /* Call interrupt hook if present to (pre)handle interrupts. */ - movi a0, _xt_intexc_hooks - l32i a0, a0, XCHAL_NMILEVEL<<2 - beqz a0, 1f -.Ln_xt_nmi_call_hook: - callx0 a0 /* must NOT disturb stack! */ -1: - #endif - - /* USER_EDIT: - ADD HIGH PRIORITY NON-MASKABLE INTERRUPT (NMI) HANDLER CODE HERE. - */ - - .align 4 -.L_xt_nmi_exit: - rsr a0, EXCSAVE + XCHAL_NMILEVEL /* restore a0 */ - rfi XCHAL_NMILEVEL - -#endif /* NMI */ - - -/******************************************************************************* - -WINDOW OVERFLOW AND UNDERFLOW EXCEPTION VECTORS AND ALLOCA EXCEPTION HANDLER - -Here is the code for each window overflow/underflow exception vector and -(interspersed) efficient code for handling the alloca exception cause. -Window exceptions are handled entirely in the vector area and are very -tight for performance. The alloca exception is also handled entirely in -the window vector area so comes at essentially no cost in code size. -Users should never need to modify them and Cadence Design Systems recommends -they do not. - -Window handlers go at predetermined vector locations according to the -Xtensa hardware configuration, which is ensured by their placement in a -special section known to the Xtensa linker support package (LSP). Since -their offsets in that section are always the same, the LSPs do not define -a section per vector. - -These things are coded for XEA2 only (XEA1 is not supported). - -Note on Underflow Handlers: -The underflow handler for returning from call[i+1] to call[i] -must preserve all the registers from call[i+1]'s window. -In particular, a0 and a1 must be preserved because the RETW instruction -will be reexecuted (and may even underflow if an intervening exception -has flushed call[i]'s registers). -Registers a2 and up may contain return values. - -*******************************************************************************/ - -#if XCHAL_HAVE_WINDOWED - - .section .WindowVectors.text, "ax" - -/* --------------------------------------------------------------------------------- -Window Overflow Exception for Call4. - -Invoked if a call[i] referenced a register (a4-a15) -that contains data from ancestor call[j]; -call[j] had done a call4 to call[j+1]. -On entry here: - window rotated to call[j] start point; - a0-a3 are registers to be saved; - a4-a15 must be preserved; - a5 is call[j+1]'s stack pointer. --------------------------------------------------------------------------------- -*/ - - .org 0x0 - .global _WindowOverflow4 -_WindowOverflow4: - - s32e a0, a5, -16 /* save a0 to call[j+1]'s stack frame */ - s32e a1, a5, -12 /* save a1 to call[j+1]'s stack frame */ - s32e a2, a5, -8 /* save a2 to call[j+1]'s stack frame */ - s32e a3, a5, -4 /* save a3 to call[j+1]'s stack frame */ - rfwo /* rotates back to call[i] position */ - -/* --------------------------------------------------------------------------------- -Window Underflow Exception for Call4 - -Invoked by RETW returning from call[i+1] to call[i] -where call[i]'s registers must be reloaded (not live in ARs); -where call[i] had done a call4 to call[i+1]. -On entry here: - window rotated to call[i] start point; - a0-a3 are undefined, must be reloaded with call[i].reg[0..3]; - a4-a15 must be preserved (they are call[i+1].reg[0..11]); - a5 is call[i+1]'s stack pointer. --------------------------------------------------------------------------------- -*/ - - .org 0x40 - .global _WindowUnderflow4 -_WindowUnderflow4: - - l32e a0, a5, -16 /* restore a0 from call[i+1]'s stack frame */ - l32e a1, a5, -12 /* restore a1 from call[i+1]'s stack frame */ - l32e a2, a5, -8 /* restore a2 from call[i+1]'s stack frame */ - l32e a3, a5, -4 /* restore a3 from call[i+1]'s stack frame */ - rfwu - -/* --------------------------------------------------------------------------------- -Handle alloca exception generated by interruptee executing 'movsp'. -This uses space between the window vectors, so is essentially "free". -All interruptee's regs are intact except a0 which is saved in EXCSAVE_1, -and PS.EXCM has been set by the exception hardware (can't be interrupted). -The fact the alloca exception was taken means the registers associated with -the base-save area have been spilled and will be restored by the underflow -handler, so those 4 registers are available for scratch. -The code is optimized to avoid unaligned branches and minimize cache misses. --------------------------------------------------------------------------------- -*/ - - .align 4 - .global _xt_alloca_exc -_xt_alloca_exc: - - rsr a0, WINDOWBASE /* grab WINDOWBASE before rotw changes it */ - rotw -1 /* WINDOWBASE goes to a4, new a0-a3 are scratch */ - rsr a2, PS - extui a3, a2, XCHAL_PS_OWB_SHIFT, XCHAL_PS_OWB_BITS - xor a3, a3, a4 /* bits changed from old to current windowbase */ - rsr a4, EXCSAVE_1 /* restore original a0 (now in a4) */ - slli a3, a3, XCHAL_PS_OWB_SHIFT - xor a2, a2, a3 /* flip changed bits in old window base */ - wsr a2, PS /* update PS.OWB to new window base */ - rsync - - _bbci.l a4, 31, _WindowUnderflow4 - rotw -1 /* original a0 goes to a8 */ - _bbci.l a8, 30, _WindowUnderflow8 - rotw -1 - j _WindowUnderflow12 - -/* --------------------------------------------------------------------------------- -Window Overflow Exception for Call8 - -Invoked if a call[i] referenced a register (a4-a15) -that contains data from ancestor call[j]; -call[j] had done a call8 to call[j+1]. -On entry here: - window rotated to call[j] start point; - a0-a7 are registers to be saved; - a8-a15 must be preserved; - a9 is call[j+1]'s stack pointer. --------------------------------------------------------------------------------- -*/ - - .org 0x80 - .global _WindowOverflow8 -_WindowOverflow8: - - s32e a0, a9, -16 /* save a0 to call[j+1]'s stack frame */ - l32e a0, a1, -12 /* a0 <- call[j-1]'s sp - (used to find end of call[j]'s frame) */ - s32e a1, a9, -12 /* save a1 to call[j+1]'s stack frame */ - s32e a2, a9, -8 /* save a2 to call[j+1]'s stack frame */ - s32e a3, a9, -4 /* save a3 to call[j+1]'s stack frame */ - s32e a4, a0, -32 /* save a4 to call[j]'s stack frame */ - s32e a5, a0, -28 /* save a5 to call[j]'s stack frame */ - s32e a6, a0, -24 /* save a6 to call[j]'s stack frame */ - s32e a7, a0, -20 /* save a7 to call[j]'s stack frame */ - rfwo /* rotates back to call[i] position */ - -/* --------------------------------------------------------------------------------- -Window Underflow Exception for Call8 - -Invoked by RETW returning from call[i+1] to call[i] -where call[i]'s registers must be reloaded (not live in ARs); -where call[i] had done a call8 to call[i+1]. -On entry here: - window rotated to call[i] start point; - a0-a7 are undefined, must be reloaded with call[i].reg[0..7]; - a8-a15 must be preserved (they are call[i+1].reg[0..7]); - a9 is call[i+1]'s stack pointer. --------------------------------------------------------------------------------- -*/ - - .org 0xC0 - .global _WindowUnderflow8 -_WindowUnderflow8: - - l32e a0, a9, -16 /* restore a0 from call[i+1]'s stack frame */ - l32e a1, a9, -12 /* restore a1 from call[i+1]'s stack frame */ - l32e a2, a9, -8 /* restore a2 from call[i+1]'s stack frame */ - l32e a7, a1, -12 /* a7 <- call[i-1]'s sp - (used to find end of call[i]'s frame) */ - l32e a3, a9, -4 /* restore a3 from call[i+1]'s stack frame */ - l32e a4, a7, -32 /* restore a4 from call[i]'s stack frame */ - l32e a5, a7, -28 /* restore a5 from call[i]'s stack frame */ - l32e a6, a7, -24 /* restore a6 from call[i]'s stack frame */ - l32e a7, a7, -20 /* restore a7 from call[i]'s stack frame */ - rfwu - -/* --------------------------------------------------------------------------------- -Window Overflow Exception for Call12 - -Invoked if a call[i] referenced a register (a4-a15) -that contains data from ancestor call[j]; -call[j] had done a call12 to call[j+1]. -On entry here: - window rotated to call[j] start point; - a0-a11 are registers to be saved; - a12-a15 must be preserved; - a13 is call[j+1]'s stack pointer. --------------------------------------------------------------------------------- -*/ - - .org 0x100 - .global _WindowOverflow12 -_WindowOverflow12: - - s32e a0, a13, -16 /* save a0 to call[j+1]'s stack frame */ - l32e a0, a1, -12 /* a0 <- call[j-1]'s sp - (used to find end of call[j]'s frame) */ - s32e a1, a13, -12 /* save a1 to call[j+1]'s stack frame */ - s32e a2, a13, -8 /* save a2 to call[j+1]'s stack frame */ - s32e a3, a13, -4 /* save a3 to call[j+1]'s stack frame */ - s32e a4, a0, -48 /* save a4 to end of call[j]'s stack frame */ - s32e a5, a0, -44 /* save a5 to end of call[j]'s stack frame */ - s32e a6, a0, -40 /* save a6 to end of call[j]'s stack frame */ - s32e a7, a0, -36 /* save a7 to end of call[j]'s stack frame */ - s32e a8, a0, -32 /* save a8 to end of call[j]'s stack frame */ - s32e a9, a0, -28 /* save a9 to end of call[j]'s stack frame */ - s32e a10, a0, -24 /* save a10 to end of call[j]'s stack frame */ - s32e a11, a0, -20 /* save a11 to end of call[j]'s stack frame */ - rfwo /* rotates back to call[i] position */ - -/* --------------------------------------------------------------------------------- -Window Underflow Exception for Call12 - -Invoked by RETW returning from call[i+1] to call[i] -where call[i]'s registers must be reloaded (not live in ARs); -where call[i] had done a call12 to call[i+1]. -On entry here: - window rotated to call[i] start point; - a0-a11 are undefined, must be reloaded with call[i].reg[0..11]; - a12-a15 must be preserved (they are call[i+1].reg[0..3]); - a13 is call[i+1]'s stack pointer. --------------------------------------------------------------------------------- -*/ - - .org 0x140 - .global _WindowUnderflow12 -_WindowUnderflow12: - - l32e a0, a13, -16 /* restore a0 from call[i+1]'s stack frame */ - l32e a1, a13, -12 /* restore a1 from call[i+1]'s stack frame */ - l32e a2, a13, -8 /* restore a2 from call[i+1]'s stack frame */ - l32e a11, a1, -12 /* a11 <- call[i-1]'s sp - (used to find end of call[i]'s frame) */ - l32e a3, a13, -4 /* restore a3 from call[i+1]'s stack frame */ - l32e a4, a11, -48 /* restore a4 from end of call[i]'s stack frame */ - l32e a5, a11, -44 /* restore a5 from end of call[i]'s stack frame */ - l32e a6, a11, -40 /* restore a6 from end of call[i]'s stack frame */ - l32e a7, a11, -36 /* restore a7 from end of call[i]'s stack frame */ - l32e a8, a11, -32 /* restore a8 from end of call[i]'s stack frame */ - l32e a9, a11, -28 /* restore a9 from end of call[i]'s stack frame */ - l32e a10, a11, -24 /* restore a10 from end of call[i]'s stack frame */ - l32e a11, a11, -20 /* restore a11 from end of call[i]'s stack frame */ - rfwu - -#endif /* XCHAL_HAVE_WINDOWED */ - - .section .UserEnter.text, "ax" - .global call_user_start - .type call_user_start,@function - .align 4 - .literal_position - - -call_user_start: - - movi a2, 0x40040000 /* note: absolute symbol, not a ptr */ - wsr a2, vecbase - call0 user_start /* user exception handler */ 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 61adea9821..73618e0d70 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 @@ -4,7 +4,7 @@ Filter: - Add: ID: [TCPIP_IGMP_0203, ^TCPIP_TCP_0403, ^TCPIP_TCP_0408, TCPIP_UDP_0201, TCPIP_UDP_0202, ^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_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_0101, - TCPIP_DHCP_0103, TCPIP_DHCP_0102, TCPIP_DHCP_0206, TCPIP_DHCP_0207, ^TCPIP_IP_0102] + ^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] 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 9d64630e9c..b326ed721c 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,7 +2,7 @@ Config: {execute count: 1, execute order: in order} DUT: [SSC2, SSC1] Filter: - Add: - ID: [^TCPIP_UDP_0105, ^TCPIP_UDP_0107, ^TCPIP_UDP_0106, ^TCPIP_UDP_0101, TCPIP_TCP_0203, + 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_DHCP_0205, TCPIP_DHCP_0202, TCPIP_DHCP_0203, ^TCPIP_TCP_0102, TCPIP_TCP_0106, TCPIP_TCP_0107, 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 6be01f698c..314de4a7e0 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 @@ -5,6 +5,6 @@ Filter: ID: [^TCPIP_TCP_0110, ^TCPIP_TCP_0111, 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_DHCP_0102, ^TCPIP_DHCP_0103, ^TCPIP_DHCP_0208, TCPIP_TCP_0208, + ^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] 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 c22bc59bd8..9f8424f6d2 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 @@ -4,7 +4,7 @@ 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_SCAN_0105, WIFI_CONN_0401, - WIFI_ADDR_0101, WIFI_ADDR_0102, WIFI_CONN_0301, ^WIFI_CONN_0801, ^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, WIFI_CONN_0601, ^WIFI_CONN_0201] + ^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] 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 049054dbac..74e6ca612d 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,6 @@ Config: {execute count: 1, execute order: in order} DUT: [SSC2, SSC1] Filter: - Add: - ID: [^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_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] 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 9d639f9121..dd42815a91 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,6 +2,5 @@ Config: {execute count: 1, execute order: in order} DUT: [SSC2, SSC1] Filter: - Add: - ID: [WIFI_SCAN_0301, WIFI_SCAN_0303, WIFI_SCAN_0304, WIFI_SCAN_0302, 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_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] diff --git a/components/idf_test/integration_test/KnownIssues b/components/idf_test/integration_test/KnownIssues index 08bc48f185..e0991f39b0 100644 --- a/components/idf_test/integration_test/KnownIssues +++ b/components/idf_test/integration_test/KnownIssues @@ -23,10 +23,29 @@ TCPIP_IGMP_0204 ^TCPIP_IGMP_0203 ^TCPIP_IGMP_0204 +# don't support PHY mode command +WIFI_SCAN_0201 +WIFI_SCAN_0302 +WIFI_PHY_0401 +WIFI_PHY_0402 +WIFI_PHY_0403 +WIFI_PHY_0404 +WIFI_PHY_0405 +WIFI_PHY_0407 +WIFI_PHY_0406 +WIFI_PHY_0408 +WIFI_PHY_0501 +WIFI_PHY_0502 +WIFI_PHY_0503 +WIFI_PHY_0504 +WIFI_PHY_0505 +WIFI_PHY_0506 + # BUG # auth change event WIFI_CONN_0801 +^WIFI_CONN_0801 # disconnect reason WIFI_CONN_0904 @@ -37,6 +56,16 @@ WIFI_CONN_0901 # Wifi connect issue WIFI_CONN_0104 ^WIFI_CONN_0104 +^WIFI_CONN_0601 + +# Wifi scan issue +WIFI_SCAN_0303 +^WIFI_SCAN_0103 +^WIFI_SCAN_0105 + +# set mac address may lead to exception +WIFI_ADDR_0101 +^WIFI_ADDR_0101 # DHCP issues ^TCPIP_DHCP_0301 @@ -46,6 +75,10 @@ TCPIP_DHCP_0207 ^TCPIP_DHCP_0207 TCPIP_DHCP_0208 ^TCPIP_DHCP_0208 +TCPIP_DHCP_0205 +^TCPIP_DHCP_0205 +TCPIP_DHCP_0209 +^TCPIP_DHCP_0209 # TCP issue TCPIP_TCP_0402 @@ -54,6 +87,9 @@ TCPIP_TCP_0402 TCPIP_TCP_0210 ^TCPIP_TCP_0210 TCPIP_TCP_0103 +^TCPIP_TCP_0103 +TCPIP_TCP_0112 +^TCPIP_TCP_0112 # UDP issue @@ -61,6 +97,10 @@ TCPIP_UDP_0103 ^TCPIP_UDP_0103 TCPIP_UDP_0110 ^TCPIP_UDP_0110 +TCPIP_UDP_0305 +^TCPIP_UDP_0305 +^TCPIP_UDP_0304 +TCPIP_UDP_0104 diff --git a/components/idf_test/integration_test/TestCaseAll.yml b/components/idf_test/integration_test/TestCaseAll.yml index 0835c146bd..9e4823a297 100644 --- a/components/idf_test/integration_test/TestCaseAll.yml +++ b/components/idf_test/integration_test/TestCaseAll.yml @@ -87,7 +87,7 @@ test cases: - - SSC SSC1 dhcp -E -o 1 - ['R SSC1 C +DHCP:STA,OK'] - - SSC SSC1 ip -S -i 0.0.0.0 - - ['R SSC1 C +IP:OK'] + - [R SSC1 C +IP] - - SSC SSC1 sta -C -s -p - [''] - - DELAY 20 @@ -121,54 +121,7 @@ test cases: 1 SSC target connect with PC by UART.' test point 1: basic function test point 2: DHCP client function test - version: v1 (2016-8-15) -- CI ready: 'Yes' - ID: TCPIP_DHCP_0102 - SDK: '8266_NonOS - - 8266_RTOS - - ESP32_IDF' - Test App: SSC - allow fail: '' - auto test: 'Yes' - category: Function - cmd set: - - '' - - - SSC SSC1 ap -S -s -p -t - - ['R SSC1 C +SAP:OK'] - - - SSC SSC1 dhcp -E -o 2 - - ['R SSC1 C +DHCP:AP,OK'] - - - SSC SSC2 sta -C -s -p - - [''] - - - DELAY 20 - - [P PC_COM C +DELAYDONE, 'P SSC2 NC +JAP:CONNECTED'] - - - SSC SSC1 dhcp -S -o 2 - - ['R SSC1 C +DHCP:AP,OK'] - - - SSC SSC2 sta -C -s -p - - ['R SSC2 C +JAP:CONNECTED'] - comment: '' - execution time: 0.0 - expected result: "1.target1 set AP OK \n2.target1 关闭DHCP OK\n3.target2 jap target\ - \ 1,FAIL \n4.target1 打开DHCP OK\n5.target2 jap target 1,ok" - initial condition: T2_1 - initial condition description (auto): target 1 as SoftAP, target 2 as STA, will - autogen a TC with initial condition T2_2 - level: Integration - module: TCPIP - steps: "1.target1 set AP OK \n2.target1 关闭DHCP OK\n3.target2 jap target 1,FAIL \n\ - 4.target1 打开DHCP OK\n5.target2 jap target 1,ok" - sub module: DHCP - summary: dhcp server function 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: basic function - test point 2: DHCP client function test - version: v1 (2016-8-15) + version: v2 (2016-10-19) - CI ready: 'Yes' ID: TCPIP_DHCP_0103 SDK: '8266_NonOS @@ -927,7 +880,7 @@ test cases: - ['R SSC1 C +DHCP:AP,OK'] - - SSC SSC2 sta -C -s -p - ['R SSC2 C +JAP:CONNECTED'] - - - DELAY 10 + - - DELAY 30 - [''] - - SSC SSC1 ap -L - [R SSC1 C 192.168.4.2 C 192.168.4.3 P P ] @@ -967,6 +920,53 @@ test cases: test point 1: basic function test point 2: DHCP server function test version: v1 (2016-8-15) +- CI ready: 'Yes' + ID: TCPIP_DHCP_0212 + SDK: '8266_NonOS + + 8266_RTOS + + ESP32_IDF' + Test App: SSC + allow fail: '' + auto test: 'Yes' + category: Function + cmd set: + - '' + - - SSC SSC1 ap -S -s -p -t + - ['R SSC1 C +SAP:OK'] + - - SSC SSC1 dhcp -E -o 2 + - ['R SSC1 C +DHCP:AP,OK'] + - - SSC SSC2 sta -C -s -p + - [''] + - - DELAY 20 + - [P PC_COM C +DELAYDONE, 'P SSC2 NC +JAP:CONNECTED'] + - - SSC SSC1 dhcp -S -o 2 + - ['R SSC1 C +DHCP:AP,OK'] + - - SSC SSC2 sta -C -s -p + - ['R SSC2 C +JAP:CONNECTED'] + comment: '' + execution time: 0.0 + expected result: "1.target1 set AP OK \n2.target1 关闭DHCP OK\n3.target2 jap target\ + \ 1,FAIL \n4.target1 打开DHCP OK\n5.target2 jap target 1,ok" + initial condition: T2_1 + initial condition description (auto): target 1 as SoftAP, target 2 as STA, will + autogen a TC with initial condition T2_2 + level: Integration + module: TCPIP + steps: "1.target1 set AP OK \n2.target1 关闭DHCP OK\n3.target2 jap target 1,FAIL \n\ + 4.target1 打开DHCP OK\n5.target2 jap target 1,ok" + sub module: DHCP + summary: dhcp server function 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: basic function + test point 2: DHCP server function test + version: v2 (2016-10-19) - CI ready: 'Yes' ID: TCPIP_DHCP_0301 SDK: '8266_NonOS @@ -987,7 +987,7 @@ test cases: - - SSC SSC1 sta -C -s -p - ['R SSC1 C +JAP:CONNECTED'] - - SSC SSC1 ip -S -i 0.0.0.0 -o 1 - - ['R SSC1 C +IP:OK'] + - [R SSC1 C +IP] - - SSC SSC1 sta -C -s -p - [''] - - DELAY 10 @@ -1027,7 +1027,7 @@ test cases: 1 SSC target connect with PC by UART.' test point 1: interaction test point 2: static IP and DHCP interaction test - version: v1 (2016-8-15) + version: v2 (2016-10-19) - CI ready: 'Yes' ID: TCPIP_DHCP_0302 SDK: '8266_NonOS @@ -1143,7 +1143,7 @@ test cases: category: Function cmd set: - '' - - - SSC SSC1 soc -H -d iot.espressif.cn + - - SSC SSC1 soc -H -d factory.espressif.cn - ['R SSC1 A :\+HOSTIP:OK,(.+)\r\n'] - - SSC SSC1 soc -B -t TCP - ['R SSC1 A :\+BIND:(\d+),OK'] @@ -1178,7 +1178,7 @@ test cases: 1 SSC target connect with PC by UART.' test point 1: basic function test point 2: DNS function test - version: v1 (2016-8-15) + version: v2 (2016-10-19) - CI ready: 'Yes' ID: TCPIP_DNS_0103 SDK: '8266_NonOS @@ -1192,7 +1192,7 @@ test cases: category: Function cmd set: - '' - - - SSC SSC1 soc -H -d iot.espressif.cn + - - SSC SSC1 soc -H -d factory.espressif.cn - ['R SSC1 A :\+HOSTIP:OK,(.+)\r\n'] - - SSC SSC1 soc -B -t UDP - ['R SSC1 A :\+BIND:(\d+),OK'] @@ -1223,7 +1223,7 @@ test cases: 1 SSC target connect with PC by UART.' test point 1: basic function test point 2: DNS function test - version: v1 (2016-8-15) + version: v2 (2016-10-19) - CI ready: 'Yes' ID: TCPIP_ICMP_0101 SDK: '8266_NonOS @@ -3189,14 +3189,12 @@ test cases: - '' - - SOC SOC1 LISTEN - [R SOC_COM L OK] - - - SSC SSC1 soc -B -t TCP + - - SSC SSC1 soc -B -t TCP -w 0 - ['R SSC1 A :BIND:(\d+),OK'] - - SSC SSC1 soc -C -s -i -p - ['R SSC1 RE CONNECT:\d+,OK'] - - SOC SOC1 ACCEPT SOC2 - [R SOC_COM L OK] - - - SSC SSC1 soc -W -s -o 0 - - ['R SSC1 RE WORKTHREAD:\d+,OK'] - - SOC SOC2 SEND 146000 - [P SOC_COM R *] - - SSC SSC1 soc -W -s -o 1 @@ -3246,7 +3244,7 @@ test cases: 1 SSC target connect with PC by UART.' test point 1: basic function test point 2: use TCP SAP (socket/espconn API) in different state - version: v1 (2016-8-15) + version: v2 (2016-10-19) - CI ready: 'Yes' ID: TCPIP_TCP_0206 SDK: '8266_NonOS @@ -3280,7 +3278,7 @@ test cases: - ['R SSC1 A :ACCEPT:(\d+),\d+,.+,\d+'] - - SSC SSC1 soc -I - ['P SSC1 RE "SOCINFO:%%s,2,%%s,\d+,%%s,%%d"%%(,,,)', - 'P SSC1 RE "SOCINFO:%%s,2,.+,\d+,.+,\d+"%%()', 'P SSC1 RE "SOCINFO:%%s,82,.+,%%d,.+,\d+"%%(,)', + 'P SSC1 RE "SOCINFO:%%s,2,.+,\d+,.+,\d+"%%()', 'P SSC1 RE "SOCINFO:%%s,82,.+,%%d"%%(,)', 'P SSC1 RE "SOCINFO:%%s,2,%%s,%%d,%%s,\d+"%%(,,,)'] comment: '' execution time: 0.0 @@ -3531,14 +3529,12 @@ test cases: - '' - - SOC SOC1 LISTEN - [R SOC_COM L OK] - - - SSC SSC1 soc -B -t TCP + - - SSC SSC1 soc -B -t TCP -w 0 - ['R SSC1 A :BIND:(\d+),OK'] - - SSC SSC1 soc -C -s -i -p - ['R SSC1 RE CONNECT:\d+,OK'] - - SOC SOC1 ACCEPT SOC2 - [R SOC_COM L OK] - - - SSC SSC1 soc -W -s -o 0 - - ['R SSC1 RE WORKTHREAD:\d+,OK'] - - SOC SOC2 SEND 146000 - [P SOC_COM R *] - - SSC SSC1 soc -W -s -o 1 @@ -3586,7 +3582,7 @@ test cases: 1 SSC target connect with PC by UART.' test point 1: basic function test point 2: use TCP SAP (socket/espconn API) in different state - version: v1 (2016-8-15) + version: v2 (2016-10-19) - CI ready: 'Yes' ID: TCPIP_TCP_0212 SDK: '8266_NonOS @@ -3620,7 +3616,7 @@ test cases: - ['R SSC1 A :ACCEPT:(\d+),\d+,.+,\d+'] - - SSC SSC1 soc -I - ['P SSC1 RE "SOCINFO:%%s,2,%%s,\d+,%%s,%%d"%%(,,,)', - 'P SSC1 RE "SOCINFO:%%s,2,.+,\d+,.+,\d+"%%()', 'P SSC1 RE "SOCINFO:%%s,82,.+,%%d,.+,\d+"%%(,)', + 'P SSC1 RE "SOCINFO:%%s,2,.+,\d+,.+,\d+"%%()', 'P SSC1 RE "SOCINFO:%%s,82,.+,%%d"%%(,)', 'P SSC1 RE "SOCINFO:%%s,2,%%s,%%d,%%s,\d+"%%(,,,)'] comment: '' execution time: 0.0 @@ -5119,10 +5115,8 @@ test cases: - '' - - SOC SOC1 BIND - [R SOC_COM L OK] - - - SSC SSC1 soc -B -t UDP -p + - - SSC SSC1 soc -B -t UDP -p -w 0 - ['R SSC1 A :BIND:(\d+),OK'] - - - SSC SSC1 soc -W -s -o 0 - - ['R SSC1 RE WORKTHREAD:\d+,OK'] - - SOC SOC1 SENDTO 1472 - [''] - - SOC SOC1 SENDTO 1472 @@ -5180,7 +5174,7 @@ test cases: 1 SSC target connect with PC by UART.' test point 1: abnormal/special use test point 2: use UDP SAP (socket/espconn API) in different state - version: v1 (2016-8-15) + version: v2 (2016-10-19) - CI ready: 'Yes' ID: TCPIP_UDP_0202 SDK: '8266_NonOS @@ -5198,8 +5192,8 @@ test cases: - [R SOC_COM L OK] - - SSC SSC1 soc -B -t UDP -p - ['R SSC1 A :BIND:(\d+),OK'] - - - SSC SSC1 soc -W -s -o 0 - - ['R SSC1 RE WORKTHREAD:\d+,OK'] + - - SOC SOC1 SENDTO 1472 + - [''] - - SOC SOC1 SENDTO 1472 - [''] - - SOC SOC1 SENDTO 1472 @@ -5257,7 +5251,7 @@ test cases: 1 SSC target connect with PC by UART.' test point 1: abnormal/special use test point 2: use UDP SAP (socket/espconn API) in different state - version: v1 (2016-8-15) + version: v2 (2016-10-19) - CI ready: 'Yes' ID: TCPIP_UDP_0301 SDK: '8266_NonOS @@ -5874,6 +5868,8 @@ test cases: - ['R SSC2 RE "\+JAP:CONNECTED,%%s"%%()'] - - SSC SSC1 ap -S -s -n 15 - ['R SSC1 C +SAP:OK'] + - - SSC SSC2 sta -C -s -p + - ['R SSC2 RE "\+JAP:CONNECTED,%%s"%%()'] - - SSC SSC2 sta -S - ['R SSC2 RE "\+SCAN:%%s,.+,\d+,1"%%()'] comment: '' @@ -7915,7 +7911,7 @@ test cases: - - SSC SSC1 sta -S - [''] - - SSC SSC1 sta -S - - [P SSC1 C +SCANFAIL, 'P SSC1 P +SCAN:', R SSC1 C +SCANDONE] + - [P SSC1 C +SCANFAIL, 'P SSC1 C +SCAN:', R SSC1 C +SCANDONE] comment: '' execution time: 0.0 expected result: '1. second scan failed @@ -7931,14 +7927,12 @@ test cases: 2. do scan before scan finished' sub module: WIFI Scan summary: reject scan request before scan finished - test environment: SSC_T2_PhyMode - test environment description (auto): '2 SSC target connect with PC by UART. + test environment: SSC_T1_1 + test environment description (auto): 'PC has 2 wired NIC connected to AP. - PC has one WiFi NIC support capture wlan packet using libpcap. + PC has 1 WiFi NIC. - Set 4 AP with phy mode 11b, 11g, 11n HT20, 11n HT40. - - Put 4 APs near SSC targets.' + 1 SSC target connect with PC by UART.' test point 1: interaction test point 2: Scan interact with other WiFi operation version: v1 (2015-8-15) @@ -7987,14 +7981,12 @@ test cases: 3. target 2 scan in AP channel in 11b.g,n,ht40 mode' sub module: WIFI Scan summary: scan in congest channel - test environment: SSC_T2_PhyMode - test environment description (auto): '2 SSC target connect with PC by UART. + test environment: SSC_T2_1 + test environment description (auto): 'PC has 1 wired NIC connected to AP. - PC has one WiFi NIC support capture wlan packet using libpcap. + PC has 1 WiFi NIC. - Set 4 AP with phy mode 11b, 11g, 11n HT20, 11n HT40. - - Put 4 APs near SSC targets.' + 2 SSC target connect with PC by UART.' test point 1: interaction test point 2: Scan interact with other WiFi operation version: v1 (2015-8-15) @@ -8022,8 +8014,9 @@ test cases: expected result: '2. scan succeed, JAP succeed 5. JAP succeed, scan succeed' - initial condition: T2_2 - initial condition description (auto): target 1 as AP+STA, target 2 as AP+STA (autogen) + initial condition: STAM1 + initial condition description (auto): sta mode, quit AP, DHCP on, will autogen a + TC with initial condition STAAP1 level: Integration module: WIFI MAC steps: '1. target 1 STA join AP @@ -8037,14 +8030,12 @@ test cases: 5. target 1 JAP before scan succeed' sub module: WIFI Scan summary: scan during JAP - test environment: SSC_T2_PhyMode - test environment description (auto): '2 SSC target connect with PC by UART. + test environment: SSC_T1_1 + test environment description (auto): 'PC has 2 wired NIC connected to AP. - PC has one WiFi NIC support capture wlan packet using libpcap. + PC has 1 WiFi NIC. - Set 4 AP with phy mode 11b, 11g, 11n HT20, 11n HT40. - - Put 4 APs near SSC targets.' + 1 SSC target connect with PC by UART.' test point 1: interaction test point 2: Scan interact with other WiFi operation version: v1 (2015-8-15) @@ -8057,6 +8048,8 @@ test cases: category: Function cmd set: - '' + - - SSC SSC1 ap -S -s -p -t + - ['R SSC1 C +SAP:OK'] - - SSC SSC2 sta -C -s -p - ['R SSC2 C +JAP:OK'] - - SSC SSC1 sta -S @@ -8087,14 +8080,12 @@ test cases: 5. target 2 STA JAP before target 1 STA scan succeed' sub module: WIFI Scan summary: scan during ext STA join SoftAP - test environment: SSC_T2_PhyMode - test environment description (auto): '2 SSC target connect with PC by UART. + test environment: SSC_T2_1 + test environment description (auto): 'PC has 1 wired NIC connected to AP. - PC has one WiFi NIC support capture wlan packet using libpcap. + PC has 1 WiFi NIC. - Set 4 AP with phy mode 11b, 11g, 11n HT20, 11n HT40. - - Put 4 APs near SSC targets.' + 2 SSC target connect with PC by UART.' test point 1: interaction test point 2: Scan interact with other WiFi operation version: v1 (2015-8-15) @@ -8114,7 +8105,7 @@ test cases: - - SSC SSC1 dhcp -E -o 1 - ['R SSC1 C +DHCP:STA,OK'] - - SSC SSC1 ip -S -i 0.0.0.0 - - ['R SSC1 C +IP:OK'] + - [R SSC1 C +IP] - - SSC SSC1 sta -C -s -p - [''] - - DELAY 20 @@ -8148,53 +8139,7 @@ test cases: 1 SSC target connect with PC by UART.' test point 1: basic function test point 2: DHCP client function test - version: v1 (2016-8-15) -- CI ready: 'Yes' - ID: ^TCPIP_DHCP_0102 - SDK: '8266_NonOS - - 8266_RTOS - - ESP32_IDF' - Test App: SSC - allow fail: '' - auto test: 'Yes' - category: Function - cmd set: - - '' - - - SSC SSC1 ap -S -s -p -t - - ['R SSC1 C +SAP:OK'] - - - SSC SSC1 dhcp -E -o 2 - - ['R SSC1 C +DHCP:AP,OK'] - - - SSC SSC2 sta -C -s -p - - [''] - - - DELAY 20 - - [P PC_COM C +DELAYDONE, 'P SSC2 NC +JAP:CONNECTED'] - - - SSC SSC1 dhcp -S -o 2 - - ['R SSC1 C +DHCP:AP,OK'] - - - SSC SSC2 sta -C -s -p - - ['R SSC2 C +JAP:CONNECTED'] - comment: '' - execution time: 0.0 - expected result: "1.target1 set AP OK \n2.target1 关闭DHCP OK\n3.target2 jap target\ - \ 1,FAIL \n4.target1 打开DHCP OK\n5.target2 jap target 1,ok" - initial condition: T2_2 - initial condition description (auto): target 1 as AP+STA, target 2 as AP+STA (autogen) - level: Integration - module: TCPIP - steps: "1.target1 set AP OK \n2.target1 关闭DHCP OK\n3.target2 jap target 1,FAIL \n\ - 4.target1 打开DHCP OK\n5.target2 jap target 1,ok" - sub module: DHCP - summary: dhcp server function 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: basic function - test point 2: DHCP client function test - version: v1 (2016-8-15) + version: v2 (2016-10-19) - CI ready: 'Yes' ID: ^TCPIP_DHCP_0103 SDK: '8266_NonOS @@ -8943,7 +8888,7 @@ test cases: - ['R SSC1 C +DHCP:AP,OK'] - - SSC SSC2 sta -C -s -p - ['R SSC2 C +JAP:CONNECTED'] - - - DELAY 10 + - - DELAY 30 - [''] - - SSC SSC1 ap -L - [R SSC1 C 192.168.4.2 C 192.168.4.3 P P ] @@ -8982,6 +8927,52 @@ test cases: test point 1: basic function test point 2: DHCP server function test version: v1 (2016-8-15) +- CI ready: 'Yes' + ID: ^TCPIP_DHCP_0212 + SDK: '8266_NonOS + + 8266_RTOS + + ESP32_IDF' + Test App: SSC + allow fail: '' + auto test: 'Yes' + category: Function + cmd set: + - '' + - - SSC SSC1 ap -S -s -p -t + - ['R SSC1 C +SAP:OK'] + - - SSC SSC1 dhcp -E -o 2 + - ['R SSC1 C +DHCP:AP,OK'] + - - SSC SSC2 sta -C -s -p + - [''] + - - DELAY 20 + - [P PC_COM C +DELAYDONE, 'P SSC2 NC +JAP:CONNECTED'] + - - SSC SSC1 dhcp -S -o 2 + - ['R SSC1 C +DHCP:AP,OK'] + - - SSC SSC2 sta -C -s -p + - ['R SSC2 C +JAP:CONNECTED'] + comment: '' + execution time: 0.0 + expected result: "1.target1 set AP OK \n2.target1 关闭DHCP OK\n3.target2 jap target\ + \ 1,FAIL \n4.target1 打开DHCP OK\n5.target2 jap target 1,ok" + initial condition: T2_2 + initial condition description (auto): target 1 as AP+STA, target 2 as AP+STA (autogen) + level: Integration + module: TCPIP + steps: "1.target1 set AP OK \n2.target1 关闭DHCP OK\n3.target2 jap target 1,FAIL \n\ + 4.target1 打开DHCP OK\n5.target2 jap target 1,ok" + sub module: DHCP + summary: dhcp server function 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: basic function + test point 2: DHCP server function test + version: v2 (2016-10-19) - CI ready: 'Yes' ID: ^TCPIP_DHCP_0301 SDK: '8266_NonOS @@ -9002,7 +8993,7 @@ test cases: - - SSC SSC1 sta -C -s -p - ['R SSC1 C +JAP:CONNECTED'] - - SSC SSC1 ip -S -i 0.0.0.0 -o 1 - - ['R SSC1 C +IP:OK'] + - [R SSC1 C +IP] - - SSC SSC1 sta -C -s -p - [''] - - DELAY 10 @@ -9042,7 +9033,7 @@ test cases: 1 SSC target connect with PC by UART.' test point 1: interaction test point 2: static IP and DHCP interaction test - version: v1 (2016-8-15) + version: v2 (2016-10-19) - CI ready: 'Yes' ID: ^TCPIP_DHCP_0302 SDK: '8266_NonOS @@ -9157,7 +9148,7 @@ test cases: category: Function cmd set: - '' - - - SSC SSC1 soc -H -d iot.espressif.cn + - - SSC SSC1 soc -H -d factory.espressif.cn - ['R SSC1 A :\+HOSTIP:OK,(.+)\r\n'] - - SSC SSC1 soc -B -t TCP - ['R SSC1 A :\+BIND:(\d+),OK'] @@ -9192,7 +9183,7 @@ test cases: 1 SSC target connect with PC by UART.' test point 1: basic function test point 2: DNS function test - version: v1 (2016-8-15) + version: v2 (2016-10-19) - CI ready: 'Yes' ID: ^TCPIP_DNS_0103 SDK: '8266_NonOS @@ -9206,7 +9197,7 @@ test cases: category: Function cmd set: - '' - - - SSC SSC1 soc -H -d iot.espressif.cn + - - SSC SSC1 soc -H -d factory.espressif.cn - ['R SSC1 A :\+HOSTIP:OK,(.+)\r\n'] - - SSC SSC1 soc -B -t UDP - ['R SSC1 A :\+BIND:(\d+),OK'] @@ -9237,7 +9228,7 @@ test cases: 1 SSC target connect with PC by UART.' test point 1: basic function test point 2: DNS function test - version: v1 (2016-8-15) + version: v2 (2016-10-19) - CI ready: 'Yes' ID: ^TCPIP_ICMP_0101 SDK: '8266_NonOS @@ -11199,14 +11190,12 @@ test cases: - '' - - SOC SOC1 LISTEN - [R SOC_COM L OK] - - - SSC SSC1 soc -B -t TCP + - - SSC SSC1 soc -B -t TCP -w 0 - ['R SSC1 A :BIND:(\d+),OK'] - - SSC SSC1 soc -C -s -i -p - ['R SSC1 RE CONNECT:\d+,OK'] - - SOC SOC1 ACCEPT SOC2 - [R SOC_COM L OK] - - - SSC SSC1 soc -W -s -o 0 - - ['R SSC1 RE WORKTHREAD:\d+,OK'] - - SOC SOC2 SEND 146000 - [P SOC_COM R *] - - SSC SSC1 soc -W -s -o 1 @@ -11254,7 +11243,7 @@ test cases: 1 SSC target connect with PC by UART.' test point 1: basic function test point 2: use TCP SAP (socket/espconn API) in different state - version: v1 (2016-8-15) + version: v2 (2016-10-19) - CI ready: 'Yes' ID: ^TCPIP_TCP_0206 SDK: '8266_NonOS @@ -11288,7 +11277,7 @@ test cases: - ['R SSC1 A :ACCEPT:(\d+),\d+,.+,\d+'] - - SSC SSC1 soc -I - ['P SSC1 RE "SOCINFO:%%s,2,%%s,\d+,%%s,%%d"%%(,,,)', - 'P SSC1 RE "SOCINFO:%%s,2,.+,\d+,.+,\d+"%%()', 'P SSC1 RE "SOCINFO:%%s,82,.+,%%d,.+,\d+"%%(,)', + 'P SSC1 RE "SOCINFO:%%s,2,.+,\d+,.+,\d+"%%()', 'P SSC1 RE "SOCINFO:%%s,82,.+,%%d"%%(,)', 'P SSC1 RE "SOCINFO:%%s,2,%%s,%%d,%%s,\d+"%%(,,,)'] comment: '' execution time: 0.0 @@ -11539,14 +11528,12 @@ test cases: - '' - - SOC SOC1 LISTEN - [R SOC_COM L OK] - - - SSC SSC1 soc -B -t TCP + - - SSC SSC1 soc -B -t TCP -w 0 - ['R SSC1 A :BIND:(\d+),OK'] - - SSC SSC1 soc -C -s -i -p - ['R SSC1 RE CONNECT:\d+,OK'] - - SOC SOC1 ACCEPT SOC2 - [R SOC_COM L OK] - - - SSC SSC1 soc -W -s -o 0 - - ['R SSC1 RE WORKTHREAD:\d+,OK'] - - SOC SOC2 SEND 146000 - [P SOC_COM R *] - - SSC SSC1 soc -W -s -o 1 @@ -11592,7 +11579,7 @@ test cases: 1 SSC target connect with PC by UART.' test point 1: basic function test point 2: use TCP SAP (socket/espconn API) in different state - version: v1 (2016-8-15) + version: v2 (2016-10-19) - CI ready: 'Yes' ID: ^TCPIP_TCP_0212 SDK: '8266_NonOS @@ -11626,7 +11613,7 @@ test cases: - ['R SSC1 A :ACCEPT:(\d+),\d+,.+,\d+'] - - SSC SSC1 soc -I - ['P SSC1 RE "SOCINFO:%%s,2,%%s,\d+,%%s,%%d"%%(,,,)', - 'P SSC1 RE "SOCINFO:%%s,2,.+,\d+,.+,\d+"%%()', 'P SSC1 RE "SOCINFO:%%s,82,.+,%%d,.+,\d+"%%(,)', + 'P SSC1 RE "SOCINFO:%%s,2,.+,\d+,.+,\d+"%%()', 'P SSC1 RE "SOCINFO:%%s,82,.+,%%d"%%(,)', 'P SSC1 RE "SOCINFO:%%s,2,%%s,%%d,%%s,\d+"%%(,,,)'] comment: '' execution time: 0.0 @@ -12981,10 +12968,8 @@ test cases: - '' - - SOC SOC1 BIND - [R SOC_COM L OK] - - - SSC SSC1 soc -B -t UDP -p + - - SSC SSC1 soc -B -t UDP -p -w 0 - ['R SSC1 A :BIND:(\d+),OK'] - - - SSC SSC1 soc -W -s -o 0 - - ['R SSC1 RE WORKTHREAD:\d+,OK'] - - SOC SOC1 SENDTO 1472 - [''] - - SOC SOC1 SENDTO 1472 @@ -13042,7 +13027,7 @@ test cases: 1 SSC target connect with PC by UART.' test point 1: abnormal/special use test point 2: use UDP SAP (socket/espconn API) in different state - version: v1 (2016-8-15) + version: v2 (2016-10-19) - CI ready: 'Yes' ID: ^TCPIP_UDP_0202 SDK: '8266_NonOS @@ -13060,8 +13045,8 @@ test cases: - [R SOC_COM L OK] - - SSC SSC1 soc -B -t UDP -p - ['R SSC1 A :BIND:(\d+),OK'] - - - SSC SSC1 soc -W -s -o 0 - - ['R SSC1 RE WORKTHREAD:\d+,OK'] + - - SOC SOC1 SENDTO 1472 + - [''] - - SOC SOC1 SENDTO 1472 - [''] - - SOC SOC1 SENDTO 1472 @@ -13119,7 +13104,7 @@ test cases: 1 SSC target connect with PC by UART.' test point 1: abnormal/special use test point 2: use UDP SAP (socket/espconn API) in different state - version: v1 (2016-8-15) + version: v2 (2016-10-19) - CI ready: 'Yes' ID: ^TCPIP_UDP_0301 SDK: '8266_NonOS @@ -14652,67 +14637,3 @@ test cases: test point 1: basic function test point 2: scan with different config version: v1 (2016-8-15) -- CI ready: 'Yes' - ID: ^WIFI_SCAN_0105 - SDK: '8266_NonOS - - 8266_RTOS - - ESP32_IDF' - Test App: SSC - allow fail: '' - auto test: 'Yes' - category: Function - cmd set: - - '' - - - SSC SSC1 sta -D - - ['R SSC1 C +QAP:'] - - - SSC SSC1 ap -S -s -p 123456789 -t 3 -h 0 -n 11 - - ['R SSC1 C +SAP:OK'] - - - SSC SSC2 sta -S -s -b -n 11 - - [R SSC2 P C +SCANDONE] - - - SSC SSC2 sta -S -s -b -n 11 - - [R SSC2 NP C +SCANDONE] - - - SSC SSC2 sta -S -s -b ff:ff:ff:ff:ff:11 -n 11 - - [R SSC2 P , R SSC2 NP C +SCANDONE] - - - SSC SSC2 sta -S -s -b -n 10 - - [R SSC2 P , R SSC2 NP C +SCANDONE] - comment: '' - execution time: 0.0 - expected result: '1.target1 QAP - - 2. target1 set AP,set ssid broad cast,set channel 11 - - 3.target2 上查询到 - - 4.target2 上查询不到 - - 5.target2 上查询不到 - - 6.target2 上查询不到' - initial condition: T2_2 - initial condition description (auto): target 1 as AP+STA, target 2 as AP+STA (autogen) - level: Integration - module: WIFI MAC - steps: '1.target1 QAP - - 2. target1 set AP,set ssid broad cast,set channel 11 - - 3.target2 上查询到 - - 4.target2 上查询不到 - - 5.target2 上查询不到 - - 6.target2 上查询不到' - sub module: WIFI Scan - summary: scan with several configs - 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: basic function - test point 2: scan with different config - version: v1 (2016-8-15) diff --git a/components/idf_test/integration_test/TestEnvAll.yml b/components/idf_test/integration_test/TestEnvAll.yml index afa6cb812a..2e59961d97 100644 --- a/components/idf_test/integration_test/TestEnvAll.yml +++ b/components/idf_test/integration_test/TestEnvAll.yml @@ -229,6 +229,10 @@ test environment: Put 4 APs near SSC targets.', test script: EnvBase} - {PC OS: '', Special: N, Target Count: 5.0, script path: EnvBase.py, tag: SSC_T5_1, test environment detail: 5 SSC target connect with PC by UART., test script: EnvBase} +- {PC OS: '', Special: Y, Target Count: 5.0, script path: EnvBase.py, tag: SSC_T5_IOT1, + test environment detail: '5 SSC targets connect with PC by UART. + + some Android smart phone are placed near SSC targets.', test script: EnvBase} - {PC OS: '', Special: N, Target Count: 1.0, script path: EnvBase.py, tag: SSC_T6_1, test environment detail: 'PC has 1 wired NIC connected to AP. diff --git a/components/idf_test/uint_test/TestEnvAll.yml b/components/idf_test/uint_test/TestEnvAll.yml index afa6cb812a..2e59961d97 100644 --- a/components/idf_test/uint_test/TestEnvAll.yml +++ b/components/idf_test/uint_test/TestEnvAll.yml @@ -229,6 +229,10 @@ test environment: Put 4 APs near SSC targets.', test script: EnvBase} - {PC OS: '', Special: N, Target Count: 5.0, script path: EnvBase.py, tag: SSC_T5_1, test environment detail: 5 SSC target connect with PC by UART., test script: EnvBase} +- {PC OS: '', Special: Y, Target Count: 5.0, script path: EnvBase.py, tag: SSC_T5_IOT1, + test environment detail: '5 SSC targets connect with PC by UART. + + some Android smart phone are placed near SSC targets.', test script: EnvBase} - {PC OS: '', Special: N, Target Count: 1.0, script path: EnvBase.py, tag: SSC_T6_1, test environment detail: 'PC has 1 wired NIC connected to AP. diff --git a/components/lwip/Kconfig b/components/lwip/Kconfig index 715d7dd467..15c94c66ba 100644 --- a/components/lwip/Kconfig +++ b/components/lwip/Kconfig @@ -7,7 +7,8 @@ config LWIP_MAX_SOCKETS help Sockets take up a certain amount of memory, and allowing fewer sockets to be open at the same time conserves memory. Specify - the maximum amount of sockets here. + the maximum amount of sockets here. The valid value is from 1 + to 16. config LWIP_THREAD_LOCAL_STORAGE_INDEX int "Index for thread-local-storage pointer for lwip" diff --git a/components/lwip/api/api_lib.c b/components/lwip/api/api_lib.c index c38c760811..ecebf4f813 100755 --- a/components/lwip/api/api_lib.c +++ b/components/lwip/api/api_lib.c @@ -55,11 +55,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - - #define API_MSG_VAR_REF(name) API_VAR_REF(name) #define API_MSG_VAR_DECLARE(name) API_VAR_DECLARE(struct api_msg, name) #define API_MSG_VAR_ALLOC(name) API_VAR_ALLOC(struct api_msg, MEMP_API_MSG, name) @@ -178,8 +173,8 @@ netconn_delete(struct netconn *conn) return err; } -#if !LWIP_THREAD_SAFE - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("netconn_delete - free conn\n")); +#if !ESP_THREAD_SAFE + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("netconn_delete - free conn\n")); netconn_free(conn); #endif @@ -502,7 +497,7 @@ netconn_recv_data(struct netconn *conn, void **new_buf) #endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */ #if (LWIP_UDP || LWIP_RAW) { -#if LWIP_THREAD_SAFE +#if ESP_THREAD_SAFE if (buf == NULL){ API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0); return ERR_CLSD; @@ -710,17 +705,7 @@ netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size, } dontblock = netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK); -#ifdef LWIP_ESP8266 - -#ifdef FOR_XIAOMI - if (dontblock && bytes_written) { -#else - if (dontblock && !bytes_written) { -#endif - -#else if (dontblock && !bytes_written) { -#endif /* This implies netconn_write() cannot be used for non-blocking send, since it has no way to return the number of bytes written. */ return ERR_VAL; diff --git a/components/lwip/api/api_msg.c b/components/lwip/api/api_msg.c index e8e967ef40..d504bfb877 100755 --- a/components/lwip/api/api_msg.c +++ b/components/lwip/api/api_msg.c @@ -55,10 +55,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - /* netconns are polled once per second (e.g. continue write on memory error) */ #define NETCONN_TCP_POLL_INTERVAL 2 @@ -314,8 +310,8 @@ poll_tcp(void *arg, struct tcp_pcb *pcb) if (conn->flags & NETCONN_FLAG_CHECK_WRITESPACE) { /* If the queued byte- or pbuf-count drops below the configured low-water limit, let select mark this pcb as writable again. */ - if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) && - (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) { + if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT(conn->pcb.tcp)) && + (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT(conn->pcb.tcp))) { conn->flags &= ~NETCONN_FLAG_CHECK_WRITESPACE; API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); } @@ -348,8 +344,8 @@ sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len) /* If the queued byte- or pbuf-count drops below the configured low-water limit, let select mark this pcb as writable again. */ - if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) && - (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) { + if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT(conn->pcb.tcp) && + (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT(conn->pcb.tcp)))) { conn->flags &= ~NETCONN_FLAG_CHECK_WRITESPACE; API_EVENT(conn, NETCONN_EVT_SENDPLUS, len); } @@ -1216,16 +1212,7 @@ lwip_netconn_do_connect(void *m) if (msg->conn->state == NETCONN_CONNECT) { msg->err = ERR_ALREADY; } else if (msg->conn->state != NETCONN_NONE) { - -#ifdef LWIP_ESP8266 - if( msg->conn->pcb.tcp->state == ESTABLISHED ) msg->err = ERR_ISCONN; - else - msg->err = ERR_ALREADY; -#else - msg->err = ERR_ISCONN; -#endif - } else { setup_tcp(msg->conn); msg->err = tcp_connect(msg->conn->pcb.tcp, API_EXPR_REF(msg->msg.bc.ipaddr), @@ -1540,8 +1527,8 @@ err_mem: and let poll_tcp check writable space to mark the pcb writable again */ API_EVENT(conn, NETCONN_EVT_SENDMINUS, len); conn->flags |= NETCONN_FLAG_CHECK_WRITESPACE; - } else if ((tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT) || - (tcp_sndqueuelen(conn->pcb.tcp) >= TCP_SNDQUEUELOWAT)) { + } else if ((tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT(conn->pcb.tcp)) || + (tcp_sndqueuelen(conn->pcb.tcp) >= TCP_SNDQUEUELOWAT(conn->pcb.tcp))) { /* The queued byte- or pbuf-count exceeds the configured low-water limit, let select mark this pcb as non-writable. */ API_EVENT(conn, NETCONN_EVT_SENDMINUS, len); @@ -1646,14 +1633,7 @@ lwip_netconn_do_write(void *m) if (lwip_netconn_do_writemore(msg->conn, 0) != ERR_OK) { LWIP_ASSERT("state!", msg->conn->state == NETCONN_WRITE); UNLOCK_TCPIP_CORE(); - -#ifdef LWIP_ESP8266 -//#if 0 - sys_arch_sem_wait( LWIP_API_MSG_SND_SEM(msg), 0); -#else - sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0); -#endif - + sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0); LOCK_TCPIP_CORE(); LWIP_ASSERT("state!", msg->conn->state != NETCONN_WRITE); } diff --git a/components/lwip/api/lwip_debug.c b/components/lwip/api/lwip_debug.c index 1e5fed40d3..d73a23e1a3 100644 --- a/components/lwip/api/lwip_debug.c +++ b/components/lwip/api/lwip_debug.c @@ -48,6 +48,9 @@ static void dbg_lwip_tcp_pcb_one_show(struct tcp_pcb* pcb) printf("rttest=%d rtseq=%d sa=%d sv=%d\n", pcb->rttest, pcb->rtseq, pcb->sa, pcb->sv); printf("rto=%d nrtx=%d\n", pcb->rto, pcb->nrtx); printf("dupacks=%d lastack=%d\n", pcb->dupacks, pcb->lastack); +#if ESP_PER_SOC_TCP_WND + printf("per_soc_window=%d per_soc_snd_buf=%d\n", pcb->per_soc_tcp_wnd, pcb->per_soc_tcp_snd_buf); +#endif printf("cwnd=%d ssthreash=%d\n", pcb->cwnd, pcb->ssthresh); printf("snd_next=%d snd_wl1=%d snd_wl2=%d\n", pcb->snd_nxt, pcb->snd_wl1, pcb->snd_wl2); printf("snd_lbb=%d snd_wnd=%d snd_wnd_max=%d\n", pcb->snd_lbb, pcb->snd_wnd, pcb->snd_wnd_max); diff --git a/components/lwip/api/netbuf.c b/components/lwip/api/netbuf.c index 6c6dc69ccd..9ab76a4638 100755 --- a/components/lwip/api/netbuf.c +++ b/components/lwip/api/netbuf.c @@ -45,11 +45,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - - /** * Create (allocate) and initialize a new netbuf. * The netbuf doesn't yet contain a packet buffer! diff --git a/components/lwip/api/netdb.c b/components/lwip/api/netdb.c index 8fd3f41861..65510f55e9 100755 --- a/components/lwip/api/netdb.c +++ b/components/lwip/api/netdb.c @@ -47,11 +47,6 @@ #include #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - - /** helper struct for gethostbyname_r to access the char* buffer */ struct gethostbyname_r_helper { ip_addr_t *addr_list[2]; diff --git a/components/lwip/api/sockets.c b/components/lwip/api/sockets.c index 350847b57c..455d007ea7 100755 --- a/components/lwip/api/sockets.c +++ b/components/lwip/api/sockets.c @@ -61,11 +61,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - - /* If the netconn API is not required publicly, then we include the necessary files here to get the implementation */ #if !LWIP_NETCONN @@ -216,7 +211,7 @@ struct lwip_sock { /** last error that occurred on this socket (in fact, all our errnos fit into an u8_t) */ u8_t err; -#if LWIP_THREAD_SAFE +#if ESP_THREAD_SAFE /* lock is used to protect state/ref field, however this lock is not a perfect lock, e.g * taskA and taskB can access sock X, then taskA freed sock X, before taskB detect * this, taskC reuse sock X, then when taskB try to access sock X, problem may happen. @@ -239,7 +234,7 @@ struct lwip_sock { SELWAIT_T select_waiting; }; -#if LWIP_THREAD_SAFE +#if ESP_THREAD_SAFE #define LWIP_SOCK_OPEN 0 #define LWIP_SOCK_CLOSING 1 @@ -247,25 +242,25 @@ struct lwip_sock { #define LWIP_SOCK_LOCK(sock) \ do{\ - /*LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("l\n"));*/\ + /*LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("l\n"));*/\ sys_mutex_lock(&sock->lock);\ - /*LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("l ok\n"));*/\ + /*LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("l ok\n"));*/\ }while(0) #define LWIP_SOCK_UNLOCK(sock) \ do{\ sys_mutex_unlock(&sock->lock);\ - /*LWIP_DEBUGF(THREAD_SAFE_DEBUG1, ("unl\n"));*/\ + /*LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG1, ("unl\n"));*/\ }while(0) #define LWIP_FREE_SOCK(sock) \ do{\ if(sock->conn && NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP){\ - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("LWIP_FREE_SOCK:free tcp sock\n"));\ + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("LWIP_FREE_SOCK:free tcp sock\n"));\ free_socket(sock, 1);\ } else {\ - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("LWIP_FREE_SOCK:free non-tcp sock\n"));\ + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("LWIP_FREE_SOCK:free non-tcp sock\n"));\ free_socket(sock, 0);\ }\ }while(0) @@ -273,7 +268,7 @@ do{\ #define LWIP_SET_CLOSE_FLAG() \ do{\ LWIP_SOCK_LOCK(__sock);\ - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("mark sock closing\n"));\ + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("mark sock closing\n"));\ __sock->state = LWIP_SOCK_CLOSING;\ LWIP_SOCK_UNLOCK(__sock);\ }while(0) @@ -291,7 +286,7 @@ do{\ LWIP_SOCK_LOCK(__sock);\ __sock->ref ++;\ if (__sock->state != LWIP_SOCK_OPEN) {\ - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("LWIP_API_LOCK:soc is %d, return\n", __sock->state));\ + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("LWIP_API_LOCK:soc is %d, return\n", __sock->state));\ __sock->ref --;\ LWIP_SOCK_UNLOCK(__sock);\ return -1;\ @@ -306,12 +301,12 @@ do{\ __sock->ref --;\ if (__sock->state == LWIP_SOCK_CLOSING) {\ if (__sock->ref == 0){\ - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("LWIP_API_UNLOCK:ref 0, free __sock\n"));\ + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("LWIP_API_UNLOCK:ref 0, free __sock\n"));\ LWIP_FREE_SOCK(__sock);\ LWIP_SOCK_UNLOCK(__sock);\ return __ret;\ }\ - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("LWIP_API_UNLOCK: soc state is closing, return\n"));\ + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("LWIP_API_UNLOCK: soc state is closing, return\n"));\ LWIP_SOCK_UNLOCK(__sock);\ return __ret;\ }\ @@ -387,11 +382,9 @@ static void lwip_socket_unregister_membership(int s, const ip4_addr_t *if_addr, static void lwip_socket_drop_registered_memberships(int s); #endif /* LWIP_IGMP */ -#ifdef LWIP_ESP8266 - -/* Since esp_wifi_tx_is_stop/system_get_free_heap_size are not an public wifi API, so extern them here*/ -extern size_t system_get_free_heap_size(void); -extern bool esp_wifi_tx_is_stop(void); +#if ESP_LWIP +#include "esp_wifi_internal.h" +#include "esp_system.h" /* Please be notified that this flow control is just a workaround for fixing wifi Q full issue. * Under UDP/TCP pressure test, we found that the sockets may cause wifi tx queue full if the socket @@ -402,9 +395,9 @@ extern bool esp_wifi_tx_is_stop(void); */ static inline void esp32_tx_flow_ctrl(void) { - uint8_t _wait_delay = 0; + uint8_t _wait_delay = 1; - while ((system_get_free_heap_size() < HEAP_HIGHWAT) || esp_wifi_tx_is_stop()){ + while ((system_get_free_heap_size() < HEAP_HIGHWAT) || esp_wifi_internal_tx_is_stop()){ vTaskDelay(_wait_delay/portTICK_RATE_MS); if (_wait_delay < 64) _wait_delay *= 2; } @@ -416,7 +409,7 @@ static inline void esp32_tx_flow_ctrl(void) /** The global array of available sockets */ static struct lwip_sock sockets[NUM_SOCKETS]; -#if LWIP_THREAD_SAFE +#if ESP_THREAD_SAFE static bool sockets_init_flag = false; #endif /** The global list of tasks waiting for select */ @@ -427,13 +420,7 @@ static volatile int select_cb_ctr; /** Table to quickly map an lwIP error (err_t) to a socket error * by using -err as an index */ -#ifdef LWIP_ESP8266 -//TO_DO -//static const int err_to_errno_table[] ICACHE_RODATA_ATTR STORE_ATTR = { static const int err_to_errno_table[] = { -#else -static const int err_to_errno_table[] = { -#endif 0, /* ERR_OK 0 No error, everything OK. */ ENOMEM, /* ERR_MEM -1 Out of memory error. */ ENOBUFS, /* ERR_BUF -2 Buffer error. */ @@ -444,7 +431,7 @@ static const int err_to_errno_table[] = { EWOULDBLOCK, /* ERR_WOULDBLOCK -7 Operation would block. */ EADDRINUSE, /* ERR_USE -8 Address in use. */ -#ifdef LWIP_ESP8266 +#if ESP_LWIP EALREADY, /* ERR_ALREADY -9 Already connected. */ EISCONN, /* ERR_ISCONN -10 Conn already established */ ECONNABORTED, /* ERR_ABRT -11 Connection aborted. */ @@ -585,7 +572,7 @@ alloc_socket(struct netconn *newconn, int accepted) int i; SYS_ARCH_DECL_PROTECT(lev); -#if LWIP_THREAD_SAFE +#if ESP_THREAD_SAFE bool found = false; int oldest = -1; @@ -641,16 +628,16 @@ alloc_socket(struct netconn *newconn, int accepted) if (!sockets[oldest].lock){ /* one time init and never free */ if (sys_mutex_new(&sockets[oldest].lock) != ERR_OK){ - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("new sock lock fail\n")); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("new sock lock fail\n")); return -1; } } - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("alloc_socket: alloc %d ok\n", oldest)); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("alloc_socket: alloc %d ok\n", oldest)); return oldest + LWIP_SOCKET_OFFSET; } - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("alloc_socket: failed\n")); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("alloc_socket: failed\n")); #else @@ -695,12 +682,12 @@ free_socket(struct lwip_sock *sock, int is_tcp) void *lastdata; SYS_ARCH_DECL_PROTECT(lev); - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("free_sockset:free socket s=%p is_tcp=%d\n", sock, is_tcp)); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("free_sockset:free socket s=%p is_tcp=%d\n", sock, is_tcp)); lastdata = sock->lastdata; sock->lastdata = NULL; sock->lastoffset = 0; sock->err = 0; -#if LWIP_THREAD_SAFE +#if ESP_THREAD_SAFE if (sock->conn){ netconn_free(sock->conn); } @@ -718,10 +705,10 @@ free_socket(struct lwip_sock *sock, int is_tcp) if (lastdata != NULL) { if (is_tcp) { - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("free_sockset:free lastdata pbuf=%p\n", lastdata)); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("free_sockset:free lastdata pbuf=%p\n", lastdata)); pbuf_free((struct pbuf *)lastdata); } else { - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("free_sockset:free lastdata, netbuf=%p\n", lastdata)); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("free_sockset:free lastdata, netbuf=%p\n", lastdata)); netbuf_delete((struct netbuf *)lastdata); } } @@ -874,19 +861,19 @@ lwip_close(int s) int is_tcp = 0; err_t err; - LWIP_DEBUGF(SOCKETS_DEBUG|THREAD_SAFE_DEBUG, ("lwip_close: (%d)\n", s)); + LWIP_DEBUGF(SOCKETS_DEBUG|ESP_THREAD_SAFE_DEBUG, ("lwip_close: (%d)\n", s)); sock = get_socket(s); if (!sock) { - LWIP_DEBUGF(SOCKETS_DEBUG|THREAD_SAFE_DEBUG, ("lwip_close: sock is null, return -1\n")); + LWIP_DEBUGF(SOCKETS_DEBUG|ESP_THREAD_SAFE_DEBUG, ("lwip_close: sock is null, return -1\n")); return -1; } if (sock->conn != NULL) { is_tcp = NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP; - LWIP_DEBUGF(SOCKETS_DEBUG|THREAD_SAFE_DEBUG, ("lwip_close: is_tcp=%d\n", is_tcp)); + LWIP_DEBUGF(SOCKETS_DEBUG|ESP_THREAD_SAFE_DEBUG, ("lwip_close: is_tcp=%d\n", is_tcp)); } else { - LWIP_DEBUGF(SOCKETS_DEBUG|THREAD_SAFE_DEBUG, ("conn is null\n")); + LWIP_DEBUGF(SOCKETS_DEBUG|ESP_THREAD_SAFE_DEBUG, ("conn is null\n")); LWIP_ASSERT("lwip_close: sock->lastdata == NULL", sock->lastdata == NULL); } @@ -897,12 +884,12 @@ lwip_close(int s) err = netconn_delete(sock->conn); if (err != ERR_OK) { - LWIP_DEBUGF(SOCKETS_DEBUG|THREAD_SAFE_DEBUG, ("netconn_delete fail, ret=%d\n", err)); + LWIP_DEBUGF(SOCKETS_DEBUG|ESP_THREAD_SAFE_DEBUG, ("netconn_delete fail, ret=%d\n", err)); sock_set_errno(sock, err_to_errno(err)); return -1; } -#if !LWIP_THREAD_SAFE +#if !ESP_THREAD_SAFE free_socket(sock, is_tcp); #endif @@ -1132,22 +1119,13 @@ lwip_recvfrom(int s, void *mem, size_t len, int flags, ip_addr_debug_print(SOCKETS_DEBUG, fromaddr); LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", port, off)); -#ifdef LWIP_ESP8266 if (from && fromlen) -#else - -#if SOCKETS_DEBUG - if (from && fromlen) -#endif /* SOCKETS_DEBUG */ - -#endif { if (*fromlen > saddr.sa.sa_len) { *fromlen = saddr.sa.sa_len; } MEMCPY(from, &saddr, *fromlen); - -#ifdef LWIP_ESP8266 +#if ESP_LWIP } else { /*fix the code for setting the UDP PROTO's remote infomation by liuh at 2014.8.27*/ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_UDP){ @@ -1439,7 +1417,7 @@ lwip_sendto(int s, const void *data, size_t size, int flags, SOCKADDR_TO_IPADDR_PORT(to, &buf.addr, remote_port); } else { -#ifdef LWIP_ESP8266 +#if ESP_LWIP /*fix the code for getting the UDP proto's remote information by liuh at 2014.8.27*/ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_UDP){ if(NETCONNTYPE_ISIPV6(netconn_type(sock->conn))) { @@ -1455,7 +1433,7 @@ lwip_sendto(int s, const void *data, size_t size, int flags, #endif remote_port = 0; ip_addr_set_any(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), &buf.addr); -#ifdef LWIP_ESP8266 +#if ESP_LWIP } #endif @@ -1988,7 +1966,7 @@ again: int lwip_shutdown(int s, int how) { -#ifndef LWIP_ESP8266 +#if ! ESP_LWIP struct lwip_sock *sock; err_t err; @@ -2395,6 +2373,16 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt s, *(int *)optval)); break; #endif /* LWIP_TCP_KEEPALIVE */ + +#if ESP_PER_SOC_TCP_WND + case TCP_WINDOW: + *(int*)optval = (int)sock->conn->pcb.tcp->per_soc_tcp_wnd; + break; + case TCP_SNDBUF: + *(int*)optval = (int)sock->conn->pcb.tcp->per_soc_tcp_snd_buf; + break; +#endif + default: LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n", s, optname)); @@ -2792,6 +2780,16 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_ s, sock->conn->pcb.tcp->keep_cnt)); break; #endif /* LWIP_TCP_KEEPALIVE */ + +#if ESP_PER_SOC_TCP_WND + case TCP_WINDOW: + sock->conn->pcb.tcp->per_soc_tcp_wnd = ((u32_t)(*(const int*)optval)) * TCP_MSS; + break; + case TCP_SNDBUF: + sock->conn->pcb.tcp->per_soc_tcp_snd_buf = ((u32_t)(*(const int*)optval)) * TCP_MSS; + break; +#endif + default: LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n", s, optname)); @@ -3108,7 +3106,7 @@ static void lwip_socket_drop_registered_memberships(int s) } #endif /* LWIP_IGMP */ -#if LWIP_THREAD_SAFE +#if ESP_THREAD_SAFE int lwip_sendto_r(int s, const void *data, size_t size, int flags, diff --git a/components/lwip/api/tcpip.c b/components/lwip/api/tcpip.c index 9df3c38a1d..0ad60721e4 100755 --- a/components/lwip/api/tcpip.c +++ b/components/lwip/api/tcpip.c @@ -50,18 +50,13 @@ #include "lwip/pbuf.h" #include "netif/etharp.h" -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - - #define TCPIP_MSG_VAR_REF(name) API_VAR_REF(name) #define TCPIP_MSG_VAR_DECLARE(name) API_VAR_DECLARE(struct tcpip_msg, name) #define TCPIP_MSG_VAR_ALLOC(name) API_VAR_ALLOC(struct tcpip_msg, MEMP_TCPIP_MSG_API, name) #define TCPIP_MSG_VAR_FREE(name) API_VAR_FREE(MEMP_TCPIP_MSG_API, name) /* global variables */ -#ifdef PERF +#if ESP_PERF uint32_t g_rx_post_mbox_fail_cnt = 0; #endif static tcpip_init_done_fn tcpip_init_done; @@ -144,13 +139,11 @@ tcpip_thread(void *arg) case TCPIP_MSG_INPKT: LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\n", (void *)msg)); -#ifdef LWIP_ESP8266 -//#if 0 +#if ESP_LWIP if(msg->msg.inp.p != NULL && msg->msg.inp.netif != NULL) { #endif msg->msg.inp.input_fn(msg->msg.inp.p, msg->msg.inp.netif); -#ifdef LWIP_ESP8266 -//#if 0 +#if ESP_LWIP } #endif @@ -230,7 +223,7 @@ tcpip_inpkt(struct pbuf *p, struct netif *inp, netif_input_fn input_fn) msg->msg.inp.netif = inp; msg->msg.inp.input_fn = input_fn; if (sys_mbox_trypost(&mbox, msg) != ERR_OK) { -#ifdef PERF +#if ESP_PERF g_rx_post_mbox_fail_cnt ++; #endif memp_free(MEMP_TCPIP_MSG_INPKT, msg); @@ -503,7 +496,7 @@ tcpip_init(tcpip_init_done_fn initfunc, void *arg) #endif /* LWIP_TCPIP_CORE_LOCKING */ -#ifdef LWIP_ESP8266 +#if ESP_LWIP sys_thread_t xLwipTaskHandle = sys_thread_new(TCPIP_THREAD_NAME , tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO); @@ -548,8 +541,7 @@ pbuf_free_callback(struct pbuf *p) * @return ERR_OK if callback could be enqueued, an err_t if not */ -#ifdef LWIP_ESP8266 -//#if 0 +#if ESP_LWIP static void mem_free_local(void *arg) { mem_free(arg); diff --git a/components/lwip/apps/dhcpserver.c b/components/lwip/apps/dhcpserver.c index 4cdef4123d..22443e8cde 100644 --- a/components/lwip/apps/dhcpserver.c +++ b/components/lwip/apps/dhcpserver.c @@ -24,7 +24,7 @@ #include "apps/dhcpserver.h" -#ifdef LWIP_ESP8266 +#if ESP_DHCP #define BOOTP_BROADCAST 0x8000 @@ -71,10 +71,6 @@ #define DHCPS_STATE_IDLE 5 #define DHCPS_STATE_RELEASE 6 -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - //////////////////////////////////////////////////////////////////////////////////// static const u32_t magic_cookie = 0x63538263; diff --git a/components/lwip/core/dns.c b/components/lwip/core/dns.c index da8ac95b8d..8f0ac5cc81 100755 --- a/components/lwip/core/dns.c +++ b/components/lwip/core/dns.c @@ -85,10 +85,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - /** Random generator function to create random TXIDs and source ports for queries */ #ifndef DNS_RAND_TXID #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_XID) != 0) @@ -1091,7 +1087,7 @@ dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u8_t dns_err; /* This entry is now completed. */ -#ifndef LWIP_ESP8266 +#if ! ESP_DNS entry->state = DNS_STATE_DONE; #endif dns_err = hdr.flags2 & DNS_FLAG2_ERR_MASK; @@ -1105,7 +1101,7 @@ dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, if (((hdr.flags1 & DNS_FLAG1_RESPONSE) == 0) || (dns_err != 0) || (nquestions != 1)) { LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in flags\n", entry->name)); /* call callback to indicate error, clean up memory and return */ -#ifndef LWIP_ESP8266 +#if ! ESP_DNS goto responseerr; } #else diff --git a/components/lwip/core/init.c b/components/lwip/core/init.c index 2a410d0e46..8b2e92669a 100755 --- a/components/lwip/core/init.c +++ b/components/lwip/core/init.c @@ -61,7 +61,7 @@ #include "lwip/api.h" #include "netif/ppp/ppp_impl.h" -#ifndef PERF +#if ! ESP_PERF /* Compile-time sanity checks for configuration errors. * These can be done independently of LWIP_DEBUG, without penalty. */ @@ -135,21 +135,22 @@ //#endif #else /* LWIP_WND_SCALE */ -#ifndef LWIP_ESP8266 +#if ! ESP_PER_SOC_TCP_WND #if (LWIP_TCP && (TCP_WND > 0xffff)) #error "If you want to use TCP, TCP_WND must fit in an u16_t, so, you have to reduce it in your lwipopts.h (or enable window scaling)" #endif #endif #endif /* LWIP_WND_SCALE */ -#if (LWIP_TCP && (TCP_SND_QUEUELEN > 0xffff)) + +#if ! ESP_PER_SOC_TCP_WND +#if (LWIP_TCP && (TCP_SND_QUEUELEN(0) > 0xffff)) #error "If you want to use TCP, TCP_SND_QUEUELEN must fit in an u16_t, so, you have to reduce it in your lwipopts.h" #endif -#if (LWIP_TCP && (TCP_SND_QUEUELEN < 2)) +#if (LWIP_TCP && (TCP_SND_QUEUELEN(0) < 2)) #error "TCP_SND_QUEUELEN must be at least 2 for no-copy TCP writes to work" #endif -#ifndef LWIP_ESP8266 #if (LWIP_TCP && ((TCP_MAXRTX > 12) || (TCP_SYNMAXRTX > 12))) #error "If you want to use TCP, TCP_MAXRTX and TCP_SYNMAXRTX must less or equal to 12 (due to tcp_backoff table), so, you have to reduce them in your lwipopts.h" #endif @@ -285,30 +286,34 @@ /* TCP sanity checks */ #if !LWIP_DISABLE_TCP_SANITY_CHECKS +#if ! ESP_PER_SOC_TCP_WND #if LWIP_TCP -#if !MEMP_MEM_MALLOC && (MEMP_NUM_TCP_SEG < TCP_SND_QUEUELEN) +#if !MEMP_MEM_MALLOC && (MEMP_NUM_TCP_SEG < TCP_SND_QUEUELEN(0)) #error "lwip_sanity_check: WARNING: MEMP_NUM_TCP_SEG should be at least as big as TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." #endif -#if TCP_SND_BUF < (2 * TCP_MSS) + +#if TCP_SND_BUF(0) < (2 * TCP_MSS) #error "lwip_sanity_check: WARNING: TCP_SND_BUF must be at least as much as (2 * TCP_MSS) for things to work smoothly. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." #endif -#if TCP_SND_QUEUELEN < (2 * (TCP_SND_BUF / TCP_MSS)) - #error "lwip_sanity_check: WARNING: TCP_SND_QUEUELEN must be at least as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#if TCP_SND_QUEUELEN(0) < (2 * (TCP_SND_BUF(0) / TCP_MSS)) + #error "lwip_sanity_check: WARNING: TCP_SND_QUEUELEN must be at least as much as (2 * TCP_SND_BUF(0)/TCP_MSS) for things to work. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." #endif -#if TCP_SNDLOWAT >= TCP_SND_BUF +#if TCP_SNDLOWAT >= TCP_SND_BUF(0) #error "lwip_sanity_check: WARNING: TCP_SNDLOWAT must be less than TCP_SND_BUF. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." #endif #if TCP_SNDLOWAT >= (0xFFFF - (4 * TCP_MSS)) #error "lwip_sanity_check: WARNING: TCP_SNDLOWAT must at least be 4*MSS below u16_t overflow!" #endif -#if TCP_SNDQUEUELOWAT >= TCP_SND_QUEUELEN +#if TCP_SNDQUEUELOWAT >= TCP_SND_QUEUELEN(0) #error "lwip_sanity_check: WARNING: TCP_SNDQUEUELOWAT must be less than TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." #endif +#endif + #if !MEMP_MEM_MALLOC && (PBUF_POOL_BUFSIZE <= (PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)) #error "lwip_sanity_check: WARNING: PBUF_POOL_BUFSIZE does not provide enough space for protocol headers. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." #endif -#ifndef LWIP_ESP8266 +#if ! ESP_LWIP #if !MEMP_MEM_MALLOC && (TCP_WND > (PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - (PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)))) #error "lwip_sanity_check: WARNING: TCP_WND is larger than space provided by PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - protocol headers). If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." #endif @@ -328,13 +333,6 @@ void lwip_init(void) { -#ifdef LWIP_ESP8266 -// MEMP_NUM_TCP_PCB = 5; -// TCP_WND = (4 * TCP_MSS); -// TCP_MAXRTX = 12; -// TCP_SYNMAXRTX = 6; -#endif - /* Modules initialization */ stats_init(); #if !NO_SYS diff --git a/components/lwip/core/ipv4/autoip.c b/components/lwip/core/ipv4/autoip.c index 391e8eeaed..19b1928368 100755 --- a/components/lwip/core/ipv4/autoip.c +++ b/components/lwip/core/ipv4/autoip.c @@ -76,11 +76,6 @@ #include #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - - /* 169.254.0.0 */ #define AUTOIP_NET 0xA9FE0000 /* 169.254.1.0 */ diff --git a/components/lwip/core/ipv4/dhcp.c b/components/lwip/core/ipv4/dhcp.c index 1f3758fa91..33d13fb326 100755 --- a/components/lwip/core/ipv4/dhcp.c +++ b/components/lwip/core/ipv4/dhcp.c @@ -82,10 +82,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - /** DHCP_CREATE_RAND_XID: if this is set to 1, the xid is created using * LWIP_RAND() (this overrides DHCP_GLOBAL_XID) */ @@ -146,7 +142,7 @@ static u8_t dhcp_discover_select_options[] = { DHCP_OPTION_BROADCAST, DHCP_OPTION_DNS_SERVER -#ifdef LWIP_ESP8266 +#if ESP_DHCP /**add options for support more router by liuHan**/ , DHCP_OPTION_DOMAIN_NAME, DHCP_OPTION_NB_TINS, @@ -454,7 +450,7 @@ dhcp_fine_tmr(void) /* only act on DHCP configured interfaces */ if (netif->dhcp != NULL) { -//#ifdef LWIP_ESP8266 +//#if ESP_DHCP /*add DHCP retries processing by LiuHan*/ #if 0 if (DHCP_MAXRTX != 0) { @@ -997,7 +993,7 @@ dhcp_discover(struct netif *netif) dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif)); -#ifdef LWIP_ESP8266 +#if ESP_DHCP #if LWIP_NETIF_HOSTNAME dhcp_option_hostname(dhcp, netif); #endif /* LWIP_NETIF_HOSTNAME */ diff --git a/components/lwip/core/ipv4/icmp.c b/components/lwip/core/ipv4/icmp.c index c492ed75fe..9202bb650c 100755 --- a/components/lwip/core/ipv4/icmp.c +++ b/components/lwip/core/ipv4/icmp.c @@ -51,11 +51,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - - /** Small optimization: set to 0 if incoming PBUF_POOL pbuf always can be * used to modify and send a response packet (and to 1 if this is not the case, * e.g. when link header is stripped of when receiving) */ diff --git a/components/lwip/core/ipv4/igmp.c b/components/lwip/core/ipv4/igmp.c index d75fe15fd0..03f3ae384b 100755 --- a/components/lwip/core/ipv4/igmp.c +++ b/components/lwip/core/ipv4/igmp.c @@ -92,11 +92,6 @@ Steve Reynolds #include "string.h" -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - - /* * IGMP constants */ diff --git a/components/lwip/core/ipv4/ip4.c b/components/lwip/core/ipv4/ip4.c index 5f1e77a5e7..1d581d4d85 100755 --- a/components/lwip/core/ipv4/ip4.c +++ b/components/lwip/core/ipv4/ip4.c @@ -59,11 +59,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - - /** Set this to 0 in the rare case of wanting to call an extra function to * generate the IP checksum (in contrast to calculating it on-the-fly). */ #ifndef LWIP_INLINE_IP_CHKSUM @@ -150,7 +145,7 @@ ip4_route_src(const ip4_addr_t *dest, const ip4_addr_t *src) struct netif * ip4_route(const ip4_addr_t *dest) { -#ifdef LWIP_ESP8266 +#if ESP_LWIP struct netif *non_default_netif = NULL; #endif struct netif *netif; @@ -183,7 +178,7 @@ ip4_route(const ip4_addr_t *dest) } } -#ifdef LWIP_ESP8266 +#if ESP_LWIP if (non_default_netif && !ip4_addr_isbroadcast(dest, non_default_netif)){ return non_default_netif; } diff --git a/components/lwip/core/ipv4/ip4_addr.c b/components/lwip/core/ipv4/ip4_addr.c index 3053cf087e..0501b84e5f 100755 --- a/components/lwip/core/ipv4/ip4_addr.c +++ b/components/lwip/core/ipv4/ip4_addr.c @@ -45,17 +45,8 @@ /* used by IP_ADDR_ANY and IP_ADDR_BROADCAST in ip_addr.h */ -#ifdef LWIP_ESP8266 -//TO_DO -//const ip_addr_t ip_addr_any ICACHE_RODATA_ATTR STORE_ATTR = IPADDR4_INIT(IPADDR_ANY); -//const ip_addr_t ip_addr_broadcast ICACHE_RODATA_ATTR STORE_ATTR = IPADDR4_INIT(IPADDR_BROADCAST); const ip_addr_t ip_addr_any = IPADDR4_INIT(IPADDR_ANY); const ip_addr_t ip_addr_broadcast = IPADDR4_INIT(IPADDR_BROADCAST); -#else -const ip_addr_t ip_addr_any = IPADDR4_INIT(IPADDR_ANY); -const ip_addr_t ip_addr_broadcast = IPADDR4_INIT(IPADDR_BROADCAST); -#endif - /** * Determine if an address is a broadcast address on a network interface @@ -170,7 +161,7 @@ ip4addr_aton(const char *cp, ip4_addr_t *addr) u32_t parts[4]; u32_t *pp = parts; -#ifdef LWIP_ESP8266 +#if ESP_LWIP //#if 0 char ch; unsigned long cutoff; @@ -199,8 +190,7 @@ ip4addr_aton(const char *cp, ip4_addr_t *addr) } } -#ifdef LWIP_ESP8266 -//#if 0 +#if ESP_IP4_ATON cutoff =(unsigned long)0xffffffff / (unsigned long)base; cutlim =(unsigned long)0xffffffff % (unsigned long)base; for (;;) { diff --git a/components/lwip/core/ipv4/ip_frag.c b/components/lwip/core/ipv4/ip_frag.c index 1e6b053e6f..a647433506 100755 --- a/components/lwip/core/ipv4/ip_frag.c +++ b/components/lwip/core/ipv4/ip_frag.c @@ -51,11 +51,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - - #if IP_REASSEMBLY /** * The IP reassembly code currently has the following limitations: diff --git a/components/lwip/core/ipv6/icmp6.c b/components/lwip/core/ipv6/icmp6.c index 0a17da33e1..013983bde1 100755 --- a/components/lwip/core/ipv6/icmp6.c +++ b/components/lwip/core/ipv6/icmp6.c @@ -56,10 +56,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - #ifndef LWIP_ICMP6_DATASIZE #define LWIP_ICMP6_DATASIZE 8 #endif diff --git a/components/lwip/core/ipv6/ip6.c b/components/lwip/core/ipv6/ip6.c index 056d33355f..380bc290cd 100755 --- a/components/lwip/core/ipv6/ip6.c +++ b/components/lwip/core/ipv6/ip6.c @@ -59,10 +59,6 @@ #include "lwip/debug.h" #include "lwip/stats.h" -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - /** * Finds the appropriate network interface for a given IPv6 address. It tries to select * a netif following a sequence of heuristics: diff --git a/components/lwip/core/ipv6/ip6_frag.c b/components/lwip/core/ipv6/ip6_frag.c index 0792c2e1be..c9e13cd208 100755 --- a/components/lwip/core/ipv6/ip6_frag.c +++ b/components/lwip/core/ipv6/ip6_frag.c @@ -52,11 +52,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - - #if LWIP_IPV6 && LWIP_IPV6_REASS /* don't build if not configured for use in lwipopts.h */ diff --git a/components/lwip/core/ipv6/mld6.c b/components/lwip/core/ipv6/mld6.c index 6a2d55c549..489c5063a7 100755 --- a/components/lwip/core/ipv6/mld6.c +++ b/components/lwip/core/ipv6/mld6.c @@ -59,11 +59,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - - /* * MLD constants */ diff --git a/components/lwip/core/ipv6/nd6.c b/components/lwip/core/ipv6/nd6.c index 39e7bfed03..36f8f78c35 100755 --- a/components/lwip/core/ipv6/nd6.c +++ b/components/lwip/core/ipv6/nd6.c @@ -60,11 +60,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - - /* Router tables. */ struct nd6_neighbor_cache_entry neighbor_cache[LWIP_ND6_NUM_NEIGHBORS]; struct nd6_destination_cache_entry destination_cache[LWIP_ND6_NUM_DESTINATIONS]; diff --git a/components/lwip/core/mem.c b/components/lwip/core/mem.c index 42df6daeba..9ca9e3c4fc 100755 --- a/components/lwip/core/mem.c +++ b/components/lwip/core/mem.c @@ -65,10 +65,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - #if MEM_USE_POOLS #if MEMP_MEM_MALLOC diff --git a/components/lwip/core/memp.c b/components/lwip/core/memp.c index a5169abc81..7895533652 100755 --- a/components/lwip/core/memp.c +++ b/components/lwip/core/memp.c @@ -70,10 +70,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - #define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEMPOOL_DECLARE(name,num,size,desc) #include "lwip/priv/memp_std.h" diff --git a/components/lwip/core/netif.c b/components/lwip/core/netif.c index 33e030412a..5c308a957c 100755 --- a/components/lwip/core/netif.c +++ b/components/lwip/core/netif.c @@ -81,10 +81,6 @@ #define NETIF_LINK_CALLBACK(n) #endif /* LWIP_NETIF_LINK_CALLBACK */ -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - struct netif *netif_list; struct netif *netif_default; @@ -220,7 +216,7 @@ netif_add(struct netif *netif, /* netif not under DHCP control by default */ netif->dhcp = NULL; -#ifdef LWIP_ESP8266 +#if ESP_DHCP netif->dhcps_pcb = NULL; #endif @@ -233,8 +229,7 @@ netif_add(struct netif *netif, #endif /* LWIP_AUTOIP */ #if LWIP_IPV6_AUTOCONFIG -#ifdef LWIP_ESP8266 -//#if 0 +#if ESP_IPV6_AUTOCONFIG netif->ip6_autoconfig_enabled = 1; #else /* IPv6 address autoconfiguration not enabled by default */ @@ -973,7 +968,7 @@ netif_create_ip6_linklocal_address(struct netif *netif, u8_t from_mac_48bit) } } -#ifdef LWIP_ESP8266 +#if ESP_LWIP ip6_addr_set( ip_2_ip6(&netif->link_local_addr), ip_2_ip6(&netif->ip6_addr[0]) ); #endif @@ -1028,7 +1023,7 @@ netif_add_ip6_address(struct netif *netif, const ip6_addr_t *ip6addr, s8_t *chos } -#ifdef LWIP_ESP8266 +#if ESP_LWIP void netif_create_ip4_linklocal_address(struct netif * netif) { diff --git a/components/lwip/core/pbuf.c b/components/lwip/core/pbuf.c index e35f8a6b7f..29e24ef2b4 100755 --- a/components/lwip/core/pbuf.c +++ b/components/lwip/core/pbuf.c @@ -78,12 +78,8 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - -#ifdef LWIP_ESP8266 -#define EP_OFFSET 0 +#if ESP_LWIP +#include "esp_wifi_internal.h" #endif #define SIZEOF_STRUCT_PBUF LWIP_MEM_ALIGN_SIZE(sizeof(struct pbuf)) @@ -207,12 +203,7 @@ struct pbuf * pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type) { struct pbuf *p, *q, *r; - -#ifdef LWIP_ESP8266 - u16_t offset = 0; -#else - u16_t offset; -#endif + u16_t offset = 0; s32_t rem_len; /* remaining length */ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F")\n", length)); @@ -223,48 +214,16 @@ pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type) /* add room for transport (often TCP) layer header */ offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN; -#ifdef LWIP_ESP8266 //TO_DO - offset += EP_OFFSET; -#endif - break; case PBUF_IP: /* add room for IP layer header */ offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN; -#ifdef LWIP_ESP8266 //TO_DO - offset += EP_OFFSET; -#endif - break; case PBUF_LINK: /* add room for link layer header */ offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN; -#ifdef LWIP_ESP8266 //TO_DO - /* - * 1. LINK_HLEN 14Byte will be remove in WLAN layer - * 2. IEEE80211_HDR_MAX_LEN needs 40 bytes. - * 3. encryption needs exra 4 bytes ahead of actual data payload, and require - * DAddr and SAddr to be 4-byte aligned. - * 4. TRANSPORT and IP are all 20, 4 bytes aligned, nice... - * 5. LCC add 6 bytes more, We don't consider WAPI yet... - * 6. define LWIP_MEM_ALIGN to be 4 Byte aligned, pbuf struct is 16B, Only thing may be - * matter is ether_hdr is not 4B aligned. - * - * So, we need extra (40 + 4 - 14) = 30 and it's happen to be 4-Byte aligned - * - * 1. lwip - * | empty 30B | eth_hdr (14B) | payload ...| - * total: 44B ahead payload - * 2. net80211 - * | max 80211 hdr, 32B | ccmp/tkip iv (8B) | sec rsv(4B) | payload ...| - * total: 40B ahead sec_rsv and 44B ahead payload - * - */ - offset += EP_OFFSET; //remove LINK hdr in wlan -#endif - break; case PBUF_RAW_TX: /* add room for encapsulating link layer headers (e.g. 802.11) */ @@ -273,10 +232,6 @@ pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type) case PBUF_RAW: offset = 0; -#ifdef LWIP_ESP8266 //TO_DO - offset += EP_OFFSET; -#endif - break; default: LWIP_ASSERT("pbuf_alloc: bad pbuf layer", 0); @@ -395,9 +350,10 @@ pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type) /* set flags */ p->flags = 0; -#ifdef LWIP_ESP8266 +#if ESP_LWIP p->eb = NULL; #endif + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p)); return p; } @@ -763,9 +719,8 @@ pbuf_free(struct pbuf *p) /* is this a ROM or RAM referencing pbuf? */ } else if (type == PBUF_ROM || type == PBUF_REF) { -#ifdef LWIP_ESP8266 - extern void system_pp_recycle_rx_pkt(void*); - if (type == PBUF_REF && p->eb != NULL ) system_pp_recycle_rx_pkt(p->eb); +#if ESP_LWIP + if (type == PBUF_REF && p->eb != NULL ) esp_wifi_internal_free_rx_buffer(p->eb); #endif memp_free(MEMP_PBUF, p); diff --git a/components/lwip/core/raw.c b/components/lwip/core/raw.c index 72a58d381d..82ce4e3a73 100755 --- a/components/lwip/core/raw.c +++ b/components/lwip/core/raw.c @@ -54,10 +54,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - /** The list of RAW PCBs */ static struct raw_pcb *raw_pcbs; diff --git a/components/lwip/core/stats.c b/components/lwip/core/stats.c index 77ac3c675e..b47ab0b7fa 100755 --- a/components/lwip/core/stats.c +++ b/components/lwip/core/stats.c @@ -47,10 +47,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - struct stats_ lwip_stats; #if defined(LWIP_DEBUG) || LWIP_STATS_DISPLAY diff --git a/components/lwip/core/tcp.c b/components/lwip/core/tcp.c index e8fda52c8d..627df6d293 100755 --- a/components/lwip/core/tcp.c +++ b/components/lwip/core/tcp.c @@ -57,10 +57,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - #ifndef TCP_LOCAL_PORT_RANGE_START /* From http://www.iana.org/assignments/port-numbers: "The Dynamic and/or Private Ports are those from 49152 through 65535" */ @@ -77,14 +73,7 @@ static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; #define TCP_KEEP_INTVL(pcb) TCP_KEEPINTVL_DEFAULT #endif /* LWIP_TCP_KEEPALIVE */ -#ifdef LWIP_ESP8266 -//TO_DO -//char tcp_state_str[12]; -//const char tcp_state_str_rodata[][12] ICACHE_RODATA_ATTR STORE_ATTR = { const char * const tcp_state_str[] = { -#else -const char * const tcp_state_str[] = { -#endif "CLOSED", "LISTEN", "SYN_SENT", @@ -100,27 +89,14 @@ const char * const tcp_state_str[] = { /* last local TCP port */ -#ifdef LWIP_ESP8266 static s16_t tcp_port = TCP_LOCAL_PORT_RANGE_START; -#else -static u16_t tcp_port = TCP_LOCAL_PORT_RANGE_START; -#endif /* Incremented every coarse grained timer shot (typically every 500 ms). */ u32_t tcp_ticks; -#ifdef LWIP_ESP8266 -//TO_DO -//const u8_t tcp_backoff[13] ICACHE_RODATA_ATTR STORE_ATTR ={ 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7}; -//const u8_t tcp_persist_backoff[7] ICACHE_RODATA_ATTR STORE_ATTR = { 3, 6, 12, 24, 48, 96, 120 }; - -const u8_t tcp_backoff[13] = { 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7}; -const u8_t tcp_persist_backoff[7] = { 3, 6, 12, 24, 48, 96, 120 }; -#else const u8_t tcp_backoff[13] = { 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7}; /* Times per slowtmr hits */ const u8_t tcp_persist_backoff[7] = { 3, 6, 12, 24, 48, 96, 120 }; -#endif /* The TCP PCB lists. */ @@ -136,19 +112,9 @@ struct tcp_pcb *tcp_active_pcbs; struct tcp_pcb *tcp_tw_pcbs; /** An array with all (non-temporary) PCB lists, mainly used for smaller code size */ -#ifdef LWIP_ESP8266 -//TO_DO -//struct tcp_pcb ** const tcp_pcb_lists[] ICACHE_RODATA_ATTR STORE_ATTR = {&tcp_listen_pcbs.pcbs, &tcp_bound_pcbs, - // &tcp_active_pcbs, &tcp_tw_pcbs}; struct tcp_pcb ** const tcp_pcb_lists[] = {&tcp_listen_pcbs.pcbs, &tcp_bound_pcbs, &tcp_active_pcbs, &tcp_tw_pcbs}; -#else -struct tcp_pcb ** const tcp_pcb_lists[] = {&tcp_listen_pcbs.pcbs, &tcp_bound_pcbs, - &tcp_active_pcbs, &tcp_tw_pcbs}; -#endif - - u8_t tcp_active_pcbs_changed; /** Timer counter to handle calling slow-timer from tcp_tmr() */ @@ -638,7 +604,7 @@ u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb) { u32_t new_right_edge = pcb->rcv_nxt + pcb->rcv_wnd; - if (TCP_SEQ_GEQ(new_right_edge, pcb->rcv_ann_right_edge + LWIP_MIN((TCP_WND / 2), pcb->mss))) { + if (TCP_SEQ_GEQ(new_right_edge, pcb->rcv_ann_right_edge + LWIP_MIN((TCP_WND(pcb) / 2), pcb->mss))) { /* we can advertise more window */ pcb->rcv_ann_wnd = pcb->rcv_wnd; return new_right_edge - pcb->rcv_ann_right_edge; @@ -694,10 +660,10 @@ tcp_recved(struct tcp_pcb *pcb, u16_t len) wnd_inflation = tcp_update_rcv_ann_wnd(pcb); /* If the change in the right edge of window is significant (default - * watermark is TCP_WND/4), then send an explicit update now. + * watermark is TCP_WND(pcb)/4), then send an explicit update now. * Otherwise wait for a packet to be sent in the normal course of * events (or more window to be available later) */ - if (wnd_inflation >= TCP_WND_UPDATE_THRESHOLD) { + if (wnd_inflation >= TCP_WND_UPDATE_THRESHOLD(pcb)) { tcp_ack_now(pcb); tcp_output(pcb); } @@ -720,7 +686,7 @@ tcp_new_port(void) again: -#ifdef LWIP_ESP8266 +#if ESP_RANDOM_TCP_PORT tcp_port = system_get_time(); if (tcp_port < 0) tcp_port = LWIP_RAND() - tcp_port; @@ -827,9 +793,9 @@ tcp_connect(struct tcp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port, pcb->snd_lbb = iss - 1; /* Start with a window that does not need scaling. When window scaling is enabled and used, the window is enlarged when both sides agree on scaling. */ - pcb->rcv_wnd = pcb->rcv_ann_wnd = TCPWND_MIN16(TCP_WND); + pcb->rcv_wnd = pcb->rcv_ann_wnd = TCPWND_MIN16(TCP_WND(pcb)); pcb->rcv_ann_right_edge = pcb->rcv_nxt; - pcb->snd_wnd = TCP_WND; + pcb->snd_wnd = TCP_WND(pcb); /* As initial send MSS, we use TCP_MSS but limit it to 536. The send MSS is updated when an MSS option is received. */ pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS; @@ -837,7 +803,7 @@ tcp_connect(struct tcp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port, pcb->mss = tcp_eff_send_mss(pcb->mss, &pcb->local_ip, &pcb->remote_ip); #endif /* TCP_CALCULATE_EFF_SEND_MSS */ pcb->cwnd = 1; - pcb->ssthresh = TCP_WND; + pcb->ssthresh = TCP_WND(pcb); #if LWIP_CALLBACK_API pcb->connected = connected; #else /* LWIP_CALLBACK_API */ @@ -915,13 +881,7 @@ tcp_slowtmr_start: /* If snd_wnd is zero, use persist timer to send 1 byte probes * instead of using the standard retransmission mechanism. */ -#ifdef LWIP_ESP8266 -//NEED TO DO - //u8_t backoff_cnt = system_get_data_of_array_8(tcp_persist_backoff, pcb->persist_backoff-1); u8_t backoff_cnt = tcp_persist_backoff[pcb->persist_backoff-1]; -#else - u8_t backoff_cnt = tcp_persist_backoff[pcb->persist_backoff-1]; -#endif if (pcb->persist_cnt < backoff_cnt) { pcb->persist_cnt++; @@ -949,15 +909,7 @@ tcp_slowtmr_start: /* Double retransmission time-out unless we are trying to * connect to somebody (i.e., we are in SYN_SENT). */ if (pcb->state != SYN_SENT) { - -#ifdef LWIP_ESP8266 -//TO_DO -// pcb->rto = ((pcb->sa >> 3) + pcb->sv) << system_get_data_of_array_8(tcp_backoff, pcb->nrtx); pcb->rto = ((pcb->sa >> 3) + pcb->sv) << tcp_backoff[pcb->nrtx]; -#else - pcb->rto = ((pcb->sa >> 3) + pcb->sv) << tcp_backoff[pcb->nrtx]; -#endif - } /* Reset the retransmission timer. */ @@ -1436,7 +1388,7 @@ tcp_kill_timewait(void) } } -#ifdef LWIP_ESP8266 +#if ESP_LWIP /** * Kills the oldest connection that is in FIN_WAIT_2 state. * Called from tcp_alloc() if no more connections are available. @@ -1502,7 +1454,7 @@ tcp_alloc(u8_t prio) struct tcp_pcb *pcb; u32_t iss; -#ifdef LWIP_ESP8266 +#if ESP_LWIP /*Kills the oldest connection that is in TIME_WAIT state.*/ u8_t time_wait_num = 0; for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { @@ -1580,12 +1532,18 @@ tcp_alloc(u8_t prio) } if (pcb != NULL) { memset(pcb, 0, sizeof(struct tcp_pcb)); + +#if ESP_PER_SOC_TCP_WND + pcb->per_soc_tcp_wnd = TCP_WND_DEFAULT; + pcb->per_soc_tcp_snd_buf = TCP_SND_BUF_DEFAULT; +#endif + pcb->prio = prio; - pcb->snd_buf = TCP_SND_BUF; + pcb->snd_buf = TCP_SND_BUF_DEFAULT; pcb->snd_queuelen = 0; /* Start with a window that does not need scaling. When window scaling is enabled and used, the window is enlarged when both sides agree on scaling. */ - pcb->rcv_wnd = pcb->rcv_ann_wnd = TCPWND_MIN16(TCP_WND); + pcb->rcv_wnd = pcb->rcv_ann_wnd = TCPWND_MIN16(TCP_WND(pcb)); #if LWIP_WND_SCALE /* snd_scale and rcv_scale are zero unless both sides agree to use scaling */ pcb->snd_scale = 0; @@ -1608,7 +1566,6 @@ tcp_alloc(u8_t prio) pcb->snd_lbb = iss; pcb->tmr = tcp_ticks; pcb->last_timer = tcp_timer_ctr; - pcb->polltmr = 0; #if LWIP_CALLBACK_API @@ -1625,6 +1582,7 @@ tcp_alloc(u8_t prio) pcb->keep_cnt_sent = 0; } + return pcb; } @@ -2010,14 +1968,7 @@ void tcp_netif_ipv4_addr_changed(const ip4_addr_t* old_addr, const ip4_addr_t* n const char* tcp_debug_state_str(enum tcp_state s) { -#ifdef LWIP_ESP8266 -//TO_DO - //system_get_string_from_flash(tcp_state_str_rodata[s], tcp_state_str, 12); - //return tcp_state_str; return tcp_state_str[s]; -#else - return tcp_state_str[s]; -#endif } #if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG diff --git a/components/lwip/core/tcp_in.c b/components/lwip/core/tcp_in.c index 25d7403851..f3284233e7 100755 --- a/components/lwip/core/tcp_in.c +++ b/components/lwip/core/tcp_in.c @@ -60,11 +60,6 @@ #include "lwip/nd6.h" #endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */ -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - - /** Initial CWND calculation as defined RFC 2581 */ #define LWIP_TCP_CALC_INITIAL_CWND(mss) LWIP_MIN((4U * (mss)), LWIP_MAX((2U * (mss)), 4380U)); /** Initial slow start threshold value: we use the full window */ @@ -329,20 +324,6 @@ tcp_input(struct pbuf *p, struct netif *inp) if (pcb != NULL) { - -#ifdef LWIP_ESP8266 -//No Need Any more -/* - extern char RxNodeNum(void); - if(RxNodeNum() <= 2) - { -extern void pbuf_free_ooseq(void); - pbuf_free_ooseq(); - } -*/ -#endif - - /* The incoming segment belongs to a connection. */ #if TCP_INPUT_DEBUG tcp_debug_print_state(pcb->state); @@ -1745,9 +1726,9 @@ tcp_parseopt(struct tcp_pcb *pcb) pcb->rcv_scale = TCP_RCV_SCALE; pcb->flags |= TF_WND_SCALE; /* window scaling is enabled, we can use the full receive window */ - LWIP_ASSERT("window not at default value", pcb->rcv_wnd == TCPWND_MIN16(TCP_WND)); - LWIP_ASSERT("window not at default value", pcb->rcv_ann_wnd == TCPWND_MIN16(TCP_WND)); - pcb->rcv_wnd = pcb->rcv_ann_wnd = TCP_WND; + LWIP_ASSERT("window not at default value", pcb->rcv_wnd == TCPWND_MIN16(TCP_WND(pcb))); + LWIP_ASSERT("window not at default value", pcb->rcv_ann_wnd == TCPWND_MIN16(TCP_WND(pcb))); + pcb->rcv_wnd = pcb->rcv_ann_wnd = TCP_WND(pcb); } break; #endif diff --git a/components/lwip/core/tcp_out.c b/components/lwip/core/tcp_out.c index aac02e4ebe..35a8aa145d 100755 --- a/components/lwip/core/tcp_out.c +++ b/components/lwip/core/tcp_out.c @@ -59,10 +59,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - /* Define some copy-macros for checksum-on-copy so that the code looks nicer by preventing too many ifdef's. */ #if TCP_CHECKSUM_ON_COPY @@ -336,9 +332,9 @@ tcp_write_checks(struct tcp_pcb *pcb, u16_t len) /* If total number of pbufs on the unsent/unacked queues exceeds the * configured maximum, return an error */ /* check for configured max queuelen and possible overflow */ - if ((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { + if ((pcb->snd_queuelen >= TCP_SND_QUEUELEN(pcb)) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("tcp_write: too long queue %"U16_F" (max %"U16_F")\n", - pcb->snd_queuelen, TCP_SND_QUEUELEN)); + pcb->snd_queuelen, TCP_SND_QUEUELEN(pcb))); TCP_STATS_INC(tcp.memerr); pcb->flags |= TF_NAGLEMEMERR; return ERR_MEM; @@ -606,9 +602,9 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags) /* Now that there are more segments queued, we check again if the * length of the queue exceeds the configured maximum or * overflows. */ - if ((queuelen > TCP_SND_QUEUELEN) || (queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { + if ((queuelen > TCP_SND_QUEUELEN(pcb)) || (queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_write: queue too long %"U16_F" (%d)\n", - queuelen, (int)TCP_SND_QUEUELEN)); + queuelen, (int)TCP_SND_QUEUELEN(pcb))); pbuf_free(p); goto memerr; } @@ -766,10 +762,10 @@ tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags) (flags & (TCP_SYN | TCP_FIN)) != 0); /* check for configured max queuelen and possible overflow (FIN flag should always come through!) */ - if (((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) && + if (((pcb->snd_queuelen >= TCP_SND_QUEUELEN(pcb)) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) && ((flags & TCP_FIN) == 0)) { LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("tcp_enqueue_flags: too long queue %"U16_F" (max %"U16_F")\n", - pcb->snd_queuelen, TCP_SND_QUEUELEN)); + pcb->snd_queuelen, TCP_SND_QUEUELEN(pcb))); TCP_STATS_INC(tcp.memerr); pcb->flags |= TF_NAGLEMEMERR; return ERR_MEM; @@ -1301,6 +1297,7 @@ tcp_rst(u32_t seqno, u32_t ackno, struct pbuf *p; struct tcp_hdr *tcphdr; struct netif *netif; + p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM); if (p == NULL) { LWIP_DEBUGF(TCP_DEBUG, ("tcp_rst: could not allocate memory for pbuf\n")); @@ -1315,10 +1312,18 @@ tcp_rst(u32_t seqno, u32_t ackno, tcphdr->seqno = htonl(seqno); tcphdr->ackno = htonl(ackno); TCPH_HDRLEN_FLAGS_SET(tcphdr, TCP_HLEN/4, TCP_RST | TCP_ACK); +#if ESP_PER_SOC_TCP_WND #if LWIP_WND_SCALE - tcphdr->wnd = PP_HTONS(((TCP_WND >> TCP_RCV_SCALE) & 0xFFFF)); + tcphdr->wnd = PP_HTONS(((TCP_WND_DEFAULT >> TCP_RCV_SCALE) & 0xFFFF)); #else - tcphdr->wnd = PP_HTONS(TCP_WND); + tcphdr->wnd = PP_HTONS(TCP_WND_DEFAULT); +#endif +#else +#if LWIP_WND_SCALE + tcphdr->wnd = PP_HTONS(((TCP_WND_DEFAULT >> TCP_RCV_SCALE) & 0xFFFF)); +#else + tcphdr->wnd = PP_HTONS(TCP_WND_DEFAULT); +#endif #endif tcphdr->chksum = 0; tcphdr->urgp = 0; diff --git a/components/lwip/core/timers.c b/components/lwip/core/timers.c index 0a361474eb..ef47b2e187 100755 --- a/components/lwip/core/timers.c +++ b/components/lwip/core/timers.c @@ -62,11 +62,6 @@ #include "lwip/sys.h" #include "lwip/pbuf.h" -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - - /** The one and only timeout list */ static struct sys_timeo *next_timeout; #if NO_SYS @@ -162,7 +157,7 @@ dhcp_timer_coarse(void *arg) LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dhcp_coarse_tmr()\n")); dhcp_coarse_tmr(); -#ifdef LWIP_ESP8266 +#if ESP_DHCP extern void dhcps_coarse_tmr(void); dhcps_coarse_tmr(); #endif @@ -294,12 +289,6 @@ void sys_timeouts_init(void) #endif /* LWIP_ARP */ #if LWIP_DHCP -#ifdef LWIP_ESP8266 - // DHCP_MAXRTX = 0; -#endif - - - sys_timeout(DHCP_COARSE_TIMER_MSECS, dhcp_timer_coarse, NULL); sys_timeout(DHCP_FINE_TIMER_MSECS, dhcp_timer_fine, NULL); #endif /* LWIP_DHCP */ @@ -346,7 +335,7 @@ void sys_timeout_debug(u32_t msecs, sys_timeout_handler handler, void *arg, const char* handler_name) #else /* LWIP_DEBUG_TIMERNAMES */ -#ifdef LWIP_ESP8266 +#if ESP_LIGHT_SLEEP u32_t LwipTimOutLim = 0; // For light sleep. time out. limit is 3000ms #endif @@ -379,7 +368,7 @@ sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg) timeout->h = handler; timeout->arg = arg; -#ifdef LWIP_ESP8266 +#if ESP_LIGHT_SLEEP if(msecs < LwipTimOutLim) msecs = LwipTimOutLim; #endif diff --git a/components/lwip/core/udp.c b/components/lwip/core/udp.c index e44ab7e73d..37ae2c1796 100755 --- a/components/lwip/core/udp.c +++ b/components/lwip/core/udp.c @@ -67,11 +67,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - - #ifndef UDP_LOCAL_PORT_RANGE_START /* From http://www.iana.org/assignments/port-numbers: "The Dynamic and/or Private Ports are those from 49152 through 65535" */ diff --git a/components/lwip/include/lwip/lwip/api.h b/components/lwip/include/lwip/lwip/api.h index 985eb76d4a..5b6a21ecf3 100755 --- a/components/lwip/include/lwip/lwip/api.h +++ b/components/lwip/include/lwip/lwip/api.h @@ -185,10 +185,6 @@ struct netconn { /** sem that is used to synchronously execute functions in the core context */ sys_sem_t op_completed; -#ifdef LWIP_ESP8266 - sys_sem_t snd_op_completed; //only for snd semphore -#endif - #endif /** mbox where received packets are stored until they are fetched diff --git a/components/lwip/include/lwip/lwip/dhcp.h b/components/lwip/include/lwip/lwip/dhcp.h index 2d8926eca6..76ce1543ff 100755 --- a/components/lwip/include/lwip/lwip/dhcp.h +++ b/components/lwip/include/lwip/lwip/dhcp.h @@ -249,7 +249,7 @@ void dhcp_fine_tmr(void); #define DHCP_OPTION_NTP 42 #define DHCP_OPTION_END 255 -#ifdef LWIP_ESP8266 +#if ESP_LWIP /**add options for support more router by liuHan**/ #define DHCP_OPTION_DOMAIN_NAME 15 #define DHCP_OPTION_PRD 31 diff --git a/components/lwip/include/lwip/lwip/dns.h b/components/lwip/include/lwip/lwip/dns.h index 1ceed0d883..5ef12e56c2 100755 --- a/components/lwip/include/lwip/lwip/dns.h +++ b/components/lwip/include/lwip/lwip/dns.h @@ -36,7 +36,7 @@ #include "lwip/opt.h" -#ifdef LWIP_ESP8266 +#if ESP_DNS #include "lwip/err.h" #endif diff --git a/components/lwip/include/lwip/lwip/err.h b/components/lwip/include/lwip/lwip/err.h index 26fb91db9b..a766ee186d 100755 --- a/components/lwip/include/lwip/lwip/err.h +++ b/components/lwip/include/lwip/lwip/err.h @@ -60,7 +60,7 @@ typedef s8_t err_t; #define ERR_USE -8 /* Address in use. */ -#ifdef LWIP_ESP8266 +#if ESP_LWIP #define ERR_ALREADY -9 /* Already connected. */ #define ERR_ISCONN -10 /* Conn already established.*/ #define ERR_IS_FATAL(e) ((e) < ERR_ISCONN) diff --git a/components/lwip/include/lwip/lwip/mem.h b/components/lwip/include/lwip/lwip/mem.h index ca76f66322..a90d07256b 100755 --- a/components/lwip/include/lwip/lwip/mem.h +++ b/components/lwip/include/lwip/lwip/mem.h @@ -51,8 +51,6 @@ typedef size_t mem_size_t; * allow these defines to be overridden. */ -#ifndef MEMLEAK_DEBUG - #ifndef mem_free #define mem_free free #endif @@ -63,41 +61,6 @@ typedef size_t mem_size_t; #define mem_calloc calloc #endif -/* DYC_NEED_TO_DO_LATER -#ifndef mem_realloc -#define mem_realloc -#endif -#ifndef mem_zalloc -#define mem_zalloc -#endif -*/ - -#else -/* -#ifndef mem_free -#define mem_free(s) \ - do{\ - const char *file = mem_debug_file;\ - vPortFree(s, file, __LINE__);\ - }while(0) -#endif -#ifndef mem_malloc -#define mem_malloc(s) ({const char *file = mem_debug_file; pvPortMalloc(s, file, __LINE__);}) -#endif -#ifndef mem_calloc -#define mem_calloc(s) ({const char *file = mem_debug_file; pvPortCalloc(s, file, __LINE__);}) -#endif -#ifndef mem_realloc -#define mem_realloc(p, s) ({const char *file = mem_debug_file; pvPortRealloc(p, s, file, __LINE__);}) -#endif -#ifndef mem_zalloc -#define mem_zalloc(s) ({const char *file = mem_debug_file; pvPortZalloc(s, file, __LINE__);}) -#endif -*/ -#endif - - - /* Since there is no C library allocation function to shrink memory without moving it, define this to nothing. */ #ifndef mem_trim diff --git a/components/lwip/include/lwip/lwip/netif.h b/components/lwip/include/lwip/lwip/netif.h index 99066a5a1f..666f77eb96 100755 --- a/components/lwip/include/lwip/lwip/netif.h +++ b/components/lwip/include/lwip/lwip/netif.h @@ -177,7 +177,7 @@ typedef err_t (*netif_mld_mac_filter_fn)(struct netif *netif, #endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ -#ifdef LWIP_ESP8266 +#if ESP_DHCP /*add DHCP event processing by LiuHan*/ typedef void (*dhcp_event_fn)(void); #endif @@ -190,7 +190,7 @@ struct netif { /** pointer to next in linked list */ struct netif *next; -#ifdef LWIP_ESP8266 +#if ESP_LWIP //ip_addr_t is changed by marco IPV4, IPV6 ip_addr_t link_local_addr; #endif @@ -248,7 +248,7 @@ struct netif { /** the DHCP client state information for this netif */ struct dhcp *dhcp; -#ifdef LWIP_ESP8266 +#if ESP_LWIP struct udp_pcb *dhcps_pcb; dhcp_event_fn dhcp_event; #endif diff --git a/components/lwip/include/lwip/lwip/opt.h b/components/lwip/include/lwip/lwip/opt.h index 76fff88052..51d340e00b 100755 --- a/components/lwip/include/lwip/lwip/opt.h +++ b/components/lwip/include/lwip/lwip/opt.h @@ -986,7 +986,7 @@ * (2 * TCP_MSS) for things to work well */ #ifndef TCP_WND -#define TCP_WND (4 * TCP_MSS) +#define TCP_WND(pcb) (4 * TCP_MSS) #endif /** @@ -1040,7 +1040,7 @@ * To achieve good performance, this should be at least 2 * TCP_MSS. */ #ifndef TCP_SND_BUF -#define TCP_SND_BUF (2 * TCP_MSS) +#define TCP_SND_BUF(pcb) (2 * TCP_MSS) #endif /** @@ -1048,7 +1048,7 @@ * as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. */ #ifndef TCP_SND_QUEUELEN -#define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1))/(TCP_MSS)) +#define TCP_SND_QUEUELEN(pcb) ((4 * (TCP_SND_BUF((pcb))) + (TCP_MSS - 1))/(TCP_MSS)) #endif /** @@ -1057,7 +1057,7 @@ * TCP snd_buf for select to return writable (combined with TCP_SNDQUEUELOWAT). */ #ifndef TCP_SNDLOWAT -#define TCP_SNDLOWAT LWIP_MIN(LWIP_MAX(((TCP_SND_BUF)/2), (2 * TCP_MSS) + 1), (TCP_SND_BUF) - 1) +#define TCP_SNDLOWAT(pcb) LWIP_MIN(LWIP_MAX(((TCP_SND_BUF((pcb)))/2), (2 * TCP_MSS) + 1), (TCP_SND_BUF((pcb))) - 1) #endif /** @@ -1066,7 +1066,7 @@ * this number, select returns writable (combined with TCP_SNDLOWAT). */ #ifndef TCP_SNDQUEUELOWAT -#define TCP_SNDQUEUELOWAT LWIP_MAX(((TCP_SND_QUEUELEN)/2), 5) +#define TCP_SNDQUEUELOWAT(pcb) LWIP_MAX(((TCP_SND_QUEUELEN((pcb)))/2), 5) #endif /** @@ -1134,7 +1134,7 @@ * explicit window update */ #ifndef TCP_WND_UPDATE_THRESHOLD -#define TCP_WND_UPDATE_THRESHOLD LWIP_MIN((TCP_WND / 4), (TCP_MSS * 4)) +#define TCP_WND_UPDATE_THRESHOLD(pcb) LWIP_MIN((TCP_WND((pcb)) / 4), (TCP_MSS * 4)) #endif /** @@ -3008,8 +3008,8 @@ #define LWIP_PERF 0 #endif -#ifndef THREAD_SAFE_DEBUG -#define THREAD_SAFE_DEBUG 0 +#ifndef ESP_THREAD_SAFE_DEBUG +#define ESP_THREAD_SAFE_DEBUG 0 #endif #endif /* LWIP_HDR_OPT_H */ diff --git a/components/lwip/include/lwip/lwip/pbuf.h b/components/lwip/include/lwip/lwip/pbuf.h index aaf5e294af..1834c4e04c 100755 --- a/components/lwip/include/lwip/lwip/pbuf.h +++ b/components/lwip/include/lwip/lwip/pbuf.h @@ -137,7 +137,7 @@ struct pbuf { */ u16_t ref; -#ifdef LWIP_ESP8266 +#if ESP_LWIP void *eb; #endif }; diff --git a/components/lwip/include/lwip/lwip/priv/api_msg.h b/components/lwip/include/lwip/lwip/priv/api_msg.h index 329fa0de30..02d191a53c 100755 --- a/components/lwip/include/lwip/lwip/priv/api_msg.h +++ b/components/lwip/include/lwip/lwip/priv/api_msg.h @@ -187,7 +187,7 @@ struct dns_api_msg { #endif /* LWIP_DNS */ #if LWIP_NETCONN_SEM_PER_THREAD -#ifdef LWIP_ESP8266 +#if ESP_THREAD_SAFE #define LWIP_NETCONN_THREAD_SEM_GET() sys_thread_sem_get() #define LWIP_NETCONN_THREAD_SEM_ALLOC() sys_thread_sem_init() #define LWIP_NETCONN_THREAD_SEM_FREE() sys_thread_sem_deinit() @@ -222,10 +222,6 @@ struct dns_api_msg { #define TCPIP_APIMSG(m,f,e) do { (m)->function = f; (e) = tcpip_apimsg(m); } while(0) #define TCPIP_APIMSG_ACK(m) do { NETCONN_SET_SAFE_ERR((m)->conn, (m)->err); sys_sem_signal(LWIP_API_MSG_SEM(m)); } while(0) -#ifdef LWIP_ESP8266 -#define TCPIP_APIMSG_ACK_SND(m) do { NETCONN_SET_SAFE_ERR((m)->conn, (m)->err); sys_sem_signal(LWIP_API_MSG_SND_SEM(m)); } while(0) -#endif - #endif /* LWIP_TCPIP_CORE_LOCKING */ void lwip_netconn_do_newconn (void *m); diff --git a/components/lwip/include/lwip/lwip/priv/tcp_priv.h b/components/lwip/include/lwip/lwip/priv/tcp_priv.h index b5261b445c..0c498944b3 100755 --- a/components/lwip/include/lwip/lwip/priv/tcp_priv.h +++ b/components/lwip/include/lwip/lwip/priv/tcp_priv.h @@ -92,7 +92,7 @@ err_t tcp_process_refused_data(struct tcp_pcb *pcb); ((tpcb)->flags & (TF_NODELAY | TF_INFR)) || \ (((tpcb)->unsent != NULL) && (((tpcb)->unsent->next != NULL) || \ ((tpcb)->unsent->len >= (tpcb)->mss))) || \ - ((tcp_sndbuf(tpcb) == 0) || (tcp_sndqueuelen(tpcb) >= TCP_SND_QUEUELEN)) \ + ((tcp_sndbuf(tpcb) == 0) || (tcp_sndqueuelen(tpcb) >= TCP_SND_QUEUELEN(tpcb))) \ ) ? 1 : 0) #define tcp_output_nagle(tpcb) (tcp_do_output_nagle(tpcb) ? tcp_output(tpcb) : ERR_OK) diff --git a/components/lwip/include/lwip/lwip/sockets.h b/components/lwip/include/lwip/lwip/sockets.h index aafb3d5cf3..d9622ea03d 100755 --- a/components/lwip/include/lwip/lwip/sockets.h +++ b/components/lwip/include/lwip/lwip/sockets.h @@ -190,7 +190,6 @@ struct msghdr { #define SO_CONTIMEO 0x1009 /* Unimplemented: connect timeout */ #define SO_NO_CHECK 0x100a /* don't create UDP checksum */ - /* * Structure used for manipulating linger option. */ @@ -250,6 +249,11 @@ struct linger { #define TCP_KEEPIDLE 0x03 /* set pcb->keep_idle - Same as TCP_KEEPALIVE, but use seconds for get/setsockopt */ #define TCP_KEEPINTVL 0x04 /* set pcb->keep_intvl - Use seconds for get/setsockopt */ #define TCP_KEEPCNT 0x05 /* set pcb->keep_cnt - Use number of probes sent for get/setsockopt */ +#if ESP_PER_SOC_TCP_WND +#define TCP_WINDOW 0x06 /* set pcb->per_soc_tcp_wnd */ +#define TCP_SNDBUF 0x07 /* set pcb->per_soc_tcp_snd_buf */ +#endif + #endif /* LWIP_TCP */ #if LWIP_IPV6 @@ -505,7 +509,7 @@ int lwip_fcntl(int s, int cmd, int val); #if LWIP_COMPAT_SOCKETS #if LWIP_COMPAT_SOCKETS != 2 -#if LWIP_THREAD_SAFE +#if ESP_THREAD_SAFE int lwip_accept_r(int s, struct sockaddr *addr, socklen_t *addrlen); int lwip_bind_r(int s, const struct sockaddr *name, socklen_t namelen); @@ -590,7 +594,7 @@ int lwip_fcntl_r(int s, int cmd, int val); #define fcntl(s,cmd,val) lwip_fcntl(s,cmd,val) #define ioctl(s,cmd,argp) lwip_ioctl(s,cmd,argp) #endif /* LWIP_POSIX_SOCKETS_IO_NAMES */ -#endif /* LWIP_THREAD_SAFE */ +#endif /* ESP_THREAD_SAFE */ #endif /* LWIP_COMPAT_SOCKETS != 2 */ diff --git a/components/lwip/include/lwip/lwip/tcp.h b/components/lwip/include/lwip/lwip/tcp.h index d52040f99c..6b8c4b6c48 100755 --- a/components/lwip/include/lwip/lwip/tcp.h +++ b/components/lwip/include/lwip/lwip/tcp.h @@ -129,14 +129,14 @@ typedef err_t (*tcp_connected_fn)(void *arg, struct tcp_pcb *tpcb, err_t err); #define RCV_WND_SCALE(pcb, wnd) (((wnd) >> (pcb)->rcv_scale)) #define SND_WND_SCALE(pcb, wnd) (((wnd) << (pcb)->snd_scale)) #define TCPWND16(x) ((u16_t)LWIP_MIN((x), 0xFFFF)) -#define TCP_WND_MAX(pcb) ((tcpwnd_size_t)(((pcb)->flags & TF_WND_SCALE) ? TCP_WND : TCPWND16(TCP_WND))) +#define TCP_WND_MAX(pcb) ((tcpwnd_size_t)(((pcb)->flags & TF_WND_SCALE) ? TCP_WND(pcb) : TCPWND16(TCP_WND(pcb)))) typedef u32_t tcpwnd_size_t; typedef u16_t tcpflags_t; #else #define RCV_WND_SCALE(pcb, wnd) (wnd) #define SND_WND_SCALE(pcb, wnd) (wnd) #define TCPWND16(x) (x) -#define TCP_WND_MAX(pcb) TCP_WND +#define TCP_WND_MAX(pcb) TCP_WND(pcb) typedef u16_t tcpwnd_size_t; typedef u8_t tcpflags_t; #endif @@ -236,6 +236,11 @@ struct tcp_pcb { u8_t dupacks; u32_t lastack; /* Highest acknowledged seqno. */ +#if ESP_PER_SOC_TCP_WND + tcpwnd_size_t per_soc_tcp_wnd; /* per tcp socket tcp window size */ + tcpwnd_size_t per_soc_tcp_snd_buf; /* per tcp socket tcp send buffer size */ +#endif + /* congestion avoidance/control variables */ tcpwnd_size_t cwnd; tcpwnd_size_t ssthresh; @@ -402,6 +407,10 @@ const char* tcp_debug_state_str(enum tcp_state s); /* for compatibility with older implementation */ #define tcp_new_ip6() tcp_new_ip_type(IPADDR_TYPE_V6) +#if ESP_PER_SOC_TCP_WND +#define PER_SOC_WND(pcb) (pcb->per_soc_wnd) +#endif + #ifdef __cplusplus } #endif diff --git a/components/lwip/include/lwip/port/lwipopts.h b/components/lwip/include/lwip/port/lwipopts.h index 2c24b2be92..67a62b8227 100755 --- a/components/lwip/include/lwip/port/lwipopts.h +++ b/components/lwip/include/lwip/port/lwipopts.h @@ -37,7 +37,6 @@ #include "sdkconfig.h" /* Enable all Espressif-only options */ -#define LWIP_ESP8266 /* ----------------------------------------------- @@ -96,17 +95,37 @@ extern unsigned long os_random(void); ---------- Internal Memory Pool Sizes ---------- ------------------------------------------------ */ -/** - * MEMP_NUM_TCP_PCB: the number of simulatenously active TCP connections. - * (requires the LWIP_TCP option) - */ -#define MEMP_NUM_TCP_PCB 5 /** * MEMP_NUM_NETCONN: the number of struct netconns. * (only needed if you use the sequential API, like api_lib.c) */ -#define MEMP_NUM_NETCONN 10 +#define MEMP_NUM_NETCONN CONFIG_LWIP_MAX_SOCKETS + +/** + * MEMP_NUM_RAW_PCB: Number of raw connection PCBs + * (requires the LWIP_RAW option) + */ +#define MEMP_NUM_RAW_PCB 16 + +/** + * MEMP_NUM_TCP_PCB: the number of simulatenously active TCP connections. + * (requires the LWIP_TCP option) + */ +#define MEMP_NUM_TCP_PCB 16 + +/** + * MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP connections. + * (requires the LWIP_TCP option) + */ +#define MEMP_NUM_TCP_PCB_LISTEN 16 + +/** + * MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One + * per active UDP "connection". + * (requires the LWIP_UDP option) + */ +#define MEMP_NUM_UDP_PCB 16 /* -------------------------------- @@ -221,23 +240,6 @@ extern unsigned long os_random(void); ---------- TCP options ---------- --------------------------------- */ -/** - * TCP_WND: The size of a TCP window. This must be at least - * (2 * TCP_MSS) for things to work well - */ -#define PERF 1 -#ifdef PERF -extern unsigned char misc_prof_get_tcpw(void); -extern unsigned char misc_prof_get_tcp_snd_buf(void); -#define TCP_WND (misc_prof_get_tcpw()*TCP_MSS) -#define TCP_SND_BUF (misc_prof_get_tcp_snd_buf()*TCP_MSS) - -#else - -#define TCP_WND (4 * TCP_MSS) -#define TCP_SND_BUF (2 * TCP_MSS) - -#endif /** @@ -507,14 +509,43 @@ extern unsigned char misc_prof_get_tcp_snd_buf(void); */ #define TCPIP_DEBUG LWIP_DBG_OFF +/* Enable all Espressif-only options */ + +#define ESP_LWIP 1 +#define ESP_PER_SOC_TCP_WND 1 +#define ESP_THREAD_SAFE 1 +#define ESP_THREAD_SAFE_DEBUG LWIP_DBG_OFF +#define ESP_DHCP 1 +#define ESP_DNS 1 +#define ESP_IPV6_AUTOCONFIG 1 +#define ESP_PERF 0 +#define ESP_RANDOM_TCP_PORT 1 +#define ESP_IP4_ATON 1 +#define ESP_LIGHT_SLEEP 1 + + +#define TCP_WND_DEFAULT (4*TCP_MSS) +#define TCP_SND_BUF_DEFAULT (2*TCP_MSS) + +#if ESP_PER_SOC_TCP_WND +#define TCP_WND(pcb) (pcb->per_soc_tcp_wnd) +#define TCP_SND_BUF(pcb) (pcb->per_soc_tcp_snd_buf) +#else +#if ESP_PERF +extern unsigned char misc_prof_get_tcpw(void); +extern unsigned char misc_prof_get_tcp_snd_buf(void); +#define TCP_WND(pcb) (misc_prof_get_tcpw()*TCP_MSS) +#define TCP_SND_BUF(pcb) (misc_prof_get_tcp_snd_buf()*TCP_MSS) +#endif +#endif + /** * DHCP_DEBUG: Enable debugging in dhcp.c. */ #define DHCP_DEBUG LWIP_DBG_OFF -#define LWIP_DEBUG 0 +#define LWIP_DEBUG LWIP_DBG_OFF #define TCP_DEBUG LWIP_DBG_OFF -#define THREAD_SAFE_DEBUG LWIP_DBG_OFF -#define LWIP_THREAD_SAFE 1 +#define ESP_THREAD_SAFE_DEBUG LWIP_DBG_OFF #define CHECKSUM_CHECK_UDP 0 #define CHECKSUM_CHECK_IP 0 diff --git a/components/lwip/include/lwip/port/netif/wlanif.h b/components/lwip/include/lwip/port/netif/wlanif.h index 7eb303eab4..c6f7831b3d 100755 --- a/components/lwip/include/lwip/port/netif/wlanif.h +++ b/components/lwip/include/lwip/port/netif/wlanif.h @@ -8,6 +8,8 @@ #include "esp_wifi.h" +#include "esp_wifi_internal.h" + #include "lwip/err.h" #ifdef __cplusplus @@ -18,8 +20,6 @@ err_t wlanif_init(struct netif *netif); void wlanif_input(struct netif *netif, void *buffer, u16_t len, void* eb); -bool ieee80211_output(wifi_interface_t wifi_if, void *buffer, u16_t len); - wifi_interface_t wifi_get_interface(void *dev); void netif_reg_addr_change_cb(void* cb); diff --git a/components/lwip/netif/etharp.c b/components/lwip/netif/etharp.c index 5891c5cfd6..776e949f75 100755 --- a/components/lwip/netif/etharp.c +++ b/components/lwip/netif/etharp.c @@ -55,10 +55,6 @@ #include -#ifdef MEMLEAK_DEBUG -static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__; -#endif - #if LWIP_IPV4 && LWIP_ARP /* don't build if not configured for use in lwipopts.h */ /** Re-request a used ARP entry 1 minute before it would expire to prevent diff --git a/components/lwip/port/freertos/sys_arch.c b/components/lwip/port/freertos/sys_arch.c index 74a4a996a8..15ba3011d9 100755 --- a/components/lwip/port/freertos/sys_arch.c +++ b/components/lwip/port/freertos/sys_arch.c @@ -56,7 +56,7 @@ sys_mutex_new(sys_mutex_t *pxMutex) xReturn = ERR_OK; } - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sys_mutex_new: m=%p\n", *pxMutex)); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_mutex_new: m=%p\n", *pxMutex)); return xReturn; } @@ -89,7 +89,7 @@ sys_mutex_unlock(sys_mutex_t *pxMutex) void sys_mutex_free(sys_mutex_t *pxMutex) { - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sys_mutex_free: m=%p\n", *pxMutex)); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_mutex_free: m=%p\n", *pxMutex)); vQueueDelete(*pxMutex); } #endif @@ -192,20 +192,20 @@ sys_mbox_new(sys_mbox_t *mbox, int size) { *mbox = malloc(sizeof(struct sys_mbox_s)); if (*mbox == NULL){ - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("fail to new *mbox\n")); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("fail to new *mbox\n")); return ERR_MEM; } (*mbox)->os_mbox = xQueueCreate(size, sizeof(void *)); if ((*mbox)->os_mbox == NULL) { - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("fail to new *mbox->os_mbox\n")); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("fail to new *mbox->os_mbox\n")); free(*mbox); return ERR_MEM; } if (sys_mutex_new(&((*mbox)->lock)) != ERR_OK){ - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("fail to new *mbox->lock\n")); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("fail to new *mbox->lock\n")); vQueueDelete((*mbox)->os_mbox); free(*mbox); return ERR_MEM; @@ -213,7 +213,7 @@ sys_mbox_new(sys_mbox_t *mbox, int size) (*mbox)->alive = true; - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("new *mbox ok mbox=%p os_mbox=%p mbox_lock=%p\n", *mbox, (*mbox)->os_mbox, (*mbox)->lock)); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("new *mbox ok mbox=%p os_mbox=%p mbox_lock=%p\n", *mbox, (*mbox)->os_mbox, (*mbox)->lock)); return ERR_OK; } @@ -234,7 +234,7 @@ sys_mbox_trypost(sys_mbox_t *mbox, void *msg) if (xQueueSend((*mbox)->os_mbox, &msg, (portTickType)0) == pdPASS) { xReturn = ERR_OK; } else { - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("trypost mbox=%p fail\n", (*mbox)->os_mbox)); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("trypost mbox=%p fail\n", (*mbox)->os_mbox)); xReturn = ERR_MEM; } @@ -271,7 +271,7 @@ sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout) if (*mbox == NULL){ *msg = NULL; - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sys_arch_mbox_fetch: null mbox\n")); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_arch_mbox_fetch: null mbox\n")); return -1; } @@ -294,14 +294,14 @@ sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout) } else { // block forever for a message. while (1){ - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sys_arch_mbox_fetch: fetch mbox=%p os_mbox=%p lock=%p\n", mbox, (*mbox)->os_mbox, (*mbox)->lock)); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_arch_mbox_fetch: fetch mbox=%p os_mbox=%p lock=%p\n", mbox, (*mbox)->os_mbox, (*mbox)->lock)); if (pdTRUE == xQueueReceive((*mbox)->os_mbox, &(*msg), portMAX_DELAY)){ - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sys_arch_mbox_fetch:mbox rx msg=%p\n", (*msg))); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_arch_mbox_fetch:mbox rx msg=%p\n", (*msg))); break; } if ((*mbox)->alive == false){ - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sys_arch_mbox_fetch:mbox not alive\n")); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_arch_mbox_fetch:mbox not alive\n")); *msg = NULL; break; } @@ -356,24 +356,24 @@ sys_mbox_free(sys_mbox_t *mbox) uint16_t count = 0; bool post_null = true; - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sys_mbox_free: set alive false\n")); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_mbox_free: set alive false\n")); (*mbox)->alive = false; while ( count++ < MAX_POLL_CNT ){ //ESP32_WORKAROUND - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sys_mbox_free:try lock=%d\n", count)); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_mbox_free:try lock=%d\n", count)); if (!sys_mutex_trylock( &(*mbox)->lock )){ - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sys_mbox_free:get lock ok %d\n", count)); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_mbox_free:get lock ok %d\n", count)); sys_mutex_unlock( &(*mbox)->lock ); break; } if (post_null){ - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sys_mbox_free: post null to mbox\n")); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_mbox_free: post null to mbox\n")); if (sys_mbox_trypost( mbox, NULL) != ERR_OK){ - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sys_mbox_free: post null mbox fail\n")); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_mbox_free: post null mbox fail\n")); } else { post_null = false; - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sys_mbox_free: post null mbox ok\n")); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_mbox_free: post null mbox ok\n")); } } @@ -383,7 +383,7 @@ sys_mbox_free(sys_mbox_t *mbox) sys_delay_ms(PER_POLL_DELAY); } - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sys_mbox_free:free mbox\n")); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sys_mbox_free:free mbox\n")); if (uxQueueMessagesWaiting((*mbox)->os_mbox)) { xQueueReset((*mbox)->os_mbox); @@ -491,7 +491,7 @@ sys_sem_t* sys_thread_sem_get(void) if (!sem){ sem = sys_thread_sem_init(); } - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sem_get s=%p\n", sem)); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sem_get s=%p\n", sem)); return sem; } @@ -500,12 +500,12 @@ static void sys_thread_tls_free(int index, void* data) sys_sem_t *sem = (sys_sem_t*)(data); if (sem && *sem){ - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sem del, i=%d sem=%p\n", index, *sem)); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sem del, i=%d sem=%p\n", index, *sem)); vSemaphoreDelete(*sem); } if (sem){ - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sem pointer del, i=%d sem_p=%p\n", index, sem)); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sem pointer del, i=%d sem_p=%p\n", index, sem)); free(sem); } } @@ -526,7 +526,7 @@ sys_sem_t* sys_thread_sem_init(void) return 0; } - LWIP_DEBUGF(THREAD_SAFE_DEBUG, ("sem init sem_p=%p sem=%p cb=%p\n", sem, *sem, sys_thread_tls_free)); + LWIP_DEBUGF(ESP_THREAD_SAFE_DEBUG, ("sem init sem_p=%p sem=%p cb=%p\n", sem, *sem, sys_thread_tls_free)); vTaskSetThreadLocalStoragePointerAndDelCallback(xTaskGetCurrentTaskHandle(), SYS_TLS_INDEX, sem, (TlsDeleteCallbackFunction_t)sys_thread_tls_free); return sem; diff --git a/components/lwip/port/netif/wlanif.c b/components/lwip/port/netif/wlanif.c index 9832c41aff..548bb7f970 100755 --- a/components/lwip/port/netif/wlanif.c +++ b/components/lwip/port/netif/wlanif.c @@ -56,16 +56,8 @@ #define IFNAME0 'e' #define IFNAME1 'n' -#ifdef LWIP_ESP8266 -//TO_DO -//char *hostname; -//bool default_hostname = 1; - static char hostname[16]; -#else -static char hostname[16]; -#endif -#ifdef PERF +#if ESP_PERF uint32_t g_rx_alloc_pbuf_fail_cnt = 0; #endif @@ -95,7 +87,7 @@ low_level_init(struct netif *netif) /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */ netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP; -#ifdef LWIP_ESP8266 +#if ESP_LWIP #if LWIP_IGMP @@ -133,7 +125,7 @@ low_level_output(struct netif *netif, struct pbuf *p) return ERR_IF; } -#ifdef LWIP_ESP8266 +#if ESP_LWIP q = p; u16_t pbuf_x_len = 0; pbuf_x_len = q->len; @@ -150,12 +142,12 @@ low_level_output(struct netif *netif, struct pbuf *p) } } - ieee80211_output(wifi_if, q->payload, pbuf_x_len); + esp_wifi_internal_tx(wifi_if, q->payload, pbuf_x_len); return ERR_OK; #else for(q = p; q != NULL; q = q->next) { - ieee80211_output(wifi_if, q->payload, q->len); + esp_wifi_internal_tx(wifi_if, q->payload, q->len); } #endif @@ -172,7 +164,7 @@ low_level_output(struct netif *netif, struct pbuf *p) * @param netif the lwip network interface structure for this ethernetif */ void -#ifdef LWIP_ESP8266 +#if ESP_LWIP wlanif_input(struct netif *netif, void *buffer, u16_t len, void* eb) #else wlanif_input(struct netif *netif, void *buffer, uint16 len) @@ -180,17 +172,17 @@ wlanif_input(struct netif *netif, void *buffer, uint16 len) { struct pbuf *p; -#ifdef LWIP_ESP8266 +#if ESP_LWIP if(buffer== NULL) goto _exit; if(netif == NULL) goto _exit; #endif -#ifdef LWIP_ESP8266 +#if ESP_LWIP p = pbuf_alloc(PBUF_RAW, len, PBUF_REF); if (p == NULL){ -#ifdef PERF +#if ESP_PERF g_rx_alloc_pbuf_fail_cnt++; #endif return; @@ -236,7 +228,7 @@ wlanif_init(struct netif *netif) #if LWIP_NETIF_HOSTNAME /* Initialize interface hostname */ -#ifdef LWIP_ESP8266 +#if ESP_LWIP //TO_DO /* if ((struct netif *)wifi_get_netif(STATION_IF) == netif) { diff --git a/components/newlib/component.mk b/components/newlib/component.mk index 7c8c74debe..3731b5ef0e 100644 --- a/components/newlib/component.mk +++ b/components/newlib/component.mk @@ -1,8 +1,5 @@ -COMPONENT_ADD_LDFLAGS := $(abspath lib/libc.a) $(abspath lib/libm.a) +COMPONENT_ADD_LDFLAGS := $(abspath lib/libc.a) $(abspath lib/libm.a) -lnewlib - -define COMPONENT_BUILDRECIPE - #Nothing to do; this does not generate a library. -endef +COMPONENT_ADD_INCLUDEDIRS := include platform_include include $(IDF_PATH)/make/component_common.mk diff --git a/components/esp32/syscalls.c b/components/newlib/locks.c similarity index 54% rename from components/esp32/syscalls.c rename to components/newlib/locks.c index 052605ee3e..21b974a1f1 100644 --- a/components/esp32/syscalls.c +++ b/components/newlib/locks.c @@ -3,7 +3,7 @@ // 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 @@ -11,181 +11,17 @@ // 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 -#include -#include -#include -#include -#include + +#include #include +#include #include "esp_attr.h" -#include "rom/libc_stubs.h" -#include "rom/uart.h" #include "soc/cpu.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include "freertos/portmacro.h" #include "freertos/task.h" -void abort() { - do - { - __asm__ ("break 0,0"); - *((int*) 0) = 0; - } while(true); -} - -void* _malloc_r(struct _reent *r, size_t size) { - return pvPortMalloc(size); -} - -void _free_r(struct _reent *r, void* ptr) { - return vPortFree(ptr); -} - -void* _realloc_r(struct _reent *r, void* ptr, size_t size) { - void* new_chunk; - if (size == 0) { - if (ptr) { - vPortFree(ptr); - } - return NULL; - } - - new_chunk = pvPortMalloc(size); - if (new_chunk && ptr) { - memcpy(new_chunk, ptr, size); - vPortFree(ptr); - } - // realloc behaviour: don't free original chunk if alloc failed - return new_chunk; -} - -void* _calloc_r(struct _reent *r, size_t count, size_t size) { - void* result = pvPortMalloc(count * size); - if (result) - { - memset(result, 0, count * size); - } - return result; -} - -int _system_r(struct _reent *r, const char *str) { - abort(); - return 0; -} - -int _rename_r(struct _reent *r, const char *src, const char *dst) { - abort(); - return 0; -} - -clock_t _times_r(struct _reent *r, struct tms *ptms) { - abort(); - return 0; -} - -// TODO: read time from RTC -int _gettimeofday_r(struct _reent *r, struct timeval *tv, void *tz) { - abort(); - return 0; -} - -void _raise_r(struct _reent *r) { - abort(); -} - -int _unlink_r(struct _reent *r, const char *path) { - abort(); - return 0; -} - -int _link_r(struct _reent *r, const char* n1, const char* n2) { - abort(); - return 0; -} - -int _stat_r(struct _reent *r, const char * path, struct stat * st) { - return 0; -} - -int _fstat_r(struct _reent *r, int fd, struct stat * st) { - st->st_mode = S_IFCHR; - return 0; -} - -void* _sbrk_r(struct _reent *r, ptrdiff_t sz) { - abort(); - return 0; -} - -int _getpid_r(struct _reent *r) { - abort(); - return 0; -} - -int _kill_r(struct _reent *r, int pid, int sig) { - abort(); - return 0; -} - -void _exit_r(struct _reent *r, int e) { - abort(); -} - -int _close_r(struct _reent *r, int fd) { - return 0; -} - -int _open_r(struct _reent *r, const char * path, int flags, int mode) { - return 0; -} - -void _exit(int __status) { - abort(); -} - -ssize_t _write_r(struct _reent *r, int fd, const void * data, size_t size) { - const char *data_c = (const char *)data; - if (fd == STDOUT_FILENO) { - static _lock_t stdout_lock; /* lazily initialised */ - /* Even though newlib does stream locking on stdout, we need - a dedicated stdout UART lock... - - This is because each task has its own _reent structure with - unique FILEs for stdin/stdout/stderr, so these are - per-thread (lazily initialised by __sinit the first time a - stdio function is used, see findfp.c:235. - - It seems like overkill to allocate a FILE-per-task and lock - a thread-local stream, but I see no easy way to fix this - (pre-__sinit_, tasks have "fake" FILEs ie __sf_fake_stdout - which aren't fully valid.) - */ - _lock_acquire_recursive(&stdout_lock); - for (size_t i = 0; i < size; i++) { -#if CONFIG_NEWLIB_STDOUT_ADDCR - if (data_c[i]=='\n') { - uart_tx_one_char('\r'); - } -#endif - uart_tx_one_char(data_c[i]); - } - _lock_release_recursive(&stdout_lock); - } - return size; -} - -_off_t _lseek_r(struct _reent *r, int fd, _off_t size, int mode) { - return 0; -} - -// TODO: implement reading from UART -ssize_t _read_r(struct _reent *r, int fd, void * dst, size_t size) { - return 0; -} - /* Notes on our newlib lock implementation: * * - Use FreeRTOS mutex semaphores as locks. @@ -369,89 +205,3 @@ void IRAM_ATTR _lock_release(_lock_t *lock) { void IRAM_ATTR _lock_release_recursive(_lock_t *lock) { lock_release_generic(lock, queueQUEUE_TYPE_RECURSIVE_MUTEX); } - -// This function is not part on newlib API, it is defined in libc/stdio/local.h -// It is called as part of _reclaim_reent via a pointer in __cleanup member -// of struct _reent. -// This function doesn't call _fclose_r for _stdin, _stdout, _stderr members -// of struct reent. Not doing so causes a memory leak each time a task is -// terminated. We replace __cleanup member with _extra_cleanup_r (below) to work -// around this. -extern void _cleanup_r(struct _reent* r); - -void _extra_cleanup_r(struct _reent* r) -{ - _cleanup_r(r); - _fclose_r(r, r->_stdout); - _fclose_r(r, r->_stderr); - _fclose_r(r, r->_stdin); -} - -static struct _reent s_reent; - -/* - General ToDo that the Xtensa newlib support code did but we do not: Close every open fd a running task had when the task - is killed. Do we want that too? - JD -*/ - -extern int _printf_float(struct _reent *rptr, - void *pdata, - FILE * fp, - int (*pfunc) (struct _reent *, FILE *, _CONST char *, size_t len), - va_list * ap); - - -extern int _scanf_float(struct _reent *rptr, - void *pdata, - FILE *fp, - va_list *ap); - - -static struct syscall_stub_table s_stub_table = { - .__getreent = &__getreent, - ._malloc_r = &_malloc_r, - ._free_r = &_free_r, - ._realloc_r = &_realloc_r, - ._calloc_r = &_calloc_r, - ._abort = &abort, - ._system_r = &_system_r, - ._rename_r = &_rename_r, - ._times_r = &_times_r, - ._gettimeofday_r = &_gettimeofday_r, - ._raise_r = &_raise_r, - ._unlink_r = &_unlink_r, - ._link_r = &_link_r, - ._stat_r = &_stat_r, - ._fstat_r = &_fstat_r, - ._sbrk_r = &_sbrk_r, - ._getpid_r = &_getpid_r, - ._kill_r = &_kill_r, - ._exit_r = &_exit_r, - ._close_r = &_close_r, - ._open_r = &_open_r, - ._write_r = (int (*)(struct _reent *r, int, const void *, int)) &_write_r, - ._lseek_r = (int (*)(struct _reent *r, int, int, int)) &_lseek_r, - ._read_r = (int (*)(struct _reent *r, int, void *, int)) &_read_r, - ._lock_init = &_lock_init, - ._lock_init_recursive = &_lock_init_recursive, - ._lock_close = &_lock_close, - ._lock_close_recursive = &_lock_close, - ._lock_acquire = &_lock_acquire, - ._lock_acquire_recursive = &_lock_acquire_recursive, - ._lock_try_acquire = &_lock_try_acquire, - ._lock_try_acquire_recursive = &_lock_try_acquire_recursive, - ._lock_release = &_lock_release, - ._lock_release_recursive = &_lock_release_recursive, - ._printf_float = &_printf_float, - ._scanf_float = &_scanf_float, -}; - -void ets_setup_syscalls() { - syscall_table_ptr_pro = &s_stub_table; - syscall_table_ptr_app = &s_stub_table; - _GLOBAL_REENT = &s_reent; - environ = malloc(sizeof(char*)); - environ[0] = NULL; -} - - diff --git a/components/newlib/platform_include/esp_newlib.h b/components/newlib/platform_include/esp_newlib.h new file mode 100644 index 0000000000..468f2ae34f --- /dev/null +++ b/components/newlib/platform_include/esp_newlib.h @@ -0,0 +1,37 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef __ESP_NEWLIB_H__ +#define __ESP_NEWLIB_H__ + +#include + +/** + * Replacement for newlib's _REENT_INIT_PTR and __sinit. + * + * Called from startup code and FreeRTOS, not intended to be called from + * application code. + */ +void esp_reent_init(struct _reent* r); + +/** + * Function which sets up syscall table used by newlib functions in ROM. + * + * Called from the startup code, not intended to be called from application + * code. + */ +void esp_setup_syscalls(); + + +#endif //__ESP_NEWLIB_H__ diff --git a/components/newlib/reent_init.c b/components/newlib/reent_init.c new file mode 100644 index 0000000000..5c29e898c6 --- /dev/null +++ b/components/newlib/reent_init.c @@ -0,0 +1,45 @@ +// 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 +#include "esp_attr.h" + +/* This function is not part on newlib API, it is defined in libc/stdio/local.h + * There is no nice way to get __cleanup member populated while avoiding __sinit, + * so extern declaration is used here. + */ +extern void _cleanup_r(struct _reent* r); + +/** + * This is the replacement for newlib's _REENT_INIT_PTR and __sinit. + * The problem with __sinit is that it allocates three FILE structures + * (stdin, stdout, stderr). Having individual standard streams for each task + * is a bit too much on a small embedded system. So we point streams + * to the streams of the global struct _reent, which are initialized in + * startup code. + */ +void IRAM_ATTR esp_reent_init(struct _reent* r) +{ + memset(r, 0, sizeof(*r)); + r->_stdout = _GLOBAL_REENT->_stdout; + r->_stderr = _GLOBAL_REENT->_stderr; + r->_stdin = _GLOBAL_REENT->_stdin; + r->__cleanup = &_cleanup_r; + r->__sdidinit = 1; + r->__sglue._next = NULL; + r->__sglue._niobs = 0; + r->__sglue._iobs = NULL; + r->_current_locale = "C"; +} diff --git a/components/newlib/syscall_table.c b/components/newlib/syscall_table.c new file mode 100644 index 0000000000..b6414af554 --- /dev/null +++ b/components/newlib/syscall_table.c @@ -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. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rom/libc_stubs.h" +#include "esp_vfs.h" + +static struct _reent s_reent; + +extern int _printf_float(struct _reent *rptr, + void *pdata, + FILE * fp, + int (*pfunc) (struct _reent *, FILE *, _CONST char *, size_t len), + va_list * ap); + + +extern int _scanf_float(struct _reent *rptr, + void *pdata, + FILE *fp, + va_list *ap); + + +static struct syscall_stub_table s_stub_table = { + .__getreent = &__getreent, + ._malloc_r = &_malloc_r, + ._free_r = &_free_r, + ._realloc_r = &_realloc_r, + ._calloc_r = &_calloc_r, + ._abort = &abort, + ._system_r = &_system_r, + ._rename_r = &esp_vfs_rename, + ._times_r = &_times_r, + ._gettimeofday_r = &_gettimeofday_r, + ._raise_r = (void (*)(struct _reent *r)) &_raise_r, + ._unlink_r = &esp_vfs_unlink, + ._link_r = &esp_vfs_link, + ._stat_r = &esp_vfs_stat, + ._fstat_r = &esp_vfs_fstat, + ._sbrk_r = &_sbrk_r, + ._getpid_r = &_getpid_r, + ._kill_r = &_kill_r, + ._exit_r = NULL, // never called in ROM + ._close_r = &esp_vfs_close, + ._open_r = &esp_vfs_open, + ._write_r = (int (*)(struct _reent *r, int, const void *, int)) &esp_vfs_write, + ._lseek_r = (int (*)(struct _reent *r, int, int, int)) &esp_vfs_lseek, + ._read_r = (int (*)(struct _reent *r, int, void *, int)) &esp_vfs_read, + ._lock_init = &_lock_init, + ._lock_init_recursive = &_lock_init_recursive, + ._lock_close = &_lock_close, + ._lock_close_recursive = &_lock_close, + ._lock_acquire = &_lock_acquire, + ._lock_acquire_recursive = &_lock_acquire_recursive, + ._lock_try_acquire = &_lock_try_acquire, + ._lock_try_acquire_recursive = &_lock_try_acquire_recursive, + ._lock_release = &_lock_release, + ._lock_release_recursive = &_lock_release_recursive, + ._printf_float = &_printf_float, + ._scanf_float = &_scanf_float, +}; + +void esp_setup_syscalls() +{ + syscall_table_ptr_pro = &s_stub_table; + syscall_table_ptr_app = &s_stub_table; + _GLOBAL_REENT = &s_reent; + environ = malloc(sizeof(char*)); + environ[0] = NULL; +} + + diff --git a/components/newlib/syscalls.c b/components/newlib/syscalls.c new file mode 100644 index 0000000000..3b2fbf62ca --- /dev/null +++ b/components/newlib/syscalls.c @@ -0,0 +1,105 @@ +// 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 +#include +#include +#include +#include +#include +#include "esp_attr.h" +#include "freertos/FreeRTOS.h" + +void IRAM_ATTR abort() +{ + do + { + __asm__ ("break 0,0"); + *((int*) 0) = 0; + } while(true); +} + +void* IRAM_ATTR _malloc_r(struct _reent *r, size_t size) +{ + return pvPortMalloc(size); +} + +void IRAM_ATTR _free_r(struct _reent *r, void* ptr) +{ + vPortFree(ptr); +} + +void* IRAM_ATTR _realloc_r(struct _reent *r, void* ptr, size_t size) +{ + void* new_chunk; + if (size == 0) { + if (ptr) { + vPortFree(ptr); + } + return NULL; + } + + new_chunk = pvPortMalloc(size); + if (new_chunk && ptr) { + memcpy(new_chunk, ptr, size); + vPortFree(ptr); + } + // realloc behaviour: don't free original chunk if alloc failed + return new_chunk; +} + +void* IRAM_ATTR _calloc_r(struct _reent *r, size_t count, size_t size) +{ + void* result = pvPortMalloc(count * size); + if (result) + { + memset(result, 0, count * size); + } + return result; +} + +int _system_r(struct _reent *r, const char *str) +{ + __errno_r(r) = ENOSYS; + return -1; +} + +void _raise_r(struct _reent *r) +{ + abort(); +} + +void* _sbrk_r(struct _reent *r, ptrdiff_t sz) +{ + abort(); +} + +int _getpid_r(struct _reent *r) +{ + __errno_r(r) = ENOSYS; + return -1; +} + +int _kill_r(struct _reent *r, int pid, int sig) +{ + __errno_r(r) = ENOSYS; + return -1; +} + +void _exit(int __status) +{ + abort(); +} + diff --git a/components/newlib/time.c b/components/newlib/time.c new file mode 100644 index 0000000000..021b295451 --- /dev/null +++ b/components/newlib/time.c @@ -0,0 +1,35 @@ +// 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 +#include +#include +#include +#include +#include "esp_attr.h" + + +clock_t _times_r(struct _reent *r, struct tms *ptms) +{ + __errno_r(r) = ENOSYS; + return (clock_t) -1; +} + +// TODO: read time from RTC +int _gettimeofday_r(struct _reent *r, struct timeval *tv, void *tz) +{ + __errno_r(r) = ENOSYS; + return -1; +} diff --git a/components/nvs_flash/src/nvs_api.cpp b/components/nvs_flash/src/nvs_api.cpp index 0a56925c0c..c1a910260e 100644 --- a/components/nvs_flash/src/nvs_api.cpp +++ b/components/nvs_flash/src/nvs_api.cpp @@ -116,6 +116,7 @@ extern "C" void nvs_close(nvs_handle handle) return; } s_nvs_handles.erase(it); + delete static_cast(it); } extern "C" esp_err_t nvs_erase_key(nvs_handle handle, const char* key) diff --git a/components/nvs_flash/src/nvs_item_hash_list.cpp b/components/nvs_flash/src/nvs_item_hash_list.cpp index 7fa019dffe..cf48477d61 100644 --- a/components/nvs_flash/src/nvs_item_hash_list.cpp +++ b/components/nvs_flash/src/nvs_item_hash_list.cpp @@ -17,7 +17,11 @@ namespace nvs { -HashList::~HashList() +HashList::HashList() +{ +} + +void HashList::clear() { for (auto it = mBlockList.begin(); it != mBlockList.end();) { auto tmp = it; @@ -26,6 +30,11 @@ HashList::~HashList() delete static_cast(tmp); } } + +HashList::~HashList() +{ + clear(); +} HashList::HashListBlock::HashListBlock() { diff --git a/components/nvs_flash/src/nvs_item_hash_list.hpp b/components/nvs_flash/src/nvs_item_hash_list.hpp index b40a53d615..3f8dcc850a 100644 --- a/components/nvs_flash/src/nvs_item_hash_list.hpp +++ b/components/nvs_flash/src/nvs_item_hash_list.hpp @@ -25,11 +25,18 @@ namespace nvs class HashList { public: + HashList(); ~HashList(); + void insert(const Item& item, size_t index); void erase(const size_t index); size_t find(size_t start, const Item& item); - + void clear(); + +private: + HashList(const HashList& other); + const HashList& operator= (const HashList& rhs); + protected: struct HashListNode { @@ -57,7 +64,6 @@ protected: HashListNode mNodes[ENTRY_COUNT]; }; - typedef intrusive_list TBlockList; TBlockList mBlockList; }; // class HashList diff --git a/components/nvs_flash/src/nvs_page.cpp b/components/nvs_flash/src/nvs_page.cpp index f4fc5430cd..fae1f6f1b7 100644 --- a/components/nvs_flash/src/nvs_page.cpp +++ b/components/nvs_flash/src/nvs_page.cpp @@ -37,7 +37,7 @@ esp_err_t Page::load(uint32_t sectorNumber) mErasedEntryCount = 0; Header header; - auto rc = spi_flash_read(mBaseAddress, reinterpret_cast(&header), sizeof(header)); + auto rc = spi_flash_read(mBaseAddress, &header, sizeof(header)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -86,7 +86,7 @@ esp_err_t Page::load(uint32_t sectorNumber) esp_err_t Page::writeEntry(const Item& item) { - auto rc = spi_flash_write(getEntryAddress(mNextFreeEntry), reinterpret_cast(&item), sizeof(item)); + auto rc = spi_flash_write(getEntryAddress(mNextFreeEntry), &item, sizeof(item)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -114,7 +114,7 @@ esp_err_t Page::writeEntryData(const uint8_t* data, size_t size) assert(mFirstUsedEntry != INVALID_ENTRY); const uint16_t count = size / ENTRY_SIZE; - auto rc = spi_flash_write(getEntryAddress(mNextFreeEntry), reinterpret_cast(data), static_cast(size)); + auto rc = spi_flash_write(getEntryAddress(mNextFreeEntry), data, size); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -397,7 +397,7 @@ esp_err_t Page::mLoadEntryTable() mState == PageState::FULL || mState == PageState::FREEING) { auto rc = spi_flash_read(mBaseAddress + ENTRY_TABLE_OFFSET, mEntryTable.data(), - static_cast(mEntryTable.byteSize())); + mEntryTable.byteSize()); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -559,7 +559,7 @@ esp_err_t Page::initialize() header.mSeqNumber = mSeqNumber; header.mCrc32 = header.calculateCrc32(); - auto rc = spi_flash_write(mBaseAddress, reinterpret_cast(&header), sizeof(header)); + auto rc = spi_flash_write(mBaseAddress, &header, sizeof(header)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -577,7 +577,8 @@ esp_err_t Page::alterEntryState(size_t index, EntryState state) mEntryTable.set(index, state); size_t wordToWrite = mEntryTable.getWordIndex(index); uint32_t word = mEntryTable.data()[wordToWrite]; - auto rc = spi_flash_write(mBaseAddress + ENTRY_TABLE_OFFSET + static_cast(wordToWrite) * 4, &word, 4); + auto rc = spi_flash_write(mBaseAddress + ENTRY_TABLE_OFFSET + static_cast(wordToWrite) * 4, + &word, sizeof(word)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -600,7 +601,8 @@ esp_err_t Page::alterEntryRangeState(size_t begin, size_t end, EntryState state) } if (nextWordIndex != wordIndex) { uint32_t word = mEntryTable.data()[wordIndex]; - auto rc = spi_flash_write(mBaseAddress + ENTRY_TABLE_OFFSET + static_cast(wordIndex) * 4, &word, 4); + auto rc = spi_flash_write(mBaseAddress + ENTRY_TABLE_OFFSET + static_cast(wordIndex) * 4, + &word, 4); if (rc != ESP_OK) { return rc; } @@ -612,7 +614,8 @@ esp_err_t Page::alterEntryRangeState(size_t begin, size_t end, EntryState state) esp_err_t Page::alterPageState(PageState state) { - auto rc = spi_flash_write(mBaseAddress, reinterpret_cast(&state), sizeof(state)); + uint32_t state_val = static_cast(state); + auto rc = spi_flash_write(mBaseAddress, &state_val, sizeof(state)); if (rc != ESP_OK) { mState = PageState::INVALID; return rc; @@ -623,7 +626,7 @@ esp_err_t Page::alterPageState(PageState state) esp_err_t Page::readEntry(size_t index, Item& dst) const { - auto rc = spi_flash_read(getEntryAddress(index), reinterpret_cast(&dst), sizeof(dst)); + auto rc = spi_flash_read(getEntryAddress(index), &dst, sizeof(dst)); if (rc != ESP_OK) { return rc; } @@ -741,7 +744,7 @@ esp_err_t Page::erase() mFirstUsedEntry = INVALID_ENTRY; mNextFreeEntry = INVALID_ENTRY; mState = PageState::UNINITIALIZED; - mHashList = HashList(); + mHashList.clear(); return ESP_OK; } diff --git a/components/nvs_flash/test/spi_flash_emulation.cpp b/components/nvs_flash/test/spi_flash_emulation.cpp index 5185bd34cb..914efc1452 100644 --- a/components/nvs_flash/test/spi_flash_emulation.cpp +++ b/components/nvs_flash/test/spi_flash_emulation.cpp @@ -22,7 +22,7 @@ void spi_flash_emulator_set(SpiFlashEmulator* e) s_emulator = e; } -esp_err_t spi_flash_erase_sector(uint16_t sec) +esp_err_t spi_flash_erase_sector(size_t sec) { if (!s_emulator) { return ESP_ERR_FLASH_OP_TIMEOUT; @@ -35,26 +35,26 @@ esp_err_t spi_flash_erase_sector(uint16_t sec) return ESP_OK; } -esp_err_t spi_flash_write(uint32_t des_addr, const uint32_t *src_addr, uint32_t size) +esp_err_t spi_flash_write(size_t des_addr, const void *src_addr, size_t size) { if (!s_emulator) { return ESP_ERR_FLASH_OP_TIMEOUT; } - if (!s_emulator->write(des_addr, src_addr, size)) { + if (!s_emulator->write(des_addr, reinterpret_cast(src_addr), size)) { return ESP_ERR_FLASH_OP_FAIL; } return ESP_OK; } -esp_err_t spi_flash_read(uint32_t src_addr, uint32_t *des_addr, uint32_t size) +esp_err_t spi_flash_read(size_t src_addr, void *des_addr, size_t size) { if (!s_emulator) { return ESP_ERR_FLASH_OP_TIMEOUT; } - if (!s_emulator->read(des_addr, src_addr, size)) { + if (!s_emulator->read(reinterpret_cast(des_addr), src_addr, size)) { return ESP_ERR_FLASH_OP_FAIL; } diff --git a/components/nvs_flash/test/spi_flash_emulation.h b/components/nvs_flash/test/spi_flash_emulation.h index d5a242b240..ba50c4f9e4 100644 --- a/components/nvs_flash/test/spi_flash_emulation.h +++ b/components/nvs_flash/test/spi_flash_emulation.h @@ -44,7 +44,7 @@ public: spi_flash_emulator_set(nullptr); } - bool read(uint32_t* dest, uint32_t srcAddr, size_t size) const + bool read(uint32_t* dest, size_t srcAddr, size_t size) const { if (srcAddr % 4 != 0 || size % 4 != 0 || @@ -60,7 +60,7 @@ public: return true; } - bool write(uint32_t dstAddr, const uint32_t* src, size_t size) + bool write(size_t dstAddr, const uint32_t* src, size_t size) { uint32_t sectorNumber = dstAddr/SPI_FLASH_SEC_SIZE; if (sectorNumber < mLowerSectorBound || sectorNumber >= mUpperSectorBound) { @@ -96,7 +96,7 @@ public: return true; } - bool erase(uint32_t sectorNumber) + bool erase(size_t sectorNumber) { size_t offset = sectorNumber * SPI_FLASH_SEC_SIZE / 4; if (offset > mData.size()) { diff --git a/components/nvs_flash/test/test_nvs.cpp b/components/nvs_flash/test/test_nvs.cpp index 3db9b45aeb..ce552578db 100644 --- a/components/nvs_flash/test/test_nvs.cpp +++ b/components/nvs_flash/test/test_nvs.cpp @@ -933,6 +933,24 @@ TEST_CASE("test recovery from sudden poweroff", "[.][long][nvs][recovery][monkey } } +TEST_CASE("test for memory leaks in open/set", "[leaks]") +{ + SpiFlashEmulator emu(10); + const uint32_t NVS_FLASH_SECTOR = 6; + const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; + emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); + TEST_ESP_OK(nvs_flash_init_custom(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)); + + for (int i = 0; i < 100000; ++i) { + nvs_handle light_handle = 0; + char lightbulb[1024] = {12, 13, 14, 15, 16}; + TEST_ESP_OK(nvs_open("light", NVS_READWRITE, &light_handle)); + TEST_ESP_OK(nvs_set_blob(light_handle, "key", lightbulb, sizeof(lightbulb))); + TEST_ESP_OK(nvs_commit(light_handle)); + nvs_close(light_handle); + } +} + TEST_CASE("dump all performance data", "[nvs]") { std::cout << "====================" << std::endl << "Dumping benchmarks" << std::endl; diff --git a/components/nvs_flash/test/test_spi_flash_emulation.cpp b/components/nvs_flash/test/test_spi_flash_emulation.cpp index ea233da61b..0c77aa9669 100644 --- a/components/nvs_flash/test/test_spi_flash_emulation.cpp +++ b/components/nvs_flash/test/test_spi_flash_emulation.cpp @@ -30,7 +30,7 @@ TEST_CASE("flash starts with all bytes == 0xff", "[spi_flash_emu]") uint8_t sector[SPI_FLASH_SEC_SIZE]; for (int i = 0; i < 4; ++i) { - CHECK(spi_flash_read(0, reinterpret_cast(sector), sizeof(sector)) == ESP_OK); + CHECK(spi_flash_read(0, sector, sizeof(sector)) == ESP_OK); for (auto v: sector) { CHECK(v == 0xff); } @@ -83,7 +83,7 @@ TEST_CASE("after erase the sector is set to 0xff", "[spi_flash_emu]") TEST_CASE("read/write/erase operation times are calculated correctly", "[spi_flash_emu]") { SpiFlashEmulator emu(1); - uint32_t data[128]; + uint8_t data[512]; spi_flash_read(0, data, 4); CHECK(emu.getTotalTime() == 7); CHECK(emu.getReadOps() == 1); @@ -141,7 +141,7 @@ TEST_CASE("read/write/erase operation times are calculated correctly", "[spi_fla CHECK(emu.getTotalTime() == 37142); } -TEST_CASE("data is randomized predicatbly", "[spi_flash_emu]") +TEST_CASE("data is randomized predictably", "[spi_flash_emu]") { SpiFlashEmulator emu1(3); emu1.randomize(0x12345678); diff --git a/components/spi_flash/README.rst b/components/spi_flash/README.rst new file mode 100644 index 0000000000..b479c3b0e9 --- /dev/null +++ b/components/spi_flash/README.rst @@ -0,0 +1,158 @@ +SPI flash related APIs +====================== + +Overview +-------- +Spi_flash component contains APIs related to reading, writing, erasing, +memory mapping data in the external SPI flash. It also has higher-level +APIs which work with partition table and partitions. + +Note that all the functionality is limited to the "main" flash chip, +i.e. the flash chip from which program runs. For ``spi_flash_*`` functions, +this is software limitation. Underlying ROM functions which work with SPI flash +do not have provisions for working with flash chips attached to SPI peripherals +other than SPI0. + +SPI flash access APIs +--------------------- + +This is the set of APIs for working with data in flash: + +- ``spi_flash_read`` used to read data from flash to RAM +- ``spi_flash_write`` used to write data from RAM to flash +- ``spi_flash_erase_sector`` used to erase individual sectors of flash +- ``spi_flash_erase_range`` used to erase range of addresses in flash +- ``spi_flash_get_chip_size`` returns flash chip size, in bytes, as configured in menuconfig + +There are some data alignment limitations which need to be considered when using +spi_flash_read/spi_flash_write functions: + +- buffer in RAM must be 4-byte aligned +- size must be 4-byte aligned +- address in flash must be 4-byte aligned + +These alignment limitations are purely software, and should be removed in future +versions. + +It is assumed that correct SPI flash chip size is set at compile time using +menuconfig. While run-time detection of SPI flash chip size is possible, it is +not implemented yet. Applications which need this (e.g. to provide one firmware +binary for different flash sizes) can do flash chip size detection and set +the correct flash chip size in ``chip_size`` member of ``g_rom_flashchip`` +structure. This size is used by ``spi_flash_*`` functions for bounds checking. + +SPI flash APIs disable instruction and data caches while reading/writing/erasing. +See implementation notes below on details how this happens. For application +this means that at some periods of time, code can not be run from flash, +and constant data can not be fetched from flash by the CPU. This is not an +issue for normal code which runs in a task, because SPI flash APIs prevent +other tasks from running while caches are disabled. This is an issue for +interrupt handlers, which can still be called while flash operation is in +progress. If the interrupt handler is not placed into IRAM, there is a +possibility that interrupt will happen at the time when caches are disabled, +which will cause an illegal instruction exception. + +To prevent this, make sure that all ISR code, and all functions called from ISR +code are placed into IRAM, or are located in ROM. Most useful C library +functions are located in ROM, so they can be called from ISR. + +To place a function into IRAM, use ``IRAM_ATTR`` attribute, e.g.:: + + #include "esp_attr.h" + + void IRAM_ATTR gpio_isr_handler(void* arg) + { + // ... + } + +When flash encryption is enabled, ``spi_flash_read`` will read data as it is +stored in flash (without decryption), and ``spi_flash_write`` will write data +in plain text. In other words, ``spi_flash_read/write`` APIs don't have +provisions to deal with encrypted data. + + +Partition table APIs +-------------------- + +ESP-IDF uses partition table to maintain information about various regions of +SPI flash memory (bootloader, various application binaries, data, filesystems). +More information about partition tables can be found in docs/partition_tables.rst. + +This component provides APIs to enumerate partitions found in the partition table +and perform operations on them. These functions are declared in ``esp_partition.h``: + +- ``esp_partition_find`` used to search partition table for entries with specific type, returns an opaque iterator +- ``esp_partition_get`` returns a structure describing the partition, for the given iterator +- ``esp_partition_next`` advances iterator to the next partition found +- ``esp_partition_iterator_release`` releases iterator returned by ``esp_partition_find`` +- ``esp_partition_find_first`` is a convenience function which returns structure describing the first partition found by esp_partition_find +- ``esp_partition_read``, ``esp_partition_write``, ``esp_partition_erase_range`` are equivalent to ``spi_flash_read``, ``spi_flash_write``, ``spi_flash_erase_range``, but operate within partition boundaries + +Most application code should use ``esp_partition_*`` APIs instead of lower level +``spi_flash_*`` APIs. Partition APIs do bounds checking and calculate correct +offsets in flash based on data stored in partition table. + +Memory mapping APIs +------------------- + +ESP32 features memory hardware which allows regions of flash memory to be mapped +into instruction and data address spaces. This mapping works only for read operations, +it is not possible to modify contents of flash memory by writing to mapped memory +region. Mapping happens in 64KB pages. Memory mapping hardware can map up to +4 megabytes of flash into data address space, and up to 16 megabytes of flash into +instruction address space. See the technical reference manual for more details +about memory mapping hardware. + +Note that some number of 64KB pages is used to map the application +itself into memory, so the actual number of available 64KB pages may be less. + +Reading data from flash using a memory mapped region is the only way to decrypt +contents of flash when flash encryption is enabled. Decryption is performed at +hardware level. + +Memory mapping APIs are declared in ``esp_spi_flash.h`` and ``esp_partition.h``: + +- ``spi_flash_mmap`` maps a region of physical flash addresses into instruction space or data space of the CPU +- ``spi_flash_munmap`` unmaps previously mapped region +- ``esp_partition_mmap`` maps part of a partition into the instruction space or data space of the CPU + +Differences between ``spi_flash_mmap`` and ``esp_partition_mmap`` are as follows: + +- ``spi_flash_mmap`` must be given a 64KB aligned physical address +- ``esp_partition_mmap`` may be given an arbitrary offset within the partition, it will adjust returned pointer to mapped memory as necessary + +Note that because memory mapping happens in 64KB blocks, it may be possible to +read data outside of the partition provided to ``esp_partition_mmap``. + +Implementation notes +-------------------- + +In order to perform some flash operations, we need to make sure both CPUs +are not running any code from flash for the duration of the flash operation. +In a single-core setup this is easy: we disable interrupts/scheduler and do +the flash operation. In the dual-core setup this is slightly more complicated. +We need to make sure that the other CPU doesn't run any code from flash. + + +When SPI flash API is called on CPU A (can be PRO or APP), we start +spi_flash_op_block_func function on CPU B using esp_ipc_call API. This API +wakes up high priority task on CPU B and tells it to execute given function, +in this case spi_flash_op_block_func. This function disables cache on CPU B and +signals that cache is disabled by setting s_flash_op_can_start flag. +Then the task on CPU A disables cache as well, and proceeds to execute flash +operation. + +While flash operation is running, interrupts can still run on CPUs A and B. +We assume that all interrupt code is placed into RAM. Once interrupt allocation +API is added, we should add a flag to request interrupt to be disabled for +the duration of flash operations. + +Once flash operation is complete, function on CPU A sets another flag, +s_flash_op_complete, to let the task on CPU B know that it can re-enable +cache and release the CPU. Then the function on CPU A re-enables the cache on +CPU A as well and returns control to the calling code. + +Additionally, all API functions are protected with a mutex (s_flash_op_mutex). + +In a single core environment (CONFIG_FREERTOS_UNICORE enabled), we simply +disable both caches, no inter-CPU communication takes place. diff --git a/components/spi_flash/esp_spi_flash.c b/components/spi_flash/cache_utils.c similarity index 53% rename from components/spi_flash/esp_spi_flash.c rename to components/spi_flash/cache_utils.c index d702f3b815..6ae47bdb3e 100644 --- a/components/spi_flash/esp_spi_flash.c +++ b/components/spi_flash/cache_utils.c @@ -3,7 +3,7 @@ // 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 @@ -30,39 +30,7 @@ #include "esp_spi_flash.h" #include "esp_log.h" -/* - Driver for SPI flash read/write/erase operations - In order to perform some flash operations, we need to make sure both CPUs - are not running any code from flash for the duration of the flash operation. - In a single-core setup this is easy: we disable interrupts/scheduler and do - the flash operation. In the dual-core setup this is slightly more complicated. - We need to make sure that the other CPU doesn't run any code from flash. - - - When SPI flash API is called on CPU A (can be PRO or APP), we start - spi_flash_op_block_func function on CPU B using esp_ipc_call API. This API - wakes up high priority task on CPU B and tells it to execute given function, - in this case spi_flash_op_block_func. This function disables cache on CPU B and - signals that cache is disabled by setting s_flash_op_can_start flag. - Then the task on CPU A disables cache as well, and proceeds to execute flash - operation. - - While flash operation is running, interrupts can still run on CPU B. - We assume that all interrupt code is placed into RAM. - - Once flash operation is complete, function on CPU A sets another flag, - s_flash_op_complete, to let the task on CPU B know that it can re-enable - cache and release the CPU. Then the function on CPU A re-enables the cache on - CPU A as well and returns control to the calling code. - - Additionally, all API functions are protected with a mutex (s_flash_op_mutex). - - In a single core environment (CONFIG_FREERTOS_UNICORE enabled), we simply - disable both caches, no inter-CPU communication takes place. -*/ - -static esp_err_t spi_flash_translate_rc(SpiFlashOpResult rc); static void IRAM_ATTR spi_flash_disable_cache(uint32_t cpuid, uint32_t* saved_state); static void IRAM_ATTR spi_flash_restore_cache(uint32_t cpuid, uint32_t saved_state); @@ -72,25 +40,23 @@ static uint32_t s_flash_op_cache_state[2]; static SemaphoreHandle_t s_flash_op_mutex; static bool s_flash_op_can_start = false; static bool s_flash_op_complete = false; -#endif //CONFIG_FREERTOS_UNICORE -#if CONFIG_SPI_FLASH_ENABLE_COUNTERS -static const char* TAG = "spi_flash"; -static spi_flash_counters_t s_flash_stats; +void spi_flash_init_lock() +{ + s_flash_op_mutex = xSemaphoreCreateMutex(); +} -#define COUNTER_START() uint32_t ts_begin = xthal_get_ccount() -#define COUNTER_STOP(counter) do{ s_flash_stats.counter.count++; s_flash_stats.counter.time += (xthal_get_ccount() - ts_begin) / (XT_CLOCK_FREQ / 1000000); } while(0) -#define COUNTER_ADD_BYTES(counter, size) do { s_flash_stats.counter.bytes += size; } while (0) -#else -#define COUNTER_START() -#define COUNTER_STOP(counter) -#define COUNTER_ADD_BYTES(counter, size) +void spi_flash_op_lock() +{ + xSemaphoreTake(s_flash_op_mutex, portMAX_DELAY); +} -#endif //CONFIG_SPI_FLASH_ENABLE_COUNTERS +void spi_flash_op_unlock() +{ + xSemaphoreGive(s_flash_op_mutex); +} -#ifndef CONFIG_FREERTOS_UNICORE - -static void IRAM_ATTR spi_flash_op_block_func(void* arg) +void IRAM_ATTR spi_flash_op_block_func(void* arg) { // Disable scheduler on this CPU vTaskSuspendAll(); @@ -108,19 +74,9 @@ static void IRAM_ATTR spi_flash_op_block_func(void* arg) xTaskResumeAll(); } -void spi_flash_init() +void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu() { - s_flash_op_mutex = xSemaphoreCreateMutex(); - -#if CONFIG_SPI_FLASH_ENABLE_COUNTERS - spi_flash_reset_counters(); -#endif -} - -static void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu() -{ - // Take the API lock - xSemaphoreTake(s_flash_op_mutex, portMAX_DELAY); + spi_flash_op_lock(); const uint32_t cpuid = xPortGetCoreID(); const uint32_t other_cpuid = (cpuid == 0) ? 1 : 0; @@ -152,7 +108,7 @@ static void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu() spi_flash_disable_cache(cpuid, &s_flash_op_cache_state[cpuid]); } -static void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu() +void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu() { const uint32_t cpuid = xPortGetCoreID(); const uint32_t other_cpuid = (cpuid == 0) ? 1 : 0; @@ -173,98 +129,45 @@ static void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu() xTaskResumeAll(); } // Release API lock - xSemaphoreGive(s_flash_op_mutex); + spi_flash_op_unlock(); } -#else // CONFIG_FREERTOS_UNICORE +#else // CONFIG_FREERTOS_UNICORE -void spi_flash_init() +void spi_flash_init_lock() { -#if CONFIG_SPI_FLASH_ENABLE_COUNTERS - spi_flash_reset_counters(); -#endif } -static void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu() +void spi_flash_op_lock() { vTaskSuspendAll(); +} + +void spi_flash_op_unlock() +{ + xTaskResumeAll(); +} + + +void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu() +{ + spi_flash_op_lock(); spi_flash_disable_cache(0, &s_flash_op_cache_state[0]); } -static void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu() +void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu() { spi_flash_restore_cache(0, s_flash_op_cache_state[0]); - xTaskResumeAll(); + spi_flash_op_unlock(); } #endif // CONFIG_FREERTOS_UNICORE - -SpiFlashOpResult IRAM_ATTR spi_flash_unlock() -{ - static bool unlocked = false; - if (!unlocked) { - SpiFlashOpResult rc = SPIUnlock(); - if (rc != SPI_FLASH_RESULT_OK) { - return rc; - } - unlocked = true; - } - return SPI_FLASH_RESULT_OK; -} - -esp_err_t IRAM_ATTR spi_flash_erase_sector(uint16_t sec) -{ - COUNTER_START(); - spi_flash_disable_interrupts_caches_and_other_cpu(); - SpiFlashOpResult rc; - rc = spi_flash_unlock(); - if (rc == SPI_FLASH_RESULT_OK) { - rc = SPIEraseSector(sec); - } - spi_flash_enable_interrupts_caches_and_other_cpu(); - COUNTER_STOP(erase); - return spi_flash_translate_rc(rc); -} - -esp_err_t IRAM_ATTR spi_flash_write(uint32_t dest_addr, const uint32_t *src, uint32_t size) -{ - COUNTER_START(); - spi_flash_disable_interrupts_caches_and_other_cpu(); - SpiFlashOpResult rc; - rc = spi_flash_unlock(); - if (rc == SPI_FLASH_RESULT_OK) { - rc = SPIWrite(dest_addr, src, (int32_t) size); - COUNTER_ADD_BYTES(write, size); - } - spi_flash_enable_interrupts_caches_and_other_cpu(); - COUNTER_STOP(write); - return spi_flash_translate_rc(rc); -} - -esp_err_t IRAM_ATTR spi_flash_read(uint32_t src_addr, uint32_t *dest, uint32_t size) -{ - COUNTER_START(); - spi_flash_disable_interrupts_caches_and_other_cpu(); - SpiFlashOpResult rc = SPIRead(src_addr, dest, (int32_t) size); - COUNTER_ADD_BYTES(read, size); - spi_flash_enable_interrupts_caches_and_other_cpu(); - COUNTER_STOP(read); - return spi_flash_translate_rc(rc); -} - -static esp_err_t spi_flash_translate_rc(SpiFlashOpResult rc) -{ - switch (rc) { - case SPI_FLASH_RESULT_OK: - return ESP_OK; - case SPI_FLASH_RESULT_TIMEOUT: - return ESP_ERR_FLASH_OP_TIMEOUT; - case SPI_FLASH_RESULT_ERR: - default: - return ESP_ERR_FLASH_OP_FAIL; - } -} +/** + * The following two functions are replacements for Cache_Read_Disable and Cache_Read_Enable + * function in ROM. They are used to work around a bug where Cache_Read_Disable requires a call to + * Cache_Flush before Cache_Read_Enable, even if cached data was not modified. + */ static const uint32_t cache_mask = DPORT_APP_CACHE_MASK_OPSDRAM | DPORT_APP_CACHE_MASK_DROM0 | DPORT_APP_CACHE_MASK_DRAM1 | DPORT_APP_CACHE_MASK_IROM0 | @@ -300,29 +203,3 @@ static void IRAM_ATTR spi_flash_restore_cache(uint32_t cpuid, uint32_t saved_sta } } -#if CONFIG_SPI_FLASH_ENABLE_COUNTERS - -static inline void dump_counter(spi_flash_counter_t* counter, const char* name) -{ - ESP_LOGI(TAG, "%s count=%8d time=%8dms bytes=%8d\n", name, - counter->count, counter->time, counter->bytes); -} - -const spi_flash_counters_t* spi_flash_get_counters() -{ - return &s_flash_stats; -} - -void spi_flash_reset_counters() -{ - memset(&s_flash_stats, 0, sizeof(s_flash_stats)); -} - -void spi_flash_dump_counters() -{ - dump_counter(&s_flash_stats.read, "read "); - dump_counter(&s_flash_stats.write, "write"); - dump_counter(&s_flash_stats.erase, "erase"); -} - -#endif //CONFIG_SPI_FLASH_ENABLE_COUNTERS diff --git a/components/spi_flash/cache_utils.h b/components/spi_flash/cache_utils.h new file mode 100644 index 0000000000..899a31c651 --- /dev/null +++ b/components/spi_flash/cache_utils.h @@ -0,0 +1,44 @@ +// 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_SPI_FLASH_CACHE_UTILS_H +#define ESP_SPI_FLASH_CACHE_UTILS_H + +/** + * This header file contains declarations of cache manipulation functions + * used both in flash_ops.c and flash_mmap.c. + * + * These functions are considered internal and are not designed to be called from applications. + */ + +// Init mutex protecting access to spi_flash_* APIs +void spi_flash_init_lock(); + +// Take mutex protecting access to spi_flash_* APIs +void spi_flash_op_lock(); + +// Release said mutex +void spi_flash_op_unlock(); + +// Suspend the scheduler on both CPUs, disable cache. +// Contrary to its name this doesn't do anything with interrupts, yet. +// Interrupt disabling capability will be added once we implement +// interrupt allocation API. +void spi_flash_disable_interrupts_caches_and_other_cpu(); + +// Enable cache, enable interrupts (to be added in future), resume scheduler +void spi_flash_enable_interrupts_caches_and_other_cpu(); + + +#endif //ESP_SPI_FLASH_CACHE_UTILS_H diff --git a/components/spi_flash/component.mk b/components/spi_flash/component.mk index ef497a7ecb..459da06419 100755 --- a/components/spi_flash/component.mk +++ b/components/spi_flash/component.mk @@ -1,3 +1,8 @@ COMPONENT_ADD_INCLUDEDIRS := include +ifdef IS_BOOTLOADER_BUILD +# Bootloader needs updated SPIUnlock from this file +COMPONENT_OBJS := spi_flash_rom_patch.o +endif + include $(IDF_PATH)/make/component_common.mk diff --git a/components/spi_flash/flash_mmap.c b/components/spi_flash/flash_mmap.c new file mode 100644 index 0000000000..2165a784d1 --- /dev/null +++ b/components/spi_flash/flash_mmap.c @@ -0,0 +1,214 @@ +// 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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "sdkconfig.h" +#include "esp_ipc.h" +#include "esp_attr.h" +#include "esp_spi_flash.h" +#include "esp_log.h" +#include "cache_utils.h" + +#ifndef NDEBUG +// Enable built-in checks in queue.h in debug builds +#define INVARIANTS +#endif +#include "rom/queue.h" + +#define REGIONS_COUNT 4 +#define PAGES_PER_REGION 64 +#define FLASH_PAGE_SIZE 0x10000 +#define INVALID_ENTRY_VAL 0x100 +#define VADDR0_START_ADDR 0x3F400000 +#define VADDR1_START_ADDR 0x40000000 +#define VADDR1_FIRST_USABLE_ADDR 0x400D0000 +#define PRO_IRAM0_FIRST_USABLE_PAGE ((VADDR1_FIRST_USABLE_ADDR - VADDR1_START_ADDR) / FLASH_PAGE_SIZE + 64) + + +typedef struct mmap_entry_{ + uint32_t handle; + int page; + int count; + LIST_ENTRY(mmap_entry_) entries; +} mmap_entry_t; + + +static LIST_HEAD(mmap_entries_head, mmap_entry_) s_mmap_entries_head = + LIST_HEAD_INITIALIZER(s_mmap_entries_head); +static uint8_t s_mmap_page_refcnt[REGIONS_COUNT * PAGES_PER_REGION] = {0}; +static uint32_t s_mmap_last_handle = 0; + + +static void IRAM_ATTR spi_flash_mmap_init() +{ + for (int i = 0; i < REGIONS_COUNT * PAGES_PER_REGION; ++i) { + uint32_t entry_pro = DPORT_PRO_FLASH_MMU_TABLE[i]; + uint32_t entry_app = DPORT_APP_FLASH_MMU_TABLE[i]; + if (entry_pro != entry_app) { + // clean up entries used by boot loader + entry_pro = 0; + DPORT_PRO_FLASH_MMU_TABLE[i] = 0; + } + if ((entry_pro & 0x100) == 0 && (i == 0 || i == PRO_IRAM0_FIRST_USABLE_PAGE || entry_pro != 0)) { + s_mmap_page_refcnt[i] = 1; + } + } +} + +esp_err_t IRAM_ATTR spi_flash_mmap(uint32_t src_addr, size_t size, spi_flash_mmap_memory_t memory, + const void** out_ptr, spi_flash_mmap_handle_t* out_handle) +{ + esp_err_t ret; + mmap_entry_t* new_entry = (mmap_entry_t*) malloc(sizeof(mmap_entry_t)); + if (new_entry == 0) { + return ESP_ERR_NO_MEM; + } + if (src_addr & 0xffff) { + return ESP_ERR_INVALID_ARG; + } + if (src_addr + size > g_rom_flashchip.chip_size) { + return ESP_ERR_INVALID_ARG; + } + spi_flash_disable_interrupts_caches_and_other_cpu(); + if (s_mmap_page_refcnt[0] == 0) { + spi_flash_mmap_init(); + } + // figure out the memory region where we should look for pages + int region_begin; // first page to check + int region_size; // number of pages to check + uint32_t region_addr; // base address of memory region + if (memory == SPI_FLASH_MMAP_DATA) { + // Vaddr0 + region_begin = 0; + region_size = 64; + region_addr = VADDR0_START_ADDR; + } else { + // only part of VAddr1 is usable, so adjust for that + region_begin = VADDR1_FIRST_USABLE_ADDR; + region_size = 3 * 64 - region_begin; + region_addr = VADDR1_FIRST_USABLE_ADDR; + } + // region which should be mapped + int phys_page = src_addr / FLASH_PAGE_SIZE; + int page_count = (size + FLASH_PAGE_SIZE - 1) / FLASH_PAGE_SIZE; + // The following part searches for a range of MMU entries which can be used. + // Algorithm is essentially naïve strstr algorithm, except that unused MMU + // entries are treated as wildcards. + int start; + int end = region_begin + region_size - page_count; + for (start = region_begin; start < end; ++start) { + int page = phys_page; + int pos; + for (pos = start; pos < start + page_count; ++pos, ++page) { + int table_val = (int) DPORT_PRO_FLASH_MMU_TABLE[pos]; + uint8_t refcnt = s_mmap_page_refcnt[pos]; + if (refcnt != 0 && table_val != page) { + break; + } + } + // whole mapping range matched, bail out + if (pos - start == page_count) { + break; + } + } + // checked all the region(s) and haven't found anything? + if (start == end) { + *out_handle = 0; + *out_ptr = NULL; + ret = ESP_ERR_NO_MEM; + } else { + // set up mapping using pages [start, start + page_count) + uint32_t entry_val = (uint32_t) phys_page; + for (int i = start; i != start + page_count; ++i, ++entry_val) { + // sanity check: we won't reconfigure entries with non-zero reference count + assert(s_mmap_page_refcnt[i] == 0 || + (DPORT_PRO_FLASH_MMU_TABLE[i] == entry_val && + DPORT_APP_FLASH_MMU_TABLE[i] == entry_val)); + if (s_mmap_page_refcnt[i] == 0) { + DPORT_PRO_FLASH_MMU_TABLE[i] = entry_val; + DPORT_APP_FLASH_MMU_TABLE[i] = entry_val; + } + ++s_mmap_page_refcnt[i]; + } + + LIST_INSERT_HEAD(&s_mmap_entries_head, new_entry, entries); + new_entry->page = start; + new_entry->count = page_count; + new_entry->handle = ++s_mmap_last_handle; + *out_handle = new_entry->handle; + *out_ptr = (void*) (region_addr + start * FLASH_PAGE_SIZE); + ret = ESP_OK; + } + spi_flash_enable_interrupts_caches_and_other_cpu(); + if (*out_ptr == NULL) { + free(new_entry); + } + return ret; +} + +void IRAM_ATTR spi_flash_munmap(spi_flash_mmap_handle_t handle) +{ + spi_flash_disable_interrupts_caches_and_other_cpu(); + mmap_entry_t* it; + // look for handle in linked list + for (it = LIST_FIRST(&s_mmap_entries_head); it != NULL; it = LIST_NEXT(it, entries)) { + if (it->handle == handle) { + // for each page, decrement reference counter + // if reference count is zero, disable MMU table entry to + // facilitate debugging of use-after-free conditions + for (int i = it->page; i < it->page + it->count; ++i) { + assert(s_mmap_page_refcnt[i] > 0); + if (--s_mmap_page_refcnt[i] == 0) { + DPORT_PRO_FLASH_MMU_TABLE[i] = INVALID_ENTRY_VAL; + DPORT_APP_FLASH_MMU_TABLE[i] = INVALID_ENTRY_VAL; + } + } + LIST_REMOVE(it, entries); + break; + } + } + spi_flash_enable_interrupts_caches_and_other_cpu(); + if (it == NULL) { + assert(0 && "invalid handle, or handle already unmapped"); + } + free(it); +} + +void spi_flash_mmap_dump() +{ + if (s_mmap_page_refcnt[0] == 0) { + spi_flash_mmap_init(); + } + mmap_entry_t* it; + for (it = LIST_FIRST(&s_mmap_entries_head); it != NULL; it = LIST_NEXT(it, entries)) { + printf("handle=%d page=%d count=%d\n", it->handle, it->page, it->count); + } + for (int i = 0; i < REGIONS_COUNT * PAGES_PER_REGION; ++i) { + if (s_mmap_page_refcnt[i] != 0) { + printf("page %d: refcnt=%d paddr=%d\n", + i, (int) s_mmap_page_refcnt[i], DPORT_PRO_FLASH_MMU_TABLE[i]); + } + } +} diff --git a/components/spi_flash/flash_ops.c b/components/spi_flash/flash_ops.c new file mode 100644 index 0000000000..ae72568aa5 --- /dev/null +++ b/components/spi_flash/flash_ops.c @@ -0,0 +1,227 @@ +// 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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "sdkconfig.h" +#include "esp_ipc.h" +#include "esp_attr.h" +#include "esp_spi_flash.h" +#include "esp_log.h" +#include "cache_utils.h" + +#if CONFIG_SPI_FLASH_ENABLE_COUNTERS +static const char* TAG = "spi_flash"; +static spi_flash_counters_t s_flash_stats; + +#define COUNTER_START() uint32_t ts_begin = xthal_get_ccount() +#define COUNTER_STOP(counter) \ + do{ \ + s_flash_stats.counter.count++; \ + s_flash_stats.counter.time += (xthal_get_ccount() - ts_begin) / (XT_CLOCK_FREQ / 1000000); \\ + } while(0) + +#define COUNTER_ADD_BYTES(counter, size) \ + do { \ + s_flash_stats.counter.bytes += size; \ + } while (0) + +#else +#define COUNTER_START() +#define COUNTER_STOP(counter) +#define COUNTER_ADD_BYTES(counter, size) + +#endif //CONFIG_SPI_FLASH_ENABLE_COUNTERS + +static esp_err_t spi_flash_translate_rc(SpiFlashOpResult rc); + +void spi_flash_init() +{ + spi_flash_init_lock(); +#if CONFIG_SPI_FLASH_ENABLE_COUNTERS + spi_flash_reset_counters(); +#endif +} + +size_t spi_flash_get_chip_size() +{ + return g_rom_flashchip.chip_size; +} + +SpiFlashOpResult IRAM_ATTR spi_flash_unlock() +{ + static bool unlocked = false; + if (!unlocked) { + SpiFlashOpResult rc = SPIUnlock(); + if (rc != SPI_FLASH_RESULT_OK) { + return rc; + } + unlocked = true; + } + return SPI_FLASH_RESULT_OK; +} + +esp_err_t IRAM_ATTR spi_flash_erase_sector(size_t sec) +{ + return spi_flash_erase_range(sec * SPI_FLASH_SEC_SIZE, SPI_FLASH_SEC_SIZE); +} + +esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size) +{ + if (start_addr % SPI_FLASH_SEC_SIZE != 0) { + return ESP_ERR_INVALID_ARG; + } + if (size % SPI_FLASH_SEC_SIZE != 0) { + return ESP_ERR_INVALID_SIZE; + } + if (size + start_addr > spi_flash_get_chip_size()) { + return ESP_ERR_INVALID_SIZE; + } + size_t start = start_addr / SPI_FLASH_SEC_SIZE; + size_t end = start + size / SPI_FLASH_SEC_SIZE; + const size_t sectors_per_block = 16; + COUNTER_START(); + spi_flash_disable_interrupts_caches_and_other_cpu(); + SpiFlashOpResult rc; + rc = spi_flash_unlock(); + if (rc == SPI_FLASH_RESULT_OK) { + for (size_t sector = start; sector != end && rc == SPI_FLASH_RESULT_OK; ) { + if (sector % sectors_per_block == 0 && end - sector > sectors_per_block) { + rc = SPIEraseBlock(sector / sectors_per_block); + sector += sectors_per_block; + COUNTER_ADD_BYTES(erase, sectors_per_block * SPI_FLASH_SEC_SIZE); + } + else { + rc = SPIEraseSector(sector); + ++sector; + COUNTER_ADD_BYTES(erase, SPI_FLASH_SEC_SIZE); + } + } + } + spi_flash_enable_interrupts_caches_and_other_cpu(); + COUNTER_STOP(erase); + return spi_flash_translate_rc(rc); +} + +esp_err_t IRAM_ATTR spi_flash_write(size_t dest_addr, const void *src, size_t size) +{ + // TODO: replace this check with code which deals with unaligned sources + if (((ptrdiff_t) src) % 4 != 0) { + return ESP_ERR_INVALID_ARG; + } + // Destination alignment is also checked in ROM code, but we can give + // better error code here + // TODO: add handling of unaligned destinations + if (dest_addr % 4 != 0) { + return ESP_ERR_INVALID_ARG; + } + if (size % 4 != 0) { + return ESP_ERR_INVALID_SIZE; + } + // Out of bound writes are checked in ROM code, but we can give better + // error code here + if (dest_addr + size > g_rom_flashchip.chip_size) { + return ESP_ERR_INVALID_SIZE; + } + COUNTER_START(); + spi_flash_disable_interrupts_caches_and_other_cpu(); + SpiFlashOpResult rc; + rc = spi_flash_unlock(); + if (rc == SPI_FLASH_RESULT_OK) { + rc = SPIWrite((uint32_t) dest_addr, (const uint32_t*) src, (int32_t) size); + COUNTER_ADD_BYTES(write, size); + } + spi_flash_enable_interrupts_caches_and_other_cpu(); + COUNTER_STOP(write); + return spi_flash_translate_rc(rc); +} + +esp_err_t IRAM_ATTR spi_flash_read(size_t src_addr, void *dest, size_t size) +{ + // TODO: replace this check with code which deals with unaligned destinations + if (((ptrdiff_t) dest) % 4 != 0) { + return ESP_ERR_INVALID_ARG; + } + // Source alignment is also checked in ROM code, but we can give + // better error code here + // TODO: add handling of unaligned destinations + if (src_addr % 4 != 0) { + return ESP_ERR_INVALID_ARG; + } + if (size % 4 != 0) { + return ESP_ERR_INVALID_SIZE; + } + // Out of bound reads are checked in ROM code, but we can give better + // error code here + if (src_addr + size > g_rom_flashchip.chip_size) { + return ESP_ERR_INVALID_SIZE; + } + COUNTER_START(); + spi_flash_disable_interrupts_caches_and_other_cpu(); + SpiFlashOpResult rc = SPIRead((uint32_t) src_addr, (uint32_t*) dest, (int32_t) size); + COUNTER_ADD_BYTES(read, size); + spi_flash_enable_interrupts_caches_and_other_cpu(); + COUNTER_STOP(read); + return spi_flash_translate_rc(rc); +} + +static esp_err_t spi_flash_translate_rc(SpiFlashOpResult rc) +{ + switch (rc) { + case SPI_FLASH_RESULT_OK: + return ESP_OK; + case SPI_FLASH_RESULT_TIMEOUT: + return ESP_ERR_FLASH_OP_TIMEOUT; + case SPI_FLASH_RESULT_ERR: + default: + return ESP_ERR_FLASH_OP_FAIL; + } +} + +#if CONFIG_SPI_FLASH_ENABLE_COUNTERS + +static inline void dump_counter(spi_flash_counter_t* counter, const char* name) +{ + ESP_LOGI(TAG, "%s count=%8d time=%8dms bytes=%8d\n", name, + counter->count, counter->time, counter->bytes); +} + +const spi_flash_counters_t* spi_flash_get_counters() +{ + return &s_flash_stats; +} + +void spi_flash_reset_counters() +{ + memset(&s_flash_stats, 0, sizeof(s_flash_stats)); +} + +void spi_flash_dump_counters() +{ + dump_counter(&s_flash_stats.read, "read "); + dump_counter(&s_flash_stats.write, "write"); + dump_counter(&s_flash_stats.erase, "erase"); +} + +#endif //CONFIG_SPI_FLASH_ENABLE_COUNTERS diff --git a/components/spi_flash/include/esp_partition.h b/components/spi_flash/include/esp_partition.h new file mode 100644 index 0000000000..ae0185dcd7 --- /dev/null +++ b/components/spi_flash/include/esp_partition.h @@ -0,0 +1,242 @@ +// 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_PARTITION_H__ +#define __ESP_PARTITION_H__ + +#include +#include +#include +#include "esp_err.h" +#include "esp_spi_flash.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + ESP_PARTITION_TYPE_APP = 0x00, + ESP_PARTITION_TYPE_DATA = 0x01, + ESP_PARTITION_TYPE_FILESYSTEM = 0x02, +} esp_partition_type_t; + +typedef enum { + ESP_PARTITION_SUBTYPE_APP_FACTORY = 0x00, + ESP_PARTITION_SUBTYPE_APP_OTA_MIN = 0x10, + ESP_PARTITION_SUBTYPE_APP_OTA_0 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 0, + ESP_PARTITION_SUBTYPE_APP_OTA_1 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 1, + ESP_PARTITION_SUBTYPE_APP_OTA_2 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 2, + ESP_PARTITION_SUBTYPE_APP_OTA_3 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 3, + ESP_PARTITION_SUBTYPE_APP_OTA_4 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 4, + ESP_PARTITION_SUBTYPE_APP_OTA_5 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 5, + ESP_PARTITION_SUBTYPE_APP_OTA_6 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 6, + ESP_PARTITION_SUBTYPE_APP_OTA_7 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 7, + ESP_PARTITION_SUBTYPE_APP_OTA_8 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 8, + ESP_PARTITION_SUBTYPE_APP_OTA_9 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 9, + ESP_PARTITION_SUBTYPE_APP_OTA_10 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 10, + ESP_PARTITION_SUBTYPE_APP_OTA_11 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 11, + ESP_PARTITION_SUBTYPE_APP_OTA_12 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 12, + ESP_PARTITION_SUBTYPE_APP_OTA_13 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 13, + ESP_PARTITION_SUBTYPE_APP_OTA_14 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 14, + ESP_PARTITION_SUBTYPE_APP_OTA_15 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 15, + ESP_PARTITION_SUBTYPE_APP_OTA_MAX = 15, + ESP_PARTITION_SUBTYPE_APP_TEST = 0x20, + + ESP_PARTITION_SUBTYPE_DATA_OTA = 0x00, + ESP_PARTITION_SUBTYPE_DATA_RF = 0x01, + ESP_PARTITION_SUBTYPE_DATA_NVS = 0x02, + + ESP_PARTITION_SUBTYPE_FILESYSTEM_ESPHTTPD = 0x00, + ESP_PARTITION_SUBTYPE_FILESYSTEM_FAT = 0x01, + ESP_PARTITION_SUBTYPE_FILESYSTEM_SPIFFS = 0x02, + + ESP_PARTITION_SUBTYPE_ANY = 0xff, +} esp_partition_subtype_t; + +#define ESP_PARTITION_SUBTYPE_OTA(i) ((esp_partition_subtype_t)(ESP_PARTITION_SUBTYPE_APP_OTA_MIN + ((i) & 0xf))) + + +typedef struct esp_partition_iterator_opaque_* esp_partition_iterator_t; + +typedef struct { + esp_partition_type_t type; + esp_partition_subtype_t subtype; + uint32_t address; + uint32_t size; + char label[17]; + bool encrypted; +} esp_partition_t; + +/** + * @brief Find partition based on one or more parameters + * + * @param type Partition type, one of esp_partition_type_t values + * @param subtype Partition subtype, one of esp_partition_subtype_t values. + * To find all partitions of given type, use + * ESP_PARTITION_SUBTYPE_ANY. + * @param label (optional) Partition label. Set this value if looking + * for partition with a specific name. Pass NULL otherwise. + * + * @return iterator which can be used to enumerate all the partitions found, + * or NULL if no partitions were found. + * Iterator obtained through this function has to be released + * using esp_partition_iterator_release when not used any more. + */ +esp_partition_iterator_t esp_partition_find(esp_partition_type_t type, esp_partition_subtype_t subtype, const char* label); + +/** + * @brief Find first partition based on one or more parameters + * + * @param type Partition type, one of esp_partition_type_t values + * @param subtype Partition subtype, one of esp_partition_subtype_t values. + * To find all partitions of given type, use + * ESP_PARTITION_SUBTYPE_ANY. + * @param label (optional) Partition label. Set this value if looking + * for partition with a specific name. Pass NULL otherwise. + * + * @return pointer to esp_partition_t structure, or NULL if no partition is found. + * This pointer is valid for the lifetime of the application. + */ +const esp_partition_t* esp_partition_find_first(esp_partition_type_t type, esp_partition_subtype_t subtype, const char* label); + +/** + * @brief Get esp_partition_t structure for given partition + * + * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. + * + * @return pointer to esp_partition_t structure. This pointer is valid for the lifetime + * of the application. + */ +const esp_partition_t* esp_partition_get(esp_partition_iterator_t iterator); + +/** + * @brief Move partition iterator to the next partition found + * + * Any copies of the iterator will be invalid after this call. + * + * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. + * + * @return NULL if no partition was found, valid esp_partition_iterator_t otherwise. + */ +esp_partition_iterator_t esp_partition_next(esp_partition_iterator_t iterator); + +/** + * @brief Release partition iterator + * + * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. + * + */ +void esp_partition_iterator_release(esp_partition_iterator_t iterator); + +/** + * @brief Read data from the partition + * + * @param partition Pointer to partition structure obtained using + * esp_partition_find_first or esp_partition_get. + * Must be non-NULL. + * @param dst Pointer to the buffer where data should be stored. + * Pointer must be non-NULL and buffer must be at least 'size' bytes long. + * @param src_offset Address of the data to be read, relative to the + * beginning of the partition. + * @param size Size of data to be read, in bytes. + * + * @return ESP_OK, if data was read successfully; + * ESP_ERR_INVALID_ARG, if src_offset exceeds partition size; + * ESP_ERR_INVALID_SIZE, if read would go out of bounds of the partition; + * or one of error codes from lower-level flash driver. + */ +esp_err_t esp_partition_read(const esp_partition_t* partition, + size_t src_offset, void* dst, size_t size); + +/** + * @brief Write data to the partition + * + * Before writing data to flash, corresponding region of flash needs to be erased. + * This can be done using esp_partition_erase_range function. + * + * @param partition Pointer to partition structure obtained using + * esp_partition_find_first or esp_partition_get. + * Must be non-NULL. + * @param dst_offset Address where the data should be written, relative to the + * beginning of the partition. + * @param src Pointer to the source buffer. Pointer must be non-NULL and + * buffer must be at least 'size' bytes long. + * @param size Size of data to be written, in bytes. + * + * @note Prior to writing to flash memory, make sure it has been erased with + * esp_partition_erase_range call. + * + * @return ESP_OK, if data was written successfully; + * ESP_ERR_INVALID_ARG, if dst_offset exceeds partition size; + * ESP_ERR_INVALID_SIZE, if write would go out of bounds of the partition; + * or one of error codes from lower-level flash driver. + */ +esp_err_t esp_partition_write(const esp_partition_t* partition, + size_t dst_offset, const void* src, size_t size); + +/** + * @brief Erase part of the partition + * + * @param partition Pointer to partition structure obtained using + * esp_partition_find_first or esp_partition_get. + * Must be non-NULL. + * @param start_addr Address where erase operation should start. Must be aligned + * to 4 kilobytes. + * @param size Size of the range which should be erased, in bytes. + * Must be divisible by 4 kilobytes. + * + * @return ESP_OK, if the range was erased successfully; + * ESP_ERR_INVALID_ARG, if iterator or dst are NULL; + * ESP_ERR_INVALID_SIZE, if erase would go out of bounds of the partition; + * or one of error codes from lower-level flash driver. + */ +esp_err_t esp_partition_erase_range(const esp_partition_t* partition, + uint32_t start_addr, uint32_t size); + +/** + * @brief Configure MMU to map partition into data memory + * + * Unlike spi_flash_mmap function, which requires a 64kB aligned base address, + * this function doesn't impose such a requirement. + * If offset results in a flash address which is not aligned to 64kB boundary, + * address will be rounded to the lower 64kB boundary, so that mapped region + * includes requested range. + * Pointer returned via out_ptr argument will be adjusted to point to the + * requested offset (not necessarily to the beginning of mmap-ed region). + * + * To release mapped memory, pass handle returned via out_handle argument to + * spi_flash_munmap function. + * + * @param partition Pointer to partition structure obtained using + * esp_partition_find_first or esp_partition_get. + * Must be non-NULL. + * @param offset Offset from the beginning of partition where mapping should start. + * @param size Size of the area to be mapped. + * @param memory Memory space where the region should be mapped + * @param out_ptr Output, pointer to the mapped memory region + * @param out_handle Output, handle which should be used for spi_flash_munmap call + * + * @return ESP_OK, if successful + */ +esp_err_t esp_partition_mmap(const esp_partition_t* partition, uint32_t offset, uint32_t size, + spi_flash_mmap_memory_t memory, + const void** out_ptr, spi_flash_mmap_handle_t* out_handle); + + +#ifdef __cplusplus +} +#endif + + +#endif /* __ESP_PARTITION_H__ */ diff --git a/components/spi_flash/include/esp_spi_flash.h b/components/spi_flash/include/esp_spi_flash.h index 6d635880eb..c65eaa5836 100644 --- a/components/spi_flash/include/esp_spi_flash.h +++ b/components/spi_flash/include/esp_spi_flash.h @@ -16,6 +16,7 @@ #define ESP_SPI_FLASH_H #include +#include #include "esp_err.h" #include "sdkconfig.h" @@ -34,41 +35,126 @@ extern "C" { * * This function must be called exactly once, before any other * spi_flash_* functions are called. + * Currently this function is called from startup code. There is + * no need to call it from application code. * */ void spi_flash_init(); +/** + * @brief Get flash chip size, as set in binary image header + * + * @note This value does not necessarily match real flash size. + * + * @return size of flash chip, in bytes + */ +size_t spi_flash_get_chip_size(); + /** * @brief Erase the Flash sector. * - * @param uint16 sec : Sector number, the count starts at sector 0, 4KB per sector. + * @param sector Sector number, the count starts at sector 0, 4KB per sector. * * @return esp_err_t */ -esp_err_t spi_flash_erase_sector(uint16_t sec); +esp_err_t spi_flash_erase_sector(size_t sector); + +/** + * @brief Erase a range of flash sectors + * + * @param uint32_t start_address : Address where erase operation has to start. + * Must be 4kB-aligned + * @param uint32_t size : Size of erased range, in bytes. Must be divisible by 4kB. + * + * @return esp_err_t + */ +esp_err_t spi_flash_erase_range(size_t start_addr, size_t size); + /** * @brief Write data to Flash. * - * @param uint32 des_addr : destination address in Flash. - * @param uint32 *src_addr : source address of the data. - * @param uint32 size : length of data + * @note Both des_addr and src_addr have to be 4-byte aligned. + * This is a temporary limitation which will be removed. + * + * @param dest destination address in Flash + * @param src pointer to the source buffer + * @param size length of data, in bytes * * @return esp_err_t */ -esp_err_t spi_flash_write(uint32_t des_addr, const uint32_t *src_addr, uint32_t size); +esp_err_t spi_flash_write(size_t dest, const void *src, size_t size); /** * @brief Read data from Flash. * - * @param uint32 src_addr : source address of the data in Flash. - * @param uint32 *des_addr : destination address. - * @param uint32 size : length of data + * @note Both des_addr and src_addr have to be 4-byte aligned. + * This is a temporary limitation which will be removed. + * + * @param src source address of the data in Flash. + * @param dest pointer to the destination buffer + * @param size length of data * * @return esp_err_t */ -esp_err_t spi_flash_read(uint32_t src_addr, uint32_t *des_addr, uint32_t size); +esp_err_t spi_flash_read(size_t src, void *dest, size_t size); +/** + * @brief Enumeration which specifies memory space requested in an mmap call + */ +typedef enum { + SPI_FLASH_MMAP_DATA, /**< map to data memory (Vaddr0), allows byte-aligned access, 4 MB total */ + SPI_FLASH_MMAP_INST, /**< map to instruction memory (Vaddr1-3), allows only 4-byte-aligned access, 11 MB total */ +} spi_flash_mmap_memory_t; + +/** + * @brief Opaque handle for memory region obtained from spi_flash_mmap. + */ +typedef uint32_t spi_flash_mmap_handle_t; + +/** + * @brief Map region of flash memory into data or instruction address space + * + * This function allocates sufficient number of 64k MMU pages and configures + * them to map request region of flash memory into data address space or into + * instruction address space. It may reuse MMU pages which already provide + * required mapping. As with any allocator, there is possibility of fragmentation + * of address space if mmap/munmap are heavily used. To troubleshoot issues with + * page allocation, use spi_flash_mmap_dump function. + * + * @param src_addr Physical address in flash where requested region starts. + * This address *must* be aligned to 64kB boundary. + * @param size Size of region which has to be mapped. This size will be rounded + * up to a 64k boundary. + * @param memory Memory space where the region should be mapped + * @param out_ptr Output, pointer to the mapped memory region + * @param out_handle Output, handle which should be used for spi_flash_munmap call + * + * @return ESP_OK on success, ESP_ERR_NO_MEM if pages can not be allocated + */ +esp_err_t spi_flash_mmap(uint32_t src_addr, size_t size, spi_flash_mmap_memory_t memory, + const void** out_ptr, spi_flash_mmap_handle_t* out_handle); + +/** + * @brief Release region previously obtained using spi_flash_mmap + * + * @note Calling this function will not necessarily unmap memory region. + * Region will only be unmapped when there are no other handles which + * reference this region. In case of partially overlapping regions + * it is possible that memory will be unmapped partially. + * + * @param handle Handle obtained from spi_flash_mmap + */ +void spi_flash_munmap(spi_flash_mmap_handle_t handle); + +/** + * @brief Display information about mapped regions + * + * This function lists handles obtained using spi_flash_mmap, along with range + * of pages allocated to each handle. It also lists all non-zero entries of + * MMU table and corresponding reference counts. + */ +void spi_flash_mmap_dump(); #if CONFIG_SPI_FLASH_ENABLE_COUNTERS @@ -78,7 +164,7 @@ esp_err_t spi_flash_read(uint32_t src_addr, uint32_t *des_addr, uint32_t size); typedef struct { uint32_t count; // number of times operation was executed uint32_t time; // total time taken, in microseconds - uint32_t bytes; // total number of bytes, for read and write operations + uint32_t bytes; // total number of bytes } spi_flash_counter_t; typedef struct { diff --git a/components/spi_flash/partition.c b/components/spi_flash/partition.c new file mode 100644 index 0000000000..21013d96fa --- /dev/null +++ b/components/spi_flash/partition.c @@ -0,0 +1,269 @@ +// 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 +#include +#include +#include + +#include "esp_attr.h" +#include "esp_flash_data_types.h" +#include "esp_spi_flash.h" +#include "esp_partition.h" +#include "esp_log.h" + + +#ifndef NDEBUG +// Enable built-in checks in queue.h in debug builds +#define INVARIANTS +#endif +#include "rom/queue.h" + + +typedef struct partition_list_item_ { + esp_partition_t info; + SLIST_ENTRY(partition_list_item_) next; +} partition_list_item_t; + +typedef struct esp_partition_iterator_opaque_ { + esp_partition_type_t type; // requested type + esp_partition_subtype_t subtype; // requested subtype + const char* label; // requested label (can be NULL) + partition_list_item_t* next_item; // next item to iterate to + esp_partition_t* info; // pointer to info (it is redundant, but makes code more readable) +} esp_partition_iterator_opaque_t; + + +static esp_partition_iterator_opaque_t* iterator_create(esp_partition_type_t type, esp_partition_subtype_t subtype, const char* label); +static esp_err_t load_partitions(); + + +static SLIST_HEAD(partition_list_head_, partition_list_item_) s_partition_list = + SLIST_HEAD_INITIALIZER(s_partition_list); +static _lock_t s_partition_list_lock; + + +esp_partition_iterator_t esp_partition_find(esp_partition_type_t type, + esp_partition_subtype_t subtype, const char* label) +{ + if (SLIST_EMPTY(&s_partition_list)) { + // only lock if list is empty (and check again after acquiring lock) + _lock_acquire(&s_partition_list_lock); + esp_err_t err = ESP_OK; + if (SLIST_EMPTY(&s_partition_list)) { + err = load_partitions(); + } + _lock_release(&s_partition_list_lock); + if (err != ESP_OK) { + return NULL; + } + } + // create an iterator pointing to the start of the list + // (next item will be the first one) + esp_partition_iterator_t it = iterator_create(type, subtype, label); + // advance iterator to the next item which matches constraints + it = esp_partition_next(it); + // if nothing found, it == NULL and iterator has been released + return it; +} + +esp_partition_iterator_t esp_partition_next(esp_partition_iterator_t it) +{ + assert(it); + // iterator reached the end of linked list? + if (it->next_item == NULL) { + return NULL; + } + _lock_acquire(&s_partition_list_lock); + for (; it->next_item != NULL; it->next_item = SLIST_NEXT(it->next_item, next)) { + esp_partition_t* p = &it->next_item->info; + if (it->type != p->type) { + continue; + } + if (it->subtype != 0xff && it->subtype != p->subtype) { + continue; + } + if (it->label != NULL && strcmp(it->label, p->label) != 0) { + continue; + } + // all constraints match, bail out + break; + } + _lock_release(&s_partition_list_lock); + if (it->next_item == NULL) { + esp_partition_iterator_release(it); + return NULL; + } + it->info = &it->next_item->info; + it->next_item = SLIST_NEXT(it->next_item, next); + return it; +} + +const esp_partition_t* esp_partition_find_first(esp_partition_type_t type, + esp_partition_subtype_t subtype, const char* label) +{ + esp_partition_iterator_t it = esp_partition_find(type, subtype, label); + if (it == NULL) { + return NULL; + } + const esp_partition_t* res = esp_partition_get(it); + esp_partition_iterator_release(it); + return res; +} + +static esp_partition_iterator_opaque_t* iterator_create(esp_partition_type_t type, + esp_partition_subtype_t subtype, const char* label) +{ + esp_partition_iterator_opaque_t* it = + (esp_partition_iterator_opaque_t*) malloc(sizeof(esp_partition_iterator_opaque_t)); + it->type = type; + it->subtype = subtype; + it->label = label; + it->next_item = SLIST_FIRST(&s_partition_list); + it->info = NULL; + return it; +} + +// Create linked list of partition_list_item_t structures. +// This function is called only once, with s_partition_list_lock taken. +static esp_err_t load_partitions() +{ + const uint32_t* ptr; + spi_flash_mmap_handle_t handle; + // map 64kB block where partition table is located + esp_err_t err = spi_flash_mmap(ESP_PARTITION_TABLE_ADDR & 0xffff0000, + SPI_FLASH_SEC_SIZE, SPI_FLASH_MMAP_DATA, (const void**) &ptr, &handle); + if (err != ESP_OK) { + return err; + } + // calculate partition address within mmap-ed region + const esp_partition_info_t* it = (const esp_partition_info_t*) + (ptr + (ESP_PARTITION_TABLE_ADDR & 0xffff) / sizeof(*ptr)); + const esp_partition_info_t* end = it + SPI_FLASH_SEC_SIZE / sizeof(*it); + // tail of the linked list of partitions + partition_list_item_t* last = NULL; + for (; it != end; ++it) { + if (it->magic != ESP_PARTITION_MAGIC) { + break; + } + // allocate new linked list item and populate it with data from partition table + partition_list_item_t* item = (partition_list_item_t*) malloc(sizeof(partition_list_item_t)); + item->info.address = it->pos.offset; + item->info.size = it->pos.size; + item->info.type = it->type; + item->info.subtype = it->subtype; + item->info.encrypted = false; + // 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; + // add it to the list + if (last == NULL) { + SLIST_INSERT_HEAD(&s_partition_list, item, next); + } else { + SLIST_INSERT_AFTER(last, item, next); + } + } + spi_flash_munmap(handle); + return ESP_OK; +} + +void esp_partition_iterator_release(esp_partition_iterator_t iterator) +{ + // iterator == NULL is okay + free(iterator); +} + +const esp_partition_t* esp_partition_get(esp_partition_iterator_t iterator) +{ + assert(iterator != NULL); + return iterator->info; +} + +esp_err_t esp_partition_read(const esp_partition_t* partition, + size_t src_offset, void* dst, size_t size) +{ + assert(partition != NULL); + if (src_offset > partition->size) { + return ESP_ERR_INVALID_ARG; + } + if (src_offset + size > partition->size) { + return ESP_ERR_INVALID_SIZE; + } + return spi_flash_read(partition->address + src_offset, dst, size); +} + +esp_err_t esp_partition_write(const esp_partition_t* partition, + size_t dst_offset, const void* src, size_t size) +{ + assert(partition != NULL); + if (dst_offset > partition->size) { + return ESP_ERR_INVALID_ARG; + } + if (dst_offset + size > partition->size) { + return ESP_ERR_INVALID_SIZE; + } + return spi_flash_write(partition->address + dst_offset, src, size); +} + +esp_err_t esp_partition_erase_range(const esp_partition_t* partition, + size_t start_addr, size_t size) +{ + assert(partition != NULL); + if (start_addr > partition->size) { + return ESP_ERR_INVALID_ARG; + } + if (start_addr + size > partition->size) { + return ESP_ERR_INVALID_SIZE; + } + if (size % SPI_FLASH_SEC_SIZE != 0) { + return ESP_ERR_INVALID_SIZE; + } + if (start_addr % SPI_FLASH_SEC_SIZE != 0) { + return ESP_ERR_INVALID_ARG; + } + return spi_flash_erase_range(partition->address + start_addr, size); + +} + +/* + * Note: current implementation ignores the possibility of multiple regions in the same partition being + * mapped. Reference counting and address space re-use is delegated to spi_flash_mmap. + * + * If this becomes a performance issue (i.e. if we need to map multiple regions within the partition), + * we can add esp_partition_mmapv which will accept an array of offsets and sizes, and return array of + * mmaped pointers, and a single handle for all these regions. + */ +esp_err_t esp_partition_mmap(const esp_partition_t* partition, uint32_t offset, uint32_t size, + spi_flash_mmap_memory_t memory, + const void** out_ptr, spi_flash_mmap_handle_t* out_handle) +{ + assert(partition != NULL); + if (offset > partition->size) { + return ESP_ERR_INVALID_ARG; + } + if (offset + size > partition->size) { + return ESP_ERR_INVALID_SIZE; + } + size_t phys_addr = partition->address + offset; + // offset within 64kB block + size_t region_offset = phys_addr & 0xffff; + size_t mmap_addr = phys_addr & 0xffff0000; + esp_err_t rc = spi_flash_mmap(mmap_addr, size, memory, out_ptr, out_handle); + // adjust returned pointer to point to the correct offset + if (rc == ESP_OK) { + *out_ptr = (void*) (((ptrdiff_t) *out_ptr) + region_offset); + } + return rc; +} diff --git a/components/spi_flash/spi_flash_rom_patch.c b/components/spi_flash/spi_flash_rom_patch.c new file mode 100644 index 0000000000..7e23beaea2 --- /dev/null +++ b/components/spi_flash/spi_flash_rom_patch.c @@ -0,0 +1,72 @@ +// 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 "rom/spi_flash.h" +#include "soc/spi_reg.h" + +static const uint32_t STATUS_QIE_BIT = (1 << 9); /* Quad Enable */ + +#define SPI_IDX 1 +#define OTH_IDX 0 + +extern SpiFlashChip SPI_flashchip_data; + +static void IRAM_ATTR Wait_SPI_Idle(void) +{ + /* Wait for SPI state machine to be idle */ + while((REG_READ(SPI_EXT2_REG(SPI_IDX)) & SPI_ST)) { + } + while(REG_READ(SPI_EXT2_REG(OTH_IDX)) & SPI_ST) { + } +} + +/* Modified version of SPIUnlock() that replaces version in ROM. + + This works around a bug where SPIUnlock sometimes reads the wrong + high status byte (RDSR2 result) and then copies it back to the + flash status, which can cause the CMP bit or Status Register + Protect bit to become set. + + Like other ROM SPI functions, this function is not designed to be + called directly from an RTOS environment without taking precautions + about interrupts, CPU coordination, flash mapping. However some of + the functions in esp_spi_flash.c call it. + */ +SpiFlashOpResult IRAM_ATTR SPIUnlock(void) +{ + uint32_t status; + + Wait_SPI_Idle(); + + if (SPI_read_status_high(&status) != SPI_FLASH_RESULT_OK) { + return SPI_FLASH_RESULT_ERR; + } + + /* Clear all bits except QIE, if it is set. + (This is different from ROM SPIUnlock, which keeps all bits as-is.) + */ + status &= STATUS_QIE_BIT; + + Wait_SPI_Idle(); + REG_WRITE(SPI_CMD_REG(SPI_IDX), SPI_FLASH_WREN); + while(REG_READ(SPI_CMD_REG(SPI_IDX)) != 0) { + } + Wait_SPI_Idle(); + + SET_PERI_REG_MASK(SPI_CTRL_REG(SPI_IDX), SPI_WRSR_2B); + if (SPI_write_status(&SPI_flashchip_data, status) != SPI_FLASH_RESULT_OK) { + return SPI_FLASH_RESULT_ERR; + } + + return SPI_FLASH_RESULT_OK; +} diff --git a/components/tcpip_adapter/include/tcpip_adapter.h b/components/tcpip_adapter/include/tcpip_adapter.h index 5b0fc4c627..e847016884 100644 --- a/components/tcpip_adapter/include/tcpip_adapter.h +++ b/components/tcpip_adapter/include/tcpip_adapter.h @@ -67,11 +67,15 @@ typedef struct { typedef dhcps_lease_t tcpip_adapter_dhcps_lease_t; #if CONFIG_DHCP_STA_LIST -struct station_list { - STAILQ_ENTRY(station_list) next; +typedef struct { uint8_t mac[6]; ip4_addr_t ip; -}; +}tcpip_adapter_sta_info_t; + +typedef struct { + tcpip_adapter_sta_info_t sta[ESP_WIFI_MAX_CONN_NUM]; + int num; +}tcpip_adapter_sta_list_t; #endif #endif @@ -359,26 +363,14 @@ wifi_interface_t tcpip_adapter_get_wifi_if(void *dev); /** * @brief Get the station information list * - * @note This function should be called in AP mode and dhcp server started, and the list should - * be by using tcpip_adapter_free_sta_list. - * - * @param[in] sta_info: station information - * @param[out] sta_list: station information list + * @param[in] wifi_sta_list_t *wifi_sta_list: station list info + * @param[out] tcpip_adapter_sta_list_t *tcpip_sta_list: station list info * * @return ESP_OK * ESP_ERR_TCPIP_ADAPTER_NO_MEM * ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS */ -esp_err_t tcpip_adapter_get_sta_list(struct station_info *sta_info, struct station_list **sta_list); - -/** - * @brief Free the station information list's memory - * - * @param sta_list: station information list - * - * @return ESP_OK - */ -esp_err_t tcpip_adapter_free_sta_list(struct station_list *sta_list); +esp_err_t tcpip_adapter_get_sta_list(wifi_sta_list_t *wifi_sta_list, tcpip_adapter_sta_list_t *tcpip_sta_list); #ifdef __cplusplus } diff --git a/components/tcpip_adapter/tcpip_adapter_lwip.c b/components/tcpip_adapter/tcpip_adapter_lwip.c index 78fecf2cbf..9b6e9d94fa 100644 --- a/components/tcpip_adapter/tcpip_adapter_lwip.c +++ b/components/tcpip_adapter/tcpip_adapter_lwip.c @@ -103,7 +103,9 @@ esp_err_t tcpip_adapter_stop(tcpip_adapter_if_t tcpip_if) if (tcpip_if == TCPIP_ADAPTER_IF_AP) { dhcps_stop(esp_netif[tcpip_if]); // TODO: dhcps checks status by its self - dhcps_status = TCPIP_ADAPTER_DHCP_INIT; + if (TCPIP_ADAPTER_DHCP_STOPPED != dhcps_status){ + dhcps_status = TCPIP_ADAPTER_DHCP_INIT; + } } else if (tcpip_if == TCPIP_ADAPTER_IF_STA) { dhcp_release(esp_netif[tcpip_if]); dhcp_stop(esp_netif[tcpip_if]); @@ -588,45 +590,18 @@ wifi_interface_t tcpip_adapter_get_wifi_if(void *dev) return WIFI_IF_MAX; } -esp_err_t tcpip_adapter_get_sta_list(struct station_info *sta_info, struct station_list **sta_list) +esp_err_t tcpip_adapter_get_sta_list(wifi_sta_list_t *wifi_sta_list, tcpip_adapter_sta_list_t *tcpip_sta_list) { - struct station_info *info = sta_info; - struct station_list *list; - STAILQ_HEAD(, station_list) list_head; + int i; - if (sta_list == NULL) + if ((wifi_sta_list == NULL) || (tcpip_sta_list == NULL)) return ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS; - STAILQ_INIT(&list_head); - - while (info != NULL) { - list = (struct station_list *)malloc(sizeof(struct station_list)); - memset(list, 0, sizeof (struct station_list)); - - if (list == NULL) { - return ESP_ERR_TCPIP_ADAPTER_NO_MEM; - } - - memcpy(list->mac, info->bssid, 6); - dhcp_search_ip_on_mac(list->mac, &list->ip); - STAILQ_INSERT_TAIL(&list_head, list, next); - - info = STAILQ_NEXT(info, next); - } - - *sta_list = STAILQ_FIRST(&list_head); - - return ESP_OK; -} - -esp_err_t tcpip_adapter_free_sta_list(struct station_list *sta_list) -{ - struct station_list *list = sta_list; - - while (sta_list != NULL) { - list = sta_list; - sta_list = STAILQ_NEXT(sta_list, next); - free(list); + memset(tcpip_sta_list, 0, sizeof(tcpip_adapter_sta_list_t)); + tcpip_sta_list->num = wifi_sta_list->num; + for (i=0; inum; i++){ + memcpy(tcpip_sta_list->sta[i].mac, wifi_sta_list->sta[i].mac, 6); + dhcp_search_ip_on_mac(tcpip_sta_list->sta[i].mac, &tcpip_sta_list->sta[i].ip); } return ESP_OK; diff --git a/components/vfs/README.rst b/components/vfs/README.rst new file mode 100644 index 0000000000..c58161c900 --- /dev/null +++ b/components/vfs/README.rst @@ -0,0 +1,128 @@ +Virtual filesystem component +============================ + +Overview +-------- + +Virtual filesystem (VFS) component provides a unified interface for drivers which can perform operations on file-like objects. This can be a real filesystems (FAT, SPIFFS, etc.), or device drivers which exposes file-like interface. + +This component allows C library functions, such as fopen and fprintf, to work with FS drivers. At high level, each FS driver is associated with some path prefix. When one of C library functions needs to open a file, VFS component searches for the FS driver associated with the file's path, and forwards the call to that driver. VFS also forwards read, write, and other calls for the given file to the same FS driver. + +For example, one can register a FAT filesystem driver with ``/fat`` prefix, and call ``fopen("/fat/file.txt", "w")``. VFS component will then call ``open`` function of FAT driver and pass ``/file.txt`` argument to it (and appropriate mode flags). All subsequent calls to C library functions for the returned ``FILE*`` stream will also be forwarded to the FAT driver. + +FS registration +--------------- + +To register an FS driver, application needs to define in instance of esp_vfs_t structure and populate it with function pointers to FS APIs:: + + esp_vfs_t myfs = { + .fd_offset = 0, + .flags = ESP_VFS_FLAG_DEFAULT, + .write = &myfs_write, + .open = &myfs_open, + .fstat = &myfs_fstat, + .close = &myfs_close, + .read = &myfs_read, + .lseek = NULL, + .stat = NULL, + .link = NULL, + .unlink = NULL, + .rename = NULL + }; + + ESP_ERROR_CHECK(esp_vfs_register("/data", &myfs, NULL)); + +Depending on the way FS driver declares its APIs, either ``read``, ``write``, etc., or ``read_p``, ``write_p``, etc. should be used. + +Case 1: API functions are declared without an extra context pointer (FS driver is a singleton):: + + size_t myfs_write(int fd, const void * data, size_t size); + + // In definition of esp_vfs_t: + .flags = ESP_VFS_FLAG_DEFAULT, + .write = &myfs_write, + // ... other members initialized + + // When registering FS, context pointer (third argument) is NULL: + ESP_ERROR_CHECK(esp_vfs_register("/data", &myfs, NULL)); + +Case 2: API functions are declared with an extra context pointer (FS driver supports multiple instances):: + + size_t myfs_write(myfs_t* fs, int fd, const void * data, size_t size); + + // In definition of esp_vfs_t: + .flags = ESP_VFS_FLAG_CONTEXT_PTR, + .write_p = &myfs_write, + // ... other members initialized + + // When registering FS, pass the FS context pointer into the third argument + // (hypothetical myfs_mount function is used for illustrative purposes) + myfs_t* myfs_inst1 = myfs_mount(partition1->offset, partition1->size); + ESP_ERROR_CHECK(esp_vfs_register("/data1", &myfs, myfs_inst1)); + + // Can register another instance: + myfs_t* myfs_inst2 = myfs_mount(partition2->offset, partition2->size); + ESP_ERROR_CHECK(esp_vfs_register("/data2", &myfs, myfs_inst2)); + +Paths +----- + +Each registered FS has a path prefix associated with it. This prefix may be considered a "mount point" of this partition. + +Registering mount points which have another mount point as a prefix is not supported and results in undefined behavior. For instance, the following is correct and supported: + +- FS 1 on /data/fs1 +- FS 2 on /data/fs2 + +This **will not work** as expected: + +- FS 1 on /data +- FS 2 on /data/fs2 + +When opening files, FS driver will only be given relative path to files. For example: + +- ``myfs`` driver is registered with ``/data`` as path prefix +- and application calls ``fopen("/data/config.json", ...)`` +- then VFS component will call ``myfs_open("/config.json", ...)``. +- ``myfs`` driver will open ``/config.json`` file + +VFS doesn't impose a limit on total file path length, but it does limit FS path prefix to ``ESP_VFS_PATH_MAX`` characters. Individual FS drivers may have their own filename length limitations. + + +File descriptors +---------------- + +It is suggested that filesystem drivers should use small positive integers as file descriptors. VFS component assumes that ``CONFIG_MAX_FD_BITS`` bits (12 by default) are sufficient to represent a file descriptor. + +If filesystem is configured with an option to offset all file descriptors by a constant value, such value should be passed to ``fd_offset`` field of ``esp_vfs_t`` structure. VFS component will then remove this offset when working with FDs of that specific FS, bringing them into the range of small positive integers. + +While file descriptors returned by VFS component to newlib library are rarely seen by the application, the following details may be useful for debugging purposes. File descriptors returned by VFS component are composed of two parts: FS driver ID, and the actual file descriptor. Because newlib stores file descriptors as 16-bit integers, VFS component is also limited by 16 bits to store both parts. + +Lower ``CONFIG_MAX_FD_BITS`` bits are used to store zero-based file descriptor. If FS driver has a non-zero ``fd_offset`` field, this ``fd_offset`` is subtracted FDs obtained from the FS ``open`` call, and the result is stored in the lower bits of the FD. Higher bits are used to save the index of FS in the internal table of registered filesystems. + +When VFS component receives a call from newlib which has a file descriptor, this file descriptor is translated back to the FS-specific file descriptor. First, higher bits of FD are used to identify the FS. Then ``fd_offset`` field of the FS is added to the lower ``CONFIG_MAX_FD_BITS`` bits of the fd, and resulting FD is passed to the FS driver. + +:: + + FD as seen by newlib FD as seen by FS driver + +-----+ + +-------+---------------+ | | +------------------------+ + | FS id | Zero—based FD | +---------------> sum +----> | + +---+---+------+--------+ | | | +------------------------+ + | | | +--^--+ + | +--------------+ | + | | + | +-------------+ | + | | Table of | | + | | registered | | + | | filesystems | | + | +-------------+ +-------------+ | + +-------> entry +----> esp_vfs_t | | + index +-------------+ | structure | | + | | | | | + | | | + fd_offset +---+ + +-------------+ | | + +-------------+ + + + diff --git a/components/vfs/component.mk b/components/vfs/component.mk new file mode 100755 index 0000000000..fccf88db8a --- /dev/null +++ b/components/vfs/component.mk @@ -0,0 +1 @@ +include $(IDF_PATH)/make/component_common.mk diff --git a/components/vfs/include/esp_vfs.h b/components/vfs/include/esp_vfs.h new file mode 100644 index 0000000000..2d9e52c5af --- /dev/null +++ b/components/vfs/include/esp_vfs.h @@ -0,0 +1,157 @@ +// 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_VFS_H__ +#define __ESP_VFS_H__ + +#include +#include +#include "esp_err.h" +#include +#include +#include +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Maximum length of path prefix (not including zero terminator) + */ +#define ESP_VFS_PATH_MAX 15 + +/** + * Default value of flags member in esp_vfs_t structure. + */ +#define ESP_VFS_FLAG_DEFAULT 0 + +/** + * Flag which indicates that FS needs extra context pointer in syscalls. + */ +#define ESP_VFS_FLAG_CONTEXT_PTR 1 + +/** + * @brief VFS definition structure + * + * This structure should be filled with pointers to corresponding + * FS driver functions. + * + * If the FS implementation has an option to use certain offset for + * all file descriptors, this value should be passed into fd_offset + * field. Otherwise VFS component will translate all FDs to start + * at zero offset. + * + * Some FS implementations expect some state (e.g. pointer to some structure) + * to be passed in as a first argument. For these implementations, + * populate the members of this structure which have _p suffix, set + * flags member to ESP_VFS_FLAG_CONTEXT_PTR and provide the context pointer + * to esp_vfs_register function. + * If the implementation doesn't use this extra argument, populate the + * members without _p suffix and set flags memeber to ESP_VFS_FLAG_DEFAULT. + * + * If the FS driver doesn't provide some of the functions, set corresponding + * members to NULL. + */ +typedef struct +{ + int fd_offset; + int flags; + union { + size_t (*write_p)(void* p, int fd, const void * data, size_t size); + size_t (*write)(int fd, const void * data, size_t size); + }; + union { + off_t (*lseek_p)(void* p, int fd, off_t size, int mode); + off_t (*lseek)(int fd, off_t size, int mode); + }; + union { + ssize_t (*read_p)(void* ctx, int fd, void * dst, size_t size); + ssize_t (*read)(int fd, void * dst, size_t size); + }; + union { + int (*open_p)(void* ctx, const char * path, int flags, int mode); + int (*open)(const char * path, int flags, int mode); + }; + union { + int (*close_p)(void* ctx, int fd); + int (*close)(int fd); + }; + union { + int (*fstat_p)(void* ctx, int fd, struct stat * st); + int (*fstat)(int fd, struct stat * st); + }; + union { + int (*stat_p)(void* ctx, const char * path, struct stat * st); + int (*stat)(const char * path, struct stat * st); + }; + union { + int (*link_p)(void* ctx, const char* n1, const char* n2); + int (*link)(const char* n1, const char* n2); + }; + union { + int (*unlink_p)(void* ctx, const char *path); + int (*unlink)(const char *path); + }; + union { + int (*rename_p)(void* ctx, const char *src, const char *dst); + int (*rename)(const char *src, const char *dst); + }; +} esp_vfs_t; + + +/** + * Register a virtual filesystem for given path prefix. + * + * @param base_path file path prefix associated with the filesystem. + * Must be a zero-terminated C string, up to ESP_VFS_PATH_MAX + * characters long, and at least 2 characters long. + * Name must start with a "/" and must not end with "/". + * For example, "/data" or "/dev/spi" are valid. + * These VFSes would then be called to handle file paths such as + * "/data/myfile.txt" or "/dev/spi/0". + * @param vfs Pointer to esp_vfs_t, a structure which maps syscalls to + * the filesystem driver functions. VFS component doesn't + * assume ownership of this pointer. + * @param ctx If vfs->flags has ESP_VFS_FLAG_CONTEXT_PTR set, a pointer + * which should be passed to VFS functions. Otherwise, NULL. + * + * @return ESP_OK if successful, ESP_ERR_NO_MEM if too many VFSes are + * registered. + */ +esp_err_t esp_vfs_register(const char* base_path, const esp_vfs_t* vfs, void* ctx); + + +/** + * These functions are to be used in newlib syscall table. They will be called by + * newlib when it needs to use any of the syscalls. + */ + +ssize_t esp_vfs_write(struct _reent *r, int fd, const void * data, size_t size); +off_t esp_vfs_lseek(struct _reent *r, int fd, off_t size, int mode); +ssize_t esp_vfs_read(struct _reent *r, int fd, void * dst, size_t size); +int esp_vfs_open(struct _reent *r, const char * path, int flags, int mode); +int esp_vfs_close(struct _reent *r, int fd); +int esp_vfs_fstat(struct _reent *r, int fd, struct stat * st); +int esp_vfs_stat(struct _reent *r, const char * path, struct stat * st); +int esp_vfs_link(struct _reent *r, const char* n1, const char* n2); +int esp_vfs_unlink(struct _reent *r, const char *path); +int esp_vfs_rename(struct _reent *r, const char *src, const char *dst); + + + +#ifdef __cplusplus +} // extern "C" +#endif + + +#endif //__ESP_VFS_H__ diff --git a/components/vfs/include/esp_vfs_dev.h b/components/vfs/include/esp_vfs_dev.h new file mode 100644 index 0000000000..bb2579ee0a --- /dev/null +++ b/components/vfs/include/esp_vfs_dev.h @@ -0,0 +1,28 @@ +// 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_VFS_DEV_H__ +#define __ESP_VFS_DEV_H__ + +#include "esp_vfs.h" + +/** + * @brief add /dev/uart virtual filesystem driver + * + * This function is called from startup code to enable serial output + */ +void esp_vfs_dev_uart_register(); + + +#endif //__ESP_VFS_DEV_H__ diff --git a/components/vfs/vfs.c b/components/vfs/vfs.c new file mode 100644 index 0000000000..bf26968ff7 --- /dev/null +++ b/components/vfs/vfs.c @@ -0,0 +1,285 @@ +// 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 +#include +#include +#include "esp_vfs.h" +#include "esp_log.h" + +/* + * File descriptors visible by the applications are composed of two parts. + * Lower CONFIG_MAX_FD_BITS bits are used for the actual file descriptor. + * Next (16 - CONFIG_MAX_FD_BITS - 1) bits are used to identify the VFS this + * descriptor corresponds to. + * Highest bit is zero. + * We can only use 16 bits because newlib stores file descriptor as short int. + */ + +#ifndef CONFIG_MAX_FD_BITS +#define CONFIG_MAX_FD_BITS 12 +#endif + +#define MAX_VFS_ID_BITS (16 - CONFIG_MAX_FD_BITS - 1) +// mask of actual file descriptor (e.g. 0x00000fff) +#define VFS_FD_MASK ((1 << CONFIG_MAX_FD_BITS) - 1) +// max number of VFS entries +#define VFS_MAX_COUNT ((1 << MAX_VFS_ID_BITS) - 1) +// mask of VFS id (e.g. 0x00007000) +#define VFS_INDEX_MASK (VFS_MAX_COUNT << CONFIG_MAX_FD_BITS) +#define VFS_INDEX_S CONFIG_MAX_FD_BITS + +typedef struct vfs_entry_ { + esp_vfs_t vfs; // contains pointers to VFS functions + char path_prefix[ESP_VFS_PATH_MAX]; // path prefix mapped to this VFS + size_t path_prefix_len; // micro-optimization to avoid doing extra strlen + void* ctx; // optional pointer which can be passed to VFS + int offset; // index of this structure in s_vfs array +} vfs_entry_t; + +static vfs_entry_t* s_vfs[VFS_MAX_COUNT] = { 0 }; +static size_t s_vfs_count = 0; + +esp_err_t esp_vfs_register(const char* base_path, const esp_vfs_t* vfs, void* ctx) +{ + if (s_vfs_count >= VFS_MAX_COUNT) { + return ESP_ERR_NO_MEM; + } + size_t len = strlen(base_path); + if (len < 2 || len > ESP_VFS_PATH_MAX) { + return ESP_ERR_INVALID_ARG; + } + if (base_path[0] != '/' || base_path[len - 1] == '/') { + return ESP_ERR_INVALID_ARG; + } + vfs_entry_t *entry = (vfs_entry_t*) malloc(sizeof(vfs_entry_t)); + if (entry == NULL) { + return ESP_ERR_NO_MEM; + } + strcpy(entry->path_prefix, base_path); // we have already verified argument length + memcpy(&entry->vfs, vfs, sizeof(esp_vfs_t)); + entry->path_prefix_len = len; + entry->ctx = ctx; + entry->offset = s_vfs_count; + s_vfs[s_vfs_count] = entry; + ++s_vfs_count; + return ESP_OK; +} + +static const vfs_entry_t* get_vfs_for_fd(int fd) +{ + int index = ((fd & VFS_INDEX_MASK) >> VFS_INDEX_S); + if (index >= s_vfs_count) { + return NULL; + } + return s_vfs[index]; +} + +static int translate_fd(const vfs_entry_t* vfs, int fd) +{ + return (fd & VFS_FD_MASK) + vfs->vfs.fd_offset; +} + +static const char* translate_path(const vfs_entry_t* vfs, const char* src_path) +{ + assert(strncmp(src_path, vfs->path_prefix, vfs->path_prefix_len) == 0); + return src_path + vfs->path_prefix_len; +} + +static const vfs_entry_t* get_vfs_for_path(const char* path) +{ + size_t len = strlen(path); + for (size_t i = 0; i < s_vfs_count; ++i) { + const vfs_entry_t* vfs = s_vfs[i]; + if (len < vfs->path_prefix_len + 1) { // +1 is for the trailing slash after base path + continue; + } + if (memcmp(path, vfs->path_prefix, vfs->path_prefix_len) != 0) { // match prefix + continue; + } + if (path[vfs->path_prefix_len] != '/') { // don't match "/data" prefix for "/data1/foo.txt" + continue; + } + return vfs; + } + return NULL; +} + +/* + * Using huge multi-line macros is never nice, but in this case + * the only alternative is to repeat this chunk of code (with different function names) + * for each syscall being implemented. Given that this define is contained within a single + * file, this looks like a good tradeoff. + * + * First we check if syscall is implemented by VFS (corresponding member is not NULL), + * then call the right flavor of the method (e.g. open or open_p) depending on + * ESP_VFS_FLAG_CONTEXT_PTR flag. If ESP_VFS_FLAG_CONTEXT_PTR is set, context is passed + * in as first argument and _p variant is used for the call. + * It is enough to check just one of them for NULL, as both variants are part of a union. + */ +#define CHECK_AND_CALL(ret, r, pvfs, func, ...) \ + if (pvfs->vfs.func == NULL) { \ + __errno_r(r) = ENOSYS; \ + return -1; \ + } \ + if (pvfs->vfs.flags & ESP_VFS_FLAG_CONTEXT_PTR) { \ + ret = (*pvfs->vfs.func ## _p)(pvfs->ctx, __VA_ARGS__); \ + } else { \ + ret = (*pvfs->vfs.func)(__VA_ARGS__);\ + } + + +int esp_vfs_open(struct _reent *r, const char * path, int flags, int mode) +{ + const vfs_entry_t* vfs = get_vfs_for_path(path); + if (vfs == NULL) { + __errno_r(r) = ENOENT; + return -1; + } + const char* path_within_vfs = translate_path(vfs, path); + int ret; + CHECK_AND_CALL(ret, r, vfs, open, path_within_vfs, flags, mode); + return ret - vfs->vfs.fd_offset + (vfs->offset << VFS_INDEX_S); +} + +ssize_t esp_vfs_write(struct _reent *r, int fd, const void * data, size_t size) +{ + const vfs_entry_t* vfs = get_vfs_for_fd(fd); + if (vfs == NULL) { + __errno_r(r) = EBADF; + return -1; + } + int local_fd = translate_fd(vfs, fd); + int ret; + CHECK_AND_CALL(ret, r, vfs, write, local_fd, data, size); + return ret; +} + +off_t esp_vfs_lseek(struct _reent *r, int fd, off_t size, int mode) +{ + const vfs_entry_t* vfs = get_vfs_for_fd(fd); + if (vfs == NULL) { + __errno_r(r) = EBADF; + return -1; + } + int local_fd = translate_fd(vfs, fd); + int ret; + CHECK_AND_CALL(ret, r, vfs, lseek, local_fd, size, mode); + return ret; +} + +ssize_t esp_vfs_read(struct _reent *r, int fd, void * dst, size_t size) +{ + const vfs_entry_t* vfs = get_vfs_for_fd(fd); + if (vfs == NULL) { + __errno_r(r) = EBADF; + return -1; + } + int local_fd = translate_fd(vfs, fd); + int ret; + CHECK_AND_CALL(ret, r, vfs, read, local_fd, dst, size); + return ret; +} + + +int esp_vfs_close(struct _reent *r, int fd) +{ + const vfs_entry_t* vfs = get_vfs_for_fd(fd); + if (vfs == NULL) { + __errno_r(r) = EBADF; + return -1; + } + int local_fd = translate_fd(vfs, fd); + int ret; + CHECK_AND_CALL(ret, r, vfs, close, local_fd); + return ret; +} + +int esp_vfs_fstat(struct _reent *r, int fd, struct stat * st) +{ + const vfs_entry_t* vfs = get_vfs_for_fd(fd); + if (vfs == NULL) { + __errno_r(r) = EBADF; + return -1; + } + int local_fd = translate_fd(vfs, fd); + int ret; + CHECK_AND_CALL(ret, r, vfs, fstat, local_fd, st); + return ret; +} + +int esp_vfs_stat(struct _reent *r, const char * path, struct stat * st) +{ + const vfs_entry_t* vfs = get_vfs_for_path(path); + if (vfs == NULL) { + __errno_r(r) = ENOENT; + return -1; + } + const char* path_within_vfs = translate_path(vfs, path); + int ret; + CHECK_AND_CALL(ret, r, vfs, stat, path_within_vfs, st); + return ret; +} + +int esp_vfs_link(struct _reent *r, const char* n1, const char* n2) +{ + const vfs_entry_t* vfs = get_vfs_for_path(n1); + if (vfs == NULL) { + __errno_r(r) = ENOENT; + return -1; + } + const vfs_entry_t* vfs2 = get_vfs_for_path(n2); + if (vfs != vfs2) { + __errno_r(r) = EXDEV; + return -1; + } + const char* path1_within_vfs = translate_path(vfs, n1); + const char* path2_within_vfs = translate_path(vfs, n2); + int ret; + CHECK_AND_CALL(ret, r, vfs, link, path1_within_vfs, path2_within_vfs); + return ret; +} + +int esp_vfs_unlink(struct _reent *r, const char *path) +{ + const vfs_entry_t* vfs = get_vfs_for_path(path); + if (vfs == NULL) { + __errno_r(r) = ENOENT; + return -1; + } + const char* path_within_vfs = translate_path(vfs, path); + int ret; + CHECK_AND_CALL(ret, r, vfs, unlink, path_within_vfs); + return ret; +} + +int esp_vfs_rename(struct _reent *r, const char *src, const char *dst) +{ + const vfs_entry_t* vfs = get_vfs_for_path(src); + if (vfs == NULL) { + __errno_r(r) = ENOENT; + return -1; + } + const vfs_entry_t* vfs_dst = get_vfs_for_path(dst); + if (vfs != vfs_dst) { + __errno_r(r) = EXDEV; + return -1; + } + const char* src_within_vfs = translate_path(vfs, src); + const char* dst_within_vfs = translate_path(vfs, dst); + int ret; + CHECK_AND_CALL(ret, r, vfs, rename, src_within_vfs, dst_within_vfs); + return ret; +} diff --git a/components/vfs/vfs_uart.c b/components/vfs/vfs_uart.c new file mode 100644 index 0000000000..d9d755f9be --- /dev/null +++ b/components/vfs/vfs_uart.c @@ -0,0 +1,103 @@ +// 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_vfs.h" +#include "esp_attr.h" +#include "sys/errno.h" +#include "sys/lock.h" +#include "soc/uart_struct.h" +#include "sdkconfig.h" + +static uart_dev_t* s_uarts[3] = {&UART0, &UART1, &UART2}; +static _lock_t s_uart_locks[3]; // per-UART locks, lazily initialized + +static int IRAM_ATTR uart_open(const char * path, int flags, int mode) +{ + // this is fairly primitive, we should check if file is opened read only, + // and error out if write is requested + if (strcmp(path, "/0") == 0) { + return 0; + } else if (strcmp(path, "/1") == 0) { + return 1; + } else if (strcmp(path, "/2") == 0) { + return 2; + } + errno = ENOENT; + return -1; +} + +static void IRAM_ATTR uart_tx_char(uart_dev_t* uart, int c) +{ + while (uart->status.txfifo_cnt >= 127) { + ; + } + uart->fifo.rw_byte = c; +} + + +static size_t IRAM_ATTR uart_write(int fd, const void * data, size_t size) +{ + assert(fd >=0 && fd < 3); + const char *data_c = (const char *)data; + uart_dev_t* uart = s_uarts[fd]; + /* + * Even though newlib does stream locking on each individual stream, we need + * a dedicated UART lock if two streams (stdout and stderr) point to the + * same UART. + */ + _lock_acquire_recursive(&s_uart_locks[fd]); + for (size_t i = 0; i < size; i++) { +#if CONFIG_NEWLIB_STDOUT_ADDCR + if (data_c[i]=='\n') { + uart_tx_char(uart, '\r'); + } +#endif + uart_tx_char(uart, data_c[i]); + } + _lock_release_recursive(&s_uart_locks[fd]); + return size; +} + +static int IRAM_ATTR uart_fstat(int fd, struct stat * st) +{ + assert(fd >=0 && fd < 3); + st->st_mode = S_IFCHR; + return 0; +} + +static int IRAM_ATTR uart_close(int fd) +{ + assert(fd >=0 && fd < 3); + return 0; +} + +void esp_vfs_dev_uart_register() +{ + esp_vfs_t vfs = { + .fd_offset = 0, + .flags = ESP_VFS_FLAG_DEFAULT, + .write = &uart_write, + .open = &uart_open, + .fstat = &uart_fstat, + .close = &uart_close, + .read = NULL, // TODO: implement reading from UART + .lseek = NULL, + .stat = NULL, + .link = NULL, + .unlink = NULL, + .rename = NULL + }; + ESP_ERROR_CHECK(esp_vfs_register("/dev/uart", &vfs, NULL)); +} diff --git a/make/test_build_system.sh b/make/test_build_system.sh index 7c8cbc1f1e..d08ae6c8a2 100755 --- a/make/test_build_system.sh +++ b/make/test_build_system.sh @@ -59,9 +59,9 @@ function run_tests() print_status "Updating component source file rebuilds component" # touch a file & do a build take_build_snapshot - touch ${IDF_PATH}/components/esp32/syscalls.c + touch ${IDF_PATH}/components/esp32/cpu_start.c make || failure "Failed to partial build" - assert_rebuilt ${APP_BINS} esp32/libesp32.a esp32/syscalls.o + assert_rebuilt ${APP_BINS} esp32/libesp32.a esp32/cpu_start.o assert_not_rebuilt lwip/liblwip.a freertos/libfreertos.a ${BOOTLOADER_BINS} partitions_singleapp.bin print_status "Bootloader source file rebuilds bootloader"