diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 96bc56e3e6..27663db32b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -167,6 +167,7 @@ test_report: only: - master - triggers + - /^release\/v.*$/ tags: - report variables: @@ -231,7 +232,7 @@ push_master_to_github: - chmod 600 ~/.ssh/id_rsa - echo -e "Host github.com\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config - git remote add github git@github.com:espressif/esp-idf.git - - git push --follow-tags github HEAD:master + - git push --follow-tags github HEAD deploy_docs: @@ -269,7 +270,7 @@ check_doc_links: - triggers script: # must be triggered with CHECK_LINKS=Yes, otherwise exit without test - - test $CHECK_LINKS = "Yes" || exit 0 + - test "$CHECK_LINKS" = "Yes" || exit 0 # can only run on master branch (otherwise the commit is not on Github yet) - test "${CI_BUILD_REF_NAME}" = "master" || exit 0 - cd docs @@ -287,6 +288,7 @@ check_doc_links: when: on_success only: - master + - /^release\/v.*$/ - triggers allow_failure: true diff --git a/components/app_update/esp_ota_ops.c b/components/app_update/esp_ota_ops.c index c702a203e2..96e32cecfe 100644 --- a/components/app_update/esp_ota_ops.c +++ b/components/app_update/esp_ota_ops.c @@ -33,6 +33,7 @@ #include "esp_ota_ops.h" #include "rom/queue.h" #include "rom/crc.h" +#include "soc/dport_reg.h" #include "esp_log.h" @@ -42,7 +43,7 @@ typedef struct ota_ops_entry_ { uint32_t handle; - esp_partition_t part; + const esp_partition_t *part; uint32_t erased_size; uint32_t wrote_size; #ifdef CONFIG_FLASH_ENCRYPTION_ENABLED @@ -68,21 +69,38 @@ static ota_select s_ota_select[2]; const static char *TAG = "esp_ota_ops"; +/* Return true if this is an OTA app partition */ +static bool is_ota_partition(const esp_partition_t *p) +{ + return (p != NULL + && p->type == ESP_PARTITION_TYPE_APP + && p->subtype >= ESP_PARTITION_SUBTYPE_APP_OTA_0 + && p->subtype < ESP_PARTITION_SUBTYPE_APP_OTA_MAX); +} + esp_err_t esp_ota_begin(const esp_partition_t *partition, size_t image_size, esp_ota_handle_t *out_handle) { + ota_ops_entry_t *new_entry; esp_err_t ret = ESP_OK; if ((partition == NULL) || (out_handle == NULL)) { return ESP_ERR_INVALID_ARG; } - ota_ops_entry_t *new_entry = (ota_ops_entry_t *) calloc(sizeof(ota_ops_entry_t), 1); - - if (new_entry == 0) { - return ESP_ERR_NO_MEM; + partition = esp_partition_verify(partition); + if (partition == NULL) { + return ESP_ERR_NOT_FOUND; } - // if input image size is 0 or OTA_SIZE_UNKNOWN, will erase all areas in this partition + if (!is_ota_partition(partition)) { + return ESP_ERR_INVALID_ARG; + } + + if (partition == esp_ota_get_running_partition()) { + return ESP_ERR_OTA_PARTITION_CONFLICT; + } + + // If input image size is 0 or OTA_SIZE_UNKNOWN, erase entire partition if ((image_size == 0) || (image_size == OTA_SIZE_UNKNOWN)) { ret = esp_partition_erase_range(partition, 0, partition->size); } else { @@ -90,11 +108,14 @@ esp_err_t esp_ota_begin(const esp_partition_t *partition, size_t image_size, esp } if (ret != ESP_OK) { - free(new_entry); - new_entry = NULL; return ret; } + new_entry = (ota_ops_entry_t *) calloc(sizeof(ota_ops_entry_t), 1); + if (new_entry == NULL) { + return ESP_ERR_NO_MEM; + } + LIST_INSERT_HEAD(&s_ota_ops_entries_head, new_entry, entries); if ((image_size == 0) || (image_size == OTA_SIZE_UNKNOWN)) { @@ -103,7 +124,7 @@ esp_err_t esp_ota_begin(const esp_partition_t *partition, size_t image_size, esp new_entry->erased_size = image_size; } - memcpy(&new_entry->part, partition, sizeof(esp_partition_t)); + new_entry->part = partition; new_entry->handle = ++s_ota_ops_last_handle; *out_handle = new_entry->handle; return ESP_OK; @@ -165,7 +186,7 @@ esp_err_t esp_ota_write(esp_ota_handle_t handle, const void *data, size_t size) } #endif - ret = esp_partition_write(&it->part, it->wrote_size, data_bytes, size); + ret = esp_partition_write(it->part, it->wrote_size, data_bytes, size); if(ret == ESP_OK){ it->wrote_size += size; } @@ -215,13 +236,13 @@ esp_err_t esp_ota_end(esp_ota_handle_t handle) } #endif - if (esp_image_basic_verify(it->part.address, true, &image_size) != ESP_OK) { + if (esp_image_basic_verify(it->part->address, true, &image_size) != ESP_OK) { ret = ESP_ERR_OTA_VALIDATE_FAILED; goto cleanup; } #ifdef CONFIG_SECURE_BOOT_ENABLED - ret = esp_secure_boot_verify_signature(it->part.address, image_size); + ret = esp_secure_boot_verify_signature(it->part->address, image_size); if (ret != ESP_OK) { ret = ESP_ERR_OTA_VALIDATE_FAILED; goto cleanup; @@ -301,7 +322,7 @@ static esp_err_t esp_rewrite_ota_data(esp_partition_subtype_t subtype) //so current ota app sub type id is x , dest bin subtype is y,total ota app count is n //seq will add (x + n*1 + 1 - seq)%n if (SUB_TYPE_ID(subtype) >= ota_app_count) { - return ESP_ERR_NOT_FOUND; + return ESP_ERR_INVALID_ARG; } ret = esp_partition_mmap(find_partition, 0, find_partition->size, SPI_FLASH_MMAP_DATA, &result, &ota_data_map); @@ -321,9 +342,9 @@ static esp_err_t esp_rewrite_ota_data(esp_partition_subtype_t subtype) } if (s_ota_select[0].ota_seq >= s_ota_select[1].ota_seq) { - return rewrite_ota_seq((SUB_TYPE_ID(subtype) + 1) % ota_app_count + i * ota_app_count, 0, find_partition); - } else { return rewrite_ota_seq((SUB_TYPE_ID(subtype) + 1) % ota_app_count + i * ota_app_count, 1, find_partition); + } else { + return rewrite_ota_seq((SUB_TYPE_ID(subtype) + 1) % ota_app_count + i * ota_app_count, 0, find_partition); } } else if (ota_select_valid(&s_ota_select[0])) { @@ -446,3 +467,79 @@ const esp_partition_t *esp_ota_get_boot_partition(void) return esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_FACTORY, NULL); } } + + +const esp_partition_t* esp_ota_get_running_partition(void) +{ + /* Find the flash address of this exact function. By definition that is part + of the currently running firmware. Then find the enclosing partition. */ + + size_t phys_offs = spi_flash_cache2phys(esp_ota_get_running_partition); + + assert (phys_offs != SPI_FLASH_CACHE2PHYS_FAIL); /* indicates cache2phys lookup is buggy */ + + esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_APP, + ESP_PARTITION_SUBTYPE_ANY, + NULL); + assert(it != NULL); /* has to be at least one app partition */ + + while (it != NULL) { + const esp_partition_t *p = esp_partition_get(it); + if (p->address <= phys_offs && p->address + p->size > phys_offs) { + esp_partition_iterator_release(it); + return p; + } + it = esp_partition_next(it); + } + + abort(); /* Partition table is invalid or corrupt */ +} + + +const esp_partition_t* esp_ota_get_next_update_partition(const esp_partition_t *start_from) +{ + const esp_partition_t *default_ota = NULL; + bool next_is_result = false; + if (start_from == NULL) { + start_from = esp_ota_get_running_partition(); + } else { + start_from = esp_partition_verify(start_from); + } + assert (start_from != NULL); + /* at this point, 'start_from' points to actual partition table data in flash */ + + + /* Two possibilities: either we want the OTA partition immediately after the current running OTA partition, or we + want the first OTA partition in the table (for the case when the last OTA partition is the running partition, or + if the current running partition is not OTA.) + + This loop iterates subtypes instead of using esp_partition_find, so we + get all OTA partitions in a known order (low slot to high slot). + */ + + for (esp_partition_subtype_t t = ESP_PARTITION_SUBTYPE_APP_OTA_0; + t != ESP_PARTITION_SUBTYPE_APP_OTA_MAX; + t++) { + const esp_partition_t *p = esp_partition_find_first(ESP_PARTITION_TYPE_APP, t, NULL); + if (p == NULL) { + continue; + } + + if (default_ota == NULL) { + /* Default to first OTA partition we find, + will be used if nothing else matches */ + default_ota = p; + } + + if (p == start_from) { + /* Next OTA partition is the one to use */ + next_is_result = true; + } + else if (next_is_result) { + return p; + } + } + + return default_ota; + +} diff --git a/components/app_update/include/esp_ota_ops.h b/components/app_update/include/esp_ota_ops.h index fe3307763f..33c03030aa 100755 --- a/components/app_update/include/esp_ota_ops.h +++ b/components/app_update/include/esp_ota_ops.h @@ -27,57 +27,76 @@ extern "C" { #endif -#define OTA_SIZE_UNKNOWN 0xffffffff +#define OTA_SIZE_UNKNOWN 0xffffffff /*!< Used for esp_ota_begin() if new image size is unknown */ -#define ESP_ERR_OTA_BASE 0x1500 /*!< base error code for ota_ops api */ -#define ESP_ERR_OTA_PARTITION_CONFLICT (ESP_ERR_OTA_BASE + 0x01) /*!< want to write or erase current running partition */ -#define ESP_ERR_OTA_SELECT_INFO_INVALID (ESP_ERR_OTA_BASE + 0x02) /*!< ota data partition info is error */ -#define ESP_ERR_OTA_VALIDATE_FAILED (ESP_ERR_OTA_BASE + 0x03) /*!< validate ota image failed */ +#define ESP_ERR_OTA_BASE 0x1500 /*!< Base error code for ota_ops api */ +#define ESP_ERR_OTA_PARTITION_CONFLICT (ESP_ERR_OTA_BASE + 0x01) /*!< Error if request was to write or erase the current running partition */ +#define ESP_ERR_OTA_SELECT_INFO_INVALID (ESP_ERR_OTA_BASE + 0x02) /*!< Error if OTA data partition contains invalid content */ +#define ESP_ERR_OTA_VALIDATE_FAILED (ESP_ERR_OTA_BASE + 0x03) /*!< Error if OTA app image is invalid */ /** - * @brief Opaque handle for application update obtained from app_ops. + * @brief Opaque handle for an application OTA update + * + * esp_ota_begin() returns a handle which is then used for subsequent + * calls to esp_ota_write() and esp_ota_end(). */ typedef uint32_t esp_ota_handle_t; /** - * @brief format input partition in flash to 0xFF as input image size, - * if unkown image size ,pass 0x0 or 0xFFFFFFFF, it will erase all the - * partition ,Otherwise, erase the required range - * - * @param partition Pointer to partition structure which need to be updated - * Must be non-NULL. - * @param image_size size of image need to be updated - * @param out_handle handle which should be used for esp_ota_write or esp_ota_end call + * @brief Commence an OTA update writing to the specified partition. - * @return: - * - ESP_OK: if format ota image OK - * - ESP_ERR_OTA_PARTITION_CONFLICT: operate current running bin - * - ESP_ERR_OTA_SELECT_INFO_INVALID: ota bin select info invalid + * The specified partition is erased to the specified image size. + * + * If image size is not yet known, pass OTA_SIZE_UNKNOWN which will + * cause the entire partition to be erased. + * + * On success, this function allocates memory that remains in use + * until esp_ota_end() is called with the returned handle. + * + * @param partition Pointer to info for partition which will receive the OTA update. Required. + * @param image_size Size of new OTA app image. Partition will be erased in order to receive this size of image. If 0 or OTA_SIZE_UNKNOWN, the entire partition is erased. + * @param out_handle On success, returns a handle which should be used for subsequent esp_ota_write() and esp_ota_end() calls. + + * @return + * - ESP_OK: OTA operation commenced successfully. + * - ESP_ERR_INVALID_ARG: partition or out_handle arguments were NULL, or partition doesn't point to an OTA app partition. + * - ESP_ERR_NO_MEM: Cannot allocate memory for OTA operation. + * - ESP_ERR_OTA_PARTITION_CONFLICT: Partition holds the currently running firmware, cannot update in place. + * - ESP_ERR_NOT_FOUND: Partition argument not found in partition table. + * - ESP_ERR_OTA_SELECT_INFO_INVALID: The OTA data partition contains invalid data. + * - ESP_ERR_INVALID_SIZE: Partition doesn't fit in configured flash size. + * - ESP_ERR_FLASH_OP_TIMEOUT or ESP_ERR_FLASH_OP_FAIL: Flash write failed. */ esp_err_t esp_ota_begin(const esp_partition_t* partition, size_t image_size, esp_ota_handle_t* out_handle); /** - * @brief Write data to input input partition + * @brief Write OTA update data to partition * - * @param handle Handle obtained from esp_ota_begin - * @param data Pointer to data write to flash - * @param size data size of recieved data + * This function can be called multiple times as + * data is received during the OTA operation. Data is written + * sequentially to the partition. * - * @return: - * - ESP_OK: if write flash data OK - * - ESP_ERR_OTA_PARTITION_CONFLICT: operate current running bin - * - ESP_ERR_OTA_SELECT_INFO_INVALID: ota bin select info invalid + * @param handle Handle obtained from esp_ota_begin + * @param data Data buffer to write + * @param size Size of data buffer in bytes. + * + * @return + * - ESP_OK: Data was written to flash successfully. + * - ESP_ERR_INVALID_ARG: handle is invalid. + * - ESP_ERR_OTA_VALIDATE_FAILED: First byte of image contains invalid app image magic byte. + * - ESP_ERR_FLASH_OP_TIMEOUT or ESP_ERR_FLASH_OP_FAIL: Flash write failed. + * - ESP_ERR_OTA_SELECT_INFO_INVALID: OTA data partition has invalid contents */ esp_err_t esp_ota_write(esp_ota_handle_t handle, const void* data, size_t size); - + /** - * @brief Finish the update and validate written data + * @brief Finish OTA update and validate newly written app image. * - * @param handle Handle obtained from esp_ota_begin. + * @param handle Handle obtained from esp_ota_begin(). * * @note After calling esp_ota_end(), the handle is no longer valid and any memory associated with it is freed (regardless of result). * - * @return: + * @return * - ESP_OK: Newly written OTA app image is valid. * - ESP_ERR_NOT_FOUND: OTA handle was not found. * - ESP_ERR_INVALID_ARG: Handle was never written to. @@ -87,27 +106,61 @@ esp_err_t esp_ota_write(esp_ota_handle_t handle, const void* data, size_t size); esp_err_t esp_ota_end(esp_ota_handle_t handle); /** - * @brief Set next boot partition, call system_restart() will switch to run it + * @brief Configure OTA data for a new boot partition * - * @note if you want switch to run a bin file - * has never been checked before,please validate it's signature firstly + * @note If this function returns ESP_OK, calling esp_restart() will boot the newly configured app partition. * - * @param partition Pointer to partition structure which need to boot + * @param partition Pointer to info for partition containing app image to boot. * - * @return: - * - ESP_OK: if set next boot partition OK - * - ESP_ERR_OTA_SELECT_INFO_INVALID: ota bin select info invalid + * @return + * - ESP_OK: OTA data updated, next reboot will use specified partition. + * - ESP_ERR_INVALID_ARG: partition argument was NULL or didn't point to a valid OTA partition of type "app". + * - ESP_ERR_OTA_VALIDATE_FAILED: Partition contained invalid app image. Also returned if secure boot is enabled and signature validation failed. + * - ESP_ERR_NOT_FOUND: OTA data partition not found. + * - ESP_ERR_FLASH_OP_TIMEOUT or ESP_ERR_FLASH_OP_FAIL: Flash erase or write failed. */ esp_err_t esp_ota_set_boot_partition(const esp_partition_t* partition); /** - * @brief Get partition info of current running image - * - * @return pointer to esp_partition_t structure, or NULL if no partition is found or - * operate flash failed,This pointer is valid for the lifetime of the application. + * @brief Get partition info of currently configured boot app + * + * If esp_ota_set_boot_partition() has been called, the partition which was set by that function will be returned. + * + * If esp_ota_set_boot_partition() has not been called, the result is + * equivalent to esp_ota_get_running_partition(). + * + * @return Pointer to info for partition structure, or NULL if no partition is found or flash read operation failed. Returned pointer is valid for the lifetime of the application. */ const esp_partition_t* esp_ota_get_boot_partition(void); + +/** + * @brief Get partition info of currently running app + * + * This function is different to esp_ota_get_boot_partition() in that + * it ignores any change of selected boot partition caused by + * esp_ota_set_boot_partition(). Only the app whose code is currently + * running will have its partition information returned. + * + * @return Pointer to info for partition structure, or NULL if no partition is found or flash read operation failed. Returned pointer is valid for the lifetime of the application. + */ +const esp_partition_t* esp_ota_get_running_partition(void); + + +/** + * @brief Return the next OTA app partition which should be written with a new firmware. + * + * Call this function to find an OTA app partition which can be passed to esp_ota_begin(). + * + * Finds next partition round-robin, starting from the current running partition. + * + * @param start_from If set, treat this partition info as describing the current running partition. Can be NULL, in which case esp_ota_get_running_partition() is used to find the currently running partition. The result of this function is never the same as this argument. + * + * @return Pointer to info for partition which should be updated next. NULL result indicates invalid OTA data partition, or that no eligible OTA app slot partition was found. + * + */ +const esp_partition_t* esp_ota_get_next_update_partition(const esp_partition_t *start_from); + #ifdef __cplusplus } #endif diff --git a/components/app_update/test/component.mk b/components/app_update/test/component.mk new file mode 100644 index 0000000000..5dd172bdb7 --- /dev/null +++ b/components/app_update/test/component.mk @@ -0,0 +1,5 @@ +# +#Component Makefile +# + +COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive diff --git a/components/app_update/test/test_ota_ops.c b/components/app_update/test/test_ota_ops.c new file mode 100644 index 0000000000..e0a7864b5f --- /dev/null +++ b/components/app_update/test/test_ota_ops.c @@ -0,0 +1,86 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +/* These OTA tests currently don't assume an OTA partition exists + on the device, so they're a bit limited +*/ + +TEST_CASE("esp_ota_begin() verifies arguments", "[ota]") +{ + const esp_partition_t *running = esp_ota_get_running_partition(); + esp_partition_t partition; + static esp_ota_handle_t handle = 0; + + if (handle != 0) { /* clean up from any previous test */ + esp_ota_end(handle); + handle = 0; + } + + /* running partition & configured boot partition are same */ + TEST_ASSERT_NOT_NULL(running); + + /* trying to 'begin' on running partition fails */ + TEST_ASSERT_NOT_EQUAL(ESP_OK, esp_ota_begin(running, OTA_SIZE_UNKNOWN, &handle)); + TEST_ASSERT_EQUAL(0, handle); + + memcpy(&partition, running, sizeof(esp_partition_t)); + partition.address--; + + /* non existent partition fails */ + TEST_ASSERT_EQUAL_HEX(ESP_ERR_NOT_FOUND, esp_ota_begin(&partition, OTA_SIZE_UNKNOWN, &handle)); + TEST_ASSERT_EQUAL(0, handle); +} + +TEST_CASE("esp_ota_get_next_update_partition logic", "[ota]") +{ + const esp_partition_t *running = esp_ota_get_running_partition(); + const esp_partition_t *factory = esp_partition_find_first(ESP_PARTITION_TYPE_APP, + ESP_PARTITION_SUBTYPE_APP_FACTORY, NULL); + const esp_partition_t *ota_0 = esp_partition_find_first(ESP_PARTITION_TYPE_APP, + ESP_PARTITION_SUBTYPE_APP_OTA_0, NULL); + const esp_partition_t *ota_1 = esp_partition_find_first(ESP_PARTITION_TYPE_APP, + ESP_PARTITION_SUBTYPE_APP_OTA_1, NULL); + const esp_partition_t *ota_2 = esp_partition_find_first(ESP_PARTITION_TYPE_APP, + ESP_PARTITION_SUBTYPE_APP_OTA_2, NULL); + + TEST_ASSERT_NOT_NULL(running); + TEST_ASSERT_NOT_NULL(factory); + TEST_ASSERT_NOT_NULL(ota_0); + TEST_ASSERT_NOT_NULL(ota_1); + TEST_ASSERT_NULL(ota_2); /* this partition shouldn't exist in test partition table */ + + TEST_ASSERT_EQUAL_PTR(factory, running); /* this may not be true if/when we get OTA tests that do OTA updates */ + + /* (The test steps verify subtypes before verifying pointer equality, because the failure messages are more readable + this way.) + */ + + /* Factory app OTA updates OTA 0 slot */ + const esp_partition_t *p = esp_ota_get_next_update_partition(NULL); + TEST_ASSERT_EQUAL_HEX8(ESP_PARTITION_SUBTYPE_APP_OTA_0, p->subtype); + TEST_ASSERT_EQUAL_PTR(ota_0, p); + + p = esp_ota_get_next_update_partition(factory); + TEST_ASSERT_EQUAL_HEX8(ESP_PARTITION_SUBTYPE_APP_OTA_0, p->subtype); + TEST_ASSERT_EQUAL_PTR(ota_0, p); + + + /* OTA slot 0 updates OTA slot 1 */ + p = esp_ota_get_next_update_partition(ota_0); + TEST_ASSERT_EQUAL_HEX8(ESP_PARTITION_SUBTYPE_APP_OTA_1, p->subtype); + TEST_ASSERT_EQUAL_PTR(ota_1, p); + /* OTA slot 1 updates OTA slot 0 */ + p = esp_ota_get_next_update_partition(ota_1); + TEST_ASSERT_EQUAL_HEX8(ESP_PARTITION_SUBTYPE_APP_OTA_0, p->subtype);; + TEST_ASSERT_EQUAL_PTR(ota_0, p); +} + diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index 43066aa3d7..0df7d02944 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -46,6 +46,7 @@ #include "bootloader_random.h" #include "bootloader_config.h" #include "rtc.h" +#include "flash_qio_mode.h" extern int _bss_start; extern int _bss_end; @@ -106,14 +107,13 @@ void IRAM_ATTR call_start_cpu0() } -/** - * @function : load_partition_table - * @description: Parse partition table, get useful data such as location of - * OTA info sector, factory app sector, and test app sector. +/** @brief Load partition table * - * @inputs: bs bootloader state structure used to save the data - * @return: return true, if the partition table is loaded (and MD5 checksum is valid) + * Parse partition table, get useful data such as location of + * OTA data partition, factory app partition, and test app partition. * + * @param bs bootloader state structure used to save read data + * @return return true if the partition table was succesfully loaded and MD5 checksum is valid. */ bool load_partition_table(bootloader_state_t* bs) { @@ -263,6 +263,10 @@ void bootloader_main() ESP_LOGI(TAG, "Enabling RNG early entropy source..."); bootloader_random_enable(); +#if CONFIG_FLASHMODE_QIO || CONFIG_FLASHMODE_QOUT + bootloader_enable_qio_mode(); +#endif + if(esp_image_load_header(0x1000, true, &fhdr) != ESP_OK) { ESP_LOGE(TAG, "failed to load bootloader header!"); return; @@ -635,28 +639,21 @@ void print_flash_info(const esp_image_header_t* phdr) } ESP_LOGI(TAG, "SPI Speed : %s", str ); - switch ( phdr->spi_mode ) { - case ESP_IMAGE_SPI_MODE_QIO: + /* SPI mode could have been set to QIO during boot already, + so test the SPI registers not the flash header */ + uint32_t spi_ctrl = REG_READ(SPI_CTRL_REG(0)); + if (spi_ctrl & SPI_FREAD_QIO) { str = "QIO"; - break; - case ESP_IMAGE_SPI_MODE_QOUT: + } else if (spi_ctrl & SPI_FREAD_QUAD) { str = "QOUT"; - break; - case ESP_IMAGE_SPI_MODE_DIO: + } else if (spi_ctrl & SPI_FREAD_DIO) { str = "DIO"; - break; - case ESP_IMAGE_SPI_MODE_DOUT: + } else if (spi_ctrl & SPI_FREAD_DUAL) { str = "DOUT"; - break; - case ESP_IMAGE_SPI_MODE_FAST_READ: + } else if (spi_ctrl & SPI_FASTRD_MODE) { str = "FAST READ"; - break; - case ESP_IMAGE_SPI_MODE_SLOW_READ: + } else { str = "SLOW READ"; - break; - default: - str = "DIO"; - break; } ESP_LOGI(TAG, "SPI Mode : %s", str ); diff --git a/components/bootloader/src/main/component.mk b/components/bootloader/src/main/component.mk index 12fdf8efa0..b3d3c084f1 100644 --- a/components/bootloader/src/main/component.mk +++ b/components/bootloader/src/main/component.mk @@ -8,6 +8,7 @@ LINKER_SCRIPTS := \ esp32.bootloader.ld \ $(IDF_PATH)/components/esp32/ld/esp32.rom.ld \ + $(IDF_PATH)/components/esp32/ld/esp32.peripherals.ld \ esp32.bootloader.rom.ld COMPONENT_ADD_LDFLAGS := -L $(COMPONENT_PATH) -lmain $(addprefix -T ,$(LINKER_SCRIPTS)) diff --git a/components/bootloader/src/main/flash_qio_mode.c b/components/bootloader/src/main/flash_qio_mode.c new file mode 100644 index 0000000000..c1afc1e03f --- /dev/null +++ b/components/bootloader/src/main/flash_qio_mode.c @@ -0,0 +1,230 @@ +// 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 "flash_qio_mode.h" +#include "esp_log.h" +#include "rom/spi_flash.h" +#include "soc/spi_struct.h" +#include "sdkconfig.h" + +/* SPI flash controller */ +#define SPIFLASH SPI1 + +/* SPI commands (actual on-wire commands not SPI controller bitmasks) + Suitable for use with the execute_flash_command static function. +*/ +#define CMD_RDID 0x9F +#define CMD_WRSR 0x01 +#define CMD_WRSR2 0x31 /* Not all SPI flash uses this command */ +#define CMD_WREN 0x06 +#define CMD_WRDI 0x04 +#define CMD_RDSR 0x05 +#define CMD_RDSR2 0x35 /* Not all SPI flash uses this command */ + +static const char *TAG = "qio_mode"; + +typedef unsigned (*read_status_fn_t)(); +typedef void (*write_status_fn_t)(unsigned); + +typedef struct __attribute__((packed)) { + const char *manufacturer; + uint8_t mfg_id; /* 8-bit JEDEC manufacturer ID */ + uint16_t flash_id; /* 16-bit JEDEC flash chip ID */ + uint16_t id_mask; /* Bits to match on in flash chip ID */ + read_status_fn_t read_status_fn; + write_status_fn_t write_status_fn; + uint8_t status_qio_bit; +} qio_info_t; + +/* Read 8 bit status using RDSR command */ +static unsigned read_status_8b_rdsr(); +/* Read 8 bit status (second byte) using RDSR2 command */ +static unsigned read_status_8b_rdsr2(); +/* read 16 bit status using RDSR & RDSR2 (low and high bytes) */ +static unsigned read_status_16b_rdsr_rdsr2(); + +/* Write 8 bit status using WRSR */ +static void write_status_8b_wrsr(unsigned new_status); +/* Write 8 bit status (second byte) using WRSR2 */ +static void write_status_8b_wrsr2(unsigned new_status); +/* Write 16 bit status using WRSR */ +static void write_status_16b_wrsr(unsigned new_status); + +/* Array of known flash chips and data to enable Quad I/O mode + + Manufacturer & flash ID can be tested by running "esptool.py + flash_id" + + If manufacturer ID matches, and flash ID ORed with flash ID mask + matches, enable_qio_mode() will execute "Read Cmd", test if bit + number "QIE Bit" is set, and if not set it will call "Write Cmd" + with this bit set. + + Searching of this table stops when the first match is found. + */ +const static qio_info_t chip_data[] = { +/* Manufacturer, mfg_id, flash_id, id mask, Read Status, Write Status, QIE Bit */ + { "MXIC", 0xC2, 0x2000, 0xFF00, read_status_8b_rdsr, write_status_8b_wrsr, 6 }, + { "ISSI", 0x9D, 0x4000, 0xFF00, read_status_8b_rdsr, write_status_8b_wrsr, 6 }, + { "WinBond", 0xEF, 0x4000, 0xFF00, read_status_16b_rdsr_rdsr2, write_status_16b_wrsr, 9 }, + + /* Final entry is default entry, if no other IDs have matched. + + This approach works for chips including: + GigaDevice (mfg ID 0xC8, flash IDs including 4016), + FM25Q32 (QOUT mode only, mfg ID 0xA1, flash IDs including 4016) + */ + { NULL, 0xFF, 0xFFFF, 0xFFFF, read_status_8b_rdsr2, write_status_8b_wrsr2, 1 }, +}; + +#define NUM_CHIPS (sizeof(chip_data) / sizeof(qio_info_t)) + +static void enable_qio_mode(read_status_fn_t read_status_fn, + write_status_fn_t write_status_fn, + uint8_t status_qio_bit); + +/* Generic function to use the "user command" SPI controller functionality + to send commands to the SPI flash and read the respopnse. + + The command passed here is always the on-the-wire command given to the SPI flash unit. +*/ +static uint32_t execute_flash_command(uint8_t command, uint32_t mosi_data, uint8_t mosi_len, uint8_t miso_len); + +void bootloader_enable_qio_mode(void) +{ + uint32_t raw_flash_id; + uint8_t mfg_id; + uint16_t flash_id; + int i; + + ESP_LOGD(TAG, "Probing for QIO mode enable..."); + SPI_Wait_Idle(&g_rom_flashchip); + + /* Set up some of the SPIFLASH user/ctrl variables which don't change + while we're probing using execute_flash_command() */ + SPIFLASH.ctrl.val = 0; + SPIFLASH.user.usr_dummy = 0; + SPIFLASH.user.usr_addr = 0; + SPIFLASH.user.usr_command = 1; + SPIFLASH.user2.usr_command_bitlen = 7; + + raw_flash_id = execute_flash_command(CMD_RDID, 0, 0, 24); + ESP_LOGD(TAG, "Raw SPI flash chip id 0x%x", raw_flash_id); + + mfg_id = raw_flash_id & 0xFF; + flash_id = (raw_flash_id >> 16) | (raw_flash_id & 0xFF00); + ESP_LOGD(TAG, "Manufacturer ID 0x%02x chip ID 0x%04x", mfg_id, flash_id); + + for (i = 0; i < NUM_CHIPS-1; i++) { + const qio_info_t *chip = &chip_data[i]; + if (mfg_id == chip->mfg_id && (flash_id & chip->id_mask) == (chip->flash_id & chip->id_mask)) { + ESP_LOGI(TAG, "Enabling QIO for flash chip %s", chip_data[i].manufacturer); + break; + } + } + + if (i == NUM_CHIPS - 1) { + ESP_LOGI(TAG, "Enabling default flash chip QIO"); + } + + enable_qio_mode(chip_data[i].read_status_fn, + chip_data[i].write_status_fn, + chip_data[i].status_qio_bit); +} + +static void enable_qio_mode(read_status_fn_t read_status_fn, + write_status_fn_t write_status_fn, + uint8_t status_qio_bit) +{ + uint32_t status; + + SPI_Wait_Idle(&g_rom_flashchip); + + status = read_status_fn(); + ESP_LOGD(TAG, "Initial flash chip status 0x%x", status); + + if ((status & (1< 0; + SPIFLASH.miso_dlen.usr_miso_dbitlen = miso_len ? (miso_len - 1) : 0; + SPIFLASH.user.usr_mosi = mosi_len > 0; + SPIFLASH.mosi_dlen.usr_mosi_dbitlen = mosi_len ? (mosi_len - 1) : 0; + SPIFLASH.data_buf[0] = mosi_data; + + SPIFLASH.cmd.usr = 1; + while(SPIFLASH.cmd.usr != 0) + { } + + return SPIFLASH.data_buf[0]; +} diff --git a/components/bootloader/src/main/flash_qio_mode.h b/components/bootloader/src/main/flash_qio_mode.h new file mode 100644 index 0000000000..9efa1313e2 --- /dev/null +++ b/components/bootloader/src/main/flash_qio_mode.h @@ -0,0 +1,29 @@ +// 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. +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Enable Quad I/O mode in bootloader (if configured) + * + * Queries attached SPI flash ID and sends correct SPI flash + * commands to enable QIO or QOUT mode, then enables this mode. + */ +void bootloader_enable_qio_mode(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/bootloader_support/include/esp_flash_encrypt.h b/components/bootloader_support/include/esp_flash_encrypt.h index 09e365724e..ba370644a4 100644 --- a/components/bootloader_support/include/esp_flash_encrypt.h +++ b/components/bootloader_support/include/esp_flash_encrypt.h @@ -33,7 +33,7 @@ * * @return true if flash encryption is enabled. */ -static inline IRAM_ATTR bool esp_flash_encryption_enabled(void) { +static inline /** @cond */ IRAM_ATTR /** @endcond */ bool esp_flash_encryption_enabled(void) { uint32_t flash_crypt_cnt = REG_GET_FIELD(EFUSE_BLK0_RDATA0_REG, EFUSE_RD_FLASH_CRYPT_CNT); /* __builtin_parity is in flash, so we calculate parity inline */ bool enabled = false; diff --git a/components/bootloader_support/src/bootloader_random.c b/components/bootloader_support/src/bootloader_random.c index 5a00d0cf5a..fe5a019f84 100644 --- a/components/bootloader_support/src/bootloader_random.c +++ b/components/bootloader_support/src/bootloader_random.c @@ -62,6 +62,11 @@ void bootloader_fill_random(void *buffer, size_t length) void bootloader_random_enable(void) { + /* Ensure the hardware RNG is enabled following a soft reset. This should always be the case already (this clock is + never disabled while the CPU is running), this is a "belts and braces" type check. + */ + SET_PERI_REG_MASK(DPORT_WIFI_CLK_EN_REG, DPORT_WIFI_CLK_RNG_EN); + /* Enable SAR ADC in test mode to feed ADC readings of the 1.1V reference via I2S into the RNG entropy input. diff --git a/components/bootloader_support/src/esp_image_format.c b/components/bootloader_support/src/esp_image_format.c index 8b156680c6..942ffd4bf8 100644 --- a/components/bootloader_support/src/esp_image_format.c +++ b/components/bootloader_support/src/esp_image_format.c @@ -108,6 +108,16 @@ esp_err_t esp_image_basic_verify(uint32_t src_addr, bool log_errors, uint32_t *p *p_length = 0; } + if (src_addr % SPI_FLASH_MMU_PAGE_SIZE != 0) { + /* Image must start on a 64KB boundary + + (This is not a technical limitation, only the flash mapped regions need to be 64KB aligned. But the most + consistent way to do this is to have all the offsets internal to the image correctly 64KB aligned, and then + start the image on a 64KB boundary also.) + */ + return ESP_ERR_INVALID_ARG; + } + err = esp_image_load_header(src_addr, log_errors, &image_header); if (err != ESP_OK) { return err; diff --git a/components/bt/Kconfig b/components/bt/Kconfig index d2227868ce..63dce8d1f9 100644 --- a/components/bt/Kconfig +++ b/components/bt/Kconfig @@ -17,6 +17,15 @@ config BLUEDROID_MEM_DEBUG help Bluedroid memory debug +config BT_DRAM_RELEASE + bool "Release DRAM from Classic BT controller" + depends on BT_ENABLED + default n + help + This option should only be used when BLE only. + Open this option will release about 30K DRAM from Classic BT. + The released DRAM will be used as system heap memory. + # Memory reserved at start of DRAM for Bluetooth stack config BT_RESERVE_DRAM hex diff --git a/components/bt/bluedroid/api/esp_bt_main.c b/components/bt/bluedroid/api/esp_bt_main.c index 5f7265e99c..549865e743 100644 --- a/components/bt/bluedroid/api/esp_bt_main.c +++ b/components/bt/bluedroid/api/esp_bt_main.c @@ -19,13 +19,13 @@ #include "bt.h" #include "future.h" -static bool esp_already_enable = false; -static bool esp_already_init = false; +static bool bd_already_enable = false; +static bool bd_already_init = false; esp_bluedroid_status_t esp_bluedroid_get_status(void) { - if (esp_already_init) { - if (esp_already_enable) { + if (bd_already_init) { + if (bd_already_enable) { return ESP_BLUEDROID_STATUS_ENABLED; } else { return ESP_BLUEDROID_STATUS_INITIALIZED; @@ -40,15 +40,20 @@ esp_err_t esp_bluedroid_enable(void) btc_msg_t msg; future_t **future_p; - if (esp_already_enable) { - LOG_ERROR("%s already enable\n", __func__); + if (!bd_already_init) { + LOG_ERROR("Bludroid not initialised\n"); + return ESP_ERR_INVALID_STATE; + } + + if (bd_already_enable) { + LOG_ERROR("Bluedroid already enabled\n"); return ESP_ERR_INVALID_STATE; } future_p = btc_main_get_future_p(BTC_MAIN_ENABLE_FUTURE); *future_p = future_new(); if (*future_p == NULL) { - LOG_ERROR("%s failed\n", __func__); + LOG_ERROR("Bluedroid enable failed\n"); return ESP_ERR_NO_MEM; } @@ -58,11 +63,11 @@ esp_err_t esp_bluedroid_enable(void) btc_transfer_context(&msg, NULL, 0, NULL); if (future_await(*future_p) == FUTURE_FAIL) { - LOG_ERROR("%s failed\n", __func__); + LOG_ERROR("Bluedroid enable failed\n"); return ESP_FAIL; } - esp_already_enable = true; + bd_already_enable = true; return ESP_OK; } @@ -72,15 +77,15 @@ esp_err_t esp_bluedroid_disable(void) btc_msg_t msg; future_t **future_p; - if (!esp_already_enable) { - LOG_ERROR("%s already disable\n", __func__); + if (!bd_already_enable) { + LOG_ERROR("Bluedroid already disabled\n"); return ESP_ERR_INVALID_STATE; } future_p = btc_main_get_future_p(BTC_MAIN_DISABLE_FUTURE); *future_p = future_new(); if (*future_p == NULL) { - LOG_ERROR("%s failed\n", __func__); + LOG_ERROR("Bluedroid disable failed\n"); return ESP_ERR_NO_MEM; } @@ -90,11 +95,11 @@ esp_err_t esp_bluedroid_disable(void) btc_transfer_context(&msg, NULL, 0, NULL); if (future_await(*future_p) == FUTURE_FAIL) { - LOG_ERROR("%s failed\n", __func__); + LOG_ERROR("Bluedroid disable failed\n"); return ESP_FAIL; } - esp_already_enable = false; + bd_already_enable = false; return ESP_OK; } @@ -105,19 +110,19 @@ esp_err_t esp_bluedroid_init(void) future_t **future_p; if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) { - LOG_ERROR("%s conroller not init\n", __func__); + LOG_ERROR("Conroller not initialised\n"); return ESP_ERR_INVALID_STATE; } - if (esp_already_init) { - LOG_ERROR("%s already init\n", __func__); + if (bd_already_init) { + LOG_ERROR("Bluedroid already initialised\n"); return ESP_ERR_INVALID_STATE; } future_p = btc_main_get_future_p(BTC_MAIN_INIT_FUTURE); *future_p = future_new(); if (*future_p == NULL) { - LOG_ERROR("%s failed\n", __func__); + LOG_ERROR("Bluedroid initialise failed\n"); return ESP_ERR_NO_MEM; } @@ -129,11 +134,11 @@ esp_err_t esp_bluedroid_init(void) btc_transfer_context(&msg, NULL, 0, NULL); if (future_await(*future_p) == FUTURE_FAIL) { - LOG_ERROR("%s failed\n", __func__); + LOG_ERROR("Bluedroid initialise failed\n"); return ESP_FAIL; } - esp_already_init = true; + bd_already_init = true; return ESP_OK; } @@ -144,15 +149,20 @@ esp_err_t esp_bluedroid_deinit(void) btc_msg_t msg; future_t **future_p; - if (!esp_already_init) { - LOG_ERROR("%s already deinit\n", __func__); + if (!bd_already_init) { + LOG_ERROR("Bluedroid already de-initialised\n"); + return ESP_ERR_INVALID_STATE; + } + + if (bd_already_enable) { + LOG_ERROR("Bludroid already enabled, do disable first\n"); return ESP_ERR_INVALID_STATE; } future_p = btc_main_get_future_p(BTC_MAIN_DEINIT_FUTURE); *future_p = future_new(); if (*future_p == NULL) { - LOG_ERROR("%s failed\n", __func__); + LOG_ERROR("Bluedroid de-initialise failed\n"); return ESP_ERR_NO_MEM; } @@ -162,15 +172,14 @@ esp_err_t esp_bluedroid_deinit(void) btc_transfer_context(&msg, NULL, 0, NULL); if (future_await(*future_p) == FUTURE_FAIL) { - LOG_ERROR("%s failed\n", __func__); + LOG_ERROR("Bluedroid de-initialise failed\n"); return ESP_FAIL; } btc_deinit(); - esp_already_init = false; + bd_already_init = false; return ESP_OK; } - diff --git a/components/bt/bluedroid/api/esp_gatts_api.c b/components/bt/bluedroid/api/esp_gatts_api.c index f5ebe59a2d..318dd25bf2 100644 --- a/components/bt/bluedroid/api/esp_gatts_api.c +++ b/components/bt/bluedroid/api/esp_gatts_api.c @@ -142,6 +142,27 @@ esp_err_t esp_ble_gatts_add_char(uint16_t service_handle, esp_bt_uuid_t *char_ return ESP_ERR_INVALID_STATE; } + /* parameter validation check */ + if ((control != NULL) && (control->auto_rsp == GATT_STACK_RSP)){ + if (char_val == NULL){ + LOG_ERROR("Error in %s, line=%d, for stack respond attribute, char_val should not be NULL here\n",\ + __func__, __LINE__); + return ESP_ERR_INVALID_ARG; + } else if (char_val->attr_max_len == 0){ + LOG_ERROR("Error in %s, line=%d, for stack respond attribute, attribute max length should not be 0\n",\ + __func__, __LINE__); + return ESP_ERR_INVALID_ARG; + } + } + + if (char_val != NULL){ + if (char_val->attr_len > char_val->attr_max_len){ + LOG_ERROR("Error in %s, line=%d,attribute actual length (%d) should not be larger than max length (%d)\n",\ + __func__, __LINE__, char_val->attr_len, char_val->attr_max_len); + return ESP_ERR_INVALID_ARG; + } + } + memset(&arg, 0, sizeof(btc_ble_gatts_args_t)); msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; @@ -175,6 +196,29 @@ esp_err_t esp_ble_gatts_add_char_descr (uint16_t service_handle, if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } + + /* parameter validation check */ + if ((control != NULL) && (control->auto_rsp == GATT_STACK_RSP)){ + if (char_descr_val == NULL){ + LOG_ERROR("Error in %s, line=%d, for stack respond attribute, char_descr_val should not be NULL here\n",\ + __func__, __LINE__); + return ESP_ERR_INVALID_ARG; + } + else if (char_descr_val->attr_max_len == 0){ + LOG_ERROR("Error in %s, line=%d, for stack respond attribute, attribute max length should not be 0\n",\ + __func__, __LINE__); + return ESP_ERR_INVALID_ARG; + } + } + + if (char_descr_val != NULL){ + if (char_descr_val->attr_len > char_descr_val->attr_max_len){ + LOG_ERROR("Error in %s, line=%d,attribute actual length (%d) should not be larger than max length (%d)\n",\ + __func__, __LINE__, char_descr_val->attr_len, char_descr_val->attr_max_len); + return ESP_ERR_INVALID_ARG; + } + } + memset(&arg, 0, sizeof(btc_ble_gatts_args_t)); msg.sig = BTC_SIG_API_CALL; diff --git a/components/bt/bluedroid/api/include/esp_gap_ble_api.h b/components/bt/bluedroid/api/include/esp_gap_ble_api.h index 64aff1fb1a..8b5882d270 100644 --- a/components/bt/bluedroid/api/include/esp_gap_ble_api.h +++ b/components/bt/bluedroid/api/include/esp_gap_ble_api.h @@ -46,6 +46,8 @@ typedef enum { ESP_GAP_BLE_SCAN_RESULT_EVT, /*!< When one scan result ready, the event comes each time */ ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT, /*!< When raw advertising data set complete, the event comes */ ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT, /*!< When raw advertising data set complete, the event comes */ + ESP_GAP_BLE_ADV_START_COMPLETE_EVT, /*!< When start advertising complete, the event comes */ + ESP_GAP_BLE_SCAN_START_COMPLETE_EVT, /*!< When start scan complete, the event comes */ } esp_gap_ble_cb_event_t; /// Advertising data maximum length @@ -284,6 +286,18 @@ typedef union { struct ble_scan_rsp_data_raw_cmpl_evt_param { esp_bt_status_t status; /*!< Indicate the set raw advertising data operation success status */ } scan_rsp_data_raw_cmpl; /*!< Event parameter of ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_ADV_START_COMPLETE_EVT + */ + struct ble_adv_start_cmpl_evt_param { + esp_bt_status_t status; /*!< Indicate advertising start operation success status */ + } adv_start_cmpl; /*!< Event parameter of ESP_GAP_BLE_ADV_START_COMPLETE_EVT */ + /** + * @brief ESP_GAP_BLE_SCAN_START_COMPLETE_EVT + */ + struct ble_scan_start_cmpl_evt_param { + esp_bt_status_t status; /*!< Indicate scan start operation success status */ + } scan_start_cmpl; /*!< Event parameter of ESP_GAP_BLE_SCAN_START_COMPLETE_EVT */ } esp_ble_gap_cb_param_t; /** diff --git a/components/bt/bluedroid/bta/av/bta_av_act.c b/components/bt/bluedroid/bta/av/bta_av_act.c index b1b0e5805b..8c9840e4b8 100755 --- a/components/bt/bluedroid/bta/av/bta_av_act.c +++ b/components/bt/bluedroid/bta/av/bta_av_act.c @@ -506,7 +506,7 @@ void bta_av_rc_opened(tBTA_AV_CB *p_cb, tBTA_AV_DATA *p_data) p_scb->rc_handle = p_data->rc_conn_chg.handle; APPL_TRACE_DEBUG("bta_av_rc_opened shdl:%d, srch %d", i + 1, p_scb->rc_handle); shdl = i+1; - LOG_INFO("%s allow incoming AVRCP connections:%d", __func__, p_scb->use_rc); + APPL_TRACE_DEBUG("%s allow incoming AVRCP connections:%d", __func__, p_scb->use_rc); bta_sys_stop_timer(&p_scb->timer); disc = p_scb->hndl; break; diff --git a/components/bt/bluedroid/bta/dm/bta_dm_act.c b/components/bt/bluedroid/bta/dm/bta_dm_act.c index 66eb55dac2..8ebd3a8f31 100644 --- a/components/bt/bluedroid/bta/dm/bta_dm_act.c +++ b/components/bt/bluedroid/bta/dm/bta_dm_act.c @@ -4534,12 +4534,11 @@ void bta_dm_ble_observe (tBTA_DM_MSG *p_data) bta_dm_search_cb.p_scan_cback = p_data->ble_observe.p_cback; if ((status = BTM_BleObserve(TRUE, p_data->ble_observe.duration, bta_dm_observe_results_cb, bta_dm_observe_cmpl_cb)) != BTM_CMD_STARTED) { - tBTA_DM_SEARCH data; APPL_TRACE_WARNING(" %s BTM_BleObserve failed. status %d\n", __FUNCTION__, status); - data.inq_cmpl.num_resps = 0; - if (bta_dm_search_cb.p_scan_cback) { - bta_dm_search_cb.p_scan_cback(BTA_DM_INQ_CMPL_EVT, &data); - } + } + if (p_data->ble_observe.p_start_scan_cback) { + status = (status == BTM_CMD_STARTED ? BTA_SUCCESS : BTA_FAILURE); + p_data->ble_observe.p_start_scan_cback(status); } } else { bta_dm_search_cb.p_scan_cback = NULL; @@ -4576,13 +4575,21 @@ void bta_dm_ble_set_adv_params (tBTA_DM_MSG *p_data) *******************************************************************************/ void bta_dm_ble_set_adv_params_all (tBTA_DM_MSG *p_data) { - BTM_BleSetAdvParamsStartAdv(p_data->ble_set_adv_params_all.adv_int_min, + tBTA_STATUS status = BTA_FAILURE; + + if (BTM_BleSetAdvParamsStartAdv(p_data->ble_set_adv_params_all.adv_int_min, p_data->ble_set_adv_params_all.adv_int_max, p_data->ble_set_adv_params_all.adv_type, p_data->ble_set_adv_params_all.addr_type_own, p_data->ble_set_adv_params_all.p_dir_bda, p_data->ble_set_adv_params_all.channel_map, - p_data->ble_set_adv_params_all.adv_filter_policy); + p_data->ble_set_adv_params_all.adv_filter_policy) == BTM_SUCCESS) { + status = BTA_SUCCESS; + } + + if (p_data->ble_set_adv_params_all.p_start_adv_cback) { + (*p_data->ble_set_adv_params_all.p_start_adv_cback)(status); + } } /******************************************************************************* diff --git a/components/bt/bluedroid/bta/dm/bta_dm_api.c b/components/bt/bluedroid/bta/dm/bta_dm_api.c index 132d2ea8f7..413d6de559 100644 --- a/components/bt/bluedroid/bta/dm/bta_dm_api.c +++ b/components/bt/bluedroid/bta/dm/bta_dm_api.c @@ -1009,7 +1009,7 @@ void BTA_DmSetBleAdvParams (UINT16 adv_int_min, UINT16 adv_int_max, void BTA_DmSetBleAdvParamsAll (UINT16 adv_int_min, UINT16 adv_int_max, UINT8 adv_type, tBLE_ADDR_TYPE addr_type_own, tBTM_BLE_ADV_CHNL_MAP chnl_map, tBTM_BLE_AFP adv_fil_pol, - tBLE_BD_ADDR *p_dir_bda) + tBLE_BD_ADDR *p_dir_bda, tBTA_START_ADV_CMPL_CBACK p_start_adv_cb) { #if BLE_INCLUDED == TRUE tBTA_DM_API_BLE_ADV_PARAMS_ALL *p_msg; @@ -1029,6 +1029,7 @@ void BTA_DmSetBleAdvParamsAll (UINT16 adv_int_min, UINT16 adv_int_max, p_msg->addr_type_own = addr_type_own; p_msg->channel_map = chnl_map; p_msg->adv_filter_policy = adv_fil_pol; + p_msg->p_start_adv_cback = p_start_adv_cb; if (p_dir_bda != NULL) { p_msg->p_dir_bda = (tBLE_BD_ADDR *)(p_msg + 1); memcpy(p_msg->p_dir_bda, p_dir_bda, sizeof(tBLE_BD_ADDR)); @@ -2127,7 +2128,8 @@ void BTA_DmCloseACL(BD_ADDR bd_addr, BOOLEAN remove_dev, tBTA_TRANSPORT transpor ** *******************************************************************************/ extern void BTA_DmBleObserve(BOOLEAN start, UINT8 duration, - tBTA_DM_SEARCH_CBACK *p_results_cb) + tBTA_DM_SEARCH_CBACK *p_results_cb, + tBTA_START_SCAN_CMPL_CBACK *p_start_scan_cb) { tBTA_DM_API_BLE_OBSERVE *p_msg; @@ -2140,6 +2142,7 @@ extern void BTA_DmBleObserve(BOOLEAN start, UINT8 duration, p_msg->start = start; p_msg->duration = duration; p_msg->p_cback = p_results_cb; + p_msg->p_start_scan_cback = p_start_scan_cb; bta_sys_sendmsg(p_msg); } diff --git a/components/bt/bluedroid/bta/dm/bta_dm_int.h b/components/bt/bluedroid/bta/dm/bta_dm_int.h index 7475ad254e..9c5cbc4d8d 100644 --- a/components/bt/bluedroid/bta/dm/bta_dm_int.h +++ b/components/bt/bluedroid/bta/dm/bta_dm_int.h @@ -471,7 +471,8 @@ typedef struct { BT_HDR hdr; BOOLEAN start; UINT16 duration; - tBTA_DM_SEARCH_CBACK *p_cback; + tBTA_DM_SEARCH_CBACK *p_cback; + tBTA_START_SCAN_CMPL_CBACK *p_start_scan_cback; } tBTA_DM_API_BLE_OBSERVE; typedef struct { @@ -506,6 +507,7 @@ typedef struct { tBTM_BLE_ADV_CHNL_MAP channel_map; tBTM_BLE_AFP adv_filter_policy; tBLE_BD_ADDR *p_dir_bda; + tBTA_START_ADV_CMPL_CBACK *p_start_adv_cback; } tBTA_DM_API_BLE_ADV_PARAMS_ALL; diff --git a/components/bt/bluedroid/bta/include/bta_api.h b/components/bt/bluedroid/bta/include/bta_api.h index 7385c18674..3e8e6bbdf6 100644 --- a/components/bt/bluedroid/bta/include/bta_api.h +++ b/components/bt/bluedroid/bta/include/bta_api.h @@ -400,6 +400,8 @@ typedef struct { typedef void (tBTA_SET_ADV_DATA_CMPL_CBACK) (tBTA_STATUS status); +typedef void (tBTA_START_ADV_CMPL_CBACK) (tBTA_STATUS status); + /* advertising channel map */ #define BTA_BLE_ADV_CHNL_37 BTM_BLE_ADV_CHNL_37 #define BTA_BLE_ADV_CHNL_38 BTM_BLE_ADV_CHNL_38 @@ -1095,6 +1097,8 @@ typedef void (tBTA_BLE_SCAN_SETUP_CBACK) (tBTA_BLE_BATCH_SCAN_EVT evt, tBTA_DM_BLE_REF_VALUE ref_value, tBTA_STATUS status); +typedef void (tBTA_START_SCAN_CMPL_CBACK) (tBTA_STATUS status); + typedef void (tBTA_BLE_TRACK_ADV_CMPL_CBACK)(int action, tBTA_STATUS status, tBTA_DM_BLE_PF_AVBL_SPACE avbl_space, tBTA_DM_BLE_REF_VALUE ref_value); @@ -1891,7 +1895,7 @@ extern void BTA_DmSetBleAdvParams (UINT16 adv_int_min, UINT16 adv_int_max, extern void BTA_DmSetBleAdvParamsAll (UINT16 adv_int_min, UINT16 adv_int_max, UINT8 adv_type, tBLE_ADDR_TYPE addr_type_own, tBTM_BLE_ADV_CHNL_MAP chnl_map, tBTM_BLE_AFP adv_fil_pol, - tBLE_BD_ADDR *p_dir_bda); + tBLE_BD_ADDR *p_dir_bda, tBTA_START_ADV_CMPL_CBACK p_start_adv_cb); /******************************************************************************* @@ -1997,7 +2001,8 @@ extern void BTA_DmSetEncryption(BD_ADDR bd_addr, tBTA_TRANSPORT transport, ** *******************************************************************************/ extern void BTA_DmBleObserve(BOOLEAN start, UINT8 duration, - tBTA_DM_SEARCH_CBACK *p_results_cb); + tBTA_DM_SEARCH_CBACK *p_results_cb, + tBTA_START_SCAN_CMPL_CBACK *p_start_scan_cb); extern void BTA_DmBleStopAdvertising(void); diff --git a/components/bt/bluedroid/btc/core/btc_profile_queue.c b/components/bt/bluedroid/btc/core/btc_profile_queue.c index 9d45db62c9..64406af4f0 100644 --- a/components/bt/bluedroid/btc/core/btc_profile_queue.c +++ b/components/bt/bluedroid/btc/core/btc_profile_queue.c @@ -49,7 +49,7 @@ static void queue_int_add(connect_node_t *p_param) for (const list_node_t *node = list_begin(connect_queue); node != list_end(connect_queue); node = list_next(node)) { if (((connect_node_t *)list_node(node))->uuid == p_param->uuid) { - LOG_INFO("%s dropping duplicate connect request for uuid: %04x", __func__, p_param->uuid); + LOG_DEBUG("%s dropping duplicate connect request for uuid: %04x", __func__, p_param->uuid); return; } } diff --git a/components/bt/bluedroid/btc/core/btc_storage.c b/components/bt/bluedroid/btc/core/btc_storage.c index 57634fc04d..f3ae7a8b53 100644 --- a/components/bt/bluedroid/btc/core/btc_storage.c +++ b/components/bt/bluedroid/btc/core/btc_storage.c @@ -41,14 +41,14 @@ bt_status_t btc_storage_add_bonded_device(bt_bdaddr_t *remote_bd_addr, bdstr_t bdstr; bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); - LOG_INFO("add to storage: Remote device:%s\n", bdstr); + LOG_DEBUG("add to storage: Remote device:%s\n", bdstr); int ret = btc_config_set_int(bdstr, "LinkKeyType", (int)key_type); ret &= btc_config_set_int(bdstr, "PinLength", (int)pin_length); ret &= btc_config_set_bin(bdstr, "LinkKey", link_key, sizeof(LINK_KEY)); /* write bonded info immediately */ btc_config_flush(); - LOG_INFO("Storage add rslt %d\n", ret); + LOG_DEBUG("Storage add rslt %d\n", ret); return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL; } @@ -72,7 +72,7 @@ static bt_status_t btc_in_fetch_bonded_devices(int add) continue; } - LOG_INFO("Remote device:%s\n", name); + LOG_DEBUG("Remote device:%s\n", name); LINK_KEY link_key; size_t size = sizeof(link_key); if (btc_config_get_bin(name, "LinkKey", link_key, &size)) { @@ -99,7 +99,7 @@ static bt_status_t btc_in_fetch_bonded_devices(int add) } } if (!bt_linkkey_file_found) { - LOG_INFO("Remote device:%s, no link key\n", name); + LOG_DEBUG("Remote device:%s, no link key\n", name); } } return BT_STATUS_SUCCESS; @@ -122,7 +122,7 @@ bt_status_t btc_storage_load_bonded_devices(void) { bt_status_t status; status = btc_in_fetch_bonded_devices(1); - LOG_INFO("Storage load rslt %d\n", status); + LOG_DEBUG("Storage load rslt %d\n", status); return status; } @@ -140,7 +140,7 @@ bt_status_t btc_storage_remove_bonded_device(bt_bdaddr_t *remote_bd_addr) { bdstr_t bdstr; bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); - LOG_INFO("Add to storage: Remote device:%s\n", bdstr); + LOG_DEBUG("Add to storage: Remote device:%s\n", bdstr); int ret = 1; if (btc_config_exist(bdstr, "LinkKeyType")) { diff --git a/components/bt/bluedroid/btc/profile/std/a2dp/btc_avk.c b/components/bt/bluedroid/btc/profile/std/a2dp/btc_avk.c index 90b1c95f27..4e037b3c8c 100644 --- a/components/bt/bluedroid/btc/profile/std/a2dp/btc_avk.c +++ b/components/bt/bluedroid/btc/profile/std/a2dp/btc_avk.c @@ -606,7 +606,7 @@ static BOOLEAN btc_av_state_opened_handler(btc_sm_event_t event, void *p_data) case BTA_AV_RECONFIG_EVT: if ((btc_av_cb.flags & BTC_AV_FLAG_PENDING_START) && (p_av->reconfig.status == BTA_AV_SUCCESS)) { - APPL_TRACE_WARNING("reconfig done BTA_AVstart()\n"); + LOG_WARN("reconfig done BTA_AVstart()\n"); BTA_AvStart(); } else if (btc_av_cb.flags & BTC_AV_FLAG_PENDING_START) { btc_av_cb.flags &= ~BTC_AV_FLAG_PENDING_START; @@ -905,7 +905,7 @@ static void bte_av_media_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data) memcpy(&(arg.mcc.cie), (uint8_t *)p_data + 3, ESP_A2D_CIE_LEN_SBC); btc_transfer_context(&msg, &arg, sizeof(btc_av_args_t), NULL); } else { - APPL_TRACE_ERROR("ERROR dump_codec_info A2D_ParsSbcInfo fail:%d\n", a2d_status); + LOG_ERROR("ERROR dump_codec_info A2D_ParsSbcInfo fail:%d\n", a2d_status); } } } @@ -972,7 +972,7 @@ static bt_status_t connect_int(bt_bdaddr_t *bd_addr, uint16_t uuid) btc_av_connect_req_t connect_req; connect_req.target_bda = bd_addr; connect_req.uuid = uuid; - LOG_INFO("%s\n", __FUNCTION__); + LOG_DEBUG("%s\n", __FUNCTION__); btc_sm_dispatch(btc_av_cb.sm_handle, BTC_AV_CONNECT_REQ_EVT, (char *)&connect_req); @@ -998,7 +998,7 @@ bt_status_t btc_a2d_sink_connect(bt_bdaddr_t* remote_bda) *******************************************************************************/ static void btc_a2d_sink_deinit(void) { - LOG_INFO("%s\n", __FUNCTION__); + LOG_DEBUG("%s\n", __FUNCTION__); btc_a2dp_stop_media_task(); diff --git a/components/bt/bluedroid/btc/profile/std/avrc/btc_avrc.c b/components/bt/bluedroid/btc/profile/std/avrc/btc_avrc.c index 397c51c4bd..d4088d3911 100644 --- a/components/bt/bluedroid/btc/profile/std/avrc/btc_avrc.c +++ b/components/bt/bluedroid/btc/profile/std/avrc/btc_avrc.c @@ -145,8 +145,6 @@ static void handle_rc_features(void) } LOG_DEBUG("%s: rc_features=0x%x", __FUNCTION__, rc_features); - // todo: uncomment the following line when added the AVRC target role - // BTC_HAL_CBACK(bt_rc_callbacks, remote_features_cb, &rc_addr, rc_features) } @@ -386,7 +384,7 @@ BOOLEAN btc_rc_get_connected_peer(BD_ADDR peer_addr) *******************************************************************************/ static void btc_avrc_ct_init(void) { - LOG_INFO("## %s ##", __FUNCTION__); + LOG_DEBUG("## %s ##", __FUNCTION__); memset (&btc_rc_vb, 0, sizeof(btc_rc_vb)); btc_rc_vb.rc_vol_label=MAX_LABEL; diff --git a/components/bt/bluedroid/btc/profile/std/gap/btc_gap_ble.c b/components/bt/bluedroid/btc/profile/std/gap/btc_gap_ble.c index 339ff68420..9e158c592e 100644 --- a/components/bt/bluedroid/btc/profile/std/gap/btc_gap_ble.c +++ b/components/bt/bluedroid/btc/profile/std/gap/btc_gap_ble.c @@ -367,7 +367,26 @@ static void btc_ble_set_scan_rsp_data_raw(uint8_t *raw_scan_rsp, uint32_t raw_sc BTA_DmBleSetScanRspRaw(raw_scan_rsp, raw_scan_rsp_len, p_scan_rsp_data_cback); } -static void btc_ble_start_advertising (esp_ble_adv_params_t *ble_adv_params) +static void btc_start_adv_callback(tBTA_STATUS status) +{ + esp_ble_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BLE; + msg.act = ESP_GAP_BLE_ADV_START_COMPLETE_EVT; + param.adv_start_cmpl.status = status; + + ret = btc_transfer_context(&msg, ¶m, + sizeof(esp_ble_gap_cb_param_t), NULL); + + if (ret != BT_STATUS_SUCCESS) { + LOG_ERROR("%s btc_transfer_context failed\n", __func__); + } +} + +static void btc_ble_start_advertising (esp_ble_adv_params_t *ble_adv_params, tBTA_START_ADV_CMPL_CBACK start_adv_cback) { tBLE_BD_ADDR peer_addr; @@ -398,7 +417,8 @@ static void btc_ble_start_advertising (esp_ble_adv_params_t *ble_adv_params) ble_adv_params->own_addr_type, ble_adv_params->channel_map, ble_adv_params->adv_filter_policy, - &peer_addr); + &peer_addr, + start_adv_cback); } @@ -456,8 +476,7 @@ static void btc_search_callback(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_data param.scan_rst.ble_addr_type = p_data->inq_res.ble_addr_type; param.scan_rst.ble_evt_type = p_data->inq_res.ble_evt_type; param.scan_rst.flag = p_data->inq_res.flag; - memcpy(param.scan_rst.ble_adv, p_data->inq_res.p_eir, - ESP_BLE_ADV_DATA_LEN_MAX); + memcpy(param.scan_rst.ble_adv, p_data->inq_res.p_eir, sizeof(param.scan_rst.ble_adv)); break; } case BTA_DM_INQ_CMPL_EVT: { @@ -487,12 +506,33 @@ static void btc_search_callback(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_data btc_transfer_context(&msg, ¶m, sizeof(esp_ble_gap_cb_param_t), NULL); } - -static void btc_ble_start_scanning(uint8_t duration, tBTA_DM_SEARCH_CBACK *results_cb) +static void btc_start_scan_callback(tBTA_STATUS status) { - if ((duration != 0) && (results_cb != NULL)) { + esp_ble_gap_cb_param_t param; + bt_status_t ret; + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_GAP_BLE; + msg.act = ESP_GAP_BLE_SCAN_START_COMPLETE_EVT; + param.scan_start_cmpl.status = status; + + + ret = btc_transfer_context(&msg, ¶m, + sizeof(esp_ble_gap_cb_param_t), NULL); + + if (ret != BT_STATUS_SUCCESS) { + LOG_ERROR("%s btc_transfer_context failed\n", __func__); + } +} + +static void btc_ble_start_scanning(uint8_t duration, + tBTA_DM_SEARCH_CBACK *results_cb, + tBTA_START_SCAN_CMPL_CBACK *start_scan_cb) +{ + if ((duration != 0) && (results_cb != NULL) && (start_scan_cb != NULL)) { ///Start scan the device - BTA_DmBleObserve(true, duration, results_cb); + BTA_DmBleObserve(true, duration, results_cb, start_scan_cb); } else { LOG_ERROR("The scan duration or p_results_cb invalid\n"); } @@ -501,7 +541,7 @@ static void btc_ble_start_scanning(uint8_t duration, tBTA_DM_SEARCH_CBACK *resul static void btc_ble_stop_scanning(void) { uint8_t duration = 0; - BTA_DmBleObserve(false, duration, NULL); + BTA_DmBleObserve(false, duration, NULL, NULL); } @@ -576,6 +616,12 @@ void btc_gap_ble_cb_handler(btc_msg_t *msg) case ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT: btc_gap_ble_cb_to_app(ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT, param); break; + case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: + btc_gap_ble_cb_to_app(ESP_GAP_BLE_ADV_START_COMPLETE_EVT, param); + break; + case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT: + btc_gap_ble_cb_to_app(ESP_GAP_BLE_SCAN_START_COMPLETE_EVT, param); + break; default: break; @@ -695,13 +741,13 @@ void btc_gap_ble_call_handler(btc_msg_t *msg) btc_ble_set_scan_params(&arg->set_scan_param.scan_params, btc_scan_params_callback); break; case BTC_GAP_BLE_ACT_START_SCAN: - btc_ble_start_scanning(arg->start_scan.duration, btc_search_callback); + btc_ble_start_scanning(arg->start_scan.duration, btc_search_callback, btc_start_scan_callback); break; case BTC_GAP_BLE_ACT_STOP_SCAN: btc_ble_stop_scanning(); break; case BTC_GAP_BLE_ACT_START_ADV: - btc_ble_start_advertising(&arg->start_adv.adv_params); + btc_ble_start_advertising(&arg->start_adv.adv_params, btc_start_adv_callback); break; case BTC_GAP_BLE_ACT_STOP_ADV: btc_ble_stop_advertising(); diff --git a/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c b/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c index 2ba3ec0f14..da8d4d5f68 100644 --- a/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c +++ b/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c @@ -182,13 +182,16 @@ static void btc_gatts_act_create_attr_tab(esp_gatts_attr_db_t *gatts_attr_db, future_t *future_p; esp_ble_gatts_cb_param_t param; - //set the attribute table create service flag to ture + //set the attribute table create service flag to true btc_creat_tab_env.is_tab_creat_svc = true; btc_creat_tab_env.num_handle = max_nb_attr; for(int i = 0; i < max_nb_attr; i++){ if(gatts_attr_db[i].att_desc.uuid_length== ESP_UUID_LEN_16){ uuid = (gatts_attr_db[i].att_desc.uuid_p[1] << 8) + (gatts_attr_db[i].att_desc.uuid_p[0]); } + else{ + continue; + } future_p = future_new(); if (future_p == NULL) { LOG_ERROR("%s failed:no mem\n", __func__); diff --git a/components/bt/bluedroid/osi/include/thread.h b/components/bt/bluedroid/osi/include/thread.h index c9bd3c6db4..e297a6b2d2 100644 --- a/components/bt/bluedroid/osi/include/thread.h +++ b/components/bt/bluedroid/osi/include/thread.h @@ -20,7 +20,7 @@ #include "freertos/FreeRTOS.h" #include "freertos/queue.h" #include "freertos/task.h" - +#include "esp_task.h" #include "bt_defs.h" #define portBASE_TYPE int @@ -43,25 +43,25 @@ enum { // SIG_BTIF_WORK = 0xff }; -#define HCI_HOST_TASK_STACK_SIZE 1500 -#define HCI_HOST_TASK_PRIO (configMAX_PRIORITIES - 2) -#define HCI_HOST_TASK_NAME "hciHostT" -#define HCI_HOST_QUEUE_NUM 40 +#define HCI_HOST_TASK_STACK_SIZE (2048 + BT_TASK_EXTRA_STACK_SIZE) +#define HCI_HOST_TASK_PRIO (configMAX_PRIORITIES - 2) +#define HCI_HOST_TASK_NAME "hciHostT" +#define HCI_HOST_QUEUE_NUM 40 -#define HCI_H4_TASK_STACK_SIZE 1500 -#define HCI_H4_TASK_PRIO (configMAX_PRIORITIES - 3) -#define HCI_H4_TASK_NAME "hciH4T" -#define HCI_H4_QUEUE_NUM 60 +#define HCI_H4_TASK_STACK_SIZE (2048 + BT_TASK_EXTRA_STACK_SIZE) +#define HCI_H4_TASK_PRIO (configMAX_PRIORITIES - 3) +#define HCI_H4_TASK_NAME "hciH4T" +#define HCI_H4_QUEUE_NUM 60 -#define BTU_TASK_STACK_SIZE 4096 -#define BTU_TASK_PRIO (configMAX_PRIORITIES - 4) -#define BTU_TASK_NAME "btuT" -#define BTU_QUEUE_NUM 50 +#define BTU_TASK_STACK_SIZE (3584 + BT_TASK_EXTRA_STACK_SIZE) +#define BTU_TASK_PRIO (configMAX_PRIORITIES - 4) +#define BTU_TASK_NAME "btuT" +#define BTU_QUEUE_NUM 50 -#define BTC_TASK_STACK_SIZE CONFIG_BTC_TASK_STACK_SIZE //by menuconfig -#define BTC_TASK_NAME "btcT" -#define BTC_TASK_PRIO (configMAX_PRIORITIES - 5) -#define BTC_TASK_QUEUE_NUM 20 +#define BTC_TASK_STACK_SIZE (CONFIG_BTC_TASK_STACK_SIZE + BT_TASK_EXTRA_STACK_SIZE) //by menuconfig +#define BTC_TASK_NAME "btcT" +#define BTC_TASK_PRIO (configMAX_PRIORITIES - 5) +#define BTC_TASK_QUEUE_NUM 20 void btu_task_post(uint32_t sig); void hci_host_task_post(void); diff --git a/components/bt/bluedroid/stack/btm/btm_ble_gap.c b/components/bt/bluedroid/stack/btm/btm_ble_gap.c index 4cfce3b667..07c63b4963 100644 --- a/components/bt/bluedroid/stack/btm/btm_ble_gap.c +++ b/components/bt/bluedroid/stack/btm/btm_ble_gap.c @@ -2935,14 +2935,18 @@ tBTM_STATUS btm_ble_start_scan(void) tBTM_BLE_INQ_CB *p_inq = &btm_cb.ble_ctr_cb.inq_var; tBTM_STATUS status = BTM_CMD_STARTED; - /* start scan, disable duplicate filtering */ - if (!btsnd_hcic_ble_set_scan_enable (BTM_BLE_SCAN_ENABLE, p_inq->scan_duplicate_filter)) { + if (p_inq->adv_mode != BTM_BLE_ADV_DISABLE) { status = BTM_NO_RESOURCES; } else { - if (p_inq->scan_type == BTM_BLE_SCAN_MODE_ACTI) { - btm_ble_set_topology_mask(BTM_BLE_STATE_ACTIVE_SCAN_BIT); + /* start scan, disable duplicate filtering */ + if (!btsnd_hcic_ble_set_scan_enable (BTM_BLE_SCAN_ENABLE, p_inq->scan_duplicate_filter)) { + status = BTM_NO_RESOURCES; } else { - btm_ble_set_topology_mask(BTM_BLE_STATE_PASSIVE_SCAN_BIT); + if (p_inq->scan_type == BTM_BLE_SCAN_MODE_ACTI) { + btm_ble_set_topology_mask(BTM_BLE_STATE_ACTIVE_SCAN_BIT); + } else { + btm_ble_set_topology_mask(BTM_BLE_STATE_PASSIVE_SCAN_BIT); + } } } return status; @@ -2961,15 +2965,17 @@ void btm_ble_stop_scan(void) { BTM_TRACE_EVENT ("btm_ble_stop_scan "); - /* Clear the inquiry callback if set */ - btm_cb.ble_ctr_cb.inq_var.scan_type = BTM_BLE_SCAN_MODE_NONE; + if (btm_cb.ble_ctr_cb.inq_var.adv_mode == BTM_BLE_ADV_DISABLE) { + /* Clear the inquiry callback if set */ + btm_cb.ble_ctr_cb.inq_var.scan_type = BTM_BLE_SCAN_MODE_NONE; - /* stop discovery now */ - btsnd_hcic_ble_set_scan_enable (BTM_BLE_SCAN_DISABLE, BTM_BLE_DUPLICATE_ENABLE); + /* stop discovery now */ + btsnd_hcic_ble_set_scan_enable (BTM_BLE_SCAN_DISABLE, BTM_BLE_DUPLICATE_ENABLE); - btm_update_scanner_filter_policy(SP_ADV_ALL); + btm_update_scanner_filter_policy(SP_ADV_ALL); - btm_cb.ble_ctr_cb.wl_state &= ~BTM_BLE_WL_SCAN; + btm_cb.ble_ctr_cb.wl_state &= ~BTM_BLE_WL_SCAN; + } } /******************************************************************************* ** @@ -3089,9 +3095,15 @@ static BOOLEAN btm_ble_adv_states_operation(BTM_TOPOLOGY_FUNC_PTR *p_handler, UI *******************************************************************************/ tBTM_STATUS btm_ble_start_adv(void) { + tBTM_BLE_CB *p_ble_cb = & btm_cb.ble_ctr_cb; tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; tBTM_STATUS rt = BTM_NO_RESOURCES; BTM_TRACE_EVENT ("btm_ble_start_adv\n"); + + if (BTM_BLE_IS_OBS_ACTIVE(p_ble_cb->scan_activity)) { + return BTM_NO_RESOURCES; + } + if (!btm_ble_adv_states_operation (btm_ble_topology_check, p_cb->evt_type)) { return BTM_WRONG_MODE; } @@ -3133,10 +3145,12 @@ tBTM_STATUS btm_ble_start_adv(void) *******************************************************************************/ tBTM_STATUS btm_ble_stop_adv(void) { + tBTM_BLE_CB *p_ble_cb = & btm_cb.ble_ctr_cb; tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; tBTM_STATUS rt = BTM_SUCCESS; - if (p_cb->adv_mode == BTM_BLE_ADV_ENABLE) { + if (p_cb->adv_mode == BTM_BLE_ADV_ENABLE + && !BTM_BLE_IS_OBS_ACTIVE(p_ble_cb->scan_activity)) { if (btsnd_hcic_ble_set_adv_enable (BTM_BLE_ADV_DISABLE)) { p_cb->fast_adv_on = FALSE; p_cb->adv_mode = BTM_BLE_ADV_DISABLE; diff --git a/components/bt/bluedroid/stack/gatt/gatt_db.c b/components/bt/bluedroid/stack/gatt/gatt_db.c index 03919d483c..ae4f6430fa 100644 --- a/components/bt/bluedroid/stack/gatt/gatt_db.c +++ b/components/bt/bluedroid/stack/gatt/gatt_db.c @@ -268,14 +268,21 @@ static tGATT_STATUS read_attr_value (void *p_attr, status = GATT_SUCCESS; } } else { /* characteristic description or characteristic value */ - if (p_attr16->control.auto_rsp == GATT_RSP_BY_STACK) { - if (p_attr16->p_value != NULL && p_attr16->p_value->attr_val.attr_val != NULL) { - uint8_t *value = p_attr16->p_value->attr_val.attr_val + offset; - len = (mtu >= p_attr16->p_value->attr_val.attr_len) ? (p_attr16->p_value->attr_val.attr_len) : mtu; - ARRAY_TO_STREAM(p, value, len); + if (p_attr16->p_value == NULL || p_attr16->p_value->attr_val.attr_val == NULL) { + status = GATT_ESP_ERROR; + } + else if (offset > p_attr16->p_value->attr_val.attr_len){ + /*if offset equal to max_len, should respond with zero byte value + //if offset is greater than max_len, should respond with an error*/ + status = GATT_INVALID_OFFSET; + } else { + UINT8 *value = (UINT8 *)(p_attr16->p_value->attr_val.attr_val) + offset; + UINT16 len_left = p_attr16->p_value->attr_val.attr_len - offset; + len = (mtu >= len_left) ? (len_left) : mtu; + ARRAY_TO_STREAM(p, value, len); + status = GATT_STACK_RSP; } - status = GATT_STACK_RSP; } else { status = GATT_PENDING; @@ -463,6 +470,27 @@ UINT16 gatts_add_characteristic (tGATT_SVC_DB *p_db, tGATT_PERM perm, tBT_UUID uuid = {LEN_UUID_16, {GATT_UUID_CHAR_DECLARE}}; GATT_TRACE_DEBUG("gatts_add_characteristic perm=0x%0x property=0x%0x\n", perm, property); + /* parameter validation check */ + if ((control != NULL) && (control->auto_rsp == GATT_STACK_RSP)){ + if (attr_val == NULL){ + GATT_TRACE_ERROR("Error in %s, line=%d, for stack respond attribute, attr_val should not be NULL here\n",\ + __func__, __LINE__); + return 0; + } else if (attr_val->attr_max_len == 0){ + GATT_TRACE_ERROR("Error in %s, line=%d, for stack respond attribute, attribute max length should not be 0\n",\ + __func__, __LINE__); + return 0; + } + } + + if (attr_val != NULL){ + if (attr_val->attr_len > attr_val->attr_max_len){ + GATT_TRACE_ERROR("Error in %s, line=%d,attribute actual length should not be larger than max length\n",\ + __func__, __LINE__); + return 0; + } + } + if ((p_char_decl = (tGATT_ATTR16 *)allocate_attr_in_db(p_db, &uuid, GATT_PERM_READ)) != NULL) { if (!copy_extra_byte_in_db(p_db, (void **)&p_char_decl->p_value, sizeof(tGATT_CHAR_DECL))) { @@ -483,10 +511,9 @@ UINT16 gatts_add_characteristic (tGATT_SVC_DB *p_db, tGATT_PERM perm, p_char_val->control.auto_rsp = control->auto_rsp; } else { p_char_val->control.auto_rsp = GATT_RSP_DEFAULT; - } - if (attr_val != NULL) { + if (attr_val != NULL) { if (!copy_extra_byte_in_db(p_db, (void **)&p_char_val->p_value, sizeof(tGATT_ATTR_VAL))) { deallocate_attr_in_db(p_db, p_char_val); return 0; @@ -496,12 +523,23 @@ UINT16 gatts_add_characteristic (tGATT_SVC_DB *p_db, tGATT_PERM perm, p_char_val->p_value->attr_val.attr_len = attr_val->attr_len; p_char_val->p_value->attr_val.attr_max_len = attr_val->attr_max_len; p_char_val->p_value->attr_val.attr_val = GKI_getbuf(attr_val->attr_max_len); - if (p_char_val->p_value->attr_val.attr_val != NULL) { - GATT_TRACE_DEBUG("attribute value not NULL"); - memcpy(p_char_val->p_value->attr_val.attr_val, attr_val->attr_val, attr_val->attr_len); + if (p_char_val->p_value->attr_val.attr_val == NULL) { + deallocate_attr_in_db(p_db, p_char_decl); + deallocate_attr_in_db(p_db, p_char_val); + GATT_TRACE_WARNING("Warning in %s, line=%d, insufficient resource to allocate for attribute value\n", __func__, __LINE__); + return 0; + } + + //initiate characteristic attribute value part + memset(p_char_val->p_value->attr_val.attr_val, 0, attr_val->attr_max_len); + if (attr_val->attr_val != NULL) { + if (attr_val->attr_max_len < attr_val->attr_len){ + GATT_TRACE_ERROR("Error in %s, Line=%d, attribute actual length (%d) should not larger than max size (%d)\n", + __func__, __LINE__, attr_val->attr_len, attr_val->attr_max_len); + } + UINT16 actual_len = (attr_val->attr_max_len < attr_val->attr_len) ? (attr_val->attr_max_len) : (attr_val->attr_len); + memcpy(p_char_val->p_value->attr_val.attr_val, attr_val->attr_val, actual_len); } - } else { - p_char_val->p_value = NULL; } return p_char_val->handle; @@ -582,14 +620,35 @@ UINT16 gatts_add_char_descr (tGATT_SVC_DB *p_db, tGATT_PERM perm, GATT_TRACE_DEBUG("gatts_add_char_descr uuid=0x%04x\n", p_descr_uuid->uu.uuid16); + /* parameter validation check */ + if ((control != NULL) && (control->auto_rsp == GATT_STACK_RSP)){ + if (attr_val == NULL){ + GATT_TRACE_ERROR("Error in %s, line=%d, for stack respond attribute, attr_val should not be NULL here\n",\ + __func__, __LINE__); + return 0; + } else if (attr_val->attr_max_len == 0){ + GATT_TRACE_ERROR("Error in %s, line=%d, for stack respond attribute, attribute max length should not be 0\n",\ + __func__, __LINE__); + return 0; + } + } + + if (attr_val != NULL){ + if (attr_val->attr_len > attr_val->attr_max_len){ + GATT_TRACE_ERROR("Error in %s, line=%d,attribute actual length (%d) should not be larger than max length (%d)\n",\ + __func__, __LINE__, attr_val->attr_len, attr_val->attr_max_len); + return 0; + } + } + + /* Add characteristic descriptors */ if ((p_char_dscptr = (tGATT_ATTR16 *)allocate_attr_in_db(p_db, p_descr_uuid, perm)) == NULL) { + deallocate_attr_in_db(p_db, p_char_dscptr); GATT_TRACE_DEBUG("gatts_add_char_descr Fail for adding char descriptors."); return 0; } else { - if (control != NULL) { - p_char_dscptr->control.auto_rsp = control->auto_rsp; - } + p_char_dscptr->control.auto_rsp = (control == NULL) ? GATT_RSP_DEFAULT : (control->auto_rsp); if (attr_val != NULL) { if (!copy_extra_byte_in_db(p_db, (void **)&p_char_dscptr->p_value, sizeof(tGATT_ATTR_VAL))) { deallocate_attr_in_db(p_db, p_char_dscptr); @@ -597,10 +656,17 @@ UINT16 gatts_add_char_descr (tGATT_SVC_DB *p_db, tGATT_PERM perm, } p_char_dscptr->p_value->attr_val.attr_len = attr_val->attr_len; p_char_dscptr->p_value->attr_val.attr_max_len = attr_val->attr_max_len; - if (attr_val->attr_val != NULL) { + if (attr_val->attr_max_len != 0) { p_char_dscptr->p_value->attr_val.attr_val = GKI_getbuf(attr_val->attr_max_len); - if (p_char_dscptr->p_value->attr_val.attr_val != NULL) { - memset(p_char_dscptr->p_value->attr_val.attr_val, 0, attr_val->attr_max_len); + if (p_char_dscptr->p_value->attr_val.attr_val == NULL) { + deallocate_attr_in_db(p_db, p_char_dscptr); + GATT_TRACE_WARNING("Warning in %s, line=%d, insufficient resource to allocate for descriptor value\n", __func__, __LINE__); + return 0; + } + + //initiate characteristic attribute value part + memset(p_char_dscptr->p_value->attr_val.attr_val, 0, attr_val->attr_max_len); + if(attr_val->attr_val != NULL) { memcpy(p_char_dscptr->p_value->attr_val.attr_val, attr_val->attr_val, attr_val->attr_len); } } @@ -625,7 +691,7 @@ UINT16 gatts_add_char_descr (tGATT_SVC_DB *p_db, tGATT_PERM perm, ** *******************************************************************************/ tGATT_STATUS gatts_set_attribute_value(tGATT_SVC_DB *p_db, UINT16 attr_handle, - UINT16 length, UINT8 *value) + UINT16 length, UINT8 *value) { tGATT_ATTR16 *p_cur; @@ -637,51 +703,46 @@ tGATT_STATUS gatts_set_attribute_value(tGATT_SVC_DB *p_db, UINT16 attr_handle, GATT_TRACE_DEBUG("gatts_set_attribute_value Fail:p_db->p_attr_list is NULL.\n"); return GATT_INVALID_PDU; } + if ((length > 0) && (value == NULL)){ + GATT_TRACE_ERROR("Error in %s, line=%d, value should not be NULL here\n",__func__, __LINE__); + return GATT_INVALID_PDU; + } p_cur = (tGATT_ATTR16 *) p_db->p_attr_list; while (p_cur != NULL) { if (p_cur->handle == attr_handle) { - + /* for characteristic should not be set, return GATT_NOT_FOUND */ if (p_cur->uuid_type == GATT_ATTR_UUID_TYPE_16) { switch (p_cur->uuid) { - case GATT_UUID_PRI_SERVICE: - case GATT_UUID_SEC_SERVICE: - case GATT_UUID_CHAR_DECLARE: - case GATT_UUID_INCLUDE_SERVICE: - return GATT_NOT_FOUND; - default: - if (p_cur->p_value != NULL && p_cur->p_value->attr_val.attr_max_len < length) { - GATT_TRACE_ERROR("gatts_set_attribute_vaule failt:Invalid value length"); - return GATT_INVALID_ATTR_LEN; - } else if (p_cur->p_value != NULL && p_cur->p_value->attr_val.attr_max_len > 0) { - memcpy(p_cur->p_value->attr_val.attr_val, value, length); - p_cur->p_value->attr_val.attr_len = length; - } else { - return GATT_INVALID_ATTR_LEN; - } - break; - } - } else { - if (p_cur->p_value != NULL && p_cur->p_value->attr_val.attr_max_len < length) { - GATT_TRACE_ERROR("gatts_set_attribute_vaule failt:Invalid value length"); - } else if (p_cur->p_value != NULL && p_cur->p_value->attr_val.attr_max_len > 0) { - memcpy(p_cur->p_value->attr_val.attr_val, value, length); - p_cur->p_value->attr_val.attr_len = length; - } else { - return GATT_INVALID_ATTR_LEN; + case GATT_UUID_PRI_SERVICE: + case GATT_UUID_SEC_SERVICE: + case GATT_UUID_CHAR_DECLARE: + return GATT_NOT_FOUND; + break; } } + + /* in other cases, value can be set*/ + if ((p_cur->p_value == NULL) || (p_cur->p_value->attr_val.attr_val == NULL) \ + || (p_cur->p_value->attr_val.attr_max_len == 0)){ + GATT_TRACE_ERROR("Error in %s, line=%d, attribute value should not be NULL here\n", __func__, __LINE__); + return GATT_NOT_FOUND; + } else if (p_cur->p_value->attr_val.attr_max_len < length) { + GATT_TRACE_ERROR("gatts_set_attribute_value failed:Invalid value length"); + return GATT_INVALID_ATTR_LEN; + } else{ + memcpy(p_cur->p_value->attr_val.attr_val, value, length); + p_cur->p_value->attr_val.attr_len = length; + } break; } - p_cur = p_cur->p_next; } return GATT_SUCCESS; } - /******************************************************************************* ** ** Function gatts_get_attribute_value @@ -872,20 +933,25 @@ tGATT_STATUS gatts_write_attr_value_by_handle(tGATT_SVC_DB *p_db, return GATT_APP_RSP; } - if (p_attr->p_value != NULL && (p_attr->p_value->attr_val.attr_max_len >= - offset + len)) { + if ((p_attr->p_value != NULL) && + (p_attr->p_value->attr_val.attr_max_len >= offset + len) && + p_attr->p_value->attr_val.attr_val != NULL) { memcpy(p_attr->p_value->attr_val.attr_val + offset, p_value, len); p_attr->p_value->attr_val.attr_len = len + offset; return GATT_SUCCESS; - } else { - return GATT_NOT_LONG; + } else if (p_attr->p_value->attr_val.attr_max_len < offset + len){ + GATT_TRACE_DEBUG("Remote device try to write with a length larger then attribute's max length\n"); + return GATT_INVALID_ATTR_LEN; + } else if ((p_attr->p_value == NULL) || (p_attr->p_value->attr_val.attr_val == NULL)){ + GATT_TRACE_ERROR("Error in %s, line=%d, %s should not be NULL here\n", __func__, __LINE__, \ + (p_attr->p_value == NULL) ? "p_value" : "attr_val.attr_val"); + return GATT_ESP_ERROR; } } p_attr = (tGATT_ATTR16 *)p_attr->p_next; } - } return status; diff --git a/components/bt/bluedroid/stack/gatt/gatt_sr.c b/components/bt/bluedroid/stack/gatt/gatt_sr.c index 2d34a05045..79d518398c 100644 --- a/components/bt/bluedroid/stack/gatt/gatt_sr.c +++ b/components/bt/bluedroid/stack/gatt/gatt_sr.c @@ -33,6 +33,42 @@ #define GATT_MTU_REQ_MIN_LEN 2 + +/******************************************************************************* +** +** Function gatt_send_packet +** +** Description This function is called to send gatt packets directly + +** +** Returns status +** +*******************************************************************************/ +tGATT_STATUS gatt_send_packet (tGATT_TCB *p_tcb, UINT8 *p_data, UINT16 len) +{ + BT_HDR *p_msg = NULL; + UINT8 *p_m = NULL; + UINT16 buf_len; + tGATT_STATUS status; + + if (len > p_tcb->payload_size){ + return GATT_ILLEGAL_PARAMETER; + } + + buf_len = (UINT16)(sizeof(BT_HDR) + p_tcb->payload_size + L2CAP_MIN_OFFSET); + if ((p_msg = (BT_HDR *)GKI_getbuf(buf_len)) == NULL) { + return GATT_NO_RESOURCES; + } + + memset(p_msg, 0, buf_len); + p_msg->len = len; + p_m = (UINT8 *)(p_msg + 1) + L2CAP_MIN_OFFSET; + memcpy(p_m, p_data, len); + + status = attp_send_sr_msg(p_tcb, p_msg); + return status; +} + /******************************************************************************* ** ** Function gatt_sr_enqueue_cmd @@ -300,7 +336,11 @@ void gatt_process_exec_write_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U UINT32 trans_id = 0; tGATT_IF gatt_if; UINT16 conn_id; - + UINT16 queue_num = 0; + BOOLEAN is_prepare_write_valid = FALSE; + BOOLEAN is_need_dequeue_sr_cmd = FALSE; + tGATT_PREPARE_WRITE_RECORD *prepare_record = NULL; + tGATT_PREPARE_WRITE_QUEUE_DATA * queue_data = NULL; UNUSED(len); #if GATT_CONFORMANCE_TESTING == TRUE @@ -319,11 +359,60 @@ void gatt_process_exec_write_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U /* mask the flag */ flag &= GATT_PREP_WRITE_EXEC; + prepare_record = &(p_tcb->prepare_write_record); + queue_num = prepare_record->queue._count; + - /* no prep write is queued */ + //if received prepare_write packets include stack_rsp and app_rsp, + //stack respond to execute_write only when stack_rsp handle has invalid_offset + //or invalid_length error; + //app need to respond to execute_write if it has received app_rsp handle packets + if (((prepare_record->error_code_app == GATT_SUCCESS) && + (prepare_record->total_num == queue_num)) + || (flag == GATT_PREP_WRITE_CANCEL)){ + tGATT_EXEC_WRITE_RSP gatt_exec_write_rsp; + gatt_exec_write_rsp.op_code = GATT_RSP_EXEC_WRITE; + gatt_send_packet(p_tcb, (UINT8 *)(&gatt_exec_write_rsp), sizeof(gatt_exec_write_rsp)); + gatt_dequeue_sr_cmd(p_tcb); + if (flag != GATT_PREP_WRITE_CANCEL){ + is_prepare_write_valid = TRUE; + } + GATT_TRACE_DEBUG("Send execute_write_rsp\n"); + } else if ((prepare_record->error_code_app == GATT_SUCCESS) && + (prepare_record->total_num > queue_num)){ + //No error for stack_rsp's handles and there exist some app_rsp's handles, + //so exec_write_rsp depends to app's response; but stack_rsp's data is valid + //TODO: there exist problem if stack_rsp's data is valid but app_rsp's data is not valid. + is_prepare_write_valid = TRUE; + } else if(prepare_record->total_num < queue_num) { + GATT_TRACE_ERROR("Error in %s, line=%d, prepare write total number (%d) \ + should not smaller than prepare queue number (%d)\n", \ + __func__, __LINE__,prepare_record->total_num, queue_num); + } else if (prepare_record->error_code_app != GATT_SUCCESS){ + GATT_TRACE_DEBUG("Send error code for execute_write, code=0x%x\n", prepare_record->error_code_app); + is_need_dequeue_sr_cmd = (prepare_record->total_num == queue_num) ? TRUE : FALSE; + gatt_send_error_rsp(p_tcb, prepare_record->error_code_app, GATT_REQ_EXEC_WRITE, 0, is_need_dequeue_sr_cmd); + } + + //dequeue prepare write data + while(GKI_getfirst(&(prepare_record->queue))) { + queue_data = GKI_dequeue(&(prepare_record->queue)); + if (is_prepare_write_valid){ + if((queue_data->p_attr->p_value != NULL) && (queue_data->p_attr->p_value->attr_val.attr_val != NULL)){ + memcpy(queue_data->p_attr->p_value->attr_val.attr_val+queue_data->offset, queue_data->value, queue_data->len); + } + } + GKI_freebuf(queue_data); + } + + /* according to ble spec, even if there is no prep write queued, + * need to respond execute_write_response + * Note: exec_write_rsp callback should be called after all data has been written*/ if (!gatt_sr_is_prep_cnt_zero(p_tcb)) { - trans_id = gatt_sr_enqueue_cmd(p_tcb, op_code, 0); - gatt_sr_copy_prep_cnt_to_cback_cnt(p_tcb); + if (prepare_record->total_num > queue_num){ + trans_id = gatt_sr_enqueue_cmd(p_tcb, op_code, 0); + gatt_sr_copy_prep_cnt_to_cback_cnt(p_tcb); + } for (i = 0; i < GATT_MAX_APPS; i++) { if (p_tcb->prep_cnt[i]) { @@ -336,10 +425,10 @@ void gatt_process_exec_write_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U p_tcb->prep_cnt[i] = 0; } } - } else { /* nothing needs to be executed , send response now */ - GATT_TRACE_ERROR("gatt_process_exec_write_req: no prepare write pending"); - gatt_send_error_rsp(p_tcb, GATT_ERROR, GATT_REQ_EXEC_WRITE, 0, FALSE); } + + prepare_record->total_num = 0; + prepare_record->error_code_app = GATT_SUCCESS; } /******************************************************************************* @@ -987,54 +1076,29 @@ void gatts_process_read_by_type_req(tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, void gatts_process_write_req (tGATT_TCB *p_tcb, UINT8 i_rcb, UINT16 handle, UINT8 op_code, UINT16 len, UINT8 *p_data) { - UINT16 buf_len = (UINT16)(sizeof(BT_HDR) + p_tcb->payload_size + L2CAP_MIN_OFFSET); tGATTS_DATA sr_data; UINT32 trans_id; tGATT_STATUS status; - UINT8 sec_flag, key_size, *p = p_data, *p_m; + UINT8 sec_flag, key_size, *p = p_data; tGATT_SR_REG *p_sreg; UINT16 conn_id, offset = 0; - BT_HDR *p_msg = NULL; + memset(&sr_data, 0, sizeof(tGATTS_DATA)); + sr_data.write_req.need_rsp = FALSE; - if ((p_msg = (BT_HDR *)GKI_getbuf(buf_len)) == NULL) { - GATT_TRACE_ERROR("gatts_process_write_req failed. no resources.\n"); - } - - memset(p_msg, 0, buf_len); - p_m = (UINT8 *)(p_msg + 1) + L2CAP_MIN_OFFSET; - *p_m ++ = op_code + 1; - p_msg->len = 1; - buf_len = p_tcb->payload_size - 1; - switch (op_code) { - case GATT_REQ_PREPARE_WRITE: - sr_data.write_req.is_prep = TRUE; - STREAM_TO_UINT16(sr_data.write_req.offset, p); - UINT16_TO_STREAM(p_m, sr_data.write_req.is_prep); - offset = sr_data.write_req.offset; - len -= 2; - /* fall through */ case GATT_SIGN_CMD_WRITE: if (op_code == GATT_SIGN_CMD_WRITE) { - GATT_TRACE_DEBUG("Write CMD with data sigining" ); + GATT_TRACE_DEBUG("Write CMD with data signing" ); len -= GATT_AUTH_SIGN_LEN; } /* fall through */ case GATT_CMD_WRITE: case GATT_REQ_WRITE: - if (op_code == GATT_REQ_WRITE || op_code == GATT_REQ_PREPARE_WRITE) { - sr_data.write_req.need_rsp = TRUE; - if(op_code == GATT_REQ_PREPARE_WRITE){ - memcpy(p_m, p, len); - p_msg->len += len; - } - } sr_data.write_req.handle = handle; sr_data.write_req.len = len; if (len != 0 && p != NULL) { memcpy (sr_data.write_req.value, p, len); - } break; } @@ -1059,42 +1123,182 @@ void gatts_process_write_req (tGATT_TCB *p_tcb, UINT8 i_rcb, UINT16 handle, conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_sreg->gatt_if); status = gatts_write_attr_value_by_handle(gatt_cb.sr_reg[i_rcb].p_db, handle, offset, p, len); - if((sr_data.write_req.need_rsp == TRUE) && (status == GATT_APP_RSP)){ + if((op_code == GATT_REQ_WRITE) && (status == GATT_APP_RSP)){ sr_data.write_req.need_rsp = TRUE; status = GATT_PENDING; } - else{ - sr_data.write_req.need_rsp = FALSE; - } - gatt_sr_send_req_callback(conn_id, trans_id, GATTS_REQ_TYPE_WRITE, &sr_data); - - if (status == GATT_SUCCESS) { - attp_send_sr_msg(p_tcb, p_msg); - gatt_dequeue_sr_cmd(p_tcb); - } else { - GKI_freebuf(p_msg); - } - } else { - GATT_TRACE_ERROR("max pending command, send error\n"); + GATT_TRACE_ERROR("Error in %s, line=%d, max pending command, send error\n", __func__, __LINE__); status = GATT_BUSY; /* max pending command, application error */ } } - /* in theroy BUSY is not possible(should already been checked), protected check */ - if (status != GATT_PENDING && status != GATT_BUSY && status != GATT_SUCCESS && - (op_code == GATT_REQ_PREPARE_WRITE || op_code == GATT_REQ_WRITE)) { - gatt_send_error_rsp (p_tcb, status, op_code, handle, FALSE); - gatt_dequeue_sr_cmd(p_tcb); + /* response should be sent only for write_request */ + if ((op_code == GATT_REQ_WRITE) && (sr_data.write_req.need_rsp == FALSE)){ + if (status == GATT_SUCCESS){ + tGATT_WRITE_REQ_RSP gatt_write_req_rsp; + gatt_write_req_rsp.op_code = GATT_RSP_WRITE; + gatt_send_packet(p_tcb, (UINT8 *)(&gatt_write_req_rsp), sizeof(gatt_write_req_rsp)); + gatt_dequeue_sr_cmd(p_tcb); + } else if (status != GATT_PENDING){ + /* note: in case of GATT_BUSY, will respond this application error to remote device */ + gatt_send_error_rsp (p_tcb, status, op_code, handle, TRUE); + } } + return; } + +/******************************************************************************* + ** + ** Function gatts_attr_process_preapre_write + ** + ** Description This function is called to process the prepare write request + ** from client. + ** + ** Returns void + ** + *******************************************************************************/ +void gatt_attr_process_prepare_write (tGATT_TCB *p_tcb, UINT8 i_rcb, UINT16 handle, + UINT8 op_code, UINT16 len, UINT8 *p_data) +{ + tGATT_STATUS status; + tGATT_PREPARE_WRITE_QUEUE_DATA * queue_data = NULL; + tGATT_ATTR16 *p_attr; + tGATT_ATTR16 *p_attr_temp; + tGATTS_DATA sr_data; + UINT32 trans_id = 0; + UINT8 sec_flag, key_size, *p = p_data; + tGATT_SR_REG *p_sreg; + UINT16 conn_id, offset = 0; + tGATT_SVC_DB *p_db; + BOOLEAN is_need_prepare_write_rsp = FALSE; + BOOLEAN is_need_queue_data = FALSE; + tGATT_PREPARE_WRITE_RECORD *prepare_record = NULL; + memset(&sr_data, 0, sizeof(tGATTS_DATA)); + + //get offset from p_data + STREAM_TO_UINT16(offset, p); + len -= 2; + p_sreg = &gatt_cb.sr_reg[i_rcb]; + conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_sreg->gatt_if); + //prepare_record = &(prepare_write_record); + prepare_record = &(p_tcb->prepare_write_record); + + gatt_sr_get_sec_info(p_tcb->peer_bda, + p_tcb->transport, + &sec_flag, + &key_size); + + status = gatts_write_attr_perm_check (gatt_cb.sr_reg[i_rcb].p_db, + op_code, + handle, + sr_data.write_req.offset, + p, + len, + sec_flag, + key_size); + + if (status == GATT_SUCCESS){ + if ((trans_id = gatt_sr_enqueue_cmd(p_tcb, op_code, handle)) != 0) { + p_db = gatt_cb.sr_reg[i_rcb].p_db; + if (p_db && p_db->p_attr_list) { + p_attr = (tGATT_ATTR16 *)p_db->p_attr_list; + while (p_attr && handle >= p_attr->handle) { + if (p_attr->handle == handle ) { + p_attr_temp = p_attr; + if (p_attr->control.auto_rsp == GATT_RSP_BY_APP) { + status = GATT_APP_RSP; + } else if (p_attr->p_value != NULL && + offset > p_attr->p_value->attr_val.attr_max_len) { + status = GATT_INVALID_OFFSET; + is_need_prepare_write_rsp = TRUE; + is_need_queue_data = TRUE; + } else if (p_attr->p_value != NULL && + ((offset + len) > p_attr->p_value->attr_val.attr_max_len)){ + status = GATT_INVALID_ATTR_LEN; + is_need_prepare_write_rsp = TRUE; + is_need_queue_data = TRUE; + } else if (p_attr->p_value == NULL) { + LOG_ERROR("Error in %s, attribute of handle 0x%x not allocate value buffer\n", + __func__, handle); + status = GATT_ESP_ERROR; + } else { + //valid prepare write request, need to send response and queue the data + //status: GATT_SUCCESS + is_need_prepare_write_rsp = TRUE; + is_need_queue_data = TRUE; + } + } + p_attr = (tGATT_ATTR16 *)p_attr->p_next; + } + } + } else{ + status = GATT_ESP_ERROR; + GATT_TRACE_ERROR("Error in %s, Line %d: GATT BUSY\n", __func__, __LINE__); + } + } + + if (is_need_queue_data){ + queue_data = (tGATT_PREPARE_WRITE_QUEUE_DATA *)GKI_getbuf(len + sizeof(tGATT_PREPARE_WRITE_QUEUE_DATA)); + if (queue_data == NULL){ + status = GATT_PREPARE_Q_FULL; + } else { + queue_data->p_attr = p_attr_temp; + queue_data->len = len; + queue_data->handle = handle; + queue_data->offset = offset; + memcpy(queue_data->value, p, len); + GKI_enqueue(&(prepare_record->queue), queue_data); + } + } + + if (is_need_prepare_write_rsp){ + //send prepare write response + if (queue_data != NULL){ + queue_data->op_code = op_code + 1; + //5: op_code 1 + handle 2 + offset 2 + tGATT_STATUS rsp_send_status = gatt_send_packet(p_tcb, &(queue_data->op_code), queue_data->len + 5); + gatt_sr_update_prep_cnt(p_tcb, p_sreg->gatt_if, TRUE, FALSE); + gatt_dequeue_sr_cmd(p_tcb); + + if (rsp_send_status != GATT_SUCCESS){ + LOG_ERROR("Error in %s, line=%d, fail to send prepare_write_rsp, status=0x%x\n", + __func__, __LINE__, rsp_send_status); + } + } else{ + LOG_ERROR("Error in %s, line=%d, queue_data should not be NULL here, fail to send prepare_write_rsp\n", + __func__, __LINE__); + } + } + + if ((status == GATT_APP_RSP) || (is_need_prepare_write_rsp)){ + prepare_record->total_num++; + memset(&sr_data, 0, sizeof(sr_data)); + sr_data.write_req.is_prep = TRUE; + sr_data.write_req.handle = handle; + sr_data.write_req.offset = offset; + sr_data.write_req.len = len; + sr_data.write_req.need_rsp = (status == GATT_APP_RSP) ? TRUE : FALSE; + memcpy(sr_data.write_req.value, p, len); + gatt_sr_send_req_callback(conn_id, trans_id, GATTS_REQ_TYPE_WRITE, &sr_data); + } else{ + gatt_send_error_rsp(p_tcb, status, GATT_REQ_PREPARE_WRITE, handle, TRUE); + } + + if ((prepare_record->error_code_app == GATT_SUCCESS) + && ((status == GATT_INVALID_OFFSET) || (status == GATT_INVALID_ATTR_LEN))){ + prepare_record->error_code_app = status; + } + +} + /******************************************************************************* ** ** Function gatts_process_read_req @@ -1226,9 +1430,11 @@ void gatts_process_attribute_req (tGATT_TCB *p_tcb, UINT8 op_code, case GATT_REQ_WRITE: /* write char/char descriptor value */ case GATT_CMD_WRITE: case GATT_SIGN_CMD_WRITE: - case GATT_REQ_PREPARE_WRITE: gatts_process_write_req(p_tcb, i, handle, op_code, len, p); break; + + case GATT_REQ_PREPARE_WRITE: + gatt_attr_process_prepare_write (p_tcb, i, handle, op_code, len, p); default: break; } diff --git a/components/bt/bluedroid/stack/gatt/gatt_utils.c b/components/bt/bluedroid/stack/gatt/gatt_utils.c index 09f4e8df2f..60f99966b2 100644 --- a/components/bt/bluedroid/stack/gatt/gatt_utils.c +++ b/components/bt/bluedroid/stack/gatt/gatt_utils.c @@ -116,6 +116,26 @@ void gatt_free_pending_enc_queue(tGATT_TCB *p_tcb) } } +/******************************************************************************* +** +** Function gatt_free_pending_prepare_write_queue +** +** Description Free all buffers in pending prepare write packets queue +** +** Returns None +** +*******************************************************************************/ +void gatt_free_pending_prepare_write_queue(tGATT_TCB *p_tcb) +{ + GATT_TRACE_DEBUG("gatt_free_pending_prepare_write_queue"); + /* release all queued prepare write packets */ + while (!GKI_queue_is_empty(&(p_tcb->prepare_write_record.queue))) { + GKI_freebuf (GKI_dequeue (&(p_tcb->prepare_write_record.queue))); + } + p_tcb->prepare_write_record.total_num = 0; + p_tcb->prepare_write_record.error_code_app = GATT_SUCCESS; +} + /******************************************************************************* ** ** Function gatt_delete_dev_from_srv_chg_clt_list @@ -2108,6 +2128,7 @@ void gatt_cleanup_upon_disc(BD_ADDR bda, UINT16 reason, tBT_TRANSPORT transport) btu_stop_timer (&p_tcb->conf_timer_ent); gatt_free_pending_ind(p_tcb); gatt_free_pending_enc_queue(p_tcb); + gatt_free_pending_prepare_write_queue(p_tcb); for (i = 0; i < GATT_MAX_APPS; i ++) { p_reg = &gatt_cb.cl_rcb[i]; diff --git a/components/bt/bluedroid/stack/gatt/include/gatt_int.h b/components/bt/bluedroid/stack/gatt/include/gatt_int.h index c9622a24c1..9aca0c5dfd 100644 --- a/components/bt/bluedroid/stack/gatt/include/gatt_int.h +++ b/components/bt/bluedroid/stack/gatt/include/gatt_int.h @@ -133,6 +133,16 @@ typedef struct { UINT8 reason; } tGATT_ERROR; +/* Execute write response structure */ +typedef struct { + UINT8 op_code; +}__attribute__((packed)) tGATT_EXEC_WRITE_RSP; + +/* Write request response structure */ +typedef struct { + UINT8 op_code; +}__attribute__((packed)) tGATT_WRITE_REQ_RSP; + /* server response message to ATT protocol */ typedef union { @@ -329,6 +339,32 @@ typedef struct { UINT16 count; } tGATT_SRV_LIST_INFO; +/* prepare write queue data */ +typedef struct{ + //len: length of value + tGATT_ATTR16 *p_attr; + UINT16 len; + UINT8 op_code; + UINT16 handle; + UINT16 offset; + UINT8 value[2]; +}__attribute__((packed)) tGATT_PREPARE_WRITE_QUEUE_DATA; + +/* structure to store prepare write packts information */ +typedef struct{ + //only store prepare write packets which need + //to be responded by stack (not by application) + BUFFER_Q queue; + + //store the total number of prepare write packets + //including that should be responded by stack or by application + UINT16 total_num; + + //store application error code for prepare write, + //invalid offset && invalid length + UINT8 error_code_app; +}tGATT_PREPARE_WRITE_RECORD; + typedef struct { BUFFER_Q pending_enc_clcb; /* pending encryption channel q */ tGATT_SEC_ACTION sec_act; @@ -362,6 +398,7 @@ typedef struct { BOOLEAN in_use; UINT8 tcb_idx; + tGATT_PREPARE_WRITE_RECORD prepare_write_record; /* prepare write packets record */ } tGATT_TCB; diff --git a/components/bt/bluedroid/stack/include/btm_ble_api.h b/components/bt/bluedroid/stack/include/btm_ble_api.h index 6eb078386c..4d3f5f889a 100644 --- a/components/bt/bluedroid/stack/include/btm_ble_api.h +++ b/components/bt/bluedroid/stack/include/btm_ble_api.h @@ -310,30 +310,41 @@ typedef void (tBTM_RAND_ENC_CB) (tBTM_RAND_ENC *p1); typedef UINT32 tBTM_BLE_AD_MASK; -#define BTM_BLE_AD_TYPE_FLAG HCI_EIR_FLAGS_TYPE /* 0x01 */ -#define BTM_BLE_AD_TYPE_16SRV_PART HCI_EIR_MORE_16BITS_UUID_TYPE /* 0x02 */ -#define BTM_BLE_AD_TYPE_16SRV_CMPL HCI_EIR_COMPLETE_16BITS_UUID_TYPE /* 0x03 */ -#define BTM_BLE_AD_TYPE_32SRV_PART HCI_EIR_MORE_32BITS_UUID_TYPE /* 0x04 */ -#define BTM_BLE_AD_TYPE_32SRV_CMPL HCI_EIR_COMPLETE_32BITS_UUID_TYPE /* 0x05 */ -#define BTM_BLE_AD_TYPE_128SRV_PART HCI_EIR_MORE_128BITS_UUID_TYPE /* 0x06 */ -#define BTM_BLE_AD_TYPE_128SRV_CMPL HCI_EIR_COMPLETE_128BITS_UUID_TYPE /* 0x07 */ -#define BTM_BLE_AD_TYPE_NAME_SHORT HCI_EIR_SHORTENED_LOCAL_NAME_TYPE /* 0x08 */ -#define BTM_BLE_AD_TYPE_NAME_CMPL HCI_EIR_COMPLETE_LOCAL_NAME_TYPE /* 0x09 */ -#define BTM_BLE_AD_TYPE_TX_PWR HCI_EIR_TX_POWER_LEVEL_TYPE /* 0x0A */ -#define BTM_BLE_AD_TYPE_DEV_CLASS 0x0D -#define BTM_BLE_AD_TYPE_SM_TK 0x10 -#define BTM_BLE_AD_TYPE_SM_OOB_FLAG 0x11 -#define BTM_BLE_AD_TYPE_INT_RANGE 0x12 -#define BTM_BLE_AD_TYPE_SOL_SRV_UUID 0x14 -#define BTM_BLE_AD_TYPE_128SOL_SRV_UUID 0x15 -#define BTM_BLE_AD_TYPE_SERVICE_DATA 0x16 -#define BTM_BLE_AD_TYPE_PUBLIC_TARGET 0x17 -#define BTM_BLE_AD_TYPE_RANDOM_TARGET 0x18 -#define BTM_BLE_AD_TYPE_APPEARANCE 0x19 -#define BTM_BLE_AD_TYPE_ADV_INT 0x1a -#define BTM_BLE_AD_TYPE_32SOL_SRV_UUID 0x1b -#define BTM_BLE_AD_TYPE_32SERVICE_DATA 0x1c -#define BTM_BLE_AD_TYPE_128SERVICE_DATA 0x1d +#define BTM_BLE_AD_TYPE_FLAG HCI_EIR_FLAGS_TYPE /* 0x01 */ +#define BTM_BLE_AD_TYPE_16SRV_PART HCI_EIR_MORE_16BITS_UUID_TYPE /* 0x02 */ +#define BTM_BLE_AD_TYPE_16SRV_CMPL HCI_EIR_COMPLETE_16BITS_UUID_TYPE /* 0x03 */ +#define BTM_BLE_AD_TYPE_32SRV_PART HCI_EIR_MORE_32BITS_UUID_TYPE /* 0x04 */ +#define BTM_BLE_AD_TYPE_32SRV_CMPL HCI_EIR_COMPLETE_32BITS_UUID_TYPE /* 0x05 */ +#define BTM_BLE_AD_TYPE_128SRV_PART HCI_EIR_MORE_128BITS_UUID_TYPE /* 0x06 */ +#define BTM_BLE_AD_TYPE_128SRV_CMPL HCI_EIR_COMPLETE_128BITS_UUID_TYPE /* 0x07 */ +#define BTM_BLE_AD_TYPE_NAME_SHORT HCI_EIR_SHORTENED_LOCAL_NAME_TYPE /* 0x08 */ +#define BTM_BLE_AD_TYPE_NAME_CMPL HCI_EIR_COMPLETE_LOCAL_NAME_TYPE /* 0x09 */ +#define BTM_BLE_AD_TYPE_TX_PWR HCI_EIR_TX_POWER_LEVEL_TYPE /* 0x0A */ +#define BTM_BLE_AD_TYPE_DEV_CLASS 0x0D +#define BTM_BLE_AD_TYPE_SM_TK 0x10 +#define BTM_BLE_AD_TYPE_SM_OOB_FLAG 0x11 +#define BTM_BLE_AD_TYPE_INT_RANGE 0x12 +#define BTM_BLE_AD_TYPE_SOL_SRV_UUID 0x14 +#define BTM_BLE_AD_TYPE_128SOL_SRV_UUID 0x15 +#define BTM_BLE_AD_TYPE_SERVICE_DATA 0x16 +#define BTM_BLE_AD_TYPE_PUBLIC_TARGET 0x17 +#define BTM_BLE_AD_TYPE_RANDOM_TARGET 0x18 +#define BTM_BLE_AD_TYPE_APPEARANCE 0x19 +#define BTM_BLE_AD_TYPE_ADV_INT 0x1a +#define BTM_BLE_AD_TYPE_LE_DEV_ADDR 0x1b +#define BTM_BLE_AD_TYPE_LE_ROLE 0x1c +#define BTM_BLE_AD_TYPE_SPAIR_C256 0x1d +#define BTM_BLE_AD_TYPE_SPAIR_R256 0x1e +#define BTM_BLE_AD_TYPE_32SOL_SRV_UUID 0x1f +#define BTM_BLE_AD_TYPE_32SERVICE_DATA 0x20 +#define BTM_BLE_AD_TYPE_128SERVICE_DATA 0x21 +#define BTM_BLE_AD_TYPE_LE_SECURE_CONFIRM 0x22 +#define BTM_BLE_AD_TYPE_LE_SECURE_RANDOM 0x23 +#define BTM_BLE_AD_TYPE_URI 0x24 +#define BTM_BLE_AD_TYPE_INDOOR_POSITION 0x25 +#define BTM_BLE_AD_TYPE_TRANS_DISC_DATA 0x26 +#define BTM_BLE_AD_TYPE_LE_SUPPORT_FEATURE 0x27 +#define BTM_BLE_AD_TYPE_CHAN_MAP_UPDATE 0x28 #define BTM_BLE_AD_TYPE_MANU HCI_EIR_MANUFACTURER_SPECIFIC_TYPE /* 0xff */ typedef UINT8 tBTM_BLE_AD_TYPE; diff --git a/components/bt/bluedroid/stack/include/gatt_api.h b/components/bt/bluedroid/stack/include/gatt_api.h index fc8da23221..f9c6b379a4 100644 --- a/components/bt/bluedroid/stack/include/gatt_api.h +++ b/components/bt/bluedroid/stack/include/gatt_api.h @@ -65,6 +65,8 @@ #define GATT_CONGESTED 0x8f #define GATT_STACK_RSP 0x90 #define GATT_APP_RSP 0x91 +//Error caused by customer application or stack bug +#define GATT_ESP_ERROR 0X9f /* 0xE0 ~ 0xFC reserved for future use */ #define GATT_CCC_CFG_ERR 0xFD /* Client Characteristic Configuration Descriptor Improperly Configured */ diff --git a/components/bt/bt.c b/components/bt/bt.c index 6a81d11acc..e8f4204ef4 100644 --- a/components/bt/bt.c +++ b/components/bt/bt.c @@ -32,9 +32,14 @@ #if CONFIG_BT_ENABLED +/* Bluetooth system and controller config */ +#define BTDM_CFG_BT_EM_RELEASE (1<<0) +#define BTDM_CFG_BT_DATA_RELEASE (1<<1) +/* Other reserved for future */ + /* not for user call, so don't put to include file */ extern void btdm_osi_funcs_register(void *osi_funcs); -extern void btdm_controller_init(void); +extern void btdm_controller_init(uint32_t config_mask); extern void btdm_controller_schedule(void); extern void btdm_controller_deinit(void); extern int btdm_controller_enable(esp_bt_mode_t mode); @@ -73,7 +78,7 @@ struct osi_funcs_t { void *(*_mutex_create)(void); int32_t (*_mutex_lock)(void *mutex); int32_t (*_mutex_unlock)(void *mutex); - esp_err_t (* _read_efuse_mac)(uint8_t mac[6]); + int32_t (* _read_efuse_mac)(uint8_t mac[6]); }; /* Static variable declare */ @@ -124,6 +129,11 @@ static int32_t IRAM_ATTR mutex_unlock_wrapper(void *mutex) return (int32_t)xSemaphoreGive(mutex); } +static int32_t IRAM_ATTR read_mac_wrapper(uint8_t mac[6]) +{ + return esp_read_mac(mac, ESP_MAC_BT); +} + static struct osi_funcs_t osi_funcs = { ._set_isr = xt_set_interrupt_handler, ._ints_on = xt_ints_on, @@ -136,7 +146,7 @@ static struct osi_funcs_t osi_funcs = { ._mutex_create = mutex_create_wrapper, ._mutex_lock = mutex_lock_wrapper, ._mutex_unlock = mutex_unlock_wrapper, - ._read_efuse_mac = esp_efuse_read_mac, + ._read_efuse_mac = read_mac_wrapper }; bool esp_vhci_host_check_send_available(void) @@ -154,11 +164,25 @@ void esp_vhci_host_register_callback(const esp_vhci_host_callback_t *callback) API_vhci_host_register_callback((const vhci_host_callback_t *)callback); } +static uint32_t btdm_config_mask_load(void) +{ + uint32_t mask = 0x0; + +#ifdef CONFIG_BT_DRAM_RELEASE + mask |= (BTDM_CFG_BT_EM_RELEASE | BTDM_CFG_BT_DATA_RELEASE); +#endif + return mask; +} + static void bt_controller_task(void *pvParam) { + uint32_t btdm_cfg_mask = 0; + btdm_osi_funcs_register(&osi_funcs); - btdm_controller_init(); + btdm_cfg_mask = btdm_config_mask_load(); + btdm_controller_init(btdm_cfg_mask); + btdm_controller_status = ESP_BT_CONTROLLER_STATUS_INITED; /* Loop */ diff --git a/components/bt/lib b/components/bt/lib index dbac82b5c2..9a4bb1d528 160000 --- a/components/bt/lib +++ b/components/bt/lib @@ -1 +1 @@ -Subproject commit dbac82b5c2694f2639161b0a2b3c0bd8c7d3efc5 +Subproject commit 9a4bb1d5287572664f170f9df4dbfd71babdfc68 diff --git a/components/driver/include/driver/sdmmc_host.h b/components/driver/include/driver/sdmmc_host.h index 0f56c266a8..dc3e9ef6bf 100644 --- a/components/driver/include/driver/sdmmc_host.h +++ b/components/driver/include/driver/sdmmc_host.h @@ -50,10 +50,12 @@ extern "C" { typedef struct { gpio_num_t gpio_cd; ///< GPIO number of card detect signal gpio_num_t gpio_wp; ///< GPIO number of write protect signal + uint8_t width; ///< Bus width used by the slot (might be less than the max width supported) } sdmmc_slot_config_t; #define SDMMC_SLOT_NO_CD ((gpio_num_t) -1) ///< indicates that card detect line is not used #define SDMMC_SLOT_NO_WP ((gpio_num_t) -1) ///< indicates that write protect line is not used +#define SDMMC_SLOT_WIDTH_DEFAULT 0 ///< use the default width for the slot (8 for slot 0, 4 for slot 1) /** * Macro defining default configuration of SDMMC host slot @@ -61,6 +63,7 @@ typedef struct { #define SDMMC_SLOT_CONFIG_DEFAULT() {\ .gpio_cd = SDMMC_SLOT_NO_CD, \ .gpio_wp = SDMMC_SLOT_NO_WP, \ + .width = SDMMC_SLOT_WIDTH_DEFAULT, \ } /** diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index 23635df27d..748735c5da 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -478,7 +478,8 @@ esp_err_t uart_intr_config(uart_port_t uart_num, const uart_intr_config_t *intr_ * @param uart_queue UART event queue handle (out param). On success, a new queue handle is written here to provide * access to UART events. If set to NULL, driver will not use an event queue. * @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred) - * ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info. + * ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info. Do not set ESP_INTR_FLAG_IRAM here + * (the driver's ISR handler is not located in IRAM) * * @return * - ESP_OK Success diff --git a/components/driver/sdmmc_host.c b/components/driver/sdmmc_host.c index 400c85b889..706a1e3fef 100644 --- a/components/driver/sdmmc_host.c +++ b/components/driver/sdmmc_host.c @@ -299,20 +299,31 @@ esp_err_t sdmmc_host_init_slot(int slot, const sdmmc_slot_config_t* slot_config) } int gpio_cd = slot_config->gpio_cd; int gpio_wp = slot_config->gpio_wp; + uint8_t slot_width = slot_config->width; // Configure pins const sdmmc_slot_info_t* pslot = &s_slot_info[slot]; + + if (slot_width == SDMMC_SLOT_WIDTH_DEFAULT) { + slot_width = pslot->width; + } + else if (slot_width > pslot->width) { + return ESP_ERR_INVALID_ARG; + } + configure_pin(pslot->clk); configure_pin(pslot->cmd); configure_pin(pslot->d0); - configure_pin(pslot->d1); - configure_pin(pslot->d2); - configure_pin(pslot->d3); - if (pslot->width == 8) { - configure_pin(pslot->d4); - configure_pin(pslot->d5); - configure_pin(pslot->d6); - configure_pin(pslot->d7); + if (slot_width >= 4) { + configure_pin(pslot->d1); + configure_pin(pslot->d2); + configure_pin(pslot->d3); + if (slot_width == 8) { + configure_pin(pslot->d4); + configure_pin(pslot->d5); + configure_pin(pslot->d6); + configure_pin(pslot->d7); + } } if (gpio_cd != -1) { gpio_set_direction(gpio_cd, GPIO_MODE_INPUT); diff --git a/components/driver/uart.c b/components/driver/uart.c index a40d58b305..e254bce033 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -470,7 +470,7 @@ esp_err_t uart_intr_config(uart_port_t uart_num, const uart_intr_config_t *intr_ } //internal isr handler for default driver code. -static void IRAM_ATTR uart_rx_intr_handler_default(void *param) +static void uart_rx_intr_handler_default(void *param) { uart_obj_t *p_uart = (uart_obj_t*) param; uint8_t uart_num = p_uart->uart_num; @@ -1002,6 +1002,9 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b ESP_LOGE(UART_TAG, "UART driver already installed"); return ESP_FAIL; } + + assert((intr_alloc_flags & ESP_INTR_FLAG_IRAM) == 0); /* uart_rx_intr_handler_default is not in IRAM */ + uart_isr_register(uart_num, uart_rx_intr_handler_default, p_uart_obj[uart_num], intr_alloc_flags, &p_uart_obj[uart_num]->intr_handle); uart_intr_config_t uart_intr = { .intr_enable_mask = UART_RXFIFO_FULL_INT_ENA_M diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index 8aa0ed3b5f..ab1e04e809 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -105,6 +105,28 @@ config MEMMAP_SPISRAM main memory map. Enable this if you have this hardware and want to use it in the same way as on-chip RAM. +choice NUMBER_OF_MAC_ADDRESS_GENERATED_FROM_EFUSE + bool "Number of MAC address generated from the hardware MAC address in efuse" + default FOUR_MAC_ADDRESS_FROM_EFUSE + help + Config the number of MAC address which is generated from the hardware MAC address in efuse. + If the number is two, the MAC addresses of WiFi station and bluetooth are generated from + the hardware MAC address in efuse. The MAC addresses of WiFi softap and ethernet are derived + from that of WiFi station and bluetooth respectively. + If the number is four, the MAC addresses of WiFi station, WiFi softap, bluetooth and ethernet + are all generated from the hardware MAC address in efuse. + +config TWO_MAC_ADDRESS_FROM_EFUSE + bool "Two" +config FOUR_MAC_ADDRESS_FROM_EFUSE + bool "Four" +endchoice + +config NUMBER_OF_MAC_ADDRESS_GENERATED_FROM_EFUSE + int + default 2 if TWO_MAC_ADDRESS_FROM_EFUSE + default 4 if FOUR_MAC_ADDRESS_FROM_EFUSE + config SYSTEM_EVENT_QUEUE_SIZE int "System event queue size" default 32 @@ -491,15 +513,61 @@ config SW_COEXIST_ENABLE automatically managed, no user intervention is required. -config ESP32_WIFI_RX_BUFFER_NUM - int "Max number of WiFi RX buffers" +config ESP32_WIFI_STATIC_RX_BUFFER_NUM + int "Max number of WiFi static RX buffers" depends on WIFI_ENABLED range 2 25 default 10 help - Set the number of WiFi rx buffers. Each buffer takes approximately 1.6KB of RAM. - Larger number for higher throughput but more memory. Smaller number for lower - throughput but less memory. + Set the number of WiFi static rx buffers. Each buffer takes approximately 1.6KB of RAM. + The static rx buffers are allocated when esp_wifi_init is called, they are not freed + until esp_wifi_deinit is called. + WiFi hardware use these buffers to receive packets, generally larger number for higher + throughput but more memory, smaller number for lower throughput but less memory. + +config ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM + int "Max number of WiFi dynamic RX buffers" + depends on WIFI_ENABLED + range 0 64 + default 0 + help + Set the number of WiFi dynamic rx buffers, 0 means no limitation for dynamic rx buffer + allocation. The size of dynamic rx buffers is not fixed. + For each received packet in static rx buffers, WiFi driver makes a copy + to dynamic rx buffers and then deliver it to high layer stack. The dynamic rx buffer + is freed when the application, such as socket, successfully received the packet. + For some applications, the WiFi driver receiving speed is faster than application + consuming speed, we may run out of memory if no limitation for the dynamic rx buffer + number. Generally the number of dynamic rx buffer should be no less than static + rx buffer number if it is not 0. + +config ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM + int "Max number of WiFi dynamic TX buffers" + depends on WIFI_ENABLED + range 16 64 + default 32 + help + Set the number of WiFi dynamic tx buffers, 0 means no limitation for dynamic tx buffer + allocation. The size of dynamic tx buffers is not fixed. + For each tx packet from high layer stack, WiFi driver make a copy of it. For some applications, + especially the UDP application, the high layer deliver speed is faster than the WiFi tx + speed, we may run out of memory if no limitation for the dynamic tx buffer number. + + +config ESP32_WIFI_AMPDU_ENABLED + bool "WiFi AMPDU" + depends on WIFI_ENABLED + default y + help + Select this option to enable AMPDU feature + + +config ESP32_WIFI_NVS_ENABLED + bool "WiFi NVS flash" + depends on WIFI_ENABLED + default y + help + Select this option to enable WiFi NVS flash config PHY_ENABLED bool diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index cf20083508..2dbfffd5a2 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -38,7 +38,7 @@ #include "tcpip_adapter.h" -#include "heap_alloc_caps.h" +#include "esp_heap_alloc_caps.h" #include "sdkconfig.h" #include "esp_system.h" #include "esp_spi_flash.h" @@ -106,8 +106,6 @@ void IRAM_ATTR call_start_cpu0() memset(&_rtc_bss_start, 0, (&_rtc_bss_end - &_rtc_bss_start) * sizeof(_rtc_bss_start)); } - // Initialize heap allocator - heap_alloc_caps_init(); ESP_EARLY_LOGI(TAG, "Pro cpu up."); @@ -131,6 +129,15 @@ void IRAM_ATTR call_start_cpu0() ESP_EARLY_LOGI(TAG, "Single core mode"); CLEAR_PERI_REG_MASK(DPORT_APPCPU_CTRL_B_REG, DPORT_APPCPU_CLKGATE_EN); #endif + + /* Initialize heap allocator. WARNING: This *needs* to happen *after* the app cpu has booted. + If the heap allocator is initialized first, it will put free memory linked list items into + memory also used by the ROM. Starting the app cpu will let its ROM initialize that memory, + corrupting those linked lists. Initializing the allocator *after* the app cpu has booted + works around this problem. */ + heap_alloc_caps_init(); + + ESP_EARLY_LOGI(TAG, "Pro cpu start user code"); start_cpu0(); } @@ -250,6 +257,8 @@ static void main_task(void* args) // Now that the application is about to start, disable boot watchdogs REG_CLR_BIT(TIMG_WDTCONFIG0_REG(0), TIMG_WDT_FLASHBOOT_MOD_EN_S); REG_CLR_BIT(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_FLASHBOOT_MOD_EN); + //Enable allocation in region where the startup stacks were located. + heap_alloc_enable_nonos_stack_tag(); app_main(); vTaskDelete(NULL); } diff --git a/components/esp32/deep_sleep.c b/components/esp32/deep_sleep.c index 4d672402e1..55e92a05e0 100644 --- a/components/esp32/deep_sleep.c +++ b/components/esp32/deep_sleep.c @@ -126,6 +126,9 @@ void IRAM_ATTR esp_deep_sleep_start() if (s_config.wakeup_triggers & EXT_EVENT1_TRIG_EN) { ext1_wakeup_prepare(); } + if (s_config.wakeup_triggers & SAR_TRIG_EN) { + SET_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_WAKEUP_FORCE_EN); + } // TODO: move timer wakeup configuration into a similar function // once rtc_sleep is opensourced. @@ -163,6 +166,10 @@ void system_deep_sleep(uint64_t) __attribute__((alias("esp_deep_sleep"))); esp_err_t esp_deep_sleep_enable_ulp_wakeup() { #ifdef CONFIG_ULP_COPROC_ENABLED + if(s_config.wakeup_triggers & RTC_EXT_EVENT0_TRIG_EN) { + ESP_LOGE(TAG, "Conflicting wake-up trigger: ext0"); + return ESP_ERR_INVALID_STATE; + } s_config.wakeup_triggers |= RTC_SAR_TRIG_EN; return ESP_OK; #else @@ -177,6 +184,26 @@ esp_err_t esp_deep_sleep_enable_timer_wakeup(uint64_t time_in_us) return ESP_OK; } +esp_err_t esp_deep_sleep_enable_touchpad_wakeup() +{ + if (s_config.wakeup_triggers & (RTC_EXT_EVENT0_TRIG_EN)) { + ESP_LOGE(TAG, "Conflicting wake-up trigger: ext0"); + return ESP_ERR_INVALID_STATE; + } + s_config.wakeup_triggers |= RTC_TOUCH_TRIG_EN; + return ESP_OK; +} + +touch_pad_t esp_deep_sleep_get_touchpad_wakeup_status() +{ + if (esp_deep_sleep_get_wakeup_cause() != ESP_DEEP_SLEEP_WAKEUP_TOUCHPAD) { + return TOUCH_PAD_MAX; + } + uint32_t touch_mask = REG_GET_FIELD(SENS_SAR_TOUCH_CTRL2_REG, SENS_TOUCH_MEAS_EN); + assert(touch_mask != 0 && "wakeup reason is RTC_TOUCH_TRIG_EN but SENS_TOUCH_MEAS_EN is zero"); + return (touch_pad_t) (__builtin_ffs(touch_mask) - 1); +} + esp_err_t esp_deep_sleep_enable_ext0_wakeup(gpio_num_t gpio_num, int level) { if (level < 0 || level > 1) { @@ -185,6 +212,10 @@ esp_err_t esp_deep_sleep_enable_ext0_wakeup(gpio_num_t gpio_num, int level) if (!RTC_GPIO_IS_VALID_GPIO(gpio_num)) { return ESP_ERR_INVALID_ARG; } + if (s_config.wakeup_triggers & (RTC_TOUCH_TRIG_EN | RTC_SAR_TRIG_EN)) { + ESP_LOGE(TAG, "Conflicting wake-up triggers: touch / ULP"); + return ESP_ERR_INVALID_STATE; + } s_config.ext0_rtc_gpio_num = rtc_gpio_desc[gpio_num].rtc_num; s_config.ext0_trigger_level = level; s_config.wakeup_triggers |= RTC_EXT_EVENT0_TRIG_EN; @@ -276,8 +307,7 @@ static void ext1_wakeup_prepare() uint64_t esp_deep_sleep_get_ext1_wakeup_status() { - int wakeup_reason = REG_GET_FIELD(RTC_CNTL_WAKEUP_STATE_REG, RTC_CNTL_WAKEUP_CAUSE); - if (wakeup_reason != RTC_EXT_EVENT1_TRIG) { + if (esp_deep_sleep_get_wakeup_cause() != ESP_DEEP_SLEEP_WAKEUP_EXT1) { return 0; } uint32_t status = REG_GET_FIELD(RTC_CNTL_EXT_WAKEUP1_STATUS_REG, RTC_CNTL_EXT_WAKEUP1_STATUS); @@ -296,6 +326,28 @@ uint64_t esp_deep_sleep_get_ext1_wakeup_status() return gpio_mask; } +esp_deep_sleep_wakeup_cause_t esp_deep_sleep_get_wakeup_cause() +{ + if (rtc_get_reset_reason(0) != DEEPSLEEP_RESET) { + return ESP_DEEP_SLEEP_WAKEUP_UNDEFINED; + } + + uint32_t wakeup_cause = REG_GET_FIELD(RTC_CNTL_WAKEUP_STATE_REG, RTC_CNTL_WAKEUP_CAUSE); + if (wakeup_cause & RTC_EXT_EVENT0_TRIG) { + return ESP_DEEP_SLEEP_WAKEUP_EXT0; + } else if (wakeup_cause & RTC_EXT_EVENT1_TRIG) { + return ESP_DEEP_SLEEP_WAKEUP_EXT1; + } else if (wakeup_cause & RTC_TIMER_EXPIRE) { + return ESP_DEEP_SLEEP_WAKEUP_TIMER; + } else if (wakeup_cause & RTC_TOUCH_TRIG) { + return ESP_DEEP_SLEEP_WAKEUP_TOUCHPAD; + } else if (wakeup_cause & RTC_SAR_TRIG) { + return ESP_DEEP_SLEEP_WAKEUP_ULP; + } else { + return ESP_DEEP_SLEEP_WAKEUP_UNDEFINED; + } +} + esp_err_t esp_deep_sleep_pd_config(esp_deep_sleep_pd_domain_t domain, esp_deep_sleep_pd_option_t option) { @@ -333,13 +385,15 @@ static uint32_t get_power_down_flags() s_config.pd_options[ESP_PD_DOMAIN_RTC_FAST_MEM] = ESP_PD_OPTION_ON; } - // RTC_PERIPH is needed for EXT0 wakeup and for ULP. - // If RTC_PERIPH is auto, and both EXT0 and ULP aren't enabled, - // power down RTC_PERIPH. + // RTC_PERIPH is needed for EXT0 wakeup. + // If RTC_PERIPH is auto, and EXT0 isn't enabled, power down RTC_PERIPH. if (s_config.pd_options[ESP_PD_DOMAIN_RTC_PERIPH] == ESP_PD_OPTION_AUTO) { - if (s_config.wakeup_triggers & - (RTC_SAR_TRIG_EN | RTC_EXT_EVENT0_TRIG_EN)) { + if (s_config.wakeup_triggers & RTC_EXT_EVENT0_TRIG_EN) { s_config.pd_options[ESP_PD_DOMAIN_RTC_PERIPH] = ESP_PD_OPTION_ON; + } else if (s_config.wakeup_triggers & (RTC_TOUCH_TRIG_EN | RTC_SAR_TRIG_EN)) { + // In both rev. 0 and rev. 1 of ESP32, forcing power up of RTC_PERIPH + // prevents ULP timer and touch FSMs from working correctly. + s_config.pd_options[ESP_PD_DOMAIN_RTC_PERIPH] = ESP_PD_OPTION_OFF; } } diff --git a/components/esp32/event_default_handlers.c b/components/esp32/event_default_handlers.c index c09775ab9d..0c7ec2ac51 100644 --- a/components/esp32/event_default_handlers.c +++ b/components/esp32/event_default_handlers.c @@ -45,14 +45,9 @@ do{\ }\ } while(0) -typedef esp_err_t (*system_event_handle_fn_t)(system_event_t *e); - -typedef struct { - system_event_id_t event_id; - system_event_handle_fn_t event_handle; -} system_event_handle_t; - +typedef esp_err_t (*system_event_handler_t)(system_event_t *e); +#ifdef CONFIG_WIFI_ENABLED static esp_err_t system_event_ap_start_handle_default(system_event_t *event); static esp_err_t system_event_ap_stop_handle_default(system_event_t *event); static esp_err_t system_event_sta_start_handle_default(system_event_t *event); @@ -60,39 +55,50 @@ static esp_err_t system_event_sta_stop_handle_default(system_event_t *event); static esp_err_t system_event_sta_connected_handle_default(system_event_t *event); static esp_err_t system_event_sta_disconnected_handle_default(system_event_t *event); static esp_err_t system_event_sta_got_ip_default(system_event_t *event); +#endif +#ifdef CONFIG_ETHERNET static esp_err_t system_event_eth_start_handle_default(system_event_t *event); static esp_err_t system_event_eth_stop_handle_default(system_event_t *event); static esp_err_t system_event_eth_connected_handle_default(system_event_t *event); static esp_err_t system_event_eth_disconnected_handle_default(system_event_t *event); +#endif -static system_event_handle_t g_system_event_handle_table[] = { - {SYSTEM_EVENT_WIFI_READY, NULL}, - {SYSTEM_EVENT_SCAN_DONE, NULL}, - {SYSTEM_EVENT_STA_START, system_event_sta_start_handle_default}, - {SYSTEM_EVENT_STA_STOP, system_event_sta_stop_handle_default}, - {SYSTEM_EVENT_STA_CONNECTED, system_event_sta_connected_handle_default}, - {SYSTEM_EVENT_STA_DISCONNECTED, system_event_sta_disconnected_handle_default}, - {SYSTEM_EVENT_STA_AUTHMODE_CHANGE, NULL}, - {SYSTEM_EVENT_STA_GOT_IP, system_event_sta_got_ip_default}, - {SYSTEM_EVENT_STA_WPS_ER_SUCCESS, NULL}, - {SYSTEM_EVENT_STA_WPS_ER_FAILED, NULL}, - {SYSTEM_EVENT_STA_WPS_ER_TIMEOUT, NULL}, - {SYSTEM_EVENT_STA_WPS_ER_PIN, NULL}, - {SYSTEM_EVENT_AP_START, system_event_ap_start_handle_default}, - {SYSTEM_EVENT_AP_STOP, system_event_ap_stop_handle_default}, - {SYSTEM_EVENT_AP_STACONNECTED, NULL}, - {SYSTEM_EVENT_AP_STADISCONNECTED, NULL}, - {SYSTEM_EVENT_AP_PROBEREQRECVED, NULL}, - {SYSTEM_EVENT_AP_STA_GOT_IP6, NULL}, - {SYSTEM_EVENT_ETH_START, system_event_eth_start_handle_default}, - {SYSTEM_EVENT_ETH_STOP, system_event_eth_stop_handle_default}, - {SYSTEM_EVENT_ETH_CONNECTED, system_event_eth_connected_handle_default}, - {SYSTEM_EVENT_ETH_DISCONNECTED, system_event_eth_disconnected_handle_default}, - {SYSTEM_EVENT_ETH_GOT_IP, NULL}, - {SYSTEM_EVENT_MAX, NULL}, +/* Default event handler functions + + Any entry in this table which is disabled by config will have a NULL handler. +*/ +static const system_event_handler_t default_event_handlers[SYSTEM_EVENT_MAX] = { +#ifdef CONFIG_WIFI_ENABLED + [SYSTEM_EVENT_WIFI_READY] = NULL, + [SYSTEM_EVENT_SCAN_DONE] = NULL, + [SYSTEM_EVENT_STA_START] = system_event_sta_start_handle_default, + [SYSTEM_EVENT_STA_STOP] = system_event_sta_stop_handle_default, + [SYSTEM_EVENT_STA_CONNECTED] = system_event_sta_connected_handle_default, + [SYSTEM_EVENT_STA_DISCONNECTED] = system_event_sta_disconnected_handle_default, + [SYSTEM_EVENT_STA_AUTHMODE_CHANGE] = NULL, + [SYSTEM_EVENT_STA_GOT_IP] = system_event_sta_got_ip_default, + [SYSTEM_EVENT_STA_WPS_ER_SUCCESS] = NULL, + [SYSTEM_EVENT_STA_WPS_ER_FAILED] = NULL, + [SYSTEM_EVENT_STA_WPS_ER_TIMEOUT] = NULL, + [SYSTEM_EVENT_STA_WPS_ER_PIN] = NULL, + [SYSTEM_EVENT_AP_START] = system_event_ap_start_handle_default, + [SYSTEM_EVENT_AP_STOP] = system_event_ap_stop_handle_default, + [SYSTEM_EVENT_AP_STACONNECTED] = NULL, + [SYSTEM_EVENT_AP_STADISCONNECTED] = NULL, + [SYSTEM_EVENT_AP_PROBEREQRECVED] = NULL, + [SYSTEM_EVENT_AP_STA_GOT_IP6] = NULL, +#endif +#ifdef CONFIG_ETHERNET + [SYSTEM_EVENT_ETH_START] = system_event_eth_start_handle_default, + [SYSTEM_EVENT_ETH_STOP] = system_event_eth_stop_handle_default, + [SYSTEM_EVENT_ETH_CONNECTED] = system_event_eth_connected_handle_default, + [SYSTEM_EVENT_ETH_DISCONNECTED] = system_event_eth_disconnected_handle_default, + [SYSTEM_EVENT_ETH_GOT_IP] = NULL, +#endif }; +#ifdef CONFIG_ETHERNET esp_err_t system_event_eth_start_handle_default(system_event_t *event) { tcpip_adapter_ip_info_t eth_ip; @@ -121,7 +127,6 @@ esp_err_t system_event_eth_connected_handle_default(system_event_t *event) tcpip_adapter_dhcpc_get_status(TCPIP_ADAPTER_IF_ETH, &status); if (status == TCPIP_ADAPTER_DHCP_INIT) { - tcpip_adapter_dhcpc_start(TCPIP_ADAPTER_IF_ETH); } else if (status == TCPIP_ADAPTER_DHCP_STOPPED) { tcpip_adapter_ip_info_t eth_ip; @@ -149,9 +154,9 @@ esp_err_t system_event_eth_disconnected_handle_default(system_event_t *event) tcpip_adapter_down(TCPIP_ADAPTER_IF_ETH); return ESP_OK; } +#endif - - +#ifdef CONFIG_WIFI_ENABLED static esp_err_t system_event_sta_got_ip_default(system_event_t *event) { WIFI_API_CALL_CHECK("esp_wifi_internal_set_sta_ip", esp_wifi_internal_set_sta_ip(), ESP_OK); @@ -245,6 +250,7 @@ esp_err_t system_event_sta_disconnected_handle_default(system_event_t *event) WIFI_API_CALL_CHECK("esp_wifi_internal_reg_rxcb", esp_wifi_internal_reg_rxcb(ESP_IF_WIFI_STA, NULL), ESP_OK); return ESP_OK; } +#endif static esp_err_t esp_system_event_debug(system_event_t *event) { @@ -377,10 +383,10 @@ esp_err_t esp_event_process_default(system_event_t *event) } esp_system_event_debug(event); - if ((event->event_id < SYSTEM_EVENT_MAX) && (event->event_id == g_system_event_handle_table[event->event_id].event_id)) { - if (g_system_event_handle_table[event->event_id].event_handle) { + if ((event->event_id < SYSTEM_EVENT_MAX)) { + if (default_event_handlers[event->event_id] != NULL) { ESP_LOGV(TAG, "enter default callback"); - g_system_event_handle_table[event->event_id].event_handle(event); + default_event_handlers[event->event_id](event); ESP_LOGV(TAG, "exit default callback"); } } else { diff --git a/components/esp32/heap_alloc_caps.c b/components/esp32/heap_alloc_caps.c index a4ff870f39..1c6df9491b 100644 --- a/components/esp32/heap_alloc_caps.c +++ b/components/esp32/heap_alloc_caps.c @@ -36,6 +36,7 @@ hardwiring addresses. //Amount of priority slots for the tag descriptors. #define NO_PRIOS 3 + typedef struct { const char *name; uint32_t prio[NO_PRIOS]; @@ -46,6 +47,9 @@ typedef struct { Tag descriptors. These describe the capabilities of a bit of memory that's tagged with the index into this table. Each tag contains NO_PRIOS entries; later entries are only taken if earlier ones can't fulfill the memory request. Make sure there are never more than HEAPREGIONS_MAX_TAGCOUNT (in heap_regions.h) tags (ex the last empty marker) + +WARNING: The current code assumes the ROM stacks are located in tag 1; no allocation from this tag can be done until +the FreeRTOS scheduler has started. */ static const tag_desc_t tag_desc[]={ { "DRAM", { MALLOC_CAP_DMA|MALLOC_CAP_8BIT, MALLOC_CAP_32BIT, 0 }, false}, //Tag 0: Plain ole D-port RAM @@ -89,8 +93,8 @@ This array is *NOT* const because it gets modified depending on what pools are/a static HeapRegionTagged_t regions[]={ { (uint8_t *)0x3F800000, 0x20000, 15, 0}, //SPI SRAM, if available { (uint8_t *)0x3FFAE000, 0x2000, 0, 0}, //pool 16 <- used for rom code - { (uint8_t *)0x3FFB0000, 0x8000, 0, 0}, //pool 15 <- can be used for BT - { (uint8_t *)0x3FFB8000, 0x8000, 0, 0}, //pool 14 <- can be used for BT + { (uint8_t *)0x3FFB0000, 0x8000, 0, 0}, //pool 15 <- if BT is enabled, used as BT HW shared memory + { (uint8_t *)0x3FFB8000, 0x8000, 0, 0}, //pool 14 <- if BT is enabled, used data memory for BT ROM functions. { (uint8_t *)0x3FFC0000, 0x2000, 0, 0}, //pool 10-13, mmu page 0 { (uint8_t *)0x3FFC2000, 0x2000, 0, 0}, //pool 10-13, mmu page 1 { (uint8_t *)0x3FFC4000, 0x2000, 0, 0}, //pool 10-13, mmu page 2 @@ -134,6 +138,16 @@ static HeapRegionTagged_t regions[]={ { NULL, 0, 0, 0} //end }; +/* For the startup code, the stacks live in memory tagged by this tag. Hence, we only enable allocating from this tag + once FreeRTOS has started up completely. */ +#define NONOS_STACK_TAG 1 + +static bool nonos_stack_in_use=true; + +void heap_alloc_enable_nonos_stack_tag() +{ + nonos_stack_in_use=false; +} //Modify regions array to disable the given range of memory. static void disable_mem_region(void *from, void *to) { @@ -185,22 +199,43 @@ void heap_alloc_caps_init() { //Disable the bits of memory where this code is loaded. disable_mem_region(&_data_start, &_heap_start); //DRAM used by bss/data static variables disable_mem_region(&_init_start, &_iram_text_end); //IRAM used by code - disable_mem_region((void*)0x3ffae000, (void*)0x3ffb0000); //knock out ROM data region disable_mem_region((void*)0x40070000, (void*)0x40078000); //CPU0 cache region disable_mem_region((void*)0x40078000, (void*)0x40080000); //CPU1 cache region - // TODO: this region should be checked, since we don't need to knock out all region finally - disable_mem_region((void*)0x3ffe0000, (void*)0x3ffe8000); //knock out ROM data region + /* Warning: The ROM stack is located in the 0x3ffe0000 area. We do not specifically disable that area here because + after the scheduler has started, the ROM stack is not used anymore by anything. We handle it instead by not allowing + any mallocs from tag 1 (the IRAM/DRAM region) until the scheduler has started. + + The 0x3ffe0000 region also contains static RAM for various ROM functions. The following lines + reserve the regions for UART and ETSC, so these functions are usable. Libraries like xtos, which are + not usable in FreeRTOS anyway, are commented out in the linker script so they cannot be used; we + do not disable their memory regions here and they will be used as general purpose heap memory. + + Enabling the heap allocator for this region but disabling allocation here until FreeRTOS is started up + is a somewhat risky action in theory, because on initializing the allocator, vPortDefineHeapRegionsTagged + will go and write linked list entries at the start and end of all regions. For the ESP32, these linked + list entries happen to end up in a region that is not touched by the stack; they can be placed safely there.*/ + disable_mem_region((void*)0x3ffe0000, (void*)0x3ffe0440); //Reserve ROM PRO data region + disable_mem_region((void*)0x3ffe4000, (void*)0x3ffe4350); //Reserve ROM APP data region #if CONFIG_BT_ENABLED - disable_mem_region((void*)0x3ffb0000, (void*)0x3ffc0000); //knock out BT data region +#if CONFIG_BT_DRAM_RELEASE + disable_mem_region((void*)0x3ffb0000, (void*)0x3ffb3000); //Reserve BT data region + disable_mem_region((void*)0x3ffb8000, (void*)0x3ffbbb28); //Reserve BT data region + disable_mem_region((void*)0x3ffbdb28, (void*)0x3ffc0000); //Reserve BT data region +#else + disable_mem_region((void*)0x3ffb0000, (void*)0x3ffc0000); //Reserve BT hardware shared memory & BT data region +#endif + disable_mem_region((void*)0x3ffae000, (void*)0x3ffaff10); //Reserve ROM data region, inc region needed for BT ROM routines +#else + disable_mem_region((void*)0x3ffae000, (void*)0x3ffae2a0); //Reserve ROM data region #endif #if CONFIG_MEMMAP_TRACEMEM #if CONFIG_MEMMAP_TRACEMEM_TWOBANKS - disable_mem_region((void*)0x3fff8000, (void*)0x40000000); //knock out trace mem region + disable_mem_region((void*)0x3fff8000, (void*)0x40000000); //Reserve trace mem region #else - disable_mem_region((void*)0x3fff8000, (void*)0x3fffc000); //knock out trace mem region + disable_mem_region((void*)0x3fff8000, (void*)0x3fffc000); //Reserve trace mem region #endif #endif @@ -311,6 +346,10 @@ void *pvPortMallocCaps( size_t xWantedSize, uint32_t caps ) for (prio=0; prio #include "esp_err.h" #include "driver/gpio.h" +#include "driver/touch_pad.h" #ifdef __cplusplus extern "C" { @@ -50,12 +51,28 @@ typedef enum { ESP_PD_OPTION_AUTO //!< Keep power domain enabled in deep sleep, if it is needed by one of the wakeup options. Otherwise power it down. } esp_deep_sleep_pd_option_t; +/** + * @brief Deep sleep wakeup cause + */ +typedef enum { + ESP_DEEP_SLEEP_WAKEUP_UNDEFINED, //! Wakeup was not caused by deep sleep + ESP_DEEP_SLEEP_WAKEUP_EXT0, //! Wakeup caused by external signal using RTC_IO + ESP_DEEP_SLEEP_WAKEUP_EXT1, //! Wakeup caused by external signal using RTC_CNTL + ESP_DEEP_SLEEP_WAKEUP_TIMER, //! Wakeup caused by timer + ESP_DEEP_SLEEP_WAKEUP_TOUCHPAD, //! Wakeup caused by touchpad + ESP_DEEP_SLEEP_WAKEUP_ULP, //! Wakeup caused by ULP program +} esp_deep_sleep_wakeup_cause_t; + /** * @brief Enable wakeup by ULP coprocessor + * @note In revisions 0 and 1 of the ESP32, ULP wakeup source + * can not be used when RTC_PERIPH power domain is forced + * to be powered on (ESP_PD_OPTION_ON) or when ext0 wakeup + * source is used. * @return * - ESP_OK on success - * - ESP_ERR_INVALID_STATE if ULP co-processor is not enabled. + * - ESP_ERR_INVALID_STATE if ULP co-processor is not enabled or if wakeup triggers conflict */ esp_err_t esp_deep_sleep_enable_ulp_wakeup(); @@ -68,6 +85,29 @@ esp_err_t esp_deep_sleep_enable_ulp_wakeup(); */ esp_err_t esp_deep_sleep_enable_timer_wakeup(uint64_t time_in_us); +/** + * @brief Enable wakeup by touch sensor + * + * @note In revisions 0 and 1 of the ESP32, touch wakeup source + * can not be used when RTC_PERIPH power domain is forced + * to be powered on (ESP_PD_OPTION_ON) or when ext0 wakeup + * source is used. + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if wakeup triggers conflict + */ +esp_err_t esp_deep_sleep_enable_touchpad_wakeup(); + +/** + * @brief Get the touch pad which caused wakeup + * + * If wakeup was caused by another source, this function will return TOUCH_PAD_MAX; + * + * @return touch pad which caused wakeup + */ +touch_pad_t esp_deep_sleep_get_touchpad_wakeup_status(); + /** * @brief Enable wakeup using a pin * @@ -81,6 +121,9 @@ esp_err_t esp_deep_sleep_enable_timer_wakeup(uint64_t time_in_us); * configured in esp_deep_sleep_start, immediately before * entering deep sleep. * + * @note In revisions 0 and 1 of the ESP32, ext0 wakeup source + * can not be used together with touch or ULP wakeup sources. + * * @param gpio_num GPIO number used as wakeup source. Only GPIOs which are have RTC * functionality can be used: 0,2,4,12-15,25-27,32-39. * @param level input level which will trigger wakeup (0=low, 1=high) @@ -88,6 +131,7 @@ esp_err_t esp_deep_sleep_enable_timer_wakeup(uint64_t time_in_us); * - ESP_OK on success * - ESP_ERR_INVALID_ARG if the selected GPIO is not an RTC GPIO, * or the mode is invalid + * - ESP_ERR_INVALID_STATE if wakeup triggers conflict */ esp_err_t esp_deep_sleep_enable_ext0_wakeup(gpio_num_t gpio_num, int level); @@ -188,6 +232,15 @@ void esp_deep_sleep(uint64_t time_in_us) __attribute__((noreturn)); */ void system_deep_sleep(uint64_t time_in_us) __attribute__((noreturn, deprecated)); + +/** + * @brief Get the source which caused deep sleep wakeup + * + * @return wakeup cause, or ESP_DEEP_SLEEP_WAKEUP_UNDEFINED if reset reason is other than deep sleep reset. + */ +esp_deep_sleep_wakeup_cause_t esp_deep_sleep_get_wakeup_cause(); + + /** * @brief Default stub to run on wake from deep sleep. * diff --git a/components/esp32/include/esp_err.h b/components/esp32/include/esp_err.h index b6a1e8b421..c7beafd375 100644 --- a/components/esp32/include/esp_err.h +++ b/components/esp32/include/esp_err.h @@ -11,10 +11,10 @@ // 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_ERR_H__ -#define __ESP_ERR_H__ +#pragma once #include +#include #include #ifdef __cplusplus @@ -40,15 +40,38 @@ typedef int32_t esp_err_t; #define ESP_ERR_WIFI_BASE 0x3000 /*!< Starting number of WiFi error codes */ +void _esp_error_check_failed(esp_err_t rc, const char *file, int line, const char *function, const char *expression) __attribute__((noreturn)); + +#ifndef __ASSERT_FUNC +/* This won't happen on IDF, which defines __ASSERT_FUNC in assert.h, but it does happen when building on the host which + uses /usr/include/assert.h or equivalent. +*/ +#ifdef __ASSERT_FUNCTION +#define __ASSERT_FUNC __ASSERT_FUNCTION /* used in glibc assert.h */ +#else +#define __ASSERT_FUNC "??" +#endif +#endif + /** * Macro which can be used to check the error code, * and terminate the program in case the code is not ESP_OK. - * Prints the failed statement to serial output. + * Prints the error code, error location, and the failed statement to serial output. + * + * Disabled if assertions are disabled. */ -#define ESP_ERROR_CHECK(x) do { esp_err_t rc = (x); if (rc != ESP_OK) { assert(0 && #x);} } while(0); +#ifdef NDEBUG +#define ESP_ERROR_CHECK(x) do { (x); } while (0) +#else +#define ESP_ERROR_CHECK(x) do { \ + esp_err_t rc = (x); \ + if (rc != ESP_OK) { \ + _esp_error_check_failed(rc, __FILE__, __LINE__, \ + __ASSERT_FUNC, #x); \ + } \ + } while(0); +#endif #ifdef __cplusplus } #endif - -#endif /* __ESP_ERR_H__ */ diff --git a/components/esp32/include/esp_heap_alloc_caps.h b/components/esp32/include/esp_heap_alloc_caps.h index 21c24de6bf..e1021c30b1 100644 --- a/components/esp32/include/esp_heap_alloc_caps.h +++ b/components/esp32/include/esp_heap_alloc_caps.h @@ -38,6 +38,16 @@ */ void heap_alloc_caps_init(); +/** + * @brief Enable the memory region where the startup stacks are located for allocation + * + * On startup, the pro/app CPUs have a certain memory region they use as stack, so we + * cannot do allocations in the regions these stack frames are. When FreeRTOS is + * completely started, they do not use that memory anymore and allocation there can + * be re-enabled. + */ +void heap_alloc_enable_nonos_stack_tag(); + /** * @brief Allocate a chunk of memory which has the given capabilities * @@ -75,4 +85,6 @@ size_t xPortGetFreeHeapSizeCaps( uint32_t caps ); */ size_t xPortGetMinimumEverFreeHeapSizeCaps( uint32_t caps ); + + #endif \ No newline at end of file diff --git a/components/esp32/include/esp_system.h b/components/esp32/include/esp_system.h index 30701761ad..9a88743b80 100644 --- a/components/esp32/include/esp_system.h +++ b/components/esp32/include/esp_system.h @@ -24,6 +24,17 @@ extern "C" { #endif +typedef enum { + ESP_MAC_WIFI_STA, + ESP_MAC_WIFI_SOFTAP, + ESP_MAC_BT, + ESP_MAC_ETH, +} esp_mac_type_t; + +#define TWO_MAC_ADDRESS_FROM_EFUSE 2 +#define FOUR_MAC_ADDRESS_FROM_EFUSE 4 +#define NUM_MAC_ADDRESS_FROM_EFUSE CONFIG_NUMBER_OF_MAC_ADDRESS_GENERATED_FROM_EFUSE + /** * @attention application don't need to call this function anymore. It do nothing and will * be removed in future version. @@ -115,6 +126,47 @@ esp_err_t esp_efuse_read_mac(uint8_t* mac); */ esp_err_t system_efuse_read_mac(uint8_t mac[6]) __attribute__ ((deprecated)); +/** + * @brief Read hardware MAC address and set MAC address of the interface. + * + * This function first reads hardware MAC address from efuse. Then set the MAC address of the interface + * including wifi station, wifi softap, bluetooth and ethernet. + * + * @param mac MAC address of the interface, length: 6 bytes. + * @param type type of MAC address, 0:wifi station, 1:wifi softap, 2:bluetooth, 3:ethernet. + * + * @return ESP_OK on success + */ +esp_err_t esp_read_mac(uint8_t* mac, esp_mac_type_t type); + +/** + * @brief Derive MAC address. + * + * This function derives a local MAC address from an universal MAC address. + * Addresses can either be universally administered addresses or locally administered addresses. + * A universally administered address is uniquely assigned to a device by its manufacturer. + * The first three octets (in transmission order) identify the organization that issued the identifier + * and are known as the Organizationally Unique Identifier (OUI).[4] The remainder of the address + * (three octets for MAC-48 and EUI-48 or five for EUI-64) are assigned by that organization in nearly + * any manner they please, subject to the constraint of uniqueness. A locally administered address is + * assigned to a device by a network administrator, overriding the burned-in address. + * Universally administered and locally administered addresses are distinguished by setting + * the second-least-significant bit of the first octet of the address. This bit is also referred to + * as the U/L bit, short for Universal/Local, which identifies how the address is administered. + * If the bit is 0, the address is universally administered. If it is 1, the address is locally administered. + * In the example address 06-00-00-00-00-00 the first octet is 06 (hex), the binary form of which is 00000110, + * where the second-least-significant bit is 1. Therefore, it is a locally administered address.[7] Consequently, + * this bit is 0 in all OUIs. + * In ESP32, universal MAC address is generated from the hardware MAC address in efuse. + * Local MAC address is derived from the universal MAC address. + * + * @param dst_mac Derived local MAC address, length: 6 bytes. + * @param src_mac Source universal MAC address, length: 6 bytes. + * + * @return ESP_OK on success + */ +esp_err_t esp_derive_mac(uint8_t* dst_mac, const uint8_t* src_mac); + /** * Get SDK version * diff --git a/components/esp32/include/esp_task.h b/components/esp32/include/esp_task.h index bb028bf485..bd26362649 100644 --- a/components/esp32/include/esp_task.h +++ b/components/esp32/include/esp_task.h @@ -31,22 +31,16 @@ #define ESP_TASK_PRIO_MAX (configMAX_PRIORITIES) #define ESP_TASK_PRIO_MIN (0) -/* Wifi library task */ -#define ESP_TASKD_WATCHDOG_PRIO (ESP_TASK_PRIO_MAX - 1) -#define ESP_TASKD_WATCHDOG_STACK 2048 -#define ESP_TASK_WPA2_PRIO (ESP_TASK_PRIO_MAX - 1) -#define ESP_TASK_WPA2_STACK 2048 -#define ESP_TASKD_WIFI_PRIO (ESP_TASK_PRIO_MAX - 2) -#define ESP_TASKD_WIFI_STACK 8196 -#define ESP_TASKD_WIFI_TIMER_PRIO (ESP_TASK_PRIO_MAX - 3) -#define ESP_TASKD_WIFI_TIMER_STACK 2048 -#define ESP_TASK_WPS_PRIO (ESP_TASK_PRIO_MIN + 2) -#define ESP_TASK_WPS_STACK 2048 - /* Bt contoller Task */ /* controller */ #define ESP_TASK_BT_CONTROLLER_PRIO (ESP_TASK_PRIO_MAX - 1) -#define ESP_TASK_BT_CONTROLLER_STACK 4096 +#ifdef CONFIG_NEWLIB_NANO_FORMAT +#define BT_TASK_EXTRA_STACK_SIZE (0) +#else +#define BT_TASK_EXTRA_STACK_SIZE (512) +#endif +#define ESP_TASK_BT_CONTROLLER_STACK (3584 + BT_TASK_EXTRA_STACK_SIZE) + /* idf task */ #define ESP_TASKD_EVENT_PRIO (ESP_TASK_PRIO_MAX - 5) diff --git a/components/esp32/include/esp_wifi.h b/components/esp32/include/esp_wifi.h index 0f7e2996e8..05a686a361 100755 --- a/components/esp32/include/esp_wifi.h +++ b/components/esp32/include/esp_wifi.h @@ -94,14 +94,45 @@ extern "C" { * @brief WiFi stack configuration parameters passed to esp_wifi_init call. */ typedef struct { - system_event_handler_t event_handler; /**< WiFi event handler */ - uint32_t rx_buf_num; /**< WiFi RX buffer number */ + system_event_handler_t event_handler; /**< WiFi event handler */ + int static_rx_buf_num; /**< WiFi static RX buffer number */ + int dynamic_rx_buf_num; /**< WiFi dynamic RX buffer number */ + int dynamic_tx_buf_num; /**< WiFi dynamic TX buffer number */ + int ampdu_enable; /**< WiFi AMPDU feature enable flag */ + int nvs_enable; /**< WiFi NVS flash enable flag */ + int nano_enable; /**< Nano option for printf/scan family enable flag */ + int magic; /**< WiFi init magic number, it should be the last field */ } wifi_init_config_t; +#if CONFIG_ESP32_WIFI_AMPDU_ENABLED +#define WIFI_AMPDU_ENABLED 1 +#else +#define WIFI_AMPDU_ENABLED 0 +#endif + +#if CONFIG_ESP32_WIFI_NVS_ENABLED +#define WIFI_NVS_ENABLED 1 +#else +#define WIFI_NVS_ENABLED 0 +#endif + +#if CONFIG_NEWLIB_NANO_FORMAT +#define WIFI_NANO_FORMAT_ENABLED 1 +#else +#define WIFI_NANO_FORMAT_ENABLED 0 +#endif + +#define WIFI_INIT_CONFIG_MAGIC 0x1F2F3F4F #ifdef CONFIG_WIFI_ENABLED #define WIFI_INIT_CONFIG_DEFAULT() { \ .event_handler = &esp_event_send, \ - .rx_buf_num = CONFIG_ESP32_WIFI_RX_BUFFER_NUM, \ + .static_rx_buf_num = CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM,\ + .dynamic_rx_buf_num = CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM,\ + .dynamic_tx_buf_num = CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM,\ + .ampdu_enable = WIFI_AMPDU_ENABLED,\ + .nvs_enable = WIFI_NVS_ENABLED,\ + .nano_enable = WIFI_NANO_FORMAT_ENABLED,\ + .magic = WIFI_INIT_CONFIG_MAGIC\ }; #else #define WIFI_INIT_CONFIG_DEFAULT #error Wifi is disabled in config, WIFI_INIT_CONFIG_DEFAULT will not work @@ -113,8 +144,11 @@ typedef struct { * WiFi NVS structure etc, this WiFi also start WiFi task * * @attention 1. This API must be called before all other WiFi API can be called - * @attention 2. event_handler field in cfg should be set to a valid event handler function. - * In most cases, use the WIFI_INIT_CONFIG_DEFAULT macro which sets esp_event_send(). + * @attention 2. Always use WIFI_INIT_CONFIG_DEFAULT macro to init the config to default values, this can + * guarantee all the fields got correct value when more fields are added into wifi_init_config_t + * in future release. If you want to set your owner initial values, overwrite the default values + * which are set by WIFI_INIT_CONFIG_DEFAULT, please be notified that the field 'magic' of + * wifi_init_config_t should always be WIFI_INIT_CONFIG_MAGIC! * * @param config provide WiFi init configuration * @@ -130,7 +164,6 @@ esp_err_t esp_wifi_init(wifi_init_config_t *config); * Free all resource allocated in esp_wifi_init and stop WiFi task * * @attention 1. This API should be called if you want to remove WiFi driver from the system - * @attention 2. This API can not be called yet and will be done in the future. * * @return ESP_OK: succeed */ @@ -422,6 +455,7 @@ esp_err_t esp_wifi_get_bandwidth(wifi_interface_t ifx, wifi_bandwidth_t *bw); * @brief Set primary/secondary channel of ESP32 * * @attention 1. This is a special API for sniffer + * @attention 2. This API should be called after esp_wifi_start() or esp_wifi_set_promiscuous() * * @param primary for HT20, primary is the channel number, for HT40, primary is the primary channel * @param second for HT20, second is ignored, for HT40, second is the second channel diff --git a/components/esp32/include/heap_alloc_caps.h b/components/esp32/include/heap_alloc_caps.h index d371ca5ed8..edab15d52d 100644 --- a/components/esp32/include/heap_alloc_caps.h +++ b/components/esp32/include/heap_alloc_caps.h @@ -1,34 +1,3 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#ifndef HEAP_ALLOC_CAPS_H -#define HEAP_ALLOC_CAPS_H - -#define MALLOC_CAP_EXEC (1<<0) //Memory must be able to run executable code -#define MALLOC_CAP_32BIT (1<<1) //Memory must allow for aligned 32-bit data accesses -#define MALLOC_CAP_8BIT (1<<2) //Memory must allow for 8/16/...-bit data accesses -#define MALLOC_CAP_DMA (1<<3) //Memory must be able to accessed by DMA -#define MALLOC_CAP_PID2 (1<<4) //Memory must be mapped to PID2 memory space -#define MALLOC_CAP_PID3 (1<<5) //Memory must be mapped to PID3 memory space -#define MALLOC_CAP_PID4 (1<<6) //Memory must be mapped to PID4 memory space -#define MALLOC_CAP_PID5 (1<<7) //Memory must be mapped to PID5 memory space -#define MALLOC_CAP_PID6 (1<<8) //Memory must be mapped to PID6 memory space -#define MALLOC_CAP_PID7 (1<<9) //Memory must be mapped to PID7 memory space -#define MALLOC_CAP_SPISRAM (1<<10) //Memory must be in SPI SRAM -#define MALLOC_CAP_INVALID (1<<31) //Memory can't be used / list end marker - - -void heap_alloc_caps_init(); -void *pvPortMallocCaps(size_t xWantedSize, uint32_t caps); - -#endif \ No newline at end of file +#pragma once +#warning heap_alloc_caps.h has been renamed to esp_heap_alloc_caps.h. The old header file is deprecated and will be removed in v3.0. +#include "esp_heap_alloc_caps.h" diff --git a/components/esp32/include/rom/spi_flash.h b/components/esp32/include/rom/spi_flash.h index 44098475d6..a9d617e754 100644 --- a/components/esp32/include/rom/spi_flash.h +++ b/components/esp32/include/rom/spi_flash.h @@ -504,12 +504,25 @@ void SPI_Write_Encrypt_Disable(void); * @param uint32_t len : Length to write, should be 32 bytes aligned. * * @return SPI_FLASH_RESULT_OK : Data written successfully. - * SPI_FLASH_RESULT_ERR : Encrypto write error. + * SPI_FLASH_RESULT_ERR : Encryption write error. * SPI_FLASH_RESULT_TIMEOUT : Encrypto write timeout. */ SpiFlashOpResult SPI_Encrypt_Write(uint32_t flash_addr, uint32_t *data, uint32_t len); +/** @brief Wait until SPI flash write operation is complete + * + * @note Please do not call this function in SDK. + * + * Reads the Write In Progress bit of the SPI flash status register, + * repeats until this bit is zero (indicating write complete). + * + * @return SPI_FLASH_RESULT_OK : Write is complete + * SPI_FLASH_RESULT_ERR : Error while reading status. + */ +SpiFlashOpResult SPI_Wait_Idle(SpiFlashChip *spi); + + /** @brief Global SpiFlashChip structure used by ROM functions * */ diff --git a/components/esp32/include/soc/cpu.h b/components/esp32/include/soc/cpu.h index 2e0ac7de80..7e1301345b 100644 --- a/components/esp32/include/soc/cpu.h +++ b/components/esp32/include/soc/cpu.h @@ -36,16 +36,6 @@ static inline void *get_sp() return sp; } -/* Return true if the CPU is in an interrupt context - (PS.UM == 0) -*/ -static inline bool cpu_in_interrupt_context(void) -{ - uint32_t ps; - RSR(PS, ps); - return (ps & PS_UM) == 0; -} - /* Functions to set page attributes for Region Protection option in the CPU. * See Xtensa ISA Reference manual for explanation of arguments (section 4.6.3.2). */ diff --git a/components/esp32/include/soc/dport_reg.h b/components/esp32/include/soc/dport_reg.h index ef231e316e..141b05b4a5 100644 --- a/components/esp32/include/soc/dport_reg.h +++ b/components/esp32/include/soc/dport_reg.h @@ -1035,11 +1035,27 @@ #define DPORT_WIFI_CLK_EN_V 0xFFFFFFFF #define DPORT_WIFI_CLK_EN_S 0 +/* Mask for all Wifi clock bits - 0, 1, 2, 3, 6, 7, 8, 9, 10, 15 */ +#define DPORT_WIFI_CLK_WIFI_EN 0x000007cf +#define DPORT_WIFI_CLK_WIFI_EN_M ((DPORT_WIFI_CLK_WIFI_EN_V)<<(DPORT_WIFI_CLK_WIFI_EN_S)) +#define DPORT_WIFI_CLK_WIFI_EN_V 0x1FF +#define DPORT_WIFI_CLK_WIFI_EN_S 0 +/* Mask for all Bluetooth clock bits - 11, 16, 17 */ +#define DPORT_WIFI_CLK_BT_EN 0x61 +#define DPORT_WIFI_CLK_BT_EN_M ((DPORT_WIFI_CLK_BT_EN_V)<<(DPORT_WIFI_CLK_BT_EN_S)) +#define DPORT_WIFI_CLK_BT_EN_V 0x61 +#define DPORT_WIFI_CLK_BT_EN_S 11 +/* Remaining single bit clock masks */ +#define DPORT_WIFI_CLK_SDIOSLAVE_EN BIT(4) +#define DPORT_WIFI_CLK_SDIO_HOST_EN BIT(13) +#define DPORT_WIFI_CLK_EMAC_EN BIT(14) +#define DPORT_WIFI_CLK_RNG_EN BIT(15) + #define DPORT_CORE_RST_EN_REG (DR_REG_DPORT_BASE + 0x0D0) /* DPORT_CORE_RST : R/W ;bitpos:[31:0] ;default: 32'h0 ; */ /*description: */ -#define DPROT_RW_BTLP_RST (BIT(10)) -#define DPROT_RW_BTMAC_RST (BIT(9)) +#define DPORT_RW_BTLP_RST (BIT(10)) +#define DPORT_RW_BTMAC_RST (BIT(9)) #define DPORT_MACPWR_RST (BIT(8)) #define DPORT_EMAC_RST (BIT(7)) #define DPORT_SDIO_HOST_RST (BIT(6)) diff --git a/components/esp32/include/soc/io_mux_reg.h b/components/esp32/include/soc/io_mux_reg.h index de8fe7ec99..459a97009c 100644 --- a/components/esp32/include/soc/io_mux_reg.h +++ b/components/esp32/include/soc/io_mux_reg.h @@ -88,7 +88,7 @@ static inline void __attribute__ ((deprecated)) PIN_PULLDWN_EN(uint32_t PIN_NAME #define FUNC_GPIO0_GPIO0_0 0 #define PERIPHS_IO_MUX_U0TXD_U (DR_REG_IO_MUX_BASE +0x88) -#define FUNC_U0TXD_EMAC_RXD2 3 +#define FUNC_U0TXD_EMAC_RXD2 5 #define FUNC_U0TXD_GPIO1 2 #define FUNC_U0TXD_CLK_OUT3 1 #define FUNC_U0TXD_U0TXD 0 @@ -181,7 +181,7 @@ static inline void __attribute__ ((deprecated)) PIN_PULLDWN_EN(uint32_t PIN_NAME #define PERIPHS_IO_MUX_MTMS_U (DR_REG_IO_MUX_BASE +0x30) #define FUNC_MTMS_EMAC_TXD2 5 #define FUNC_MTMS_SD_CLK 4 -#define FUNC_MTMS_HS2_CLk 3 +#define FUNC_MTMS_HS2_CLK 3 #define FUNC_MTMS_GPIO14 2 #define FUNC_MTMS_HSPICLK 1 #define FUNC_MTMS_MTMS 0 diff --git a/components/esp32/include/soc/rtc_cntl_reg.h b/components/esp32/include/soc/rtc_cntl_reg.h index d99cec1864..40d65f21f6 100644 --- a/components/esp32/include/soc/rtc_cntl_reg.h +++ b/components/esp32/include/soc/rtc_cntl_reg.h @@ -240,7 +240,7 @@ #define RTC_CNTL_TIME_VALID_S 30 /* frequency of RTC slow clock, Hz */ -#define RTC_CTNL_SLOWCLK_FREQ 150000 +#define RTC_CNTL_SLOWCLK_FREQ 150000 #define RTC_CNTL_TIME0_REG (DR_REG_RTCCNTL_BASE + 0x10) /* RTC_CNTL_TIME_LO : RO ;bitpos:[31:0] ;default: 32'h0 ; */ diff --git a/components/esp32/ld/esp32.rom.ld b/components/esp32/ld/esp32.rom.ld index 7543fa42aa..6cbd2fc747 100644 --- a/components/esp32/ld/esp32.rom.ld +++ b/components/esp32/ld/esp32.rom.ld @@ -60,7 +60,6 @@ PROVIDE ( cache_sram_mmu_set = 0x400097f4 ); PROVIDE ( calc_rtc_memory_crc = 0x40008170 ); PROVIDE ( calloc = 0x4000bee4 ); PROVIDE ( _calloc_r = 0x4000bbf8 ); -PROVIDE ( check_pos = 0x400068b8 ); PROVIDE ( _cleanup = 0x40001df8 ); PROVIDE ( _cleanup_r = 0x40001d48 ); PROVIDE ( __clear_cache = 0x40063860 ); @@ -101,7 +100,6 @@ PROVIDE ( dbg_state = 0x3ffb8d5d ); PROVIDE ( DebugE256PublicKey_x = 0x3ff97428 ); PROVIDE ( DebugE256PublicKey_y = 0x3ff97408 ); PROVIDE ( DebugE256SecretKey = 0x3ff973e8 ); -PROVIDE ( _DebugExceptionVector = 0x40000280 ); PROVIDE ( debug_timer = 0x3ffe042c ); PROVIDE ( debug_timerfn = 0x3ffe0430 ); PROVIDE ( dh_group14_generator = 0x3ff9ac60 ); @@ -127,7 +125,6 @@ PROVIDE ( __divdi3 = 0x4000ca84 ); PROVIDE ( __divsc3 = 0x40064200 ); PROVIDE ( __divsf3 = 0x4000234c ); PROVIDE ( __divsi3 = 0x4000c7b8 ); -PROVIDE ( _DoubleExceptionVector = 0x400003c0 ); PROVIDE ( dummy_len_plus = 0x3ffae290 ); PROVIDE ( __dummy_lock = 0x4000c728 ); PROVIDE ( __dummy_lock_try = 0x4000c730 ); @@ -141,69 +138,8 @@ PROVIDE ( __eqdf2 = 0x400636a8 ); PROVIDE ( __eqsf2 = 0x40063374 ); PROVIDE ( esp_crc8 = 0x4005d144 ); PROVIDE ( _etext = 0x4000d66c ); -PROVIDE ( ets_aes_crypt = 0x4005c9b8 ); -PROVIDE ( ets_aes_disable = 0x4005c8f8 ); -PROVIDE ( ets_aes_enable = 0x4005c8cc ); -PROVIDE ( ets_aes_set_endian = 0x4005c928 ); -PROVIDE ( ets_aes_setkey_dec = 0x4005c994 ); -PROVIDE ( ets_aes_setkey_enc = 0x4005c97c ); -PROVIDE ( ets_bigint_disable = 0x4005c4e0 ); -PROVIDE ( ets_bigint_enable = 0x4005c498 ); -PROVIDE ( ets_bigint_mod_mult_getz = 0x4005c818 ); -PROVIDE ( ets_bigint_mod_mult_prepare = 0x4005c7b4 ); -PROVIDE ( ets_bigint_mod_power_getz = 0x4005c614 ); -PROVIDE ( ets_bigint_mod_power_prepare = 0x4005c54c ); -PROVIDE ( ets_bigint_montgomery_mult_getz = 0x4005c7a4 ); -PROVIDE ( ets_bigint_montgomery_mult_prepare = 0x4005c6fc ); -PROVIDE ( ets_bigint_mult_getz = 0x4005c6e8 ); -PROVIDE ( ets_bigint_mult_prepare = 0x4005c630 ); -PROVIDE ( ets_bigint_wait_finish = 0x4005c520 ); -PROVIDE ( ets_delay_us = 0x40008534 ); -PROVIDE ( ets_efuse_get_8M_clock = 0x40008710 ); -PROVIDE ( ets_efuse_get_spiconfig = 0x40008658 ); -PROVIDE ( ets_efuse_program_op = 0x40008628 ); -PROVIDE ( ets_efuse_read_op = 0x40008600 ); -PROVIDE ( ets_get_cpu_frequency = 0x4000855c ); -PROVIDE ( ets_get_detected_xtal_freq = 0x40008588 ); -PROVIDE ( ets_get_xtal_scale = 0x4000856c ); -PROVIDE ( ets_install_putc1 = 0x40007d18 ); -PROVIDE ( ets_install_putc2 = 0x40007d38 ); -PROVIDE ( ets_install_uart_printf = 0x40007d28 ); -PROVIDE ( ets_post = 0x4000673c ); -PROVIDE ( ets_printf = 0x40007d54 ); PROVIDE ( ets_readySet_ = 0x3ffe01f0 ); -PROVIDE ( ets_run = 0x400066bc ); -PROVIDE ( ets_secure_boot_check = 0x4005cb40 ); -PROVIDE ( ets_secure_boot_check_finish = 0x4005cc04 ); -PROVIDE ( ets_secure_boot_check_start = 0x4005cbcc ); -PROVIDE ( ets_secure_boot_finish = 0x4005ca84 ); -PROVIDE ( ets_secure_boot_hash = 0x4005cad4 ); -PROVIDE ( ets_secure_boot_obtain = 0x4005cb14 ); -PROVIDE ( ets_secure_boot_rd_abstract = 0x4005cba8 ); -PROVIDE ( ets_secure_boot_rd_iv = 0x4005cb84 ); -PROVIDE ( ets_secure_boot_start = 0x4005ca34 ); -PROVIDE ( ets_set_appcpu_boot_addr = 0x4000689c ); -PROVIDE ( ets_set_idle_cb = 0x40006674 ); -PROVIDE ( ets_set_startup_callback = 0x4000688c ); -PROVIDE ( ets_set_user_start = 0x4000687c ); -PROVIDE ( ets_sha_disable = 0x4005c0a8 ); -PROVIDE ( ets_sha_enable = 0x4005c07c ); -PROVIDE ( ets_sha_finish = 0x4005c104 ); -PROVIDE ( ets_sha_init = 0x4005c0d4 ); -PROVIDE ( ets_sha_update = 0x4005c2a0 ); PROVIDE ( ets_startup_callback = 0x3ffe0404 ); -PROVIDE ( ets_task = 0x40006688 ); -PROVIDE ( ets_timer_arm = 0x40008368 ); -PROVIDE ( ets_timer_arm_us = 0x400083ac ); -PROVIDE ( ets_timer_disarm = 0x400083ec ); -PROVIDE ( ets_timer_done = 0x40008428 ); -PROVIDE ( ets_timer_handler_isr = 0x40008454 ); -PROVIDE ( ets_timer_init = 0x400084e8 ); -PROVIDE ( ets_timer_setfn = 0x40008350 ); -PROVIDE ( ets_unpack_flash_code = 0x40007018 ); -PROVIDE ( ets_unpack_flash_code_legacy = 0x4000694c ); -PROVIDE ( ets_update_cpu_frequency_rom = 0x40008550 ); /* Updates g_ticks_per_us on the current CPU only; not on the other core */ -PROVIDE ( ets_waiti0 = 0x400067d8 ); PROVIDE ( exc_cause_table = 0x3ff991d0 ); PROVIDE ( _exit_r = 0x4000bd28 ); PROVIDE ( __extendsfdf2 = 0x40002c34 ); @@ -213,8 +149,6 @@ PROVIDE ( fflush = 0x40059394 ); PROVIDE ( _fflush_r = 0x40059320 ); PROVIDE ( __ffsdi2 = 0x4000ca2c ); PROVIDE ( __ffssi2 = 0x4000c804 ); -PROVIDE ( FilePacketSendDeflatedReqMsgProc = 0x40008b24 ); -PROVIDE ( FilePacketSendReqMsgProc = 0x40008860 ); PROVIDE ( _findenv_r = 0x40001f44 ); PROVIDE ( __fixdfdi = 0x40002ac4 ); PROVIDE ( __fixdfsi = 0x40002a78 ); @@ -223,11 +157,6 @@ PROVIDE ( __fixsfsi = 0x4000240c ); PROVIDE ( __fixunsdfsi = 0x40002b30 ); PROVIDE ( __fixunssfdi = 0x40002504 ); PROVIDE ( __fixunssfsi = 0x400024ac ); -PROVIDE ( FlashDwnLdDeflatedStartMsgProc = 0x40008ad8 ); -PROVIDE ( FlashDwnLdParamCfgMsgProc = 0x4000891c ); -PROVIDE ( FlashDwnLdStartMsgProc = 0x40008820 ); -PROVIDE ( FlashDwnLdStopDeflatedReqMsgProc = 0x40008c18 ); -PROVIDE ( FlashDwnLdStopReqMsgProc = 0x400088ec ); PROVIDE ( __floatdidf = 0x4000c988 ); PROVIDE ( __floatdisf = 0x4000c8c0 ); PROVIDE ( __floatsidf = 0x4000c944 ); @@ -248,7 +177,6 @@ PROVIDE ( _fwalk = 0x4000c738 ); PROVIDE ( _fwalk_reent = 0x4000c770 ); PROVIDE ( __gcc_bcmp = 0x40064a70 ); PROVIDE ( __gedf2 = 0x40063768 ); -PROVIDE ( _GeneralException = 0x40000e14 ); PROVIDE ( __gesf2 = 0x4006340c ); PROVIDE ( __get_current_time_locale = 0x40001834 ); PROVIDE ( _getenv_r = 0x40001fbc ); @@ -256,7 +184,6 @@ PROVIDE ( _getpid_r = 0x4000bcfc ); PROVIDE ( __getreent = 0x4000be8c ); PROVIDE ( _gettimeofday_r = 0x4000bc58 ); PROVIDE ( __gettzinfo = 0x40001fcc ); -PROVIDE ( GetUartDevice = 0x40009598 ); PROVIDE ( GF_Jacobian_Point_Addition256 = 0x400163a4 ); PROVIDE ( GF_Jacobian_Point_Double256 = 0x40016260 ); PROVIDE ( GF_Point_Jacobian_To_Affine256 = 0x40016b0c ); @@ -265,31 +192,6 @@ 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 ); -PROVIDE ( gpio_intr_ack = 0x40009dd4 ); -PROVIDE ( gpio_intr_ack_high = 0x40009e1c ); -PROVIDE ( gpio_intr_handler_register = 0x40009e6c ); -PROVIDE ( gpio_intr_pending = 0x40009cec ); -PROVIDE ( gpio_intr_pending_high = 0x40009cf8 ); -PROVIDE ( gpio_matrix_in = 0x40009edc ); -PROVIDE ( gpio_matrix_out = 0x40009f0c ); -PROVIDE ( gpio_output_set = 0x40009b24 ); -PROVIDE ( gpio_output_set_high = 0x40009b5c ); -PROVIDE ( gpio_pad_hold = 0x4000a734 ); -PROVIDE ( gpio_pad_pulldown = 0x4000a348 ); -PROVIDE ( gpio_pad_pullup = 0x4000a22c ); -PROVIDE ( gpio_pad_select_gpio = 0x40009fdc ); -PROVIDE ( gpio_pad_set_drv = 0x4000a11c ); -PROVIDE ( gpio_pad_unhold = 0x4000a484 ); -PROVIDE ( gpio_pending_mask = 0x3ffe0038 ); -PROVIDE ( gpio_pending_mask_high = 0x3ffe0044 ); -PROVIDE ( gpio_pin_intr_state_set = 0x40009d04 ); -PROVIDE ( gpio_pin_wakeup_disable = 0x40009eb0 ); -PROVIDE ( gpio_pin_wakeup_enable = 0x40009e7c ); -PROVIDE ( gpio_register_get = 0x40009cbc ); -PROVIDE ( gpio_register_set = 0x40009bbc ); PROVIDE ( __gtdf2 = 0x400636dc ); PROVIDE ( __gtsf2 = 0x400633a0 ); PROVIDE ( gTxMsg = 0x3ffe0050 ); @@ -314,7 +216,6 @@ PROVIDE ( hmac_sha1 = 0x40060acc ); PROVIDE ( hmac_sha1_vector = 0x400609e4 ); PROVIDE ( hmac_sha256 = 0x40060d58 ); PROVIDE ( hmac_sha256_vector = 0x40060c84 ); -PROVIDE ( intr_matrix_set = 0x4000681c ); PROVIDE ( isalnum = 0x40000f04 ); PROVIDE ( isalpha = 0x40000f18 ); PROVIDE ( isascii = 0x4000c20c ); @@ -333,7 +234,6 @@ PROVIDE ( __itoa = 0x40056678 ); PROVIDE ( jd_decomp = 0x400613e8 ); PROVIDE ( jd_prepare = 0x40060fa8 ); PROVIDE ( ke_env = 0x3ffb93cc ); -PROVIDE ( _KernelExceptionVector = 0x40000300 ); PROVIDE ( _kill_r = 0x4000bd10 ); PROVIDE ( labs = 0x40056370 ); PROVIDE ( lb_default_handler = 0x3ff982b8 ); @@ -357,15 +257,6 @@ PROVIDE ( ld_sched_params = 0x3ffb96c0 ); PROVIDE ( ld_sync_train_channels = 0x3ff98a3c ); PROVIDE ( __ledf2 = 0x40063704 ); PROVIDE ( __lesf2 = 0x400633c0 ); -PROVIDE ( _Level2FromVector = 0x40000954 ); -PROVIDE ( _Level2Vector = 0x40000180 ); -PROVIDE ( _Level3FromVector = 0x40000a28 ); -PROVIDE ( _Level3Vector = 0x400001c0 ); -PROVIDE ( _Level4FromVector = 0x40000af8 ); -PROVIDE ( _Level4Vector = 0x40000200 ); -PROVIDE ( _Level5FromVector = 0x40000c68 ); -PROVIDE ( _Level5Vector = 0x40000240 ); -PROVIDE ( _LevelOneInterrupt = 0x40000835 ); PROVIDE ( _link_r = 0x4000bc9c ); PROVIDE ( llc_default_handler = 0x3ff98b3c ); PROVIDE ( llc_default_state_tab_p_get = 0x40046058 ); @@ -436,10 +327,7 @@ PROVIDE ( memccpy = 0x4000c220 ); PROVIDE ( memchr = 0x4000c244 ); PROVIDE ( memcmp = 0x4000c260 ); PROVIDE ( memcpy = 0x4000c2c8 ); -PROVIDE ( MemDwnLdStartMsgProc = 0x40008948 ); -PROVIDE ( MemDwnLdStopReqMsgProc = 0x400089dc ); PROVIDE ( memmove = 0x4000c3c0 ); -PROVIDE ( MemPacketSendReqMsgProc = 0x40008978 ); PROVIDE ( memrchr = 0x4000c400 ); PROVIDE ( memset = 0x4000c44c ); PROVIDE ( mktime = 0x4005a5e8 ); @@ -469,7 +357,6 @@ PROVIDE ( __negsf2 = 0x400020c0 ); PROVIDE ( __negvdi2 = 0x40002e98 ); PROVIDE ( __negvsi2 = 0x40002e78 ); PROVIDE ( __nesf2 = 0x40063374 ); -PROVIDE ( _NMIExceptionVector = 0x400002c0 ); PROVIDE ( notEqual256 = 0x40015b04 ); PROVIDE ( __nsau_data = 0x3ff96544 ); PROVIDE ( one_bits = 0x3ff971f8 ); @@ -521,7 +408,6 @@ PROVIDE ( r_co_list_push_front = 0x40013ff4 ); PROVIDE ( r_co_list_size = 0x400142ac ); PROVIDE ( r_co_nb_good_channels = 0x40014360 ); PROVIDE ( r_co_slot_to_duration = 0x40014348 ); -PROVIDE ( RcvMsg = 0x4000954c ); PROVIDE ( r_dbg_init = 0x40014394 ); PROVIDE ( r_dbg_platform_reset_complete = 0x400143d0 ); PROVIDE ( r_dbg_swdiag_init = 0x40014470 ); @@ -560,15 +446,12 @@ PROVIDE ( r_ecc_gen_new_secret_key = 0x400170e4 ); PROVIDE ( r_ecc_get_debug_Keys = 0x40017224 ); PROVIDE ( r_ecc_init = 0x40016dbc ); PROVIDE ( RecvBuff = 0x3ffe009c ); -PROVIDE ( recv_packet = 0x40009424 ); PROVIDE ( r_em_buf_init = 0x4001729c ); PROVIDE ( r_em_buf_rx_buff_addr_get = 0x400173e8 ); PROVIDE ( r_em_buf_rx_free = 0x400173c4 ); PROVIDE ( r_em_buf_tx_buff_addr_get = 0x40017404 ); PROVIDE ( r_em_buf_tx_free = 0x4001741c ); PROVIDE ( _rename_r = 0x4000bc28 ); -PROVIDE ( _ResetHandler = 0x40000450 ); -PROVIDE ( _ResetVector = 0x40000400 ); PROVIDE ( r_F1_256 = 0x400133e4 ); PROVIDE ( r_F2_256 = 0x40013568 ); PROVIDE ( r_F3_256 = 0x40013664 ); @@ -1351,7 +1234,6 @@ PROVIDE ( rom_iq_est_disable = 0x40005590 ); PROVIDE ( rom_iq_est_enable = 0x40005514 ); PROVIDE ( rom_linear_to_db = 0x40005f64 ); PROVIDE ( rom_loopback_mode_en = 0x400030f8 ); -PROVIDE ( rom_main = 0x400076c4 ); PROVIDE ( rom_meas_tone_pwr_db = 0x40006004 ); PROVIDE ( rom_mhz2ieee = 0x4000404c ); PROVIDE ( rom_noise_floor_auto_set = 0x40003bdc ); @@ -1458,10 +1340,6 @@ PROVIDE ( r_rwip_wakeup_delay_set = 0x40055e4c ); PROVIDE ( r_rwip_wakeup_end = 0x40055e18 ); PROVIDE ( r_rwip_wlcoex_set = 0x40055f60 ); PROVIDE ( r_SHA_256 = 0x40013a90 ); -PROVIDE ( rtc_boot_control = 0x4000821c ); -PROVIDE ( rtc_get_reset_reason = 0x400081d4 ); -PROVIDE ( rtc_get_wakeup_cause = 0x400081f4 ); -PROVIDE ( rtc_select_apb_bridge = 0x40008288 ); PROVIDE ( rwip_coex_cfg = 0x3ff9914c ); PROVIDE ( rwip_priority = 0x3ff99159 ); PROVIDE ( rwip_rf = 0x3ffbdb28 ); @@ -1473,13 +1351,10 @@ PROVIDE ( __sccl = 0x4000c498 ); PROVIDE ( __sclose = 0x400011b8 ); PROVIDE ( SelectSpiFunction = 0x40061f84 ); PROVIDE ( SelectSpiQIO = 0x40061ddc ); -PROVIDE ( SendMsg = 0x40009384 ); -PROVIDE ( send_packet = 0x40009340 ); PROVIDE ( __seofread = 0x40001148 ); PROVIDE ( setjmp = 0x40056268 ); PROVIDE ( setlocale = 0x40059568 ); PROVIDE ( _setlocale_r = 0x4005950c ); -PROVIDE ( set_rtc_memory_crc = 0x40008208 ); PROVIDE ( SetSpiDrvs = 0x40061e78 ); PROVIDE ( __sf_fake_stderr = 0x3ff96458 ); PROVIDE ( __sf_fake_stdin = 0x3ff96498 ); @@ -1528,8 +1403,6 @@ PROVIDE ( slc_send_to_host_chain = 0x4000b6a0 ); PROVIDE ( slc_set_host_io_max_window = 0x4000b89c ); PROVIDE ( slc_to_host_chain_recycle = 0x4000b758 ); PROVIDE ( __smakebuf_r = 0x40059108 ); -PROVIDE ( software_reset = 0x4000824c ); -PROVIDE ( software_reset_cpu = 0x40008264 ); PROVIDE ( specialModP256 = 0x4001600c ); PROVIDE ( spi_cache_sram_init = 0x400626e4 ); PROVIDE ( SPIClkConfig = 0x40062bc8 ); @@ -1563,6 +1436,8 @@ PROVIDE ( SPI_Write_Encrypt_Disable = 0x40062e60 ); PROVIDE ( SPI_Write_Encrypt_Enable = 0x40062df4 ); /* This is static function, but can be used, not generated by script*/ PROVIDE ( SPI_write_status = 0x400622f0 ); +/* This is static function, but can be used, not generated by script */ +PROVIDE ( SPI_Wait_Idle = 0x400622c0 ); PROVIDE ( srand = 0x40001004 ); PROVIDE ( __sread = 0x40001118 ); PROVIDE ( __srefill_r = 0x400593d4 ); @@ -1627,7 +1502,6 @@ PROVIDE ( __swbuf_r = 0x40058bec ); PROVIDE ( __swrite = 0x40001150 ); PROVIDE ( __swsetup_r = 0x40058cc8 ); PROVIDE ( sw_to_hw = 0x3ffb8d40 ); -PROVIDE ( _SyscallException = 0x400007cf ); PROVIDE ( syscall_table_ptr_app = 0x3ffae020 ); PROVIDE ( syscall_table_ptr_pro = 0x3ffae024 ); PROVIDE ( _system_r = 0x4000bc10 ); @@ -1658,31 +1532,7 @@ PROVIDE ( _tzname = 0x3ffae030 ); PROVIDE ( tzset = 0x40001a1c ); PROVIDE ( _tzset_r = 0x40001a28 ); PROVIDE ( __tz_unlock = 0x40001a10 ); -PROVIDE ( uartAttach = 0x40008fd0 ); -PROVIDE ( uart_baudrate_detect = 0x40009034 ); -PROVIDE ( uart_buff_switch = 0x400093c0 ); -PROVIDE ( UartConnCheck = 0x40008738 ); -PROVIDE ( UartConnectProc = 0x40008a04 ); PROVIDE ( UartDev = 0x3ffe019c ); -PROVIDE ( uart_div_modify = 0x400090cc ); -PROVIDE ( UartDwnLdProc = 0x40008ce8 ); -PROVIDE ( UartGetCmdLn = 0x40009564 ); -PROVIDE ( Uart_Init = 0x40009120 ); -PROVIDE ( UartRegReadProc = 0x40008a58 ); -PROVIDE ( UartRegWriteProc = 0x40008a14 ); -PROVIDE ( uart_rx_intr_handler = 0x40008f4c ); -PROVIDE ( uart_rx_one_char = 0x400092d0 ); -PROVIDE ( uart_rx_one_char_block = 0x400092a4 ); -PROVIDE ( uart_rx_readbuff = 0x40009394 ); -PROVIDE ( UartRxString = 0x400092fc ); -PROVIDE ( UartSetBaudProc = 0x40008aac ); -PROVIDE ( UartSpiAttachProc = 0x40008a6c ); -PROVIDE ( UartSpiReadProc = 0x40008a80 ); -PROVIDE ( uart_tx_flush = 0x40009258 ); -PROVIDE ( uart_tx_one_char = 0x40009200 ); -PROVIDE ( uart_tx_one_char2 = 0x4000922c ); -PROVIDE ( uart_tx_switch = 0x40009028 ); -PROVIDE ( uart_tx_wait_idle = 0x40009278 ); PROVIDE ( __ucmpdi2 = 0x40063840 ); PROVIDE ( __udivdi3 = 0x4000cff8 ); PROVIDE ( __udivmoddi4 = 0x40064ab0 ); @@ -1697,21 +1547,13 @@ PROVIDE ( _unlink_r = 0x4000bc84 ); PROVIDE ( __unorddf2 = 0x400637f4 ); PROVIDE ( __unordsf2 = 0x40063478 ); PROVIDE ( user_code_start = 0x3ffe0400 ); -PROVIDE ( _UserExceptionVector = 0x40000340 ); PROVIDE ( utoa = 0x40056258 ); PROVIDE ( __utoa = 0x400561f0 ); -PROVIDE ( VerifyFlashMd5Proc = 0x40008c44 ); PROVIDE ( veryBigHexP256 = 0x3ff9736c ); PROVIDE ( wcrtomb = 0x40058920 ); PROVIDE ( _wcrtomb_r = 0x400588d8 ); PROVIDE ( __wctomb = 0x3ff96540 ); PROVIDE ( _wctomb_r = 0x40058f14 ); -PROVIDE ( _WindowOverflow12 = 0x40000100 ); -PROVIDE ( _WindowOverflow4 = 0x40000000 ); -PROVIDE ( _WindowOverflow8 = 0x40000080 ); -PROVIDE ( _WindowUnderflow12 = 0x40000140 ); -PROVIDE ( _WindowUnderflow4 = 0x40000040 ); -PROVIDE ( _WindowUnderflow8 = 0x400000c0 ); PROVIDE ( write = 0x4000181c ); PROVIDE ( _write_r = 0x4000bd70 ); PROVIDE ( xthal_bcopy = 0x4000c098 ); @@ -1727,12 +1569,38 @@ PROVIDE ( xthal_set_intclear = 0x4000c1ec ); PROVIDE ( _xtos_set_intlevel = 0x4000bfdc ); PROVIDE ( g_ticks_per_us_pro = 0x3ffe01e0 ); PROVIDE ( g_ticks_per_us_app = 0x3ffe40f0 ); + /* These functions are xtos-related (or call xtos-related functions) and do not play well with multicore FreeRTOS. Where needed, we provide alternatives that are multicore -compatible. +compatible. These functions also use a chunk of static RAM, by not using them we can +allocate that RAM for general use. */ /* +PROVIDE ( _DebugExceptionVector = 0x40000280 ); +PROVIDE ( _DoubleExceptionVector = 0x400003c0 ); +PROVIDE ( _KernelExceptionVector = 0x40000300 ); +PROVIDE ( _GeneralException = 0x40000e14 ); +PROVIDE ( _ResetHandler = 0x40000450 ); +PROVIDE ( _ResetVector = 0x40000400 ); +PROVIDE ( _UserExceptionVector = 0x40000340 ); +PROVIDE ( _NMIExceptionVector = 0x400002c0 ); +PROVIDE ( _WindowOverflow12 = 0x40000100 ); +PROVIDE ( _WindowOverflow4 = 0x40000000 ); +PROVIDE ( _WindowOverflow8 = 0x40000080 ); +PROVIDE ( _WindowUnderflow12 = 0x40000140 ); +PROVIDE ( _WindowUnderflow4 = 0x40000040 ); +PROVIDE ( _WindowUnderflow8 = 0x400000c0 ); +PROVIDE ( _Level2FromVector = 0x40000954 ); +PROVIDE ( _Level3FromVector = 0x40000a28 ); +PROVIDE ( _Level4FromVector = 0x40000af8 ); +PROVIDE ( _Level5FromVector = 0x40000c68 ); +PROVIDE ( _Level2Vector = 0x40000180 ); +PROVIDE ( _Level3Vector = 0x400001c0 ); +PROVIDE ( _Level4Vector = 0x40000200 ); +PROVIDE ( _Level5Vector = 0x40000240 ); +PROVIDE ( _LevelOneInterrupt = 0x40000835 ); +PROVIDE ( _SyscallException = 0x400007cf ); PROVIDE ( _xtos_alloca_handler = 0x40000010 ); PROVIDE ( _xtos_cause3_handler = 0x40000dd8 ); PROVIDE ( _xtos_c_handler_table = 0x3ffe0548 ); @@ -1758,12 +1626,164 @@ PROVIDE ( _xtos_unhandled_exception = 0x4000c024 ); PROVIDE ( _xtos_unhandled_interrupt = 0x4000c01c ); PROVIDE ( _xtos_vpri_enabled = 0x3ffe0654 ); PROVIDE ( ets_intr_count = 0x3ffe03fc ); +*/ + +/* These functions are part of the UART downloader but also contain general UART functions. */ +PROVIDE ( FilePacketSendDeflatedReqMsgProc = 0x40008b24 ); +PROVIDE ( FilePacketSendReqMsgProc = 0x40008860 ); +PROVIDE ( FlashDwnLdDeflatedStartMsgProc = 0x40008ad8 ); +PROVIDE ( FlashDwnLdParamCfgMsgProc = 0x4000891c ); +PROVIDE ( FlashDwnLdStartMsgProc = 0x40008820 ); +PROVIDE ( FlashDwnLdStopDeflatedReqMsgProc = 0x40008c18 ); +PROVIDE ( FlashDwnLdStopReqMsgProc = 0x400088ec ); +PROVIDE ( MemDwnLdStartMsgProc = 0x40008948 ); +PROVIDE ( MemDwnLdStopReqMsgProc = 0x400089dc ); +PROVIDE ( MemPacketSendReqMsgProc = 0x40008978 ); +PROVIDE ( uart_baudrate_detect = 0x40009034 ); +PROVIDE ( uart_buff_switch = 0x400093c0 ); +PROVIDE ( UartConnCheck = 0x40008738 ); +PROVIDE ( UartConnectProc = 0x40008a04 ); +PROVIDE ( UartDwnLdProc = 0x40008ce8 ); +PROVIDE ( UartRegReadProc = 0x40008a58 ); +PROVIDE ( UartRegWriteProc = 0x40008a14 ); +PROVIDE ( UartSetBaudProc = 0x40008aac ); +PROVIDE ( UartSpiAttachProc = 0x40008a6c ); +PROVIDE ( UartSpiReadProc = 0x40008a80 ); +PROVIDE ( VerifyFlashMd5Proc = 0x40008c44 ); +PROVIDE ( GetUartDevice = 0x40009598 ); +PROVIDE ( RcvMsg = 0x4000954c ); +PROVIDE ( SendMsg = 0x40009384 ); +PROVIDE ( UartGetCmdLn = 0x40009564 ); +PROVIDE ( UartRxString = 0x400092fc ); +PROVIDE ( Uart_Init = 0x40009120 ); +PROVIDE ( recv_packet = 0x40009424 ); +PROVIDE ( send_packet = 0x40009340 ); +PROVIDE ( uartAttach = 0x40008fd0 ); +PROVIDE ( uart_div_modify = 0x400090cc ); +PROVIDE ( uart_rx_intr_handler = 0x40008f4c ); +PROVIDE ( uart_rx_one_char = 0x400092d0 ); +PROVIDE ( uart_rx_one_char_block = 0x400092a4 ); +PROVIDE ( uart_rx_readbuff = 0x40009394 ); +PROVIDE ( uart_tx_flush = 0x40009258 ); +PROVIDE ( uart_tx_one_char = 0x40009200 ); +PROVIDE ( uart_tx_one_char2 = 0x4000922c ); +PROVIDE ( uart_tx_switch = 0x40009028 ); +PROVIDE ( uart_tx_wait_idle = 0x40009278 ); + + +/* +These functions are part of the ROM GPIO driver. We do not use them; the provided esp-idf functions +replace them and this way we can re-use the fixed RAM addresses these routines need. +*/ +/* <-- So you don't read over it: This comment disables the next lines. +PROVIDE ( gpio_init = 0x40009c20 ); +PROVIDE ( gpio_intr_ack = 0x40009dd4 ); +PROVIDE ( gpio_intr_ack_high = 0x40009e1c ); +PROVIDE ( gpio_intr_handler_register = 0x40009e6c ); +PROVIDE ( gpio_intr_pending = 0x40009cec ); +PROVIDE ( gpio_intr_pending_high = 0x40009cf8 ); +PROVIDE ( gpio_pending_mask = 0x3ffe0038 ); +PROVIDE ( gpio_pending_mask_high = 0x3ffe0044 ); +PROVIDE ( gpio_pin_intr_state_set = 0x40009d04 ); +PROVIDE ( gpio_pin_wakeup_disable = 0x40009eb0 ); +PROVIDE ( gpio_pin_wakeup_enable = 0x40009e7c ); +PROVIDE ( gpio_register_get = 0x40009cbc ); +PROVIDE ( gpio_register_set = 0x40009bbc ); +*/ +/* These are still part of that driver, but have been verified not to use static RAM, so they can be used. */ +PROVIDE ( gpio_output_set = 0x40009b24 ); +PROVIDE ( gpio_output_set_high = 0x40009b5c ); +PROVIDE ( gpio_input_get = 0x40009b88 ); +PROVIDE ( gpio_input_get_high = 0x40009b9c ); +PROVIDE ( gpio_matrix_in = 0x40009edc ); +PROVIDE ( gpio_matrix_out = 0x40009f0c ); +PROVIDE ( gpio_pad_select_gpio = 0x40009fdc ); +PROVIDE ( gpio_pad_set_drv = 0x4000a11c ); +PROVIDE ( gpio_pad_pulldown = 0x4000a348 ); +PROVIDE ( gpio_pad_pullup = 0x4000a22c ); +PROVIDE ( gpio_pad_hold = 0x4000a734 ); +PROVIDE ( gpio_pad_unhold = 0x4000a484 ); + +/* +These functions are part of the non-os kernel (etsc). +*/ +PROVIDE ( ets_aes_crypt = 0x4005c9b8 ); +PROVIDE ( ets_aes_disable = 0x4005c8f8 ); +PROVIDE ( ets_aes_enable = 0x4005c8cc ); +PROVIDE ( ets_aes_set_endian = 0x4005c928 ); +PROVIDE ( ets_aes_setkey_dec = 0x4005c994 ); +PROVIDE ( ets_aes_setkey_enc = 0x4005c97c ); +PROVIDE ( ets_bigint_disable = 0x4005c4e0 ); +PROVIDE ( ets_bigint_enable = 0x4005c498 ); +PROVIDE ( ets_bigint_mod_mult_getz = 0x4005c818 ); +PROVIDE ( ets_bigint_mod_mult_prepare = 0x4005c7b4 ); +PROVIDE ( ets_bigint_mod_power_getz = 0x4005c614 ); +PROVIDE ( ets_bigint_mod_power_prepare = 0x4005c54c ); +PROVIDE ( ets_bigint_montgomery_mult_getz = 0x4005c7a4 ); +PROVIDE ( ets_bigint_montgomery_mult_prepare = 0x4005c6fc ); +PROVIDE ( ets_bigint_mult_getz = 0x4005c6e8 ); +PROVIDE ( ets_bigint_mult_prepare = 0x4005c630 ); +PROVIDE ( ets_bigint_wait_finish = 0x4005c520 ); +PROVIDE ( ets_post = 0x4000673c ); +PROVIDE ( ets_run = 0x400066bc ); +PROVIDE ( ets_set_idle_cb = 0x40006674 ); +PROVIDE ( ets_task = 0x40006688 ); +PROVIDE ( ets_efuse_get_8M_clock = 0x40008710 ); +PROVIDE ( ets_efuse_get_spiconfig = 0x40008658 ); +PROVIDE ( ets_efuse_program_op = 0x40008628 ); +PROVIDE ( ets_efuse_read_op = 0x40008600 ); PROVIDE ( ets_intr_lock = 0x400067b0 ); PROVIDE ( ets_intr_unlock = 0x400067c4 ); PROVIDE ( ets_isr_attach = 0x400067ec ); PROVIDE ( ets_isr_mask = 0x400067fc ); PROVIDE ( ets_isr_unmask = 0x40006808 ); -*/ +PROVIDE ( ets_waiti0 = 0x400067d8 ); +PROVIDE ( intr_matrix_set = 0x4000681c ); +PROVIDE ( check_pos = 0x400068b8 ); +PROVIDE ( ets_set_appcpu_boot_addr = 0x4000689c ); +PROVIDE ( ets_set_startup_callback = 0x4000688c ); +PROVIDE ( ets_set_user_start = 0x4000687c ); +PROVIDE ( ets_unpack_flash_code = 0x40007018 ); +PROVIDE ( ets_unpack_flash_code_legacy = 0x4000694c ); +PROVIDE ( rom_main = 0x400076c4 ); +PROVIDE ( ets_install_putc1 = 0x40007d18 ); +PROVIDE ( ets_install_putc2 = 0x40007d38 ); +PROVIDE ( ets_install_uart_printf = 0x40007d28 ); +PROVIDE ( ets_printf = 0x40007d54 ); +PROVIDE ( rtc_boot_control = 0x4000821c ); +PROVIDE ( rtc_get_reset_reason = 0x400081d4 ); +PROVIDE ( rtc_get_wakeup_cause = 0x400081f4 ); +PROVIDE ( rtc_select_apb_bridge = 0x40008288 ); +PROVIDE ( set_rtc_memory_crc = 0x40008208 ); +PROVIDE ( software_reset = 0x4000824c ); +PROVIDE ( software_reset_cpu = 0x40008264 ); +PROVIDE ( ets_secure_boot_check = 0x4005cb40 ); +PROVIDE ( ets_secure_boot_check_finish = 0x4005cc04 ); +PROVIDE ( ets_secure_boot_check_start = 0x4005cbcc ); +PROVIDE ( ets_secure_boot_finish = 0x4005ca84 ); +PROVIDE ( ets_secure_boot_hash = 0x4005cad4 ); +PROVIDE ( ets_secure_boot_obtain = 0x4005cb14 ); +PROVIDE ( ets_secure_boot_rd_abstract = 0x4005cba8 ); +PROVIDE ( ets_secure_boot_rd_iv = 0x4005cb84 ); +PROVIDE ( ets_secure_boot_start = 0x4005ca34 ); +PROVIDE ( ets_sha_disable = 0x4005c0a8 ); +PROVIDE ( ets_sha_enable = 0x4005c07c ); +PROVIDE ( ets_sha_finish = 0x4005c104 ); +PROVIDE ( ets_sha_init = 0x4005c0d4 ); +PROVIDE ( ets_sha_update = 0x4005c2a0 ); +PROVIDE ( ets_delay_us = 0x40008534 ); +PROVIDE ( ets_get_cpu_frequency = 0x4000855c ); +PROVIDE ( ets_get_detected_xtal_freq = 0x40008588 ); +PROVIDE ( ets_get_xtal_scale = 0x4000856c ); +PROVIDE ( ets_timer_arm = 0x40008368 ); +PROVIDE ( ets_timer_arm_us = 0x400083ac ); +PROVIDE ( ets_timer_disarm = 0x400083ec ); +PROVIDE ( ets_timer_done = 0x40008428 ); +PROVIDE ( ets_timer_handler_isr = 0x40008454 ); +PROVIDE ( ets_timer_init = 0x400084e8 ); +PROVIDE ( ets_timer_setfn = 0x40008350 ); +PROVIDE ( ets_update_cpu_frequency_rom = 0x40008550 ); /* Updates g_ticks_per_us on the current CPU only; not on the other core */ + /* Following are static data, but can be used, not generated by script <<<<< btdm data */ PROVIDE ( ld_acl_env = 0x3ffb8258 ); PROVIDE ( ld_active_ch_map = 0x3ffb8334 ); @@ -1783,3 +1803,4 @@ PROVIDE ( ld_sco_env = 0x3ffb824c ); PROVIDE ( ld_sscan_env = 0x3ffb832c ); PROVIDE ( ld_strain_env = 0x3ffb8330 ); /* Above are static data, but can be used, not generated by script >>>>> btdm data */ + diff --git a/components/esp32/lib b/components/esp32/lib index ed85cf9156..ffe5a4c14f 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit ed85cf9156f2ef358c29d07fb849a73c5758eecb +Subproject commit ffe5a4c14fe9c933c776fadc62fa9d409929e6f9 diff --git a/components/esp32/panic.c b/components/esp32/panic.c index 4f0497d6ea..cd426344e1 100644 --- a/components/esp32/panic.c +++ b/components/esp32/panic.c @@ -34,6 +34,7 @@ #include "esp_attr.h" #include "esp_err.h" #include "esp_core_dump.h" +#include "esp_spi_flash.h" /* Panic handlers; these get called when an unhandled exception occurs or the assembly-level @@ -107,11 +108,8 @@ void __attribute__((weak)) vApplicationStackOverflowHook( TaskHandle_t xTask, s static bool abort_called; -void abort() +static __attribute__((noreturn)) inline void invoke_abort() { -#if !CONFIG_ESP32_PANIC_SILENT_REBOOT - ets_printf("abort() was called at PC 0x%08x\n", (intptr_t)__builtin_return_address(0) - 3); -#endif abort_called = true; while(1) { __asm__ ("break 0,0"); @@ -119,6 +117,14 @@ void abort() } } +void abort() +{ +#if !CONFIG_ESP32_PANIC_SILENT_REBOOT + ets_printf("abort() was called at PC 0x%08x\n", (intptr_t)__builtin_return_address(0) - 3); +#endif + invoke_abort(); +} + static const char *edesc[] = { "IllegalInstruction", "Syscall", "InstructionFetchError", "LoadStoreError", @@ -441,4 +447,11 @@ void esp_clear_watchpoint(int no) } } - +void _esp_error_check_failed(esp_err_t rc, const char *file, int line, const char *function, const char *expression) +{ + ets_printf("ESP_ERROR_CHECK failed: esp_err_t 0x%x at 0x%08x\n", rc, (intptr_t)__builtin_return_address(0) - 3); + if (spi_flash_cache_enabled()) { // strings may be in flash cache + ets_printf("file: \"%s\" line %d\nfunc: %s\nexpression: %s\n", file, line, function, expression); + } + invoke_abort(); +} diff --git a/components/esp32/phy_init.c b/components/esp32/phy_init.c index 34e1a9f00e..5fbeeb7c88 100644 --- a/components/esp32/phy_init.c +++ b/components/esp32/phy_init.c @@ -60,7 +60,7 @@ esp_err_t esp_phy_rf_init(const esp_phy_init_data_t* init_data, } } // Enable WiFi peripheral clock - SET_PERI_REG_MASK(DPORT_WIFI_CLK_EN_REG, 0x87cf); + SET_PERI_REG_MASK(DPORT_WIFI_CLK_EN_REG, DPORT_WIFI_CLK_WIFI_EN | DPORT_WIFI_CLK_RNG_EN); ESP_LOGV(TAG, "register_chipv7_phy, init_data=%p, cal_data=%p, mode=%d", init_data, calibration_data, mode); phy_set_wifi_mode_only(0); @@ -82,10 +82,10 @@ esp_err_t esp_phy_rf_deinit(void) _lock_acquire(&s_phy_rf_init_lock); if (s_phy_rf_init_count == 1) { - // Disable PHY and RF. TODO: convert this function to another one. - pm_close_rf(); - // Disable WiFi peripheral clock - CLEAR_PERI_REG_MASK(DPORT_WIFI_CLK_EN_REG, 0x87cf); + // Disable PHY and RF. + phy_close_rf(); + // Disable WiFi peripheral clock. Do not disable clock for hardware RNG + CLEAR_PERI_REG_MASK(DPORT_WIFI_CLK_EN_REG, DPORT_WIFI_CLK_WIFI_EN); } else { #if CONFIG_SW_COEXIST_ENABLE coex_deinit(); diff --git a/components/esp32/rtc.h b/components/esp32/rtc.h index f21d0da83e..e1262a1ca1 100644 --- a/components/esp32/rtc.h +++ b/components/esp32/rtc.h @@ -138,7 +138,7 @@ uint32_t rtc_sleep(uint32_t cycles_h, uint32_t cycles_l, uint32_t wakeup_opt, ui /** * @brief Shutdown PHY and RF. TODO: convert this function to another one. */ -void pm_close_rf(void); +void phy_close_rf(void); #ifdef __cplusplus } diff --git a/components/esp32/system_api.c b/components/esp32/system_api.c index d984af78a7..79ec1a3963 100644 --- a/components/esp32/system_api.c +++ b/components/esp32/system_api.c @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include + #include "esp_system.h" #include "esp_attr.h" #include "esp_wifi.h" @@ -72,6 +74,87 @@ esp_err_t esp_efuse_read_mac(uint8_t* mac) esp_err_t system_efuse_read_mac(uint8_t mac[6]) __attribute__((alias("esp_efuse_read_mac"))); +esp_err_t esp_derive_mac(uint8_t* dst_mac, const uint8_t* src_mac) +{ + uint8_t idx; + + if (dst_mac == NULL || src_mac == NULL) { + ESP_LOGE(TAG, "mac address param is NULL"); + return ESP_ERR_INVALID_ARG; + } + + memcpy(dst_mac, src_mac, 6); + for (idx = 0; idx < 64; idx++) { + dst_mac[0] = src_mac[0] | 0x02; + dst_mac[0] ^= idx << 2; + + if (memcmp(dst_mac, src_mac, 6)) { + break; + } + } + + return ESP_OK; +} + +esp_err_t esp_read_mac(uint8_t* mac, esp_mac_type_t type) +{ + uint8_t efuse_mac[6]; + + if (mac == NULL) { + ESP_LOGE(TAG, "mac address param is NULL"); + return ESP_ERR_INVALID_ARG; + } + + if (type < ESP_MAC_WIFI_STA || type > ESP_MAC_ETH) { + ESP_LOGE(TAG, "mac type is incorrect"); + return ESP_ERR_INVALID_ARG; + } + + _Static_assert(NUM_MAC_ADDRESS_FROM_EFUSE == FOUR_MAC_ADDRESS_FROM_EFUSE \ + || NUM_MAC_ADDRESS_FROM_EFUSE == TWO_MAC_ADDRESS_FROM_EFUSE, \ + "incorrect NUM_MAC_ADDRESS_FROM_EFUSE value"); + + esp_efuse_read_mac(efuse_mac); + + switch (type) { + case ESP_MAC_WIFI_STA: + memcpy(mac, efuse_mac, 6); + break; + case ESP_MAC_WIFI_SOFTAP: + if (NUM_MAC_ADDRESS_FROM_EFUSE == FOUR_MAC_ADDRESS_FROM_EFUSE) { + memcpy(mac, efuse_mac, 6); + mac[5] += 1; + } + else if (NUM_MAC_ADDRESS_FROM_EFUSE == TWO_MAC_ADDRESS_FROM_EFUSE) { + esp_derive_mac(mac, efuse_mac); + } + break; + case ESP_MAC_BT: + memcpy(mac, efuse_mac, 6); + if (NUM_MAC_ADDRESS_FROM_EFUSE == FOUR_MAC_ADDRESS_FROM_EFUSE) { + mac[5] += 2; + } + else if (NUM_MAC_ADDRESS_FROM_EFUSE == TWO_MAC_ADDRESS_FROM_EFUSE) { + mac[5] += 1; + } + break; + case ESP_MAC_ETH: + if (NUM_MAC_ADDRESS_FROM_EFUSE == FOUR_MAC_ADDRESS_FROM_EFUSE) { + memcpy(mac, efuse_mac, 6); + mac[5] += 3; + } + else if (NUM_MAC_ADDRESS_FROM_EFUSE == TWO_MAC_ADDRESS_FROM_EFUSE) { + efuse_mac[5] += 1; + esp_derive_mac(mac, efuse_mac); + } + break; + default: + ESP_LOGW(TAG, "incorrect mac type"); + break; + } + + return ESP_OK; +} void esp_restart_noos() __attribute__ ((noreturn)); @@ -132,7 +215,7 @@ void IRAM_ATTR esp_restart_noos() DPORT_BB_RST | DPORT_FE_RST | DPORT_MAC_RST | DPORT_BT_RST | DPORT_BTMAC_RST | DPORT_SDIO_RST | DPORT_SDIO_HOST_RST | DPORT_EMAC_RST | DPORT_MACPWR_RST | - DPROT_RW_BTMAC_RST | DPROT_RW_BTLP_RST); + DPORT_RW_BTMAC_RST | DPORT_RW_BTLP_RST); REG_WRITE(DPORT_CORE_RST_EN_REG, 0); // Reset timer/spi/uart diff --git a/components/esp32/test/test_tsens.c b/components/esp32/test/test_tsens.c new file mode 100644 index 0000000000..ff2660d1b2 --- /dev/null +++ b/components/esp32/test/test_tsens.c @@ -0,0 +1,20 @@ +#include +#include "unity.h" +#include "rom/ets_sys.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/sens_reg.h" + +TEST_CASE("can control TSENS using registers", "[rtc][ignore]") +{ + SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR, 3, SENS_FORCE_XPD_SAR_S); + SET_PERI_REG_BITS(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_CLK_DIV, 10, SENS_TSENS_CLK_DIV_S); + CLEAR_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_POWER_UP); + CLEAR_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_DUMP_OUT); + SET_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_POWER_UP_FORCE); + SET_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_POWER_UP); + ets_delay_us(100); + SET_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_DUMP_OUT); + ets_delay_us(5); + int res = GET_PERI_REG_BITS2(SENS_SAR_SLAVE_ADDR3_REG, SENS_TSENS_OUT, SENS_TSENS_OUT_S); + printf("res=%d\n", res); +} diff --git a/components/esptool_py/Kconfig.projbuild b/components/esptool_py/Kconfig.projbuild index edff88c55d..84ca5d6831 100644 --- a/components/esptool_py/Kconfig.projbuild +++ b/components/esptool_py/Kconfig.projbuild @@ -46,30 +46,32 @@ config ESPTOOLPY_COMPRESSED decompress it on the fly before flashing it. For most payloads, this should result in a speed increase. -choice ESPTOOLPY_FLASHMODE +choice FLASHMODE prompt "Flash SPI mode" - default ESPTOOLPY_FLASHMODE_DIO + default FLASHMODE_DIO help Mode the flash chip is flashed in, as well as the default mode for the binary to run in. -config ESPTOOLPY_FLASHMODE_QIO +config FLASHMODE_QIO bool "QIO" -config ESPTOOLPY_FLASHMODE_QOUT +config FLASHMODE_QOUT bool "QOUT" -config ESPTOOLPY_FLASHMODE_DIO +config FLASHMODE_DIO bool "DIO" -config ESPTOOLPY_FLASHMODE_DOUT +config FLASHMODE_DOUT bool "DOUT" endchoice +# Note: we use esptool.py to flash bootloader in +# dio mode for QIO/QOUT, bootloader then upgrades +# itself to quad mode during initialisation config ESPTOOLPY_FLASHMODE string - default "qio" if ESPTOOLPY_FLASHMODE_QIO - default "qout" if ESPTOOLPY_FLASHMODE_QOUT - default "dio" if ESPTOOLPY_FLASHMODE_DIO - default "dout" if ESPTOOLPY_FLASHMODE_DOUT - + default "dio" if FLASHMODE_QIO + default "dio" if FLASHMODE_QOUT + default "dio" if FLASHMODE_DIO + default "dout" if FLASHMODE_DOUT choice ESPTOOLPY_FLASHFREQ prompt "Flash SPI speed" @@ -138,24 +140,16 @@ choice ESPTOOLPY_BEFORE wired from the serial port to the ESP32. Most USB development boards do this internally. - The "Reset with ESP32R0 Windows workaround" option works - around an automatic reset bug in hardware, when using Windows - with some development boards. This fix only works if you're - using a silicon revision 0 ESP32. - config ESPTOOLPY_BEFORE_RESET bool "Reset to bootloader" config ESPTOOLPY_BEFORE_NORESET bool "No reset" -config ESPTOOLPY_BEFORE_ESP32R0 - bool "Reset with ESP32R0 Windows workaround" endchoice config ESPTOOLPY_BEFORE string default "default_reset" if ESPTOOLPY_BEFORE_RESET default "no_reset" if ESPTOOLPY_BEFORE_NORESET - default "esp32r0" if ESPTOOLPY_BEFORE_ESP32R0 choice ESPTOOLPY_AFTER prompt "After flashing" diff --git a/components/esptool_py/esptool b/components/esptool_py/esptool index e9e9179f6f..907273664a 160000 --- a/components/esptool_py/esptool +++ b/components/esptool_py/esptool @@ -1 +1 @@ -Subproject commit e9e9179f6fc3f2ecfc568987d3224b5e53a05f06 +Subproject commit 907273664ada32fc33f3fbfeba99550512c67e4d diff --git a/components/ethernet/emac_main.c b/components/ethernet/emac_main.c index 5479ec399e..8c93a1830e 100644 --- a/components/ethernet/emac_main.c +++ b/components/ethernet/emac_main.c @@ -75,8 +75,7 @@ esp_err_t emac_post(emac_sig_t sig, emac_par_t par); static void emac_macaddr_init(void) { - esp_efuse_read_mac(&(emac_config.macaddr[0])); - emac_config.macaddr[5] = emac_config.macaddr[5] + 3; + esp_read_mac(&(emac_config.macaddr[0]), ESP_MAC_ETH); } void esp_eth_get_mac(uint8_t mac[6]) diff --git a/components/fatfs/src/diskio.c b/components/fatfs/src/diskio.c index 004cfc5bea..c61702f51f 100644 --- a/components/fatfs/src/diskio.c +++ b/components/fatfs/src/diskio.c @@ -18,39 +18,71 @@ #include static const char* TAG = "ff_diskio"; -static ff_diskio_impl_t s_impls[_VOLUMES] = { { 0 } }; +static ff_diskio_impl_t * s_impls[_VOLUMES]; static sdmmc_card_t* s_cards[_VOLUMES] = { NULL }; +static bool s_impls_initialized = false; PARTITION VolToPart[] = { {0, 1}, /* Logical drive 0 ==> Physical drive 0, 1st partition */ {1, 0} /* Logical drive 1 ==> Physical drive 1, auto detection */ }; +esp_err_t ff_diskio_get_drive(BYTE* out_pdrv) +{ + BYTE i; + for(i=0; i<_VOLUMES; i++) { + if (!s_impls[i]) { + *out_pdrv = i; + return ESP_OK; + } + } + return ESP_ERR_NOT_FOUND; +} + void ff_diskio_register(BYTE pdrv, const ff_diskio_impl_t* discio_impl) { assert(pdrv < _VOLUMES); - memcpy(&s_impls[pdrv], discio_impl, sizeof(ff_diskio_impl_t)); + + if (!s_impls_initialized) { + s_impls_initialized = true; + memset(s_impls, 0, _VOLUMES * sizeof(ff_diskio_impl_t*)); + } + + if (s_impls[pdrv]) { + ff_diskio_impl_t* im = s_impls[pdrv]; + s_impls[pdrv] = NULL; + free(im); + } + + if (!discio_impl) { + return; + } + + ff_diskio_impl_t * impl = (ff_diskio_impl_t *)malloc(sizeof(ff_diskio_impl_t)); + assert(impl != NULL); + memcpy(impl, discio_impl, sizeof(ff_diskio_impl_t)); + s_impls[pdrv] = impl; } DSTATUS ff_disk_initialize (BYTE pdrv) { - return s_impls[pdrv].init(pdrv); + return s_impls[pdrv]->init(pdrv); } DSTATUS ff_disk_status (BYTE pdrv) { - return s_impls[pdrv].status(pdrv); + return s_impls[pdrv]->status(pdrv); } DRESULT ff_disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count) { - return s_impls[pdrv].read(pdrv, buff, sector, count); + return s_impls[pdrv]->read(pdrv, buff, sector, count); } DRESULT ff_disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count) { - return s_impls[pdrv].write(pdrv, buff, sector, count); + return s_impls[pdrv]->write(pdrv, buff, sector, count); } DRESULT ff_disk_ioctl (BYTE pdrv, BYTE cmd, void* buff) { - return s_impls[pdrv].ioctl(pdrv, cmd, buff); + return s_impls[pdrv]->ioctl(pdrv, cmd, buff); } DWORD get_fattime(void) diff --git a/components/fatfs/src/diskio.h b/components/fatfs/src/diskio.h index 7c224809f9..64d5d5b8df 100644 --- a/components/fatfs/src/diskio.h +++ b/components/fatfs/src/diskio.h @@ -58,16 +58,19 @@ typedef struct { } ff_diskio_impl_t; /** - * Register diskio driver for given drive number. + * Register or unregister diskio driver for given drive number. * * When FATFS library calls one of disk_xxx functions for driver number pdrv, * corresponding function in discio_impl for given pdrv will be called. * * @param pdrv drive number - * @param discio_impl pointer to ff_diskio_impl_t structure with diskio functions + * @param discio_impl pointer to ff_diskio_impl_t structure with diskio functions + * or NULL to unregister and free previously registered drive */ void ff_diskio_register(BYTE pdrv, const ff_diskio_impl_t* discio_impl); +#define ff_diskio_unregister(pdrv_) ff_diskio_register(pdrv_, NULL) + /** * Register SD/MMC diskio driver * @@ -76,6 +79,16 @@ void ff_diskio_register(BYTE pdrv, const ff_diskio_impl_t* discio_impl); */ void ff_diskio_register_sdmmc(BYTE pdrv, sdmmc_card_t* card); +/** + * Get next available drive number + * + * @param out_pdrv pointer to the byte to set if successful + * + * @return ESP_OK on success + * ESP_ERR_NOT_FOUND if all drives are attached + */ +esp_err_t ff_diskio_get_drive(BYTE* out_pdrv); + /* Disk Status Bits (DSTATUS) */ #define STA_NOINIT 0x01 /* Drive not initialized */ diff --git a/components/fatfs/src/esp_vfs_fat.h b/components/fatfs/src/esp_vfs_fat.h index 087d6cf77e..4e27dce699 100644 --- a/components/fatfs/src/esp_vfs_fat.h +++ b/components/fatfs/src/esp_vfs_fat.h @@ -51,11 +51,31 @@ esp_err_t esp_vfs_fat_register(const char* base_path, const char* fat_drive, * @note FATFS structure returned by esp_vfs_fat_register is destroyed after * this call. Make sure to call f_mount function to unmount it before * calling esp_vfs_fat_unregister. + * This function is left for compatibility and will be changed in + * future versions to accept base_path and replace the method below * @return * - ESP_OK on success * - ESP_ERR_INVALID_STATE if FATFS is not registered in VFS */ -esp_err_t esp_vfs_fat_unregister(); +esp_err_t esp_vfs_fat_unregister() __attribute__((deprecated)); + +/** + * @brief Un-register FATFS from VFS + * + * @note FATFS structure returned by esp_vfs_fat_register is destroyed after + * this call. Make sure to call f_mount function to unmount it before + * calling esp_vfs_fat_unregister_ctx. + * Difference between this function and the one above is that this one + * will release the correct drive, while the one above will release + * the last registered one + * + * @param base_path path prefix where FATFS is registered. This is the same + * used when esp_vfs_fat_register was called + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if FATFS is not registered in VFS + */ +esp_err_t esp_vfs_fat_unregister_path(const char* base_path); /** * @brief Configuration arguments for esp_vfs_fat_sdmmc_mount function diff --git a/components/fatfs/src/vfs_fat.c b/components/fatfs/src/vfs_fat.c index 73bed5eaa1..b9f5e40aaf 100644 --- a/components/fatfs/src/vfs_fat.c +++ b/components/fatfs/src/vfs_fat.c @@ -28,6 +28,7 @@ typedef struct { char fat_drive[8]; + char base_path[ESP_VFS_PATH_MAX]; size_t max_files; FATFS fs; FIL files[0]; @@ -42,7 +43,6 @@ typedef struct { struct dirent cur_dirent; } vfs_fat_dir_t; - static const char* TAG = "vfs_fat"; static size_t vfs_fat_write(void* p, int fd, const void * data, size_t size); @@ -64,15 +64,42 @@ static int vfs_fat_closedir(void* ctx, DIR* pdir); static int vfs_fat_mkdir(void* ctx, const char* name, mode_t mode); static int vfs_fat_rmdir(void* ctx, const char* name); - -static char s_base_path[ESP_VFS_PATH_MAX]; +static vfs_fat_ctx_t* s_fat_ctxs[_VOLUMES] = { NULL, NULL }; +//backwards-compatibility with esp_vfs_fat_unregister() static vfs_fat_ctx_t* s_fat_ctx = NULL; +static size_t find_context_index_by_path(const char* base_path) +{ + for(size_t i=0; i<_VOLUMES; i++) { + if (s_fat_ctxs[i] && !strcmp(s_fat_ctxs[i]->base_path, base_path)) { + return i; + } + } + return _VOLUMES; +} + +static size_t find_unused_context_index() +{ + for(size_t i=0; i<_VOLUMES; i++) { + if (!s_fat_ctxs[i]) { + return i; + } + } + return _VOLUMES; +} + esp_err_t esp_vfs_fat_register(const char* base_path, const char* fat_drive, size_t max_files, FATFS** out_fs) { - if (s_fat_ctx) { + size_t ctx = find_context_index_by_path(base_path); + if (ctx < _VOLUMES) { return ESP_ERR_INVALID_STATE; } + + ctx = find_unused_context_index(); + if (ctx == _VOLUMES) { + return ESP_ERR_NO_MEM; + } + const esp_vfs_t vfs = { .flags = ESP_VFS_FLAG_CONTEXT_PTR, .write_p = &vfs_fat_write, @@ -95,22 +122,45 @@ esp_err_t esp_vfs_fat_register(const char* base_path, const char* fat_drive, siz .rmdir_p = &vfs_fat_rmdir }; size_t ctx_size = sizeof(vfs_fat_ctx_t) + max_files * sizeof(FIL); - s_fat_ctx = (vfs_fat_ctx_t*) calloc(1, ctx_size); - if (s_fat_ctx == NULL) { + vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) calloc(1, ctx_size); + if (fat_ctx == NULL) { return ESP_ERR_NO_MEM; } - s_fat_ctx->max_files = max_files; - strncpy(s_fat_ctx->fat_drive, fat_drive, sizeof(s_fat_ctx->fat_drive) - 1); - *out_fs = &s_fat_ctx->fs; - esp_err_t err = esp_vfs_register(base_path, &vfs, s_fat_ctx); + fat_ctx->max_files = max_files; + strlcpy(fat_ctx->fat_drive, fat_drive, sizeof(fat_ctx->fat_drive) - 1); + strlcpy(fat_ctx->base_path, base_path, sizeof(fat_ctx->base_path) - 1); + + esp_err_t err = esp_vfs_register(base_path, &vfs, fat_ctx); if (err != ESP_OK) { - free(s_fat_ctx); - s_fat_ctx = NULL; + free(fat_ctx); return err; } - _lock_init(&s_fat_ctx->lock); - strncpy(s_base_path, base_path, sizeof(s_base_path) - 1); - s_base_path[sizeof(s_base_path) - 1] = 0; + + _lock_init(&fat_ctx->lock); + s_fat_ctxs[ctx] = fat_ctx; + + //compatibility + s_fat_ctx = fat_ctx; + + *out_fs = &fat_ctx->fs; + + return ESP_OK; +} + +esp_err_t esp_vfs_fat_unregister_path(const char* base_path) +{ + size_t ctx = find_context_index_by_path(base_path); + if (ctx == _VOLUMES) { + return ESP_ERR_INVALID_STATE; + } + vfs_fat_ctx_t* fat_ctx = s_fat_ctxs[ctx]; + esp_err_t err = esp_vfs_unregister(fat_ctx->base_path); + if (err != ESP_OK) { + return err; + } + _lock_close(&fat_ctx->lock); + free(fat_ctx); + s_fat_ctxs[ctx] = NULL; return ESP_OK; } @@ -119,12 +169,10 @@ esp_err_t esp_vfs_fat_unregister() if (s_fat_ctx == NULL) { return ESP_ERR_INVALID_STATE; } - esp_err_t err = esp_vfs_unregister(s_base_path); + esp_err_t err = esp_vfs_fat_unregister_path(s_fat_ctx->base_path); if (err != ESP_OK) { return err; } - _lock_close(&s_fat_ctx->lock); - free(s_fat_ctx); s_fat_ctx = NULL; return ESP_OK; } @@ -197,11 +245,23 @@ static void file_cleanup(vfs_fat_ctx_t* ctx, int fd) memset(&ctx->files[fd], 0, sizeof(FIL)); } +static void prepend_drive_to_path(void * ctx, const char * path, const char * path2){ + static char buf[FILENAME_MAX+3]; + static char buf2[FILENAME_MAX+3]; + sprintf(buf, "%s%s", ((vfs_fat_ctx_t*)ctx)->fat_drive, path); + path = (const char *)buf; + if(path2){ + sprintf(buf2, "%s%s", ((vfs_fat_ctx_t*)ctx)->fat_drive, path2); + path2 = (const char *)buf; + } +} + static int vfs_fat_open(void* ctx, const char * path, int flags, int mode) { + prepend_drive_to_path(ctx, path, NULL); ESP_LOGV(TAG, "%s: path=\"%s\", flags=%x, mode=%x", __func__, path, flags, mode); vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx; - _lock_acquire(&s_fat_ctx->lock); + _lock_acquire(&fat_ctx->lock); int fd = get_next_fd(fat_ctx); if (fd < 0) { ESP_LOGE(TAG, "open: no free file descriptors"); @@ -218,7 +278,7 @@ static int vfs_fat_open(void* ctx, const char * path, int flags, int mode) goto out; } out: - _lock_release(&s_fat_ctx->lock); + _lock_release(&fat_ctx->lock); return fd; } @@ -257,7 +317,7 @@ static ssize_t vfs_fat_read(void* ctx, int fd, void * dst, size_t size) static int vfs_fat_close(void* ctx, int fd) { vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx; - _lock_acquire(&s_fat_ctx->lock); + _lock_acquire(&fat_ctx->lock); FIL* file = &fat_ctx->files[fd]; FRESULT res = f_close(file); file_cleanup(fat_ctx, fd); @@ -267,7 +327,7 @@ static int vfs_fat_close(void* ctx, int fd) errno = fresult_to_errno(res); rc = -1; } - _lock_release(&s_fat_ctx->lock); + _lock_release(&fat_ctx->lock); return rc; } @@ -308,6 +368,7 @@ static int vfs_fat_fstat(void* ctx, int fd, struct stat * st) static int vfs_fat_stat(void* ctx, const char * path, struct stat * st) { + prepend_drive_to_path(ctx, path, NULL); FILINFO info; FRESULT res = f_stat(path, &info); if (res != FR_OK) { @@ -337,6 +398,7 @@ static int vfs_fat_stat(void* ctx, const char * path, struct stat * st) static int vfs_fat_unlink(void* ctx, const char *path) { + prepend_drive_to_path(ctx, path, NULL); FRESULT res = f_unlink(path); if (res != FR_OK) { ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); @@ -348,6 +410,7 @@ static int vfs_fat_unlink(void* ctx, const char *path) static int vfs_fat_link(void* ctx, const char* n1, const char* n2) { + prepend_drive_to_path(ctx, n1, n2); const size_t copy_buf_size = 4096; void* buf = malloc(copy_buf_size); if (buf == NULL) { @@ -402,6 +465,7 @@ fail1: static int vfs_fat_rename(void* ctx, const char *src, const char *dst) { + prepend_drive_to_path(ctx, src, dst); FRESULT res = f_rename(src, dst); if (res != FR_OK) { ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); @@ -413,6 +477,7 @@ static int vfs_fat_rename(void* ctx, const char *src, const char *dst) static DIR* vfs_fat_opendir(void* ctx, const char* name) { + prepend_drive_to_path(ctx, name, NULL); vfs_fat_dir_t* fat_dir = calloc(1, sizeof(vfs_fat_dir_t)); if (!fat_dir) { errno = ENOMEM; @@ -517,6 +582,7 @@ static void vfs_fat_seekdir(void* ctx, DIR* pdir, long offset) static int vfs_fat_mkdir(void* ctx, const char* name, mode_t mode) { (void) mode; + prepend_drive_to_path(ctx, name, NULL); FRESULT res = f_mkdir(name); if (res != FR_OK) { ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); @@ -528,6 +594,7 @@ static int vfs_fat_mkdir(void* ctx, const char* name, mode_t mode) static int vfs_fat_rmdir(void* ctx, const char* name) { + prepend_drive_to_path(ctx, name, NULL); FRESULT res = f_unlink(name); if (res != FR_OK) { ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); diff --git a/components/fatfs/src/vfs_fat_sdmmc.c b/components/fatfs/src/vfs_fat_sdmmc.c index e5956dd7b2..a9e5362dc5 100644 --- a/components/fatfs/src/vfs_fat_sdmmc.c +++ b/components/fatfs/src/vfs_fat_sdmmc.c @@ -13,6 +13,7 @@ // limitations under the License. #include +#include #include "esp_log.h" #include "esp_vfs.h" #include "esp_vfs_fat.h" @@ -22,6 +23,8 @@ static const char* TAG = "vfs_fat_sdmmc"; static sdmmc_card_t* s_card = NULL; +static uint8_t s_pdrv = 0; +static char * s_base_path = NULL; esp_err_t esp_vfs_fat_sdmmc_mount(const char* base_path, const sdmmc_host_t* host_config, @@ -35,18 +38,37 @@ esp_err_t esp_vfs_fat_sdmmc_mount(const char* base_path, if (s_card != NULL) { return ESP_ERR_INVALID_STATE; } + + // connect SDMMC driver to FATFS + BYTE pdrv = 0xFF; + if (ff_diskio_get_drive(&pdrv) != ESP_OK || pdrv == 0xFF) { + ESP_LOGD(TAG, "the maximum count of volumes is already mounted"); + return ESP_ERR_NO_MEM; + } + + s_base_path = strdup(base_path); + if(!s_base_path){ + ESP_LOGD(TAG, "could not copy base_path"); + return ESP_ERR_NO_MEM; + } + // enable SDMMC sdmmc_host_init(); // enable card slot - sdmmc_host_init_slot(host_config->slot, slot_config); + esp_err_t err = sdmmc_host_init_slot(host_config->slot, slot_config); + if (err != ESP_OK) { + return err; + } + s_card = malloc(sizeof(sdmmc_card_t)); if (s_card == NULL) { - return ESP_ERR_NO_MEM; + err = ESP_ERR_NO_MEM; + goto fail; } // probe and initialize card - esp_err_t err = sdmmc_card_init(host_config, s_card); + err = sdmmc_card_init(host_config, s_card); if (err != ESP_OK) { ESP_LOGD(TAG, "sdmmc_card_init failed 0x(%x)", err); goto fail; @@ -55,12 +77,13 @@ esp_err_t esp_vfs_fat_sdmmc_mount(const char* base_path, *out_card = s_card; } - // connect SDMMC driver to FATFS - ff_diskio_register_sdmmc(0, s_card); + ff_diskio_register_sdmmc(pdrv, s_card); + s_pdrv = pdrv; + char drv[3] = {(char)('0' + pdrv), ':', 0}; // connect FATFS to VFS FATFS* fs; - err = esp_vfs_fat_register(base_path, "", mount_config->max_files, &fs); + err = esp_vfs_fat_register(base_path, drv, mount_config->max_files, &fs); if (err == ESP_ERR_INVALID_STATE) { // it's okay, already registered with VFS } else if (err != ESP_OK) { @@ -69,7 +92,7 @@ esp_err_t esp_vfs_fat_sdmmc_mount(const char* base_path, } // Try to mount partition - FRESULT res = f_mount(fs, "", 1); + FRESULT res = f_mount(fs, drv, 1); if (res != FR_OK) { err = ESP_FAIL; ESP_LOGW(TAG, "failed to mount card (%d)", res); @@ -79,7 +102,7 @@ esp_err_t esp_vfs_fat_sdmmc_mount(const char* base_path, ESP_LOGW(TAG, "partitioning card"); DWORD plist[] = {100, 0, 0, 0}; workbuf = malloc(workbuf_size); - res = f_fdisk(0, plist, workbuf); + res = f_fdisk(s_pdrv, plist, workbuf); if (res != FR_OK) { err = ESP_FAIL; ESP_LOGD(TAG, "f_fdisk failed (%d)", res); @@ -94,7 +117,7 @@ esp_err_t esp_vfs_fat_sdmmc_mount(const char* base_path, } free(workbuf); ESP_LOGW(TAG, "mounting again"); - res = f_mount(fs, "", 0); + res = f_mount(fs, drv, 0); if (res != FR_OK) { err = ESP_FAIL; ESP_LOGD(TAG, "f_mount failed after formatting (%d)", res); @@ -104,8 +127,10 @@ esp_err_t esp_vfs_fat_sdmmc_mount(const char* base_path, return ESP_OK; fail: + sdmmc_host_deinit(); free(workbuf); - esp_vfs_unregister(base_path); + esp_vfs_fat_unregister_path(base_path); + ff_diskio_unregister(pdrv); free(s_card); s_card = NULL; return err; @@ -117,10 +142,15 @@ esp_err_t esp_vfs_fat_sdmmc_unmount() return ESP_ERR_INVALID_STATE; } // unmount - f_mount(0, "", 0); + char drv[3] = {(char)('0' + s_pdrv), ':', 0}; + f_mount(0, drv, 0); // release SD driver + ff_diskio_unregister(s_pdrv); free(s_card); s_card = NULL; sdmmc_host_deinit(); - return esp_vfs_fat_unregister(); + esp_err_t err = esp_vfs_fat_unregister_path(s_base_path); + free(s_base_path); + s_base_path = NULL; + return err; } diff --git a/components/fatfs/test/test_fatfs.c b/components/fatfs/test/test_fatfs.c index 4f6482740c..d518652bc4 100644 --- a/components/fatfs/test/test_fatfs.c +++ b/components/fatfs/test/test_fatfs.c @@ -52,6 +52,25 @@ static void create_file_with_text(const char* name, const char* text) TEST_ASSERT_EQUAL(0, fclose(f)); } +TEST_CASE("Mount fails cleanly without card inserted", "[fatfs][ignore]") +{ + HEAP_SIZE_CAPTURE(); + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + esp_vfs_fat_sdmmc_mount_config_t mount_config = { + .format_if_mount_failed = false, + .max_files = 5 + }; + + for (int i = 0; i < 3; ++i) { + printf("Initializing card, attempt %d ", i); + esp_err_t err = esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, NULL); + printf(" err=%d\n", err); + TEST_ESP_ERR(ESP_FAIL, err); + } + HEAP_SIZE_CHECK(0); +} + TEST_CASE("can create and write file on sd card", "[fatfs][ignore]") { HEAP_SIZE_CAPTURE(); diff --git a/components/freertos/Kconfig b/components/freertos/Kconfig index ee2b289ef8..b4bcfc3fe1 100644 --- a/components/freertos/Kconfig +++ b/components/freertos/Kconfig @@ -76,17 +76,38 @@ config FREERTOS_CHECK_STACKOVERFLOW_CANARY (configCHECK_FOR_STACK_OVERFLOW=2) endchoice +config FREERTOS_WATCHPOINT_END_OF_STACK + bool "Set a debug watchpoint as a stack overflow check" + default n + help + FreeRTOS can check if a stack has overflown its bounds by checking either the value of + the stack pointer or by checking the integrity of canary bytes. (See FREERTOS_CHECK_STACKOVERFLOW + for more information.) These checks only happen on a context switch, and the situation that caused + the stack overflow may already be long gone by then. This option will use the debug memory + watchpoint 1 (the second one) to allow breaking into the debugger (or panic'ing) as soon as any + of the last 32 bytes on the stack of a task are overwritten. The side effect is that using gdb, you + effectively only have one watchpoint; the 2nd one is overwritten as soon as a task switch happens. + + This check only triggers if the stack overflow writes within 4 bytes of the end of the stack, rather than + overshooting further, so it is worth combining this approach with one of the other stack overflow check methods. + + When this watchpoint is hit, gdb will stop with a SIGTRAP message. When no OCD is attached, esp-idf + will panic on an unhandled debug exception. + config FREERTOS_THREAD_LOCAL_STORAGE_POINTERS - int "Amount of thread local storage pointers" - range 0 256 if !WIFI_ENABLED - range 1 256 if WIFI_ENABLED + int "Number of thread local storage pointers" + range 0 256 if !(WIFI_ENABLED || ETHERNET) + range 1 256 if WIFI_ENABLED || ETHERNET default 1 help FreeRTOS has the ability to store per-thread pointers in the task - control block. This controls the amount of pointers available; - 0 turns off this functionality. + control block. This controls the number of pointers available. - If using the WiFi stack, this value must be at least 1. + Value 0 turns off this functionality. + + If using the LWIP TCP/IP stack (with WiFi or Ethernet), this value must be at least 1. See the + LWIP_THREAD_LOCAL_STORAGE_INDEX config item in LWIP configuration to determine which thread-local-storage + pointer is reserved for LWIP. choice FREERTOS_ASSERT prompt "FreeRTOS assertions" @@ -164,6 +185,17 @@ config FREERTOS_LEGACY_TICK_HOOK endif #FREERTOS_LEGACY_HOOKS +config FREERTOS_MAX_TASK_NAME_LEN + int "Maximum task name length" + range 1 256 + default 16 + help + Changes the maximum task name length. Each task allocated will + include this many bytes for a task name. Using a shorter value + saves a small amount of RAM, a longer value allows more complex + names. + + For most uses, the default of 16 is OK. menuconfig FREERTOS_DEBUG_INTERNALS bool "Debug FreeRTOS internals" @@ -192,22 +224,6 @@ config FREERTOS_PORTMUX_DEBUG_RECURSIVE portMUX usage. endif #FREERTOS_UNICORE - -config FREERTOS_WATCHPOINT_END_OF_STACK - bool "Set a debug watchpoint at the end of each stack" - default n - help - FreeRTOS can check if a stack has overflown its bounds by checking either the value of - the stack pointer or by checking the integrity of canary bytes. (See FREERTOS_CHECK_STACKOVERFLOW - for more information.) These checks only happen on a context switch, and the situation that caused - the stack overflow may already be long gone by then. This option will use the debug memory - watchpoint 1 (the second one) to allow breaking into the debugger (or panic'ing) as soon as any - of the last 32 bytes on the stack of a task are overwritten. The side effect is that using gdb, you - effectively only have one watchpoint; the 2nd one is overwritten as soon as a task switch happens. - - When this watchpoint is hit, gdb will stop with a SIGTRAP message. When no OCD is attached, esp-idf - will panic on an unhandled debug exception. - endif # FREERTOS_DEBUG_INTERNALS endmenu diff --git a/components/freertos/event_groups.c b/components/freertos/event_groups.c index 902a4ad72a..69903817f4 100644 --- a/components/freertos/event_groups.c +++ b/components/freertos/event_groups.c @@ -119,13 +119,10 @@ typedef struct xEventGroupDefinition UBaseType_t uxEventGroupNumber; #endif + portMUX_TYPE eventGroupMux; } EventGroup_t; -/* Again: one mux for all events. Maybe this can be made more granular. ToDo: look into that. -JD */ -static portMUX_TYPE xEventGroupMux = portMUX_INITIALIZER_UNLOCKED; - - /*-----------------------------------------------------------*/ /* @@ -156,6 +153,8 @@ EventGroup_t *pxEventBits; traceEVENT_GROUP_CREATE_FAILED(); } + vPortCPUInitializeMutex(&pxEventBits->eventGroupMux); + return ( EventGroupHandle_t ) pxEventBits; } /*-----------------------------------------------------------*/ @@ -176,6 +175,7 @@ BaseType_t xTimeoutOccurred = pdFALSE; #endif vTaskSuspendAll(); + taskENTER_CRITICAL(&pxEventBits->eventGroupMux); { uxOriginalBitValue = pxEventBits->uxEventBits; @@ -217,6 +217,7 @@ BaseType_t xTimeoutOccurred = pdFALSE; } } } + taskEXIT_CRITICAL( &pxEventBits->eventGroupMux ); xAlreadyYielded = xTaskResumeAll(); if( xTicksToWait != ( TickType_t ) 0 ) @@ -239,7 +240,7 @@ BaseType_t xTimeoutOccurred = pdFALSE; if( ( uxReturn & eventUNBLOCKED_DUE_TO_BIT_SET ) == ( EventBits_t ) 0 ) { /* The task timed out, just return the current event bit value. */ - taskENTER_CRITICAL( &xEventGroupMux ); + taskENTER_CRITICAL( &pxEventBits->eventGroupMux ); { uxReturn = pxEventBits->uxEventBits; @@ -256,7 +257,7 @@ BaseType_t xTimeoutOccurred = pdFALSE; mtCOVERAGE_TEST_MARKER(); } } - taskEXIT_CRITICAL( &xEventGroupMux ); + taskEXIT_CRITICAL( &pxEventBits->eventGroupMux ); xTimeoutOccurred = pdTRUE; } @@ -295,6 +296,7 @@ BaseType_t xTimeoutOccurred = pdFALSE; #endif vTaskSuspendAll(); + taskENTER_CRITICAL( &pxEventBits->eventGroupMux ); { const EventBits_t uxCurrentEventBits = pxEventBits->uxEventBits; @@ -361,6 +363,7 @@ BaseType_t xTimeoutOccurred = pdFALSE; traceEVENT_GROUP_WAIT_BITS_BLOCK( xEventGroup, uxBitsToWaitFor ); } } + taskEXIT_CRITICAL( &pxEventBits->eventGroupMux ); xAlreadyYielded = xTaskResumeAll(); if( xTicksToWait != ( TickType_t ) 0 ) @@ -382,7 +385,7 @@ BaseType_t xTimeoutOccurred = pdFALSE; if( ( uxReturn & eventUNBLOCKED_DUE_TO_BIT_SET ) == ( EventBits_t ) 0 ) { - taskENTER_CRITICAL( &xEventGroupMux ); + taskENTER_CRITICAL( &pxEventBits->eventGroupMux ); { /* The task timed out, just return the current event bit value. */ uxReturn = pxEventBits->uxEventBits; @@ -405,7 +408,7 @@ BaseType_t xTimeoutOccurred = pdFALSE; mtCOVERAGE_TEST_MARKER(); } } - taskEXIT_CRITICAL( &xEventGroupMux ); + taskEXIT_CRITICAL( &pxEventBits->eventGroupMux ); /* Prevent compiler warnings when trace macros are not used. */ xTimeoutOccurred = pdFALSE; @@ -434,7 +437,7 @@ EventBits_t uxReturn; configASSERT( xEventGroup ); configASSERT( ( uxBitsToClear & eventEVENT_BITS_CONTROL_BYTES ) == 0 ); - taskENTER_CRITICAL( &xEventGroupMux ); + taskENTER_CRITICAL( &pxEventBits->eventGroupMux ); { traceEVENT_GROUP_CLEAR_BITS( xEventGroup, uxBitsToClear ); @@ -445,7 +448,7 @@ EventBits_t uxReturn; /* Clear the bits. */ pxEventBits->uxEventBits &= ~uxBitsToClear; } - taskEXIT_CRITICAL( &xEventGroupMux ); + taskEXIT_CRITICAL( &pxEventBits->eventGroupMux ); return uxReturn; } @@ -498,7 +501,9 @@ BaseType_t xMatchFound = pdFALSE; pxList = &( pxEventBits->xTasksWaitingForBits ); pxListEnd = listGET_END_MARKER( pxList ); /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */ + vTaskSuspendAll(); + taskENTER_CRITICAL(&pxEventBits->eventGroupMux); { traceEVENT_GROUP_SET_BITS( xEventGroup, uxBitsToSet ); @@ -570,6 +575,7 @@ BaseType_t xMatchFound = pdFALSE; bit was set in the control word. */ pxEventBits->uxEventBits &= ~uxBitsToClear; } + taskEXIT_CRITICAL(&pxEventBits->eventGroupMux); ( void ) xTaskResumeAll(); return pxEventBits->uxEventBits; @@ -578,10 +584,11 @@ BaseType_t xMatchFound = pdFALSE; void vEventGroupDelete( EventGroupHandle_t xEventGroup ) { -EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup; -const List_t *pxTasksWaitingForBits = &( pxEventBits->xTasksWaitingForBits ); + EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup; + const List_t *pxTasksWaitingForBits = &( pxEventBits->xTasksWaitingForBits ); vTaskSuspendAll(); + taskENTER_CRITICAL( &pxEventBits->eventGroupMux ); { traceEVENT_GROUP_DELETE( xEventGroup ); @@ -593,6 +600,7 @@ const List_t *pxTasksWaitingForBits = &( pxEventBits->xTasksWaitingForBits ); ( void ) xTaskRemoveFromUnorderedEventList( pxTasksWaitingForBits->xListEnd.pxNext, eventUNBLOCKED_DUE_TO_BIT_SET ); } + taskEXIT_CRITICAL( &pxEventBits->eventGroupMux ); vPortFree( pxEventBits ); } ( void ) xTaskResumeAll(); diff --git a/components/freertos/include/freertos/FreeRTOSConfig.h b/components/freertos/include/freertos/FreeRTOSConfig.h index 9deb9f4b5e..4f40330398 100644 --- a/components/freertos/include/freertos/FreeRTOSConfig.h +++ b/components/freertos/include/freertos/FreeRTOSConfig.h @@ -187,7 +187,7 @@ #define configAPPLICATION_ALLOCATED_HEAP 1 #define configTOTAL_HEAP_SIZE (&_heap_end - &_heap_start)//( ( size_t ) (64 * 1024) ) -#define configMAX_TASK_NAME_LEN ( 16 ) +#define configMAX_TASK_NAME_LEN ( CONFIG_FREERTOS_MAX_TASK_NAME_LEN ) #define configUSE_TRACE_FACILITY 0 /* Used by vTaskList in main.c */ #define configUSE_STATS_FORMATTING_FUNCTIONS 0 /* Used by vTaskList in main.c */ #define configUSE_TRACE_FACILITY_2 0 /* Provided by Xtensa port patch */ diff --git a/components/freertos/include/freertos/portable.h b/components/freertos/include/freertos/portable.h index 0c10ac36eb..e46ec17fb6 100644 --- a/components/freertos/include/freertos/portable.h +++ b/components/freertos/include/freertos/portable.h @@ -194,6 +194,12 @@ void vPortYieldOtherCore( BaseType_t coreid) PRIVILEGED_FUNCTION; */ void vPortSetStackWatchpoint( void* pxStackStart ); +/* + * Returns true if the current core is in ISR context; low prio ISR, med prio ISR or timer tick ISR. High prio ISRs + * aren't detected here, but they normally cannot call C code, so that should not be an issue anyway. + */ +BaseType_t xPortInIsrContext(); + /* * The structures and methods of manipulating the MPU are contained within the * port layer. diff --git a/components/freertos/port.c b/components/freertos/port.c index 3c26edfabf..756e14295d 100644 --- a/components/freertos/port.c +++ b/components/freertos/port.c @@ -115,7 +115,7 @@ extern void _xt_coproc_init(void); /*-----------------------------------------------------------*/ unsigned port_xSchedulerRunning[portNUM_PROCESSORS] = {0}; // Duplicate of inaccessible xSchedulerRunning; needed at startup to avoid counting nesting -unsigned port_interruptNesting[portNUM_PROCESSORS] = {0}; // Interrupt nesting level +unsigned port_interruptNesting[portNUM_PROCESSORS] = {0}; // Interrupt nesting level. Increased/decreased in portasm.c, _frxt_int_enter/_frxt_int_exit /*-----------------------------------------------------------*/ @@ -256,9 +256,24 @@ void vPortStoreTaskMPUSettings( xMPU_SETTINGS *xMPUSettings, const struct xMEMOR #endif +/* + * Returns true if the current core is in ISR context; low prio ISR, med prio ISR or timer tick ISR. High prio ISRs + * aren't detected here, but they normally cannot call C code, so that should not be an issue anyway. + */ +BaseType_t xPortInIsrContext() +{ + unsigned int irqStatus; + BaseType_t ret; + irqStatus=portENTER_CRITICAL_NESTED(); + ret=(port_interruptNesting[xPortGetCoreID()] != 0); + portEXIT_CRITICAL_NESTED(irqStatus); + return ret; +} + + void vPortAssertIfInISR() { - configASSERT(port_interruptNesting[xPortGetCoreID()]==0) + configASSERT(xPortInIsrContext()); } /* diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index 0804bb3eb5..b37e592549 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -1046,25 +1046,59 @@ UBaseType_t x; } /*-----------------------------------------------------------*/ -static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode, const BaseType_t xCoreID ) +static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode, BaseType_t xCoreID ) { - TCB_t *curTCB; - BaseType_t i; + TCB_t *curTCB, *tcb0, *tcb1; /* Ensure interrupts don't access the task lists while the lists are being updated. */ taskENTER_CRITICAL(&xTaskQueueMutex); { uxCurrentNumberOfTasks++; - //If the task has no affinity and nothing is scheduled on this core, just throw it this core. - //If it has affinity, throw it on the core that needs it if nothing is already scheduled there. - BaseType_t xMyCore = xCoreID; - if ( xMyCore == tskNO_AFFINITY) xMyCore = xPortGetCoreID(); - if( pxCurrentTCB[ xMyCore ] == NULL ) + + // Determine which core this task starts on + if ( xCoreID == tskNO_AFFINITY ) + { + if ( portNUM_PROCESSORS == 1 ) + { + xCoreID = 0; + } + else + { + // if the task has no affinity, put it on either core if nothing is currently scheduled there. Failing that, + // put it on the core where it will preempt the lowest priority running task. If neither of these are true, + // queue it on the currently running core. + tcb0 = pxCurrentTCB[0]; + tcb1 = pxCurrentTCB[1]; + if ( tcb0 == NULL ) + { + xCoreID = 0; + } + else if ( tcb1 == NULL ) + { + xCoreID = 1; + } + else if ( tcb0->uxPriority < pxNewTCB->uxPriority && tcb0->uxPriority < tcb1->uxPriority ) + { + xCoreID = 0; + } + else if ( tcb1->uxPriority < pxNewTCB->uxPriority ) + { + xCoreID = 1; + } + else + { + xCoreID = xPortGetCoreID(); // Both CPU have higher priority tasks running on them, so this won't run yet + } + } + } + + // If nothing is running on this core, put the new task there now + if( pxCurrentTCB[ xCoreID ] == NULL ) { /* There are no other tasks, or all the other tasks are in the suspended state - make this the current task. */ - pxCurrentTCB[ xMyCore ] = pxNewTCB; + pxCurrentTCB[ xCoreID ] = pxNewTCB; if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 ) { @@ -1090,19 +1124,11 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode so far. */ if( xSchedulerRunning == pdFALSE ) { - /* Scheduler isn't running yet. We need to determine on which CPU to run this task. */ - for ( i=0; iuxPriority <= pxNewTCB->uxPriority ) { - /* Can we schedule this task on core 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 <= pxNewTCB->uxPriority ) - { - pxCurrentTCB[i] = pxNewTCB; - break; - } - } + pxCurrentTCB[xCoreID] = pxNewTCB; } } else @@ -1130,37 +1156,27 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode if( xSchedulerRunning != pdFALSE ) { - taskENTER_CRITICAL(&xTaskQueueMutex); - curTCB = pxCurrentTCB[ xPortGetCoreID() ]; + taskENTER_CRITICAL(&xTaskQueueMutex); + + curTCB = pxCurrentTCB[ xCoreID ]; /* 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( curTCB->uxPriority < pxNewTCB->uxPriority ) + then it should run now. + */ + if( curTCB == NULL || curTCB->uxPriority < pxNewTCB->uxPriority ) { - /* Scheduler is running. If the created task is of a higher priority than an executing task - then it should run now. - No mux here, uxPriority is mostly atomic and there's not really any harm if this check misfires. - */ - if( tskCAN_RUN_HERE( xCoreID ) && curTCB->uxPriority < pxNewTCB->uxPriority ) + if( xCoreID == xPortGetCoreID() ) { taskYIELD_IF_USING_PREEMPTION_MUX(&xTaskQueueMutex); } - else if( xCoreID != xPortGetCoreID() ) { + else { taskYIELD_OTHER_CORE(xCoreID, pxNewTCB->uxPriority); } - else - { - mtCOVERAGE_TEST_MARKER(); - } } else { mtCOVERAGE_TEST_MARKER(); } - taskEXIT_CRITICAL(&xTaskQueueMutex); + taskEXIT_CRITICAL(&xTaskQueueMutex); } else { diff --git a/components/freertos/test/test_freertos_eventgroups.c b/components/freertos/test/test_freertos_eventgroups.c index 32dee2d201..f32c20ade9 100644 --- a/components/freertos/test/test_freertos_eventgroups.c +++ b/components/freertos/test/test_freertos_eventgroups.c @@ -11,9 +11,10 @@ #define BIT_RESPONSE(TASK) (1 << (TASK+1)) #define ALL_RESPONSE_BITS (((1 << NUM_TASKS) - 1) << 1) -static const int NUM_TASKS = 4; -static const int COUNT = 4000; +static const int NUM_TASKS = 8; +static const int COUNT = 1000; static EventGroupHandle_t eg; +static SemaphoreHandle_t done_sem; static void task_event_group_call_response(void *param) { @@ -24,47 +25,51 @@ static void task_event_group_call_response(void *param) for (int i = 0; i < COUNT; i++) { /* Wait until the common "call" bit is set, starts off all tasks (clear on return) */ - while (!xEventGroupWaitBits(eg, BIT_CALL, true, false, portMAX_DELAY)) { - } + TEST_ASSERT( xEventGroupWaitBits(eg, BIT_CALL, true, false, portMAX_DELAY) ); /* Set our individual "response" bit */ xEventGroupSetBits(eg, BIT_RESPONSE(task_num)); } printf("Task %d done\n", task_num); - - /* Delay is due to not-yet-fixed bug with deleting tasks at same time */ - vTaskDelay(100 / portTICK_PERIOD_MS); + xSemaphoreGive(done_sem); vTaskDelete(NULL); } -TEST_CASE("FreeRTOS Event Groups", "[freertos][ignore]") +TEST_CASE("FreeRTOS Event Groups", "[freertos]") { eg = xEventGroupCreate(); + done_sem = xSemaphoreCreateCounting(NUM_TASKS, 0); - /* Note: task_event_group_call_response all have higher priority than us, so will block together. + /* Note: task_event_group_call_response all have higher priority than this task, so on this core + they will always preempt this task. - This is important because we need to know they'll all have blocked on BIT_CALL each time we - signal it, or they get out of sync. + This is important because we need to know all tasks have blocked on BIT_CALL each time we signal it, + or they get out of sync. */ for (int c = 0; c < NUM_TASKS; c++) { xTaskCreatePinnedToCore(task_event_group_call_response, "tsk_call_resp", 4096, (void *)c, configMAX_PRIORITIES - 1, NULL, c % portNUM_PROCESSORS); } - /* Scheduler weirdness, if we don't sleep a few ticks here then the tasks on the other CPU aren't running yet... */ - vTaskDelay(10); + + /* Tasks all start instantly, but this task will resume running at the same time as the higher priority tasks on the + other processor may still be setting up, so give a tick for them to also block on BIT_CALL... */ + vTaskDelay(1); for (int i = 0; i < COUNT; i++) { - if (i % 100 == 0) { - //printf("Call %d\n", i); - } /* signal all tasks with "CALL" bit... */ xEventGroupSetBits(eg, BIT_CALL); - while (xEventGroupWaitBits(eg, ALL_RESPONSE_BITS, true, true, portMAX_DELAY) != ALL_RESPONSE_BITS) { - } + TEST_ASSERT_EQUAL_HEX16(ALL_RESPONSE_BITS, xEventGroupWaitBits(eg, ALL_RESPONSE_BITS, true, true, 100 / portMAX_DELAY)); } -} + /* Ensure all tasks cleaned up correctly */ + for (int c = 0; c < NUM_TASKS; c++) { + TEST_ASSERT( xSemaphoreTake(done_sem, 100/portTICK_PERIOD_MS) ); + } + + vSemaphoreDelete(done_sem); + vEventGroupDelete(eg); +} #define BIT_DONE(X) (1<<(NUM_TASKS+1+X)) @@ -82,24 +87,32 @@ static void task_test_sync(void *param) } int after_done = xEventGroupSetBits(eg, BIT_DONE(task_num)); - printf("Done %d = %x\n", task_num, after_done); + printf("Done %d = 0x%08x\n", task_num, after_done); - /* Delay is due to not-yet-fixed bug with deleting tasks at same time */ - vTaskDelay(100 / portTICK_PERIOD_MS); + xSemaphoreGive(done_sem); vTaskDelete(NULL); } -TEST_CASE("FreeRTOS Event Group Sync", "[freertos][ignore]") +TEST_CASE("FreeRTOS Event Group Sync", "[freertos]") { eg = xEventGroupCreate(); + done_sem = xSemaphoreCreateCounting(NUM_TASKS, 0); for (int c = 0; c < NUM_TASKS; c++) { xTaskCreatePinnedToCore(task_test_sync, "task_test_sync", 4096, (void *)c, configMAX_PRIORITIES - 1, NULL, c % portNUM_PROCESSORS); } for (int c = 0; c < NUM_TASKS; c++) { - printf("Waiting on %d (%x)\n", c, BIT_DONE(c)); - xEventGroupWaitBits(eg, BIT_DONE(c), false, false, portMAX_DELAY); + printf("Waiting on %d (0x%08x)\n", c, BIT_DONE(c)); + TEST_ASSERT( xEventGroupWaitBits(eg, BIT_DONE(c), false, false, portMAX_DELAY) ); } + + /* Ensure all tasks cleaned up correctly */ + for (int c = 0; c < NUM_TASKS; c++) { + TEST_ASSERT( xSemaphoreTake(done_sem, 100/portTICK_PERIOD_MS) ); + } + + vSemaphoreDelete(done_sem); + vEventGroupDelete(eg); } diff --git a/components/freertos/test/test_freertos_isinisrcontext.c b/components/freertos/test/test_freertos_isinisrcontext.c new file mode 100644 index 0000000000..9c7d31c270 --- /dev/null +++ b/components/freertos/test/test_freertos_isinisrcontext.c @@ -0,0 +1,51 @@ +/* + See if xPortInIsrContext works +*/ + +#include +#include +#include "rom/ets_sys.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" +#include "freertos/xtensa_api.h" +#include "unity.h" +#include "esp_intr_alloc.h" +#include "xtensa/hal.h" + +static volatile int in_int_context, int_handled; + + +static void testint(void *arg) { + xthal_set_ccompare(1, xthal_get_ccount()+8000000000); + ets_printf("INT!\n"); + if (xPortInIsrContext()) in_int_context++; + int_handled++; +} + + +static void testthread(void *arg) { + intr_handle_t handle; + in_int_context=0; + int_handled=0; + TEST_ASSERT(!xPortInIsrContext()); + xthal_set_ccompare(2, xthal_get_ccount()+8000000); + esp_intr_alloc(ETS_INTERNAL_TIMER1_INTR_SOURCE, 0, &testint, NULL, &handle); + vTaskDelay(100 / portTICK_PERIOD_MS); + TEST_ASSERT(int_handled); + TEST_ASSERT(in_int_context); + esp_intr_free(handle); + vTaskDelete(NULL); +} + + +TEST_CASE("xPortInIsrContext test", "[freertos]") +{ + xTaskCreatePinnedToCore(testthread, "tst" , 4096, NULL, 3, NULL, 0); + vTaskDelay(150 / portTICK_PERIOD_MS); + xTaskCreatePinnedToCore(testthread, "tst" , 4096, NULL, 3, NULL, 1); + vTaskDelay(150 / portTICK_PERIOD_MS); +} + diff --git a/components/freertos/test/test_freertos_task_delete.c b/components/freertos/test/test_freertos_task_delete.c index d8cc2755ed..68a6683fcb 100644 --- a/components/freertos/test/test_freertos_task_delete.c +++ b/components/freertos/test/test_freertos_task_delete.c @@ -10,13 +10,17 @@ static void task_delete_self(void *param) { printf("Task %p running on core %d. Deleting shortly...\n", xTaskGetCurrentTaskHandle(), xPortGetCoreID()); + vTaskDelay(5); vTaskDelete(NULL); } -TEST_CASE("FreeRTOS Delete Tasks", "[freertos][ignore]") +TEST_CASE("FreeRTOS Delete Tasks", "[freertos]") { + uint32_t before_count = uxTaskGetNumberOfTasks(); + xTaskCreatePinnedToCore(task_delete_self, "tsk_self_a", 4096, NULL, configMAX_PRIORITIES - 1, NULL, 0); xTaskCreatePinnedToCore(task_delete_self, "tsk_self_a", 4096, NULL, configMAX_PRIORITIES - 1, NULL, 0); + TEST_ASSERT_EQUAL(before_count + 2, uxTaskGetNumberOfTasks()); vTaskDelay(200 / portTICK_PERIOD_MS); - printf("Done?\n"); + TEST_ASSERT_EQUAL(before_count, uxTaskGetNumberOfTasks()); } diff --git a/components/freertos/test/test_malloc.c b/components/freertos/test/test_malloc.c new file mode 100644 index 0000000000..7bcc291e30 --- /dev/null +++ b/components/freertos/test/test_malloc.c @@ -0,0 +1,49 @@ +/* + Generic test for malloc/free +*/ + +#include +#include +#include "rom/ets_sys.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" +#include "freertos/xtensa_api.h" +#include "unity.h" +#include "soc/uart_reg.h" +#include "soc/dport_reg.h" +#include "soc/io_mux_reg.h" + +static int tryAllocMem() { + int **mem; + int i, noAllocated, j; + mem=malloc(sizeof(int)*1024); + if (!mem) return 0; + for (i=0; i<1024; i++) { + mem[i]=malloc(1024); + if (mem[i]==NULL) break; + for (j=0; j<1024/4; j++) mem[i][j]=(0xdeadbeef); + } + noAllocated=i; + for (i=0; idhcp->t2_timeout - dhcp->lease_used) / 2) >= (60 + DHCP_COARSE_TIMER_SECS / 2) ) + { + netif->dhcp->t1_renew_time = (netif->dhcp->t2_timeout - dhcp->lease_used) / 2; + } +#else if (((netif->dhcp->t2_timeout - dhcp->lease_used) / 2) >= ((60 + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS)) { netif->dhcp->t1_renew_time = ((netif->dhcp->t2_timeout - dhcp->lease_used) / 2); } +#endif } } @@ -576,10 +583,17 @@ dhcp_t2_timeout(struct netif *netif) DHCP_STATE_REBINDING, not DHCP_STATE_BOUND */ dhcp_rebind(netif); /* Calculate next timeout */ +#if ESP_DHCP_TIMER + if (((netif->dhcp->t0_timeout - dhcp->lease_used) / 2) >= (60 + DHCP_COARSE_TIMER_SECS / 2)) + { + netif->dhcp->t2_rebind_time = ((netif->dhcp->t0_timeout - dhcp->lease_used) / 2); + } +#else if (((netif->dhcp->t0_timeout - dhcp->lease_used) / 2) >= ((60 + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS)) { netif->dhcp->t2_rebind_time = ((netif->dhcp->t0_timeout - dhcp->lease_used) / 2); } +#endif } } @@ -1060,6 +1074,47 @@ dhcp_bind(struct netif *netif) /* reset time used of lease */ dhcp->lease_used = 0; +#if ESP_DHCP_TIMER + if (dhcp->offered_t0_lease != 0xffffffffUL) { + /* set renewal period timer */ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t0 renewal timer %"U32_F" secs\n", dhcp->offered_t0_lease)); + timeout = dhcp->offered_t0_lease; + dhcp->t0_timeout = timeout; + if (dhcp->t0_timeout == 0) { + dhcp->t0_timeout = 120; + } + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t0_lease*1000)); + } + + /* temporary DHCP lease? */ + if (dhcp->offered_t1_renew != 0xffffffffUL) { + /* set renewal period timer */ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t1 renewal timer %"U32_F" secs\n", dhcp->offered_t1_renew)); + timeout = dhcp->offered_t1_renew; + dhcp->t1_timeout = timeout; + if (dhcp->t1_timeout == 0) { + dhcp->t1_timeout = dhcp->t0_timeout>>1; + } + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t1_renew*1000)); + dhcp->t1_renew_time = dhcp->t1_timeout; + } + /* set renewal period timer */ + if (dhcp->offered_t2_rebind != 0xffffffffUL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t2 rebind timer %"U32_F" secs\n", dhcp->offered_t2_rebind)); + timeout = dhcp->offered_t2_rebind; + dhcp->t2_timeout = timeout; + if (dhcp->t2_timeout == 0) { + dhcp->t2_timeout = (dhcp->t0_timeout>>3)*7; + } + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t2_rebind*1000)); + dhcp->t2_rebind_time = dhcp->t2_timeout; + } + + /* If we have sub 1 minute lease, t2 and t1 will kick in at the same time. */ + if ((dhcp->t1_timeout >= dhcp->t2_timeout) && (dhcp->t2_timeout > 0)) { + dhcp->t1_timeout = 0; + } +#else if (dhcp->offered_t0_lease != 0xffffffffUL) { /* set renewal period timer */ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t0 renewal timer %"U32_F" secs\n", dhcp->offered_t0_lease)); @@ -1108,6 +1163,7 @@ dhcp_bind(struct netif *netif) if ((dhcp->t1_timeout >= dhcp->t2_timeout) && (dhcp->t2_timeout > 0)) { dhcp->t1_timeout = 0; } +#endif if (dhcp->subnet_mask_given) { /* copy offered network mask */ diff --git a/components/lwip/core/pbuf.c b/components/lwip/core/pbuf.c index b954817492..84dcc31036 100755 --- a/components/lwip/core/pbuf.c +++ b/components/lwip/core/pbuf.c @@ -78,11 +78,6 @@ #include -#if ESP_LWIP -#include "esp_wifi_internal.h" -#include "esp_eth.h" -#endif - #define SIZEOF_STRUCT_PBUF LWIP_MEM_ALIGN_SIZE(sizeof(struct pbuf)) /* Since the pool is created in memp, PBUF_POOL_BUFSIZE will be automatically aligned there. Therefore, PBUF_POOL_BUFSIZE_ALIGNED can be used here. */ @@ -350,10 +345,10 @@ pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type) p->ref = 1; /* set flags */ p->flags = 0; - + #if ESP_LWIP - p->user_buf = NULL; - p->user_flag = PBUF_USER_FLAG_OWNER_NULL; + p->l2_owner = NULL; + p->l2_buf = NULL; #endif LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p)); @@ -720,14 +715,13 @@ pbuf_free(struct pbuf *p) memp_free(MEMP_PBUF_POOL, p); /* is this a ROM or RAM referencing pbuf? */ } else if (type == PBUF_ROM || type == PBUF_REF) { - + #if ESP_LWIP - if (type == PBUF_REF && p->user_flag == PBUF_USER_FLAG_OWNER_WIFI ) { - esp_wifi_internal_free_rx_buffer(p->user_buf); - } - if (type == PBUF_REF && p->user_flag == PBUF_USER_FLAG_OWNER_ETH ) { - esp_eth_free_rx_buf(p->user_buf); - } + if (p->l2_owner != NULL + && p->l2_buf != NULL + && p->l2_owner->l2_buffer_free_notify != NULL) { + p->l2_owner->l2_buffer_free_notify(p->l2_buf); + } #endif memp_free(MEMP_PBUF, p); /* type == PBUF_RAM */ diff --git a/components/lwip/include/lwip/lwip/dhcp.h b/components/lwip/include/lwip/lwip/dhcp.h index c3a057ac0f..f282c8ab49 100755 --- a/components/lwip/include/lwip/lwip/dhcp.h +++ b/components/lwip/include/lwip/lwip/dhcp.h @@ -45,7 +45,7 @@ extern "C" { #endif /** period (in seconds) of the application calling dhcp_coarse_tmr() */ -#define DHCP_COARSE_TIMER_SECS 60 +#define DHCP_COARSE_TIMER_SECS 1 /** period (in milliseconds) of the application calling dhcp_coarse_tmr() */ #define DHCP_COARSE_TIMER_MSECS (DHCP_COARSE_TIMER_SECS * 1000UL) /** period (in milliseconds) of the application calling dhcp_fine_tmr() */ @@ -76,12 +76,12 @@ struct dhcp struct dhcp_msg *msg_out; /* outgoing msg */ u16_t options_out_len; /* outgoing msg options length */ u16_t request_timeout; /* #ticks with period DHCP_FINE_TIMER_SECS for request timeout */ - u16_t t1_timeout; /* #ticks with period DHCP_COARSE_TIMER_SECS for renewal time */ - u16_t t2_timeout; /* #ticks with period DHCP_COARSE_TIMER_SECS for rebind time */ - u16_t t1_renew_time; /* #ticks with period DHCP_COARSE_TIMER_SECS until next renew try */ - u16_t t2_rebind_time; /* #ticks with period DHCP_COARSE_TIMER_SECS until next rebind try */ - u16_t lease_used; /* #ticks with period DHCP_COARSE_TIMER_SECS since last received DHCP ack */ - u16_t t0_timeout; /* #ticks with period DHCP_COARSE_TIMER_SECS for lease time */ + u32_t t1_timeout; /* #ticks with period DHCP_COARSE_TIMER_SECS for renewal time */ + u32_t t2_timeout; /* #ticks with period DHCP_COARSE_TIMER_SECS for rebind time */ + u32_t t1_renew_time; /* #ticks with period DHCP_COARSE_TIMER_SECS until next renew try */ + u32_t t2_rebind_time; /* #ticks with period DHCP_COARSE_TIMER_SECS until next rebind try */ + u32_t lease_used; /* #ticks with period DHCP_COARSE_TIMER_SECS since last received DHCP ack */ + u32_t t0_timeout; /* #ticks with period DHCP_COARSE_TIMER_SECS for lease time */ ip_addr_t server_ip_addr; /* dhcp server address that offered this lease (ip_addr_t because passed to UDP) */ ip4_addr_t offered_ip_addr; ip4_addr_t offered_sn_mask; diff --git a/components/lwip/include/lwip/lwip/netif.h b/components/lwip/include/lwip/lwip/netif.h index 34e6d44897..13cbb798e0 100755 --- a/components/lwip/include/lwip/lwip/netif.h +++ b/components/lwip/include/lwip/lwip/netif.h @@ -330,6 +330,10 @@ struct netif { u16_t loop_cnt_current; #endif /* LWIP_LOOPBACK_MAX_PBUFS */ #endif /* ENABLE_LOOPBACK */ + +#if ESP_LWIP + void (*l2_buffer_free_notify)(void *user_buf); /* Allows LWIP to notify driver when a L2-supplied pbuf can be freed */ +#endif }; #if LWIP_CHECKSUM_CTRL_PER_NETIF diff --git a/components/lwip/include/lwip/lwip/pbuf.h b/components/lwip/include/lwip/lwip/pbuf.h index d84aabaf81..42146f6fc4 100755 --- a/components/lwip/include/lwip/lwip/pbuf.h +++ b/components/lwip/include/lwip/lwip/pbuf.h @@ -105,12 +105,6 @@ typedef enum { /** indicates this pbuf includes a TCP FIN flag */ #define PBUF_FLAG_TCP_FIN 0x20U -#if ESP_LWIP -#define PBUF_USER_FLAG_OWNER_NULL 0 -#define PBUF_USER_FLAG_OWNER_WIFI 1 -#define PBUF_USER_FLAG_OWNER_ETH 2 -#endif - struct pbuf { /** next pbuf in singly linked pbuf chain */ struct pbuf *next; @@ -142,10 +136,10 @@ struct pbuf { * the stack itself, or pbuf->next pointers from a chain. */ u16_t ref; - + #if ESP_LWIP - void *user_buf; - u8_t user_flag; + struct netif *l2_owner; + void *l2_buf; #endif }; diff --git a/components/lwip/include/lwip/port/lwipopts.h b/components/lwip/include/lwip/port/lwipopts.h index dc82649479..60d7ecf212 100644 --- a/components/lwip/include/lwip/port/lwipopts.h +++ b/components/lwip/include/lwip/port/lwipopts.h @@ -602,6 +602,7 @@ #define ESP_LIGHT_SLEEP 1 #define ESP_L2_TO_L3_COPY CONFIG_L2_TO_L3_COPY #define ESP_CNT_DEBUG 0 +#define ESP_DHCP_TIMER 1 #define TCP_WND_DEFAULT (4*TCP_MSS) #define TCP_SND_BUF_DEFAULT (2*TCP_MSS) diff --git a/components/lwip/port/netif/ethernetif.c b/components/lwip/port/netif/ethernetif.c index 90a5b241b0..1930c5e4b2 100644 --- a/components/lwip/port/netif/ethernetif.c +++ b/components/lwip/port/netif/ethernetif.c @@ -82,7 +82,10 @@ ethernet_low_level_init(struct netif *netif) netif->flags |= NETIF_FLAG_IGMP; #endif #endif - /* Do whatever else is needed to initialize interface. */ + +#ifndef CONFIG_EMAC_L2_TO_L3_RX_BUF_MODE + netif->l2_buffer_free_notify = esp_eth_free_rx_buf; +#endif } /** @@ -152,11 +155,12 @@ ethernetif_input(struct netif *netif, void *buffer, uint16_t len) if(buffer== NULL || netif == NULL) goto _exit; -#if CONFIG_EMAC_L2_TO_L3_RX_BUF_MODE +#ifdef CONFIG_EMAC_L2_TO_L3_RX_BUF_MODE p = pbuf_alloc(PBUF_RAW, len, PBUF_RAM); if (p == NULL) { return; } + p->l2_owner = NULL; memcpy(p->payload, buffer, len); /* full packet send to tcpip_thread to process */ @@ -171,13 +175,13 @@ if (netif->input(p, netif) != ERR_OK) { return; } p->payload = buffer; - p->user_flag = PBUF_USER_FLAG_OWNER_ETH; - p->user_buf = buffer; + p->l2_owner = netif; + p->l2_buf = buffer; /* full packet send to tcpip_thread to process */ if (netif->input(p, netif) != ERR_OK) { LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n")); - p->user_flag = PBUF_USER_FLAG_OWNER_NULL; + p->l2_owner = NULL; pbuf_free(p); } #endif diff --git a/components/lwip/port/netif/wlanif.c b/components/lwip/port/netif/wlanif.c index f9def49218..9f454230ab 100644 --- a/components/lwip/port/netif/wlanif.c +++ b/components/lwip/port/netif/wlanif.c @@ -84,7 +84,9 @@ low_level_init(struct netif *netif) #endif #endif - /* Do whatever else is needed to initialize interface. */ +#if !ESP_L2_TO_L3_COPY + netif->l2_buffer_free_notify = esp_wifi_internal_free_rx_buffer; +#endif } /** @@ -119,6 +121,7 @@ low_level_output(struct netif *netif, struct pbuf *p) LWIP_DEBUGF(PBUF_DEBUG, ("low_level_output: pbuf is a list, application may has bug")); q = pbuf_alloc(PBUF_RAW_TX, p->tot_len, PBUF_RAM); if (q != NULL) { + q->l2_owner = NULL; pbuf_copy(q, p); } else { return ERR_MEM; @@ -154,6 +157,7 @@ wlanif_input(struct netif *netif, void *buffer, u16_t len, void* eb) esp_wifi_internal_free_rx_buffer(eb); return; } + p->l2_owner = NULL; memcpy(p->payload, buffer, len); esp_wifi_internal_free_rx_buffer(eb); #else @@ -163,8 +167,8 @@ wlanif_input(struct netif *netif, void *buffer, u16_t len, void* eb) return; } p->payload = buffer; - p->user_buf = eb; - p->user_flag = PBUF_USER_FLAG_OWNER_WIFI; + p->l2_owner = netif; + p->l2_buf = eb; #endif /* full packet send to tcpip_thread to process */ diff --git a/components/mbedtls/include/mbedtls/ssl.h b/components/mbedtls/include/mbedtls/ssl.h index 82c07607f5..7e1a17c813 100644 --- a/components/mbedtls/include/mbedtls/ssl.h +++ b/components/mbedtls/include/mbedtls/ssl.h @@ -29,6 +29,7 @@ #include MBEDTLS_CONFIG_FILE #endif +#include "platform.h" #include "bignum.h" #include "ecp.h" diff --git a/components/mbedtls/port/esp_bignum.c b/components/mbedtls/port/esp_bignum.c index 5cce103ea3..1dd7ea447e 100644 --- a/components/mbedtls/port/esp_bignum.c +++ b/components/mbedtls/port/esp_bignum.c @@ -43,6 +43,9 @@ static const __attribute__((unused)) char *TAG = "bignum"; +#define ciL (sizeof(mbedtls_mpi_uint)) /* chars in limb */ +#define biL (ciL << 3) /* bits in limb */ + #if defined(CONFIG_MBEDTLS_MPI_USE_INTERRUPT) static SemaphoreHandle_t op_complete_sem; @@ -72,6 +75,7 @@ void esp_mpi_acquire_hardware( void ) { /* newlib locks lazy initialize on ESP-IDF */ _lock_acquire(&mpi_lock); + REG_SET_BIT(DPORT_PERI_CLK_EN_REG, DPORT_PERI_EN_RSA); /* also clear reset on digital signature, otherwise RSA is held in reset */ REG_CLR_BIT(DPORT_PERI_RST_EN_REG, @@ -82,6 +86,8 @@ void esp_mpi_acquire_hardware( void ) while(REG_READ(RSA_CLEAN_REG) != 1); + // Note: from enabling RSA clock to here takes about 1.3us + #ifdef CONFIG_MBEDTLS_MPI_USE_INTERRUPT rsa_isr_initialise(); #endif @@ -416,6 +422,7 @@ static int modular_multiply_finish(mbedtls_mpi *Z, const mbedtls_mpi *X, const m #if defined(MBEDTLS_MPI_MUL_MPI_ALT) /* MBEDTLS_MPI_MUL_MPI_ALT */ static int mpi_mult_mpi_failover_mod_mult(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t num_words); +static int mpi_mult_mpi_overlong(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t Y_bits, size_t words_result); /* Z = X * Y */ int mbedtls_mpi_mul_mpi( mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y ) @@ -468,12 +475,19 @@ int mbedtls_mpi_mul_mpi( mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi if (words_mult * 32 > 2048) { /* Calculate new length of Z */ words_z = bits_to_hardware_words(bits_x + bits_y); - if (words_z * 32 > 4096) { - ESP_LOGE(TAG, "ERROR: %d bit result %d bits * %d bits too large for hardware unit\n", words_z * 32, bits_x, bits_y); - return MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; - } - else { + if (words_z * 32 <= 4096) { + /* Note: it's possible to use mpi_mult_mpi_overlong + for this case as well, but it's very slightly + slower and requires a memory allocation. + */ return mpi_mult_mpi_failover_mod_mult(Z, X, Y, words_z); + } else { + /* Still too long for the hardware unit... */ + if(bits_y > bits_x) { + return mpi_mult_mpi_overlong(Z, X, Y, bits_y, words_z); + } else { + return mpi_mult_mpi_overlong(Z, Y, X, bits_x, words_z); + } } } @@ -561,5 +575,64 @@ static int mpi_mult_mpi_failover_mod_mult(mbedtls_mpi *Z, const mbedtls_mpi *X, return ret; } +/* Deal with the case when X & Y are too long for the hardware unit, by splitting one operand + into two halves. + + Y must be the longer operand + + Slice Y into Yp, Ypp such that: + Yp = lower 'b' bits of Y + Ypp = upper 'b' bits of Y (right shifted) + + Such that + Z = X * Y + Z = X * (Yp + Ypp<p, + .n = limbs_slice, + .s = Y->s + }; + /* Ypp holds upper bits of Y, right shifted (also reuses Y's array contents) */ + const mbedtls_mpi Ypp = { + .p = Y->p + limbs_slice, + .n = limbs_y - limbs_slice, + .s = Y->s + }; + mbedtls_mpi_init(&Ztemp); + + /* Grow Z to result size early, avoid interim allocations */ + mbedtls_mpi_grow(Z, words_result); + + /* Get result Ztemp = Yp * X (need temporary variable Ztemp) */ + MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi(&Ztemp, X, &Yp) ); + + /* Z = Ypp * Y */ + MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi(Z, X, &Ypp) ); + + /* Z = Z << b */ + MBEDTLS_MPI_CHK( mbedtls_mpi_shift_l(Z, limbs_slice * biL) ); + + /* Z += Ztemp */ + MBEDTLS_MPI_CHK( mbedtls_mpi_add_mpi(Z, Z, &Ztemp) ); + + cleanup: + mbedtls_mpi_free(&Ztemp); + + return ret; +} + #endif /* MBEDTLS_MPI_MUL_MPI_ALT */ diff --git a/components/mbedtls/test/test_rsa.c b/components/mbedtls/test/test_rsa.c new file mode 100644 index 0000000000..df6d8f238d --- /dev/null +++ b/components/mbedtls/test/test_rsa.c @@ -0,0 +1,240 @@ +/* mbedTLS RSA functionality tests + + Focus on testing functionality where we use ESP32 hardware + accelerated crypto features. + +*/ +#include +#include +#include +#include +#include "mbedtls/rsa.h" +#include "mbedtls/pk.h" +#include "mbedtls/x509_crt.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "unity.h" +#include "sdkconfig.h" + +/* Taken from openssl s_client -connect api.gigafive.com:443 -showcerts + */ +static const char *rsa4096_cert = "-----BEGIN CERTIFICATE-----\n"\ + "MIIExzCCA6+gAwIBAgIBAzANBgkqhkiG9w0BAQsFADCBkjELMAkGA1UEBhMCVVMx\n"\ + "CzAJBgNVBAgMAkNBMRQwEgYDVQQHDAtTYW50YSBDbGFyYTElMCMGA1UECgwcR2ln\n"\ + "YWZpdmUgVGVjaG5vbG9neSBQYXJ0bmVyczEZMBcGA1UEAwwQR2lnYWZpdmUgUm9v\n"\ + "dCBDQTEeMBwGCSqGSIb3DQEJARYPY2FAZ2lnYWZpdmUuY29tMB4XDTE2MDgyNzE2\n"\ + "NDYyM1oXDTI2MDgyNTE2NDYyM1owgZcxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJD\n"\ + "QTEUMBIGA1UEBwwLU2FudGEgQ2xhcmExKTAnBgNVBAoMIEdpZ2FmaXZlIFRlY2hu\n"\ + "b2xvZ3kgUGFydG5lcnMgTExDMRkwFwYDVQQDDBBhcGkuZ2lnYWZpdmUuY29tMR8w\n"\ + "HQYJKoZIhvcNAQkBFhBjcmxAZ2lnYWZpdmUuY29tMIICIjANBgkqhkiG9w0BAQEF\n"\ + "AAOCAg8AMIICCgKCAgEAof82VrEpXMpsI/ddW6RLeTeSYtxiXZZkRbDKN6otYgEk\n"\ + "vA8yRbzei2cO2A/8+Erhe9beYLAMXWF+bjoUAFwnuIcbmufgHprOYzX/7CYXCsrH\n"\ + "LrJfVF6kvjCXy2W3xSvgh8ZgHNWnBGzl13tq19Fz8x0AhK5GQ9608oJCbnQjpVSI\n"\ + "lZDl3JVOifCeXf2c7nMhVOC/reTeto0Gbchs8Ox50WyojmfYbVjOQcA7f8p1eI+D\n"\ + "XUJK01cUGVu6/KarVArGHh5LsiyXOadbyeyOXPmjyrgarG3IIBeQSNECfJZPc/OW\n"\ + "lFszjU4YLDckI4x+tReiuFQbQPN5sDplcEldmZZm/8XD36ddvAaDds+SYlPXxDK7\n"\ + "7L8RBVUG2Ylc9YZf7RE6IMDmdQmsCZDX0VxySYEmzv5lnAx4mzzaXcgS+kHMOLyK\n"\ + "n9UxmpzwQoqqC9tMZqwRaeKW1njR1dSwQLqirBPfGCWKkpkpm7C3HEfeeLrasral\n"\ + "aPf6LAwN3A4ZKHa5Jmne7W+1eYS1aTXOAOLIPcXRAh1B80H+SusIdM9d6vk2YTIg\n"\ + "khwGQV3sgM6nIO5+T/8z141UEjWbtP7pb/u0+G9Cg7TwvRoO2UukxdvOwNto1G2e\n"\ + "J3rKB/JSYsYWnPHvvh9XR+55PZ4iCf9Rqw/IP82uyGipR9gxlHqN8WhMTj9tNEkC\n"\ + "AwEAAaMhMB8wHQYDVR0OBBYEFISCemcSriz1HFhRXluw9H+Bv9lEMA0GCSqGSIb3\n"\ + "DQEBCwUAA4IBAQCMetK0xe6Y/uZpb1ARh+hHYcHI3xI+IG4opWJeoB1gDh/xpNAW\n"\ + "j6t5MGbLoqNMBXbqL26hnKVspyvCxw7ebI5ZJgjtbrD1t+0D8yrgIZzr7AWGA9Hj\n"\ + "WIHqDHGDxwkmfjVVPmuO3l5RtJmL6KV6kVL2bOvVI6gECpFLddmOTtg+iXDfSw3x\n"\ + "0+ueMYKr8QLF+TCxfzQTHvTHvOJtcZHecc1n7PYbRmI2p7tV6RoBpV69oM6NAVUV\n"\ + "i2QoSxm0pYzDzavOaxwhEPHT34Tpg6fwXy1QokFD9OtxRFtdpTjL3bMWpatZE+ba\n"\ + "cjvvf0utMW5fNjTTxu1nnpuxZM3ifTCqZJ+9\n"\ + "-----END CERTIFICATE-----\n"; + +/* Root cert from openssl s_client -connect google.com:443 -showcerts + */ +static const char *rsa2048_cert = "-----BEGIN CERTIFICATE-----\n"\ + "MIIDfTCCAuagAwIBAgIDErvmMA0GCSqGSIb3DQEBBQUAME4xCzAJBgNVBAYTAlVT\n"\ + "MRAwDgYDVQQKEwdFcXVpZmF4MS0wKwYDVQQLEyRFcXVpZmF4IFNlY3VyZSBDZXJ0\n"\ + "aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDIwNTIxMDQwMDAwWhcNMTgwODIxMDQwMDAw\n"\ + "WjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UE\n"\ + "AxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n"\ + "CgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9m\n"\ + "OSm9BXiLnTjoBbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIu\n"\ + "T8rxh0PBFpVXLVDviS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6c\n"\ + "JmTM386DGXHKTubU1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmR\n"\ + "Cw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5asz\n"\ + "PeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo4HwMIHtMB8GA1UdIwQYMBaAFEjm\n"\ + "aPkr0rKV10fYIyAQTzOYkJ/UMB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrM\n"\ + "TjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjA6BgNVHR8EMzAxMC+g\n"\ + "LaArhilodHRwOi8vY3JsLmdlb3RydXN0LmNvbS9jcmxzL3NlY3VyZWNhLmNybDBO\n"\ + "BgNVHSAERzBFMEMGBFUdIAAwOzA5BggrBgEFBQcCARYtaHR0cHM6Ly93d3cuZ2Vv\n"\ + "dHJ1c3QuY29tL3Jlc291cmNlcy9yZXBvc2l0b3J5MA0GCSqGSIb3DQEBBQUAA4GB\n"\ + "AHbhEm5OSxYShjAGsoEIz/AIx8dxfmbuwu3UOx//8PDITtZDOLC5MH0Y0FWDomrL\n"\ + "NhGc6Ehmo21/uBPUR/6LWlxz/K7ZGzIZOKuXNBSqltLroxwUCEm2u+WR74M26x1W\n"\ + "b8ravHNjkOR/ez4iyz0H7V84dJzjA1BOoa+Y7mHyhD8S\n"\ + "-----END CERTIFICATE-----\n"; + +/* Some random input bytes to public key encrypt */ +static const uint8_t pki_input[4096/8] = { + 0, 1, 4, 6, 7, 9, 33, 103, 49, 11, 56, 211, 67, 92 }; + +/* Result of an RSA4096 operation using cert's public key + (raw PKI, no padding/etc) */ +static const uint8_t pki_rsa4096_output[] = { + 0x91, 0x87, 0xcd, 0x04, 0x80, 0x7c, 0x8b, 0x0b, + 0x0c, 0xc0, 0x38, 0x37, 0x7a, 0xe3, 0x2c, 0x94, + 0xea, 0xc4, 0xcb, 0x83, 0x2c, 0x77, 0x71, 0x14, + 0x11, 0x85, 0x16, 0x61, 0xd3, 0x64, 0x2a, 0x0f, + 0xf9, 0x6b, 0x45, 0x04, 0x66, 0x5d, 0x15, 0xf1, + 0xcf, 0x69, 0x77, 0x90, 0xb9, 0x41, 0x68, 0xa9, + 0xa6, 0xfd, 0x94, 0xdc, 0x6a, 0xce, 0xc7, 0xb6, + 0x41, 0xd9, 0x44, 0x3c, 0x02, 0xb6, 0xc7, 0x26, + 0xce, 0xec, 0x66, 0x21, 0xa8, 0xe8, 0xf4, 0xa9, + 0x33, 0x4a, 0x6c, 0x28, 0x0f, 0x50, 0x30, 0x32, + 0x28, 0x00, 0xbb, 0x2c, 0xc3, 0x44, 0x72, 0x31, + 0x93, 0xd4, 0xde, 0x29, 0x6b, 0xfa, 0x31, 0xfd, + 0x3a, 0x05, 0xc6, 0xb1, 0x28, 0x43, 0x57, 0x20, + 0xf7, 0xf8, 0x13, 0x0c, 0x4a, 0x80, 0x00, 0xab, + 0x1f, 0xe8, 0x88, 0xad, 0x56, 0xf2, 0xda, 0x5a, + 0x50, 0xe9, 0x02, 0x09, 0x21, 0x2a, 0xfc, 0x82, + 0x68, 0x34, 0xf9, 0x04, 0xa3, 0x25, 0xe1, 0x0f, + 0xa8, 0x77, 0x29, 0x94, 0xb6, 0x9d, 0x5a, 0x08, + 0x33, 0x8d, 0x27, 0x6a, 0xc0, 0x3b, 0xad, 0x91, + 0x8a, 0x83, 0xa9, 0x2e, 0x48, 0xcd, 0x67, 0xa3, + 0x3a, 0x35, 0x41, 0x85, 0xfa, 0x3f, 0x61, 0x1f, + 0x80, 0xeb, 0xcd, 0x5a, 0xc5, 0x14, 0x7b, 0xab, + 0x9c, 0x45, 0x11, 0xd2, 0x25, 0x9a, 0x16, 0xeb, + 0x9c, 0xfa, 0xbe, 0x73, 0x18, 0xbd, 0x25, 0x8e, + 0x99, 0x6d, 0xb3, 0xbc, 0xac, 0x2d, 0xa2, 0x53, + 0xe8, 0x7c, 0x38, 0x1b, 0x7a, 0x75, 0xff, 0x76, + 0x4f, 0x48, 0x5b, 0x39, 0x20, 0x5a, 0x7b, 0x82, + 0xd3, 0x33, 0x33, 0x2a, 0xab, 0x6a, 0x7a, 0x42, + 0x1d, 0x1f, 0xd1, 0x61, 0x58, 0xd7, 0x38, 0x52, + 0xdf, 0xb0, 0x61, 0x98, 0x63, 0xb7, 0xa1, 0x4e, + 0xdb, 0x9b, 0xcb, 0xb7, 0x85, 0xc4, 0x3e, 0x03, + 0xe5, 0x59, 0x50, 0x28, 0x5a, 0x4d, 0x7f, 0x53, + 0x2e, 0x99, 0x1d, 0x6d, 0x85, 0x27, 0x78, 0x34, + 0x5e, 0xae, 0xc9, 0x1b, 0x37, 0x96, 0xde, 0x40, + 0x87, 0x35, 0x3c, 0x1f, 0xe0, 0x8f, 0xfb, 0x3a, + 0x58, 0x0e, 0x60, 0xe9, 0x06, 0xbd, 0x83, 0x03, + 0x92, 0xde, 0x5e, 0x69, 0x28, 0xb1, 0x00, 0xeb, + 0x44, 0xca, 0x3c, 0x49, 0x03, 0x10, 0xa8, 0x84, + 0xa6, 0xbb, 0xd5, 0xda, 0x98, 0x8c, 0x6f, 0xa3, + 0x0f, 0x39, 0xf3, 0xa7, 0x7d, 0xd5, 0x3b, 0xe2, + 0x85, 0x12, 0xda, 0xa4, 0x4d, 0x80, 0x97, 0xcb, + 0x11, 0xe0, 0x89, 0x90, 0xff, 0x5b, 0x72, 0x19, + 0x59, 0xd1, 0x39, 0x23, 0x9f, 0xb0, 0x00, 0xe2, + 0x45, 0x72, 0xc6, 0x9a, 0xbc, 0xe1, 0xd1, 0x51, + 0x6b, 0x35, 0xd2, 0x49, 0xbf, 0xb6, 0xfe, 0xab, + 0x09, 0xf7, 0x9d, 0xa4, 0x6e, 0x69, 0xb6, 0xf9, + 0xde, 0xe3, 0x57, 0x0c, 0x1a, 0x96, 0xf1, 0xcc, + 0x1c, 0x92, 0xdb, 0x44, 0xf4, 0x45, 0xfa, 0x8f, + 0x87, 0xcf, 0xf4, 0xd2, 0xa1, 0xf8, 0x69, 0x18, + 0xcf, 0xdc, 0xa0, 0x1f, 0xb0, 0x26, 0xad, 0x81, + 0xab, 0xdf, 0x78, 0x18, 0xa2, 0x74, 0xba, 0x2f, + 0xec, 0x70, 0xa2, 0x1f, 0x56, 0xee, 0xff, 0xc9, + 0xfe, 0xb1, 0xe1, 0x9b, 0xea, 0x0e, 0x33, 0x14, + 0x5f, 0x6e, 0xca, 0xee, 0x02, 0x56, 0x5a, 0x67, + 0x42, 0x9a, 0xbf, 0x55, 0xc0, 0x0f, 0x8e, 0x01, + 0x67, 0x63, 0x6e, 0xd1, 0x57, 0xf7, 0xf1, 0xc6, + 0x92, 0x9e, 0xb5, 0x45, 0xe1, 0x50, 0x58, 0x94, + 0x20, 0x90, 0x6a, 0x29, 0x2d, 0x4b, 0xd1, 0xb5, + 0x68, 0x63, 0xb5, 0xe6, 0xd8, 0x6e, 0x84, 0x80, + 0xad, 0xe6, 0x03, 0x1e, 0x51, 0xc2, 0xa8, 0x6d, + 0x84, 0xec, 0x2d, 0x7c, 0x61, 0x02, 0xd1, 0xda, + 0xf5, 0x94, 0xfa, 0x2d, 0xa6, 0xed, 0x89, 0x6a, + 0x6a, 0xda, 0x07, 0x5d, 0x83, 0xfc, 0x43, 0x76, + 0x7c, 0xca, 0x8c, 0x00, 0xfc, 0xb9, 0x2c, 0x23, +}; + +static const uint8_t pki_rsa2048_output[] = { + 0x47, 0x0b, 0xe5, 0x8a, 0xcd, 0x2f, 0x78, 0x07, + 0x69, 0x69, 0x70, 0xff, 0x81, 0xdf, 0x96, 0xf0, + 0xed, 0x82, 0x3a, 0x3d, 0x46, 0xab, 0xe9, 0xc3, + 0xb5, 0xd9, 0xca, 0xa2, 0x05, 0xa9, 0xf6, 0x6e, + 0xad, 0x6c, 0xe0, 0xd1, 0xa2, 0xb4, 0xf2, 0x78, + 0x4a, 0x93, 0xfc, 0x45, 0xe1, 0x9b, 0xdd, 0x62, + 0xf9, 0x66, 0x2a, 0x14, 0x38, 0x12, 0xb6, 0x50, + 0x0b, 0xe3, 0x53, 0x9c, 0x12, 0x56, 0xf1, 0xb7, + 0x83, 0xd5, 0xf3, 0x24, 0x81, 0xcc, 0x5a, 0xeb, + 0xec, 0xac, 0x68, 0xa8, 0x0c, 0xd7, 0x84, 0x7a, + 0xbb, 0x77, 0x7b, 0xd5, 0x5b, 0xcf, 0x7b, 0x25, + 0xd0, 0x75, 0x80, 0x21, 0x12, 0x97, 0x6b, 0xe1, + 0xb6, 0x51, 0x12, 0x52, 0x6e, 0x01, 0x92, 0xb7, + 0xcc, 0x70, 0x4b, 0x46, 0x11, 0x98, 0x5a, 0x84, + 0x1c, 0x90, 0x45, 0x0f, 0x15, 0x77, 0xdb, 0x79, + 0xe8, 0xff, 0x1f, 0xaa, 0x58, 0x95, 0xce, 0x3c, + 0x65, 0x0c, 0x66, 0x29, 0xe1, 0x9c, 0x41, 0xbb, + 0xde, 0x65, 0xb8, 0x29, 0x36, 0x94, 0xbd, 0x87, + 0x93, 0x39, 0xc5, 0xeb, 0x49, 0x21, 0xc1, 0xeb, + 0x48, 0xbd, 0x19, 0x13, 0x4d, 0x40, 0x90, 0x88, + 0xc6, 0x12, 0xd9, 0xf7, 0xdd, 0xc8, 0x4f, 0x89, + 0xc0, 0x91, 0xf8, 0xeb, 0xcf, 0xe3, 0x12, 0x17, + 0x88, 0x9c, 0x88, 0xf4, 0xf5, 0xae, 0xf4, 0x15, + 0xfe, 0x17, 0xf6, 0xa4, 0x74, 0x49, 0x02, 0x05, + 0x11, 0x3b, 0x92, 0x25, 0x39, 0x2c, 0x4b, 0x08, + 0x19, 0x76, 0x13, 0x8d, 0xf9, 0xda, 0xae, 0xdf, + 0x30, 0xda, 0xcc, 0xbb, 0x3f, 0xb9, 0xb0, 0xd6, + 0x5c, 0x78, 0x4b, 0x2b, 0x35, 0x51, 0x17, 0x48, + 0xf5, 0xd4, 0x39, 0x7e, 0x05, 0x83, 0x68, 0x86, + 0x44, 0x5f, 0x56, 0x1d, 0x2c, 0x53, 0xd3, 0x64, + 0x3a, 0xb2, 0x0c, 0x4a, 0x85, 0xd6, 0x5b, 0x7e, + 0xf9, 0xe9, 0x50, 0x29, 0x5d, 0x4f, 0xcc, 0xc9, +}; + +_Static_assert(sizeof(pki_rsa2048_output) == 2048/8, "rsa2048 output is wrong size"); +_Static_assert(sizeof(pki_rsa4096_output) == 4096/8, "rsa4096 output is wrong size"); + +static void test_cert(const char *cert, const uint8_t *expected_output, size_t output_len); + +TEST_CASE("mbedtls RSA4096 cert", "[mbedtls]") +{ + + test_cert(rsa4096_cert, pki_rsa4096_output, 4096/8); +} + +TEST_CASE("mbedtls RSA2048 cert", "[mbedtls]") +{ + test_cert(rsa2048_cert, pki_rsa2048_output, 2048/8); +} + +static void test_cert(const char *cert, const uint8_t *expected_output, size_t output_len) +{ + mbedtls_x509_crt crt; + mbedtls_rsa_context *rsa; + char buf[output_len]; + + bzero(buf, output_len); + + mbedtls_x509_crt_init(&crt); + + TEST_ASSERT_EQUAL_HEX16_MESSAGE(0, + -mbedtls_x509_crt_parse(&crt, + (const uint8_t *)cert, + strlen(cert)+1), + "parse cert"); + + rsa = mbedtls_pk_rsa(crt.pk); + TEST_ASSERT_NOT_NULL(rsa); + + TEST_ASSERT_EQUAL_HEX16_MESSAGE(0, + -mbedtls_rsa_check_pubkey(rsa), + "check cert pubkey"); + + mbedtls_x509_crt_info(buf, sizeof(buf), "", &crt); + puts(buf); + + TEST_ASSERT_EQUAL_HEX16_MESSAGE(0, + -mbedtls_rsa_public(rsa, pki_input, (uint8_t *)buf), + "RSA PK operation"); + + /* + // Dump buffer for debugging + for(int i = 0; i < output_len; i++) { + printf("0x%02x, ", buf[i]); + } + printf("\n"); + */ + + TEST_ASSERT_EQUAL_HEX8_ARRAY(expected_output, buf, output_len); + + mbedtls_x509_crt_free(&crt); +} diff --git a/components/newlib/locks.c b/components/newlib/locks.c index 21b974a1f1..c143e3a3dd 100644 --- a/components/newlib/locks.c +++ b/components/newlib/locks.c @@ -21,6 +21,7 @@ #include "freertos/semphr.h" #include "freertos/portmacro.h" #include "freertos/task.h" +#include "freertos/portable.h" /* Notes on our newlib lock implementation: * @@ -126,7 +127,7 @@ static int IRAM_ATTR lock_acquire_generic(_lock_t *lock, uint32_t delay, uint8_t } BaseType_t success; - if (cpu_in_interrupt_context()) { + if (xPortInIsrContext()) { /* In ISR Context */ if (mutex_type == queueQUEUE_TYPE_RECURSIVE_MUTEX) { abort(); /* recursive mutexes make no sense in ISR context */ @@ -180,7 +181,7 @@ static void IRAM_ATTR lock_release_generic(_lock_t *lock, uint8_t mutex_type) { return; } - if (cpu_in_interrupt_context()) { + if (xPortInIsrContext()) { if (mutex_type == queueQUEUE_TYPE_RECURSIVE_MUTEX) { abort(); /* indicates logic bug, it shouldn't be possible to lock recursively in ISR */ } diff --git a/components/newlib/time.c b/components/newlib/time.c index 7595ab82b8..fc7dec6450 100644 --- a/components/newlib/time.c +++ b/components/newlib/time.c @@ -52,7 +52,7 @@ static uint64_t get_rtc_time_us() uint64_t low = READ_PERI_REG(RTC_CNTL_TIME0_REG); uint64_t high = READ_PERI_REG(RTC_CNTL_TIME1_REG); uint64_t ticks = (high << 32) | low; - return ticks * 100 / (RTC_CTNL_SLOWCLK_FREQ / 10000); // scale RTC_CTNL_SLOWCLK_FREQ to avoid overflow + return ticks * 100 / (RTC_CNTL_SLOWCLK_FREQ / 10000); // scale RTC_CNTL_SLOWCLK_FREQ to avoid overflow } #endif // WITH_RTC diff --git a/components/nvs_flash/test_nvs_host/Makefile b/components/nvs_flash/test_nvs_host/Makefile index 6006213961..133762992d 100644 --- a/components/nvs_flash/test_nvs_host/Makefile +++ b/components/nvs_flash/test_nvs_host/Makefile @@ -2,6 +2,7 @@ TEST_PROGRAM=test_nvs all: $(TEST_PROGRAM) SOURCE_FILES = \ + esp_error_check_stub.cpp \ $(addprefix ../src/, \ nvs_types.cpp \ nvs_api.cpp \ diff --git a/components/nvs_flash/test_nvs_host/esp_error_check_stub.cpp b/components/nvs_flash/test_nvs_host/esp_error_check_stub.cpp new file mode 100644 index 0000000000..9cff4af310 --- /dev/null +++ b/components/nvs_flash/test_nvs_host/esp_error_check_stub.cpp @@ -0,0 +1,9 @@ +#include "catch.hpp" +#include "esp_err.h" + +void _esp_error_check_failed(esp_err_t rc, const char *file, int line, const char *function, const char *expression) +{ + printf("ESP_ERROR_CHECK failed: esp_err_t 0x%x at %p\n", rc, __builtin_return_address(0)); + printf("file: \"%s\" line %d\nfunc: %s\nexpression: %s\n", file, line, function, expression); + abort(); +} diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c index 1281965e2c..711af1770e 100755 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -646,13 +646,13 @@ void ssl_pm_set_bufflen(SSL *ssl, int len) long ssl_pm_get_verify_result(const SSL *ssl) { - long ret; + uint32_t ret; long verify_result; struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; ret = mbedtls_ssl_get_verify_result(&ssl_pm->ssl); if (ret) { - SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_get_verify_result() return -0x%x", -ret); + SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_get_verify_result() return 0x%x", ret); verify_result = X509_V_ERR_UNSPECIFIED; } else verify_result = X509_V_OK; diff --git a/components/partition_table/test/test_partition.c b/components/partition_table/test/test_partition.c index a268222802..6cb13bd9a7 100644 --- a/components/partition_table/test/test_partition.c +++ b/components/partition_table/test/test_partition.c @@ -1,6 +1,7 @@ #include #include #include "unity.h" +#include "test_utils.h" #include "esp_partition.h" @@ -9,26 +10,30 @@ TEST_CASE("Can read partition table", "[partition]") const esp_partition_t *p = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_ANY, NULL); TEST_ASSERT_NOT_NULL(p); - TEST_ASSERT_EQUAL(p->address, 0x10000); - TEST_ASSERT_EQUAL(p->subtype, ESP_PARTITION_SUBTYPE_APP_FACTORY); + TEST_ASSERT_EQUAL(0x10000, p->address); + TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_FACTORY, p->subtype); esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, NULL); TEST_ASSERT_NOT_NULL(it); int count = 0; + const esp_partition_t* prev = NULL; for (; it != NULL; it = esp_partition_next(it)) { const esp_partition_t *p = esp_partition_get(it); TEST_ASSERT_NOT_NULL(p); + if (prev) { + TEST_ASSERT_TRUE_MESSAGE(prev->address < p->address, "incorrect partition order"); + } + prev = p; ++count; } esp_partition_iterator_release(it); - TEST_ASSERT_EQUAL(count, 2); - - printf("%d\n", __builtin_clz(count)); + TEST_ASSERT_EQUAL(3, count); } TEST_CASE("Can write, read, mmap partition", "[partition][ignore]") { - const esp_partition_t *p = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, NULL); + const esp_partition_t *p = get_test_data_partition(); + printf("Using partition %s at 0x%x, size 0x%x\n", p->label, p->address, p->size); TEST_ASSERT_NOT_NULL(p); const size_t max_size = 2 * SPI_FLASH_SEC_SIZE; uint8_t *data = (uint8_t *) malloc(max_size); @@ -46,9 +51,6 @@ TEST_CASE("Can write, read, mmap partition", "[partition][ignore]") } for (size_t i = 0; i < block_size / 4; ++i) { ((uint32_t *) (data))[i] = rand(); - if (i == 0 && offset == 0) { - printf("write: %08x\n", ((uint32_t *) (data))[i]); - } } TEST_ASSERT_EQUAL(ESP_OK, esp_partition_write(p, offset, data, block_size)); } diff --git a/components/spi_flash/README.rst b/components/spi_flash/README.rst index b479c3b0e9..da953f0cf2 100644 --- a/components/spi_flash/README.rst +++ b/components/spi_flash/README.rst @@ -1,15 +1,15 @@ -SPI flash related APIs -====================== +SPI Flash APIs +============== Overview -------- -Spi_flash component contains APIs related to reading, writing, erasing, +The 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. +APIs which work with partitions defined in the :doc:`partition table `. -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 +Note that all the functionality is limited to the "main" SPI flash chip, +the same SPI flash chip from which program runs. For ``spi_flash_*`` functions, +this is a software limitation. The underlying ROM functions which work with SPI flash do not have provisions for working with flash chips attached to SPI peripherals other than SPI0. @@ -24,74 +24,113 @@ This is the set of APIs for working with data in 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: +Generally, try to avoid using the raw SPI flash functions in favour of +partition-specific functions. -- buffer in RAM must be 4-byte aligned -- size must be 4-byte aligned -- address in flash must be 4-byte aligned +SPI Flash Size +-------------- -These alignment limitations are purely software, and should be removed in future -versions. +The SPI flash size is configured by writing a field in the software bootloader +image header, flashed at offset 0x1000. -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. +By default, the SPI flash size is detected by esptool.py when this bootloader is +written to flash, and the header is updated with the correct +size. Alternatively, it is possible to generate a fixed flash size by disabling +detection in ``make menuconfig`` (under Serial Flasher Config). -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. +If it is necessary to override the configured flash size at runtime, is is +possible to set the ``chip_size`` member of ``g_rom_flashchip`` structure. This +size is used by ``spi_flash_*`` functions (in both software & ROM) for bounds +checking. -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. +Concurrency Constraints +----------------------- -To place a function into IRAM, use ``IRAM_ATTR`` attribute, e.g.:: +Because the SPI flash is also used for firmware execution (via the instruction & +data caches), these caches much be disabled while reading/writing/erasing. This +means that both CPUs must be running code from IRAM and only reading data from +DRAM while flash write operations occur. - #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. +Refer to the :ref:`application memory layout ` documentation for +an explanation of the differences between IRAM, DRAM and flash cache. +To avoid reading flash cache accidentally, when one CPU commences a flash write +or erase operation the other CPU is put into a blocked state and all +non-IRAM-safe interrupts are disabled on both CPUs, until the flash operation +completes. + +.. _iram-safe-interrupt-handlers: + +IRAM-Safe Interrupt Handlers +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If you have an interrupt handler that you want to execute even when a flash +operation is in progress (for example, for low latency operations), set the +``ESP_INTR_FLAG_IRAM`` flag when the :doc:`interrupt handler is registered +`. + +You must ensure all data and functions accessed by these interrupt handlers are +located in IRAM or DRAM. This includes any functions that the handler calls. + +Use the ``IRAM_ATTR`` attribute for functions:: + + #include "esp_attr.h" + + void IRAM_ATTR gpio_isr_handler(void* arg) + { + // ... + } + +Use the ``DRAM_ATTR`` and ``DRAM_STR`` attributes for constant data:: + + void IRAM_ATTR gpio_isr_handler(void* arg) + { + const static DRAM_ATTR uint8_t INDEX_DATA[] = { 45, 33, 12, 0 }; + const static char *MSG = DRAM_STR("I am a string stored in RAM"); + } + +Note that knowing which data should be marked with ``DRAM_ATTR`` can be hard, +the compiler will sometimes recognise that a variable or expression is constant +(even if it is not marked ``const``) and optimise it into flash, unless it is +marked with ``DRAM_ATTR``. + +If a function or symbol is not correctly put into IRAM/DRAM and the interrupt +handler reads from the flash cache during a flash operation, it will cause a +crash due to Illegal Instruction exception (for code which should be in IRAM) or +garbage data to be read (for constant data which should be in DRAM). Partition table APIs -------------------- -ESP-IDF uses partition table to maintain information about various regions of +ESP-IDF projects use a 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. +More information about partition tables can be found :doc:`here `. 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_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 +- ``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. +SPI Flash Encryption +-------------------- + +It is possible to encrypt SPI flash contents, and have it transparenlty decrypted by hardware. + +Refer to the :doc:`Flash Encryption documentation ` for more details. + Memory mapping APIs ------------------- @@ -105,10 +144,10 @@ 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. +contents of flash when :doc:`flash encryption ` is enabled. +Decryption is performed at hardware level. Memory mapping APIs are declared in ``esp_spi_flash.h`` and ``esp_partition.h``: @@ -119,40 +158,8 @@ Memory mapping APIs are declared in ``esp_spi_flash.h`` and ``esp_partition.h``: 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 +- ``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. +read data outside of the partition provided to ``esp_partition_mmap``. diff --git a/components/spi_flash/cache_utils.c b/components/spi_flash/cache_utils.c index 5e880ab493..6d0dd91845 100644 --- a/components/spi_flash/cache_utils.c +++ b/components/spi_flash/cache_utils.c @@ -275,3 +275,9 @@ static void IRAM_ATTR spi_flash_restore_cache(uint32_t cpuid, uint32_t saved_sta } } + +IRAM_ATTR bool spi_flash_cache_enabled() +{ + return REG_GET_BIT(DPORT_PRO_CACHE_CTRL_REG, DPORT_PRO_CACHE_ENABLE) + && REG_GET_BIT(DPORT_APP_CACHE_CTRL_REG, DPORT_APP_CACHE_ENABLE); +} diff --git a/components/spi_flash/flash_mmap.c b/components/spi_flash/flash_mmap.c index f8d2e3297d..221d2ced68 100644 --- a/components/spi_flash/flash_mmap.c +++ b/components/spi_flash/flash_mmap.c @@ -74,16 +74,23 @@ static uint32_t s_mmap_last_handle = 0; static void IRAM_ATTR spi_flash_mmap_init() { + if (s_mmap_page_refcnt[0] != 0) { + return; /* mmap data already initialised */ + } + 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; + entry_pro = INVALID_ENTRY_VAL; + DPORT_PRO_FLASH_MMU_TABLE[i] = INVALID_ENTRY_VAL; } - if ((entry_pro & 0x100) == 0 && (i == 0 || i == PRO_IRAM0_FIRST_USABLE_PAGE || entry_pro != 0)) { + if ((entry_pro & INVALID_ENTRY_VAL) == 0 && (i == 0 || i == PRO_IRAM0_FIRST_USABLE_PAGE || entry_pro != 0)) { s_mmap_page_refcnt[i] = 1; + } else { + DPORT_PRO_FLASH_MMU_TABLE[i] = INVALID_ENTRY_VAL; + DPORT_APP_FLASH_MMU_TABLE[i] = INVALID_ENTRY_VAL; } } } @@ -93,24 +100,22 @@ esp_err_t IRAM_ATTR spi_flash_mmap(size_t src_addr, size_t size, spi_flash_mmap_ { esp_err_t ret; bool did_flush, need_flush = false; - 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; } + mmap_entry_t* new_entry = (mmap_entry_t*) malloc(sizeof(mmap_entry_t)); + if (new_entry == 0) { + return ESP_ERR_NO_MEM; + } spi_flash_disable_interrupts_caches_and_other_cpu(); did_flush = spi_flash_ensure_unmodified_region(src_addr, size); - if (s_mmap_page_refcnt[0] == 0) { - spi_flash_mmap_init(); - } + 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 @@ -122,7 +127,7 @@ esp_err_t IRAM_ATTR spi_flash_mmap(size_t src_addr, size_t size, spi_flash_mmap_ region_addr = VADDR0_START_ADDR; } else { // only part of VAddr1 is usable, so adjust for that - region_begin = VADDR1_FIRST_USABLE_ADDR; + region_begin = PRO_IRAM0_FIRST_USABLE_PAGE; region_size = 3 * 64 - region_begin; region_addr = VADDR1_FIRST_USABLE_ADDR; } @@ -139,7 +144,7 @@ esp_err_t IRAM_ATTR spi_flash_mmap(size_t src_addr, size_t size, spi_flash_mmap_ 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]; + uint8_t refcnt = s_mmap_page_refcnt[pos]; if (refcnt != 0 && table_val != page) { break; } @@ -177,17 +182,17 @@ esp_err_t IRAM_ATTR spi_flash_mmap(size_t src_addr, size_t size, spi_flash_mmap_ new_entry->count = page_count; new_entry->handle = ++s_mmap_last_handle; *out_handle = new_entry->handle; - *out_ptr = (void*) (region_addr + start * SPI_FLASH_MMU_PAGE_SIZE); + *out_ptr = (void*) (region_addr + (start - region_begin) * SPI_FLASH_MMU_PAGE_SIZE); ret = ESP_OK; } /* This is a temporary fix for an issue where some - encrypted cache reads may see stale data. + cache reads may see stale data. Working on a long term fix that doesn't require invalidating entire cache. */ - if (esp_flash_encryption_enabled() && !did_flush && need_flush) { + if (!did_flush && need_flush) { Cache_Flush(0); Cache_Flush(1); } @@ -229,9 +234,7 @@ void IRAM_ATTR spi_flash_munmap(spi_flash_mmap_handle_t handle) void spi_flash_mmap_dump() { - if (s_mmap_page_refcnt[0] == 0) { - spi_flash_mmap_init(); - } + 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); @@ -305,3 +308,62 @@ static inline IRAM_ATTR bool update_written_pages(size_t start_addr, size_t leng } return false; } + + +uint32_t spi_flash_cache2phys(const void *cached) +{ + intptr_t c = (intptr_t)cached; + size_t cache_page; + if (c >= VADDR1_START_ADDR && c < VADDR1_FIRST_USABLE_ADDR) { + /* IRAM address, doesn't map to flash */ + return SPI_FLASH_CACHE2PHYS_FAIL; + } + else if (c < VADDR1_FIRST_USABLE_ADDR) { + /* expect cache is in DROM */ + cache_page = (c - VADDR0_START_ADDR) / SPI_FLASH_MMU_PAGE_SIZE; + } else { + /* expect cache is in IROM */ + cache_page = (c - VADDR1_START_ADDR) / SPI_FLASH_MMU_PAGE_SIZE + 64; + } + + if (cache_page >= 256) { + /* cached address was not in IROM or DROM */ + return SPI_FLASH_CACHE2PHYS_FAIL; + } + uint32_t phys_page = DPORT_PRO_FLASH_MMU_TABLE[cache_page]; + if (phys_page == INVALID_ENTRY_VAL) { + /* page is not mapped */ + return SPI_FLASH_CACHE2PHYS_FAIL; + } + uint32_t phys_offs = phys_page * SPI_FLASH_MMU_PAGE_SIZE; + return phys_offs | (c & (SPI_FLASH_MMU_PAGE_SIZE-1)); +} + + +const void *spi_flash_phys2cache(uint32_t phys_offs, spi_flash_mmap_memory_t memory) +{ + uint32_t phys_page = phys_offs / SPI_FLASH_MMU_PAGE_SIZE; + int start, end, page_delta; + intptr_t base; + + if (memory == SPI_FLASH_MMAP_DATA) { + start = 0; + end = 64; + base = VADDR0_START_ADDR; + page_delta = 0; + } else { + start = PRO_IRAM0_FIRST_USABLE_PAGE; + end = 256; + base = VADDR1_START_ADDR; + page_delta = 64; + } + + for (int i = start; i < end; i++) { + if (DPORT_PRO_FLASH_MMU_TABLE[i] == phys_page) { + i -= page_delta; + intptr_t cache_page = base + (SPI_FLASH_MMU_PAGE_SIZE * i); + return (const void *) (cache_page | (phys_offs & (SPI_FLASH_MMU_PAGE_SIZE-1))); + } + } + return NULL; +} diff --git a/components/spi_flash/include/esp_partition.h b/components/spi_flash/include/esp_partition.h index 28f8551dcb..f3d5a424af 100644 --- a/components/spi_flash/include/esp_partition.h +++ b/components/spi_flash/include/esp_partition.h @@ -63,7 +63,7 @@ typedef enum { ESP_PARTITION_SUBTYPE_APP_OTA_13 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 13,//!< OTA partition 13 ESP_PARTITION_SUBTYPE_APP_OTA_14 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 14,//!< OTA partition 14 ESP_PARTITION_SUBTYPE_APP_OTA_15 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 15,//!< OTA partition 15 - ESP_PARTITION_SUBTYPE_APP_OTA_MAX = 15, //!< Max subtype of OTA partition + ESP_PARTITION_SUBTYPE_APP_OTA_MAX = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 16,//!< Max subtype of OTA partition ESP_PARTITION_SUBTYPE_APP_TEST = 0x20, //!< Test application partition ESP_PARTITION_SUBTYPE_DATA_OTA = 0x00, //!< OTA selection partition @@ -165,6 +165,26 @@ esp_partition_iterator_t esp_partition_next(esp_partition_iterator_t iterator); */ void esp_partition_iterator_release(esp_partition_iterator_t iterator); +/** + * @brief Verify partition data + * + * Given a pointer to partition data, verify this partition exists in the partition table (all fields match.) + * + * This function is also useful to take partition data which may be in a RAM buffer and convert it to a pointer to the + * permanent partition data stored in flash. + * + * Pointers returned from this function can be compared directly to the address of any pointer returned from + * esp_partition_get(), as a test for equality. + * + * @param partition Pointer to partition data to verify. Must be non-NULL. All fields of this structure must match the + * partition table entry in flash for this function to return a successful match. + * + * @return + * - If partition not found, returns NULL. + * - If found, returns a pointer to the esp_partition_t structure in flash. This pointer is always valid for the lifetime of the application. + */ +const esp_partition_t *esp_partition_verify(const esp_partition_t *partition); + /** * @brief Read data from the partition * diff --git a/components/spi_flash/include/esp_spi_flash.h b/components/spi_flash/include/esp_spi_flash.h index 060d598ec0..00797b8df2 100644 --- a/components/spi_flash/include/esp_spi_flash.h +++ b/components/spi_flash/include/esp_spi_flash.h @@ -197,6 +197,48 @@ void spi_flash_munmap(spi_flash_mmap_handle_t handle); */ void spi_flash_mmap_dump(); + +#define SPI_FLASH_CACHE2PHYS_FAIL UINT32_MAX /*next_item == NULL) { + esp_partition_iterator_release(it); return NULL; } _lock_acquire(&s_partition_list_lock); @@ -183,6 +184,7 @@ static esp_err_t load_partitions() } else { SLIST_INSERT_AFTER(last, item, next); } + last = item; } spi_flash_munmap(handle); return ESP_OK; @@ -200,6 +202,28 @@ const esp_partition_t* esp_partition_get(esp_partition_iterator_t iterator) return iterator->info; } +const esp_partition_t *esp_partition_verify(const esp_partition_t *partition) +{ + assert(partition != NULL); + const char *label = (strlen(partition->label) > 0) ? partition->label : NULL; + esp_partition_iterator_t it = esp_partition_find(partition->type, + partition->subtype, + label); + while (it != NULL) { + const esp_partition_t *p = esp_partition_get(it); + /* Can't memcmp() whole structure here as padding contents may be different */ + if (p->address == partition->address + && partition->size == p->size + && partition->encrypted == p->encrypted) { + esp_partition_iterator_release(it); + return p; + } + it = esp_partition_next(it); + } + esp_partition_iterator_release(it); + return NULL; +} + esp_err_t esp_partition_read(const esp_partition_t* partition, size_t src_offset, void* dst, size_t size) { diff --git a/components/spi_flash/test/test_cache_disabled.c b/components/spi_flash/test/test_cache_disabled.c new file mode 100644 index 0000000000..8caa7e83f9 --- /dev/null +++ b/components/spi_flash/test/test_cache_disabled.c @@ -0,0 +1,53 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../cache_utils.h" + +static QueueHandle_t result_queue; + +static IRAM_ATTR void cache_test_task(void *arg) +{ + bool do_disable = (bool)arg; + bool result; + if(do_disable) { + spi_flash_disable_interrupts_caches_and_other_cpu(); + } + result = spi_flash_cache_enabled(); + if (do_disable) { + spi_flash_enable_interrupts_caches_and_other_cpu(); + } + + TEST_ASSERT( xQueueSendToBack(result_queue, &result, 0) ); + vTaskDelete(NULL); +} + +TEST_CASE("spi_flash_cache_enabled() works on both CPUs", "[spi_flash]") +{ + result_queue = xQueueCreate(1, sizeof(bool)); + + for(int cpu = 0; cpu < portNUM_PROCESSORS; cpu++) { + for(int disable = 0; disable <= 1; disable++) { + bool do_disable = disable; + bool result; + printf("Testing cpu %d disabled %d\n", cpu, do_disable); + + xTaskCreatePinnedToCore(cache_test_task, "cache_check_task", + 2048, (void *)do_disable, configMAX_PRIORITIES-1, NULL, cpu); + + TEST_ASSERT( xQueueReceive(result_queue, &result, 2) ); + TEST_ASSERT_EQUAL(!do_disable, result); + } + } + + vQueueDelete(result_queue); +} + diff --git a/components/spi_flash/test/test_flash_encryption.c b/components/spi_flash/test/test_flash_encryption.c index b8d8018ae4..16579930c6 100644 --- a/components/spi_flash/test/test_flash_encryption.c +++ b/components/spi_flash/test/test_flash_encryption.c @@ -4,23 +4,35 @@ #include #include +#include #include #include #include -#include "test_config.h" - static void test_encrypted_write(size_t offset, const uint8_t *data, size_t length); static void verify_erased_flash(size_t offset, size_t length); +static size_t start; + +static void setup_tests() +{ + if (start == 0) { + const esp_partition_t *part = get_test_data_partition(); + start = part->address; + printf("Test data partition @ 0x%x\n", start); + } +} + TEST_CASE("test 16 byte encrypted writes", "[spi_flash]") { + setup_tests(); + if (!esp_flash_encryption_enabled()) { TEST_IGNORE_MESSAGE("flash encryption disabled, skipping spi_flash_write_encrypted() tests"); } TEST_ASSERT_EQUAL_HEX(ESP_OK, - spi_flash_erase_sector(TEST_REGION_START / SPI_FLASH_SEC_SIZE)); + spi_flash_erase_sector(start / SPI_FLASH_SEC_SIZE)); uint8_t fortyeight_bytes[0x30]; // 0, 1, 2, 3, 4... 47 for(int i = 0; i < sizeof(fortyeight_bytes); i++) { @@ -29,38 +41,38 @@ TEST_CASE("test 16 byte encrypted writes", "[spi_flash]") /* Verify unaligned start or length fails */ TEST_ASSERT_EQUAL_HEX(ESP_ERR_INVALID_ARG, - spi_flash_write_encrypted(TEST_REGION_START+1, fortyeight_bytes, 32)); + spi_flash_write_encrypted(start+1, fortyeight_bytes, 32)); TEST_ASSERT_EQUAL_HEX(ESP_ERR_INVALID_SIZE, - spi_flash_write_encrypted(TEST_REGION_START, fortyeight_bytes, 15)); + spi_flash_write_encrypted(start, fortyeight_bytes, 15)); /* ensure nothing happened to the flash yet */ - verify_erased_flash(TEST_REGION_START, 0x20); + verify_erased_flash(start, 0x20); /* Write 32 byte block, this is the "normal" encrypted write */ - test_encrypted_write(TEST_REGION_START, fortyeight_bytes, 0x20); - verify_erased_flash(TEST_REGION_START + 0x20, 0x20); + test_encrypted_write(start, fortyeight_bytes, 0x20); + verify_erased_flash(start + 0x20, 0x20); /* Slip in an unaligned spi_flash_read_encrypted() test */ uint8_t buf[0x10]; - spi_flash_read_encrypted(TEST_REGION_START+0x10, buf, 0x10); + spi_flash_read_encrypted(start+0x10, buf, 0x10); TEST_ASSERT_EQUAL_HEX8_ARRAY(fortyeight_bytes+0x10, buf, 16); /* Write 16 bytes unaligned */ - test_encrypted_write(TEST_REGION_START + 0x30, fortyeight_bytes, 0x10); + test_encrypted_write(start + 0x30, fortyeight_bytes, 0x10); /* the 16 byte regions before and after the 16 bytes we just wrote should still be 0xFF */ - verify_erased_flash(TEST_REGION_START + 0x20, 0x10); - verify_erased_flash(TEST_REGION_START + 0x40, 0x10); + verify_erased_flash(start + 0x20, 0x10); + verify_erased_flash(start + 0x40, 0x10); /* Write 48 bytes starting at a 32-byte aligned offset */ - test_encrypted_write(TEST_REGION_START + 0x40, fortyeight_bytes, 0x30); + test_encrypted_write(start + 0x40, fortyeight_bytes, 0x30); /* 16 bytes after this write should still be 0xFF -unencrypted- */ - verify_erased_flash(TEST_REGION_START + 0x70, 0x10); + verify_erased_flash(start + 0x70, 0x10); /* Write 48 bytes starting at a 16-byte aligned offset */ - test_encrypted_write(TEST_REGION_START + 0x90, fortyeight_bytes, 0x30); + test_encrypted_write(start + 0x90, fortyeight_bytes, 0x30); /* 16 bytes after this write should still be 0xFF -unencrypted- */ - verify_erased_flash(TEST_REGION_START + 0x120, 0x10); + verify_erased_flash(start + 0x120, 0x10); } static void test_encrypted_write(size_t offset, const uint8_t *data, size_t length) diff --git a/components/spi_flash/test/test_mmap.c b/components/spi_flash/test/test_mmap.c index 464a876428..d61ad77b19 100644 --- a/components/spi_flash/test/test_mmap.c +++ b/components/spi_flash/test/test_mmap.c @@ -8,45 +8,142 @@ #include #include #include +#include #include -#include "test_config.h" +#include "test_utils.h" static uint32_t buffer[1024]; -/* read-only region used for mmap tests */ -static const uint32_t start = 0x100000; -static const uint32_t end = 0x200000; +/* read-only region used for mmap tests, intialised in setup_mmap_tests() */ +static uint32_t start; +static uint32_t end; +static spi_flash_mmap_handle_t handle1, handle2, handle3; -TEST_CASE("Prepare data for mmap tests", "[mmap]") +static void setup_mmap_tests() { + if (start == 0) { + const esp_partition_t *part = get_test_data_partition(); + start = part->address; + end = part->address + part->size; + printf("Test data partition @ 0x%x - 0x%x\n", start, end); + } + TEST_ASSERT(end > start); + TEST_ASSERT(end - start >= 512*1024); + + /* clean up any mmap handles left over from failed tests */ + if (handle1) { + spi_flash_munmap(handle1); + handle1 = 0; + } + if (handle2) { + spi_flash_munmap(handle2); + handle2 = 0; + } + if (handle3) { + spi_flash_munmap(handle3); + handle3 = 0; + } + + /* prepare flash contents */ srand(0); for (int block = start / 0x10000; block < end / 0x10000; ++block) { - printf("Writing block %d\n", block); for (int sector = 0; sector < 16; ++sector) { + uint32_t abs_sector = (block * 16) + sector; + uint32_t sector_offs = abs_sector * SPI_FLASH_SEC_SIZE; + bool sector_needs_write = false; + + ESP_ERROR_CHECK( spi_flash_read(sector_offs, buffer, sizeof(buffer)) ); + for (uint32_t word = 0; word < 1024; ++word) { uint32_t val = rand(); if (block == start / 0x10000 && sector == 0 && word == 0) { - printf("first word: %08x\n", val); + printf("setup_mmap_tests(): first prepped word: 0x%08x (flash holds 0x%08x)\n", val, buffer[word]); + } + if (buffer[word] != val) { + buffer[word] = val; + sector_needs_write = true; } - buffer[word] = val; } - uint32_t abs_sector = (block) * 16 + sector; - printf("Writing sector %d\n", abs_sector); - ESP_ERROR_CHECK( spi_flash_erase_sector((uint16_t) abs_sector) ); - ESP_ERROR_CHECK( spi_flash_write(abs_sector * SPI_FLASH_SEC_SIZE, (const uint8_t *) buffer, sizeof(buffer)) ); + /* Only rewrite the sector if it has changed */ + if (sector_needs_write) { + printf("setup_mmap_tests(): Prepping sector %d\n", abs_sector); + ESP_ERROR_CHECK( spi_flash_erase_sector((uint16_t) abs_sector) ); + ESP_ERROR_CHECK( spi_flash_write(sector_offs, (const uint8_t *) buffer, sizeof(buffer)) ); + } } } } -TEST_CASE("Can mmap into data address space", "[mmap]") +TEST_CASE("Can mmap into data address space", "[spi_flash]") { + setup_mmap_tests(); + + printf("Mapping %x (+%x)\n", start, end - start); + const void *ptr1; + ESP_ERROR_CHECK( spi_flash_mmap(start, end - start, SPI_FLASH_MMAP_DATA, &ptr1, &handle1) ); + printf("mmap_res: handle=%d ptr=%p\n", handle1, ptr1); + + spi_flash_mmap_dump(); + + srand(0); + const uint32_t *data = (const uint32_t *) ptr1; + for (int block = 0; block < (end - start) / 0x10000; ++block) { + printf("block %d\n", block); + for (int sector = 0; sector < 16; ++sector) { + printf("sector %d\n", sector); + for (uint32_t word = 0; word < 1024; ++word) { + TEST_ASSERT_EQUAL_HEX32(rand(), data[(block * 16 + sector) * 1024 + word]); + } + } + } + printf("Mapping %x (+%x)\n", start - 0x10000, 0x20000); + const void *ptr2; + ESP_ERROR_CHECK( spi_flash_mmap(start - 0x10000, 0x20000, SPI_FLASH_MMAP_DATA, &ptr2, &handle2) ); + printf("mmap_res: handle=%d ptr=%p\n", handle2, ptr2); + + TEST_ASSERT_EQUAL_HEX32(start - 0x10000, spi_flash_cache2phys(ptr2)); + TEST_ASSERT_EQUAL_PTR(ptr2, spi_flash_phys2cache(start - 0x10000, SPI_FLASH_MMAP_DATA)); + + spi_flash_mmap_dump(); + + printf("Mapping %x (+%x)\n", start, 0x10000); + const void *ptr3; + ESP_ERROR_CHECK( spi_flash_mmap(start, 0x10000, SPI_FLASH_MMAP_DATA, &ptr3, &handle3) ); + printf("mmap_res: handle=%d ptr=%p\n", handle3, ptr3); + + TEST_ASSERT_EQUAL_HEX32(start, spi_flash_cache2phys(ptr3)); + TEST_ASSERT_EQUAL_PTR(ptr3, spi_flash_phys2cache(start, SPI_FLASH_MMAP_DATA)); + TEST_ASSERT_EQUAL_PTR((intptr_t)ptr3 + 0x4444, spi_flash_phys2cache(start + 0x4444, SPI_FLASH_MMAP_DATA)); + + spi_flash_mmap_dump(); + + printf("Unmapping handle1\n"); + spi_flash_munmap(handle1); + handle1 = 0; + spi_flash_mmap_dump(); + + printf("Unmapping handle2\n"); + spi_flash_munmap(handle2); + handle2 = 0; + spi_flash_mmap_dump(); + + printf("Unmapping handle3\n"); + spi_flash_munmap(handle3); + handle3 = 0; + + TEST_ASSERT_EQUAL_PTR(NULL, spi_flash_phys2cache(start, SPI_FLASH_MMAP_DATA)); +} + +TEST_CASE("Can mmap into instruction address space", "[mmap]") +{ + setup_mmap_tests(); printf("Mapping %x (+%x)\n", start, end - start); spi_flash_mmap_handle_t handle1; const void *ptr1; - ESP_ERROR_CHECK( spi_flash_mmap(start, end - start, SPI_FLASH_MMAP_DATA, &ptr1, &handle1) ); + ESP_ERROR_CHECK( spi_flash_mmap(start, end - start, SPI_FLASH_MMAP_INST, &ptr1, &handle1) ); printf("mmap_res: handle=%d ptr=%p\n", handle1, ptr1); spi_flash_mmap_dump(); @@ -84,23 +181,25 @@ TEST_CASE("Can mmap into data address space", "[mmap]") printf("Unmapping handle3\n"); spi_flash_munmap(handle3); + } TEST_CASE("flash_mmap invalidates just-written data", "[spi_flash]") { - spi_flash_mmap_handle_t handle1; const void *ptr1; const size_t test_size = 128; + setup_mmap_tests(); + if (esp_flash_encryption_enabled()) { TEST_IGNORE_MESSAGE("flash encryption enabled, spi_flash_write_encrypted() test won't pass as-is"); } - ESP_ERROR_CHECK( spi_flash_erase_sector(TEST_REGION_START / SPI_FLASH_SEC_SIZE) ); + ESP_ERROR_CHECK( spi_flash_erase_sector(start / SPI_FLASH_SEC_SIZE) ); /* map erased test region to ptr1 */ - ESP_ERROR_CHECK( spi_flash_mmap(TEST_REGION_START, test_size, SPI_FLASH_MMAP_DATA, &ptr1, &handle1) ); + ESP_ERROR_CHECK( spi_flash_mmap(start, test_size, SPI_FLASH_MMAP_DATA, &ptr1, &handle1) ); printf("mmap_res ptr1: handle=%d ptr=%p\n", handle1, ptr1); /* verify it's all 0xFF */ @@ -110,18 +209,19 @@ TEST_CASE("flash_mmap invalidates just-written data", "[spi_flash]") /* unmap the erased region */ spi_flash_munmap(handle1); + handle1 = 0; /* write flash region to 0xEE */ uint8_t buf[test_size]; memset(buf, 0xEE, test_size); - ESP_ERROR_CHECK( spi_flash_write(TEST_REGION_START, buf, test_size) ); + ESP_ERROR_CHECK( spi_flash_write(start, buf, test_size) ); /* re-map the test region at ptr1. this is a fresh mmap call so should trigger a cache flush, ensuring we see the updated flash. */ - ESP_ERROR_CHECK( spi_flash_mmap(TEST_REGION_START, test_size, SPI_FLASH_MMAP_DATA, &ptr1, &handle1) ); + ESP_ERROR_CHECK( spi_flash_mmap(start, test_size, SPI_FLASH_MMAP_DATA, &ptr1, &handle1) ); printf("mmap_res ptr1 #2: handle=%d ptr=%p\n", handle1, ptr1); /* assert that ptr1 now maps to the new values on flash, @@ -130,4 +230,83 @@ TEST_CASE("flash_mmap invalidates just-written data", "[spi_flash]") TEST_ASSERT_EQUAL_HEX8_ARRAY(buf, ptr1, test_size); spi_flash_munmap(handle1); + handle1 = 0; } + +TEST_CASE("phys2cache/cache2phys basic checks", "[spi_flash]") +{ + uint8_t buf[64]; + + static const uint8_t constant_data[] = { 1, 2, 3, 7, 11, 16, 3, 88 }; + + /* esp_partition_find is in IROM */ + uint32_t phys = spi_flash_cache2phys(esp_partition_find); + TEST_ASSERT_NOT_EQUAL(SPI_FLASH_CACHE2PHYS_FAIL, phys); + TEST_ASSERT_EQUAL_PTR(esp_partition_find, spi_flash_phys2cache(phys, SPI_FLASH_MMAP_INST)); + TEST_ASSERT_EQUAL_PTR(NULL, spi_flash_phys2cache(phys, SPI_FLASH_MMAP_DATA)); + + /* Read the flash @ 'phys' and compare it to the data we get via regular cache access */ + spi_flash_read(phys, buf, sizeof(buf)); + TEST_ASSERT_EQUAL_HEX32_ARRAY((void *)esp_partition_find, buf, sizeof(buf)/sizeof(uint32_t)); + + /* spi_flash_mmap is in IRAM */ + printf("%p\n", spi_flash_mmap); + TEST_ASSERT_EQUAL_HEX32(SPI_FLASH_CACHE2PHYS_FAIL, + spi_flash_cache2phys(spi_flash_mmap)); + + /* 'constant_data' should be in DROM */ + phys = spi_flash_cache2phys(&constant_data); + TEST_ASSERT_NOT_EQUAL(SPI_FLASH_CACHE2PHYS_FAIL, phys); + TEST_ASSERT_EQUAL_PTR(&constant_data, + spi_flash_phys2cache(phys, SPI_FLASH_MMAP_DATA)); + TEST_ASSERT_EQUAL_PTR(NULL, spi_flash_phys2cache(phys, SPI_FLASH_MMAP_INST)); + + /* Read the flash @ 'phys' and compare it to the data we get via normal cache access */ + spi_flash_read(phys, buf, sizeof(constant_data)); + TEST_ASSERT_EQUAL_HEX8_ARRAY(constant_data, buf, sizeof(constant_data)); +} + +TEST_CASE("mmap consistent with phys2cache/cache2phys", "[spi_flash]") +{ + const void *ptr = NULL; + const size_t test_size = 2 * SPI_FLASH_MMU_PAGE_SIZE; + + setup_mmap_tests(); + + TEST_ASSERT_EQUAL_HEX(SPI_FLASH_CACHE2PHYS_FAIL, spi_flash_cache2phys(ptr)); + + ESP_ERROR_CHECK( spi_flash_mmap(start, test_size, SPI_FLASH_MMAP_DATA, &ptr, &handle1) ); + TEST_ASSERT_NOT_NULL(ptr); + TEST_ASSERT_NOT_EQUAL(0, handle1); + + TEST_ASSERT_EQUAL_HEX(start, spi_flash_cache2phys(ptr)); + TEST_ASSERT_EQUAL_HEX(start + 1024, spi_flash_cache2phys((void *)((intptr_t)ptr + 1024))); + TEST_ASSERT_EQUAL_HEX(start + 3000, spi_flash_cache2phys((void *)((intptr_t)ptr + 3000))); + /* this pointer lands in a different MMU table entry */ + TEST_ASSERT_EQUAL_HEX(start + test_size - 4, spi_flash_cache2phys((void *)((intptr_t)ptr + test_size - 4))); + + spi_flash_munmap(handle1); + handle1 = 0; + + TEST_ASSERT_EQUAL_HEX(SPI_FLASH_CACHE2PHYS_FAIL, spi_flash_cache2phys(ptr)); +} + +TEST_CASE("munmap followed by mmap flushes cache", "[spi_flash]") +{ + setup_mmap_tests(); + + const esp_partition_t *p = get_test_data_partition(); + + const uint32_t* data; + spi_flash_mmap_handle_t handle; + TEST_ESP_OK( esp_partition_mmap(p, 0, SPI_FLASH_MMU_PAGE_SIZE, + SPI_FLASH_MMAP_DATA, (const void **) &data, &handle) ); + uint32_t buf[16]; + memcpy(buf, data, sizeof(buf)); + + spi_flash_munmap(handle); + TEST_ESP_OK( esp_partition_mmap(p, SPI_FLASH_MMU_PAGE_SIZE, SPI_FLASH_MMU_PAGE_SIZE, + SPI_FLASH_MMAP_DATA, (const void **) &data, &handle) ); + TEST_ASSERT_NOT_EQUAL(0, memcmp(buf, data, sizeof(buf))); +} + diff --git a/components/spi_flash/test/test_read_write.c b/components/spi_flash/test/test_read_write.c index 37edbbb944..aca4850325 100644 --- a/components/spi_flash/test/test_read_write.c +++ b/components/spi_flash/test/test_read_write.c @@ -21,16 +21,24 @@ #include #include +#include #include #include #include "../cache_utils.h" #include "soc/timer_group_struct.h" #include "soc/timer_group_reg.h" -#include "test_config.h" - /* Base offset in flash for tests. */ -#define FLASH_BASE TEST_REGION_START +static size_t start; + +static void setup_tests() +{ + if (start == 0) { + const esp_partition_t *part = get_test_data_partition(); + start = part->address; + printf("Test data partition @ 0x%x\n", start); + } +} #ifndef CONFIG_SPI_FLASH_MINIMAL_TEST #define CONFIG_SPI_FLASH_MINIMAL_TEST 1 @@ -66,21 +74,22 @@ static void IRAM_ATTR test_read(int src_off, int dst_off, int len) fprintf(stderr, "src=%d dst=%d len=%d\n", src_off, dst_off, len); memset(src_buf, 0xAA, sizeof(src_buf)); fill(((char *) src_buf) + src_off, src_off, len); - ESP_ERROR_CHECK(spi_flash_erase_sector((FLASH_BASE + src_off) / SPI_FLASH_SEC_SIZE)); + ESP_ERROR_CHECK(spi_flash_erase_sector((start + src_off) / SPI_FLASH_SEC_SIZE)); spi_flash_disable_interrupts_caches_and_other_cpu(); - SpiFlashOpResult rc = SPIWrite(FLASH_BASE, src_buf, sizeof(src_buf)); + SpiFlashOpResult rc = SPIWrite(start, src_buf, sizeof(src_buf)); spi_flash_enable_interrupts_caches_and_other_cpu(); TEST_ASSERT_EQUAL_INT(rc, SPI_FLASH_RESULT_OK); memset(dst_buf, 0x55, sizeof(dst_buf)); memset(dst_gold, 0x55, sizeof(dst_gold)); fill(dst_gold + dst_off, src_off, len); - ESP_ERROR_CHECK(spi_flash_read(FLASH_BASE + src_off, dst_buf + dst_off, len)); + ESP_ERROR_CHECK(spi_flash_read(start + src_off, dst_buf + dst_off, len)); TEST_ASSERT_EQUAL_INT(cmp_or_dump(dst_buf, dst_gold, sizeof(dst_buf)), 0); } TEST_CASE("Test spi_flash_read", "[spi_flash_read]") { + setup_tests(); #if CONFIG_SPI_FLASH_MINIMAL_TEST test_read(0, 0, 0); test_read(0, 0, 4); @@ -137,7 +146,7 @@ static void IRAM_ATTR test_write(int dst_off, int src_off, int len) memset(src_buf, 0x55, sizeof(src_buf)); fill(src_buf + src_off, src_off, len); // Fills with 0xff - ESP_ERROR_CHECK(spi_flash_erase_sector((FLASH_BASE + dst_off) / SPI_FLASH_SEC_SIZE)); + ESP_ERROR_CHECK(spi_flash_erase_sector((start + dst_off) / SPI_FLASH_SEC_SIZE)); memset(dst_gold, 0xff, sizeof(dst_gold)); if (len > 0) { int pad_left_off = (dst_off & ~3U); @@ -148,9 +157,9 @@ static void IRAM_ATTR test_write(int dst_off, int src_off, int len) } fill(dst_gold + dst_off, src_off, len); } - ESP_ERROR_CHECK(spi_flash_write(FLASH_BASE + dst_off, src_buf + src_off, len)); + ESP_ERROR_CHECK(spi_flash_write(start + dst_off, src_buf + src_off, len)); spi_flash_disable_interrupts_caches_and_other_cpu(); - SpiFlashOpResult rc = SPIRead(FLASH_BASE, dst_buf, sizeof(dst_buf)); + SpiFlashOpResult rc = SPIRead(start, dst_buf, sizeof(dst_buf)); spi_flash_enable_interrupts_caches_and_other_cpu(); TEST_ASSERT_EQUAL_INT(rc, SPI_FLASH_RESULT_OK); TEST_ASSERT_EQUAL_INT(cmp_or_dump(dst_buf, dst_gold, sizeof(dst_buf)), 0); @@ -158,6 +167,7 @@ static void IRAM_ATTR test_write(int dst_off, int src_off, int len) TEST_CASE("Test spi_flash_write", "[spi_flash_write]") { + setup_tests(); #if CONFIG_SPI_FLASH_MINIMAL_TEST test_write(0, 0, 0); test_write(0, 0, 4); @@ -202,8 +212,8 @@ TEST_CASE("Test spi_flash_write", "[spi_flash_write]") * NB: At the moment these only support aligned addresses, because memcpy * is not aware of the 32-but load requirements for these regions. */ - ESP_ERROR_CHECK(spi_flash_write(FLASH_BASE, (char *) 0x40000000, 16)); - ESP_ERROR_CHECK(spi_flash_write(FLASH_BASE, (char *) 0x40070000, 16)); - ESP_ERROR_CHECK(spi_flash_write(FLASH_BASE, (char *) 0x40078000, 16)); - ESP_ERROR_CHECK(spi_flash_write(FLASH_BASE, (char *) 0x40080000, 16)); + ESP_ERROR_CHECK(spi_flash_write(start, (char *) 0x40000000, 16)); + ESP_ERROR_CHECK(spi_flash_write(start, (char *) 0x40070000, 16)); + ESP_ERROR_CHECK(spi_flash_write(start, (char *) 0x40078000, 16)); + ESP_ERROR_CHECK(spi_flash_write(start, (char *) 0x40080000, 16)); } diff --git a/components/spi_flash/test/test_spi_flash.c b/components/spi_flash/test/test_spi_flash.c index 597568ec6a..c7829ead2a 100644 --- a/components/spi_flash/test/test_spi_flash.c +++ b/components/spi_flash/test/test_spi_flash.c @@ -7,8 +7,6 @@ #include #include -#include "test_config.h" - struct flash_test_ctx { uint32_t offset; bool fail; diff --git a/components/tcpip_adapter/tcpip_adapter_lwip.c b/components/tcpip_adapter/tcpip_adapter_lwip.c index 40f235d545..7ee710f39a 100644 --- a/components/tcpip_adapter/tcpip_adapter_lwip.c +++ b/components/tcpip_adapter/tcpip_adapter_lwip.c @@ -60,8 +60,27 @@ void tcpip_adapter_init(void) } } +static netif_init_fn tcpip_if_to_netif_init_fn(tcpip_adapter_if_t tcpip_if) +{ + switch(tcpip_if) { +#ifdef CONFIG_WIFI_ENABLED + case TCPIP_ADAPTER_IF_AP: + case TCPIP_ADAPTER_IF_STA: + return wlanif_init; +#endif +#ifdef CONFIG_ETHERNET + case TCPIP_ADAPTER_IF_ETH: + return ethernetif_init; +#endif + default: + return NULL; + } +} + esp_err_t tcpip_adapter_start(tcpip_adapter_if_t tcpip_if, uint8_t *mac, tcpip_adapter_ip_info_t *ip_info) { + netif_init_fn netif_init; + if (tcpip_if >= TCPIP_ADAPTER_IF_MAX || mac == NULL || ip_info == NULL) { return ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS; } @@ -72,11 +91,10 @@ esp_err_t tcpip_adapter_start(tcpip_adapter_if_t tcpip_if, uint8_t *mac, tcpip_a return ESP_ERR_NO_MEM; } memcpy(esp_netif[tcpip_if]->hwaddr, mac, NETIF_MAX_HWADDR_LEN); - if (tcpip_if == TCPIP_ADAPTER_IF_AP || tcpip_if == TCPIP_ADAPTER_IF_STA) { - netif_add(esp_netif[tcpip_if], &ip_info->ip, &ip_info->netmask, &ip_info->gw, NULL, wlanif_init, tcpip_input); - } else if (tcpip_if == TCPIP_ADAPTER_IF_ETH) { - netif_add(esp_netif[tcpip_if], &ip_info->ip, &ip_info->netmask, &ip_info->gw, NULL, ethernetif_init, tcpip_input); - } + + netif_init = tcpip_if_to_netif_init_fn(tcpip_if); + assert(netif_init != NULL); + netif_add(esp_netif[tcpip_if], &ip_info->ip, &ip_info->netmask, &ip_info->gw, NULL, netif_init, tcpip_input); } if (tcpip_if == TCPIP_ADAPTER_IF_AP) { @@ -713,7 +731,7 @@ esp_err_t tcpip_adapter_set_hostname(tcpip_adapter_if_t tcpip_if, const char *ho { #if LWIP_NETIF_HOSTNAME struct netif *p_netif; - static char hostinfo[TCPIP_HOSTNAME_MAX_SIZE + 1][TCPIP_ADAPTER_IF_MAX]; + static char hostinfo[TCPIP_ADAPTER_IF_MAX][TCPIP_HOSTNAME_MAX_SIZE + 1]; if (tcpip_if >= TCPIP_ADAPTER_IF_MAX || hostname == NULL) { return ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS; @@ -726,7 +744,7 @@ esp_err_t tcpip_adapter_set_hostname(tcpip_adapter_if_t tcpip_if, const char *ho p_netif = esp_netif[tcpip_if]; if (p_netif != NULL) { memset(hostinfo[tcpip_if], 0, sizeof(hostinfo[tcpip_if])); - memcpy(hostinfo[tcpip_if], hostname, strlen(hostname)); + strlcpy(hostinfo[tcpip_if], hostname, sizeof(hostinfo[tcpip_if])); p_netif->hostname = hostinfo[tcpip_if]; return ESP_OK; } else { @@ -757,4 +775,4 @@ esp_err_t tcpip_adapter_get_hostname(tcpip_adapter_if_t tcpip_if, const char **h #endif } -#endif +#endif /* CONFIG_TCPIP_LWIP */ diff --git a/components/ulp/include/esp32/ulp.h b/components/ulp/include/esp32/ulp.h index f4e37e924a..7b434a06ad 100644 --- a/components/ulp/include/esp32/ulp.h +++ b/components/ulp/include/esp32/ulp.h @@ -81,7 +81,7 @@ extern "C" { #define B_CMP_L 0 /*!< Branch if R0 is less than an immediate */ #define B_CMP_GE 1 /*!< Branch if R0 is greater than or equal to an immediate */ -#define OPCODE_END 9 /*!< Stop executing the program (not implemented yet) */ +#define OPCODE_END 9 /*!< Stop executing the program */ #define SUB_OPCODE_END 0 /*!< Stop executing the program and optionally wake up the chip */ #define SUB_OPCODE_SLEEP 1 /*!< Stop executing the program and run it again after selected interval */ @@ -222,7 +222,7 @@ typedef union { struct { uint32_t dreg : 2; /*!< Register where to store temperature measurement result */ uint32_t wait_delay: 14; /*!< Cycles to wait after measurement is done */ - uint32_t cycles: 12; /*!< Cycles used to perform measurement */ + uint32_t reserved: 12; /*!< Reserved, set to 0 */ uint32_t opcode: 4; /*!< Opcode (OPCODE_TSENS) */ } tsens; /*!< Format of TSENS instruction */ @@ -271,7 +271,12 @@ _Static_assert(sizeof(ulp_insn_t) == 4, "ULP coprocessor instruction size should .cycles = cycles_ } } /** - * Halt the coprocessor + * Halt the coprocessor. + * + * This instruction halts the coprocessor, but keeps ULP timer active. + * As such, ULP program will be restarted again by timer. + * To stop the program and prevent the timer from restarting the program, + * use I_END(0) instruction. */ #define I_HALT() { .halt = {\ .unused = 0, \ @@ -307,7 +312,7 @@ static inline uint32_t SOC_REG_TO_ULP_PERIPH_SEL(uint32_t reg) { * This instruction can access RTC_CNTL_, RTC_IO_, and SENS_ peripheral registers. */ #define I_WR_REG(reg, low_bit, high_bit, val) {.wr_reg = {\ - .addr = reg & 0xff, \ + .addr = (reg & 0xff) / sizeof(uint32_t), \ .periph_sel = SOC_REG_TO_ULP_PERIPH_SEL(reg), \ .data = val, \ .low = low_bit, \ @@ -320,8 +325,8 @@ static inline uint32_t SOC_REG_TO_ULP_PERIPH_SEL(uint32_t reg) { * R0 = reg[high_bit : low_bit] * This instruction can access RTC_CNTL_, RTC_IO_, and SENS_ peripheral registers. */ -#define I_RD_REG(reg, low_bit, high_bit, val) {.wr_reg = {\ - .addr = reg & 0xff, \ +#define I_RD_REG(reg, low_bit, high_bit) {.rd_reg = {\ + .addr = (reg & 0xff) / sizeof(uint32_t), \ .periph_sel = SOC_REG_TO_ULP_PERIPH_SEL(reg), \ .unused = 0, \ .low = low_bit, \ @@ -329,25 +334,105 @@ static inline uint32_t SOC_REG_TO_ULP_PERIPH_SEL(uint32_t reg) { .opcode = OPCODE_RD_REG } } /** - * End program. + * Set or clear a bit in the peripheral register. * - * If wake == 1, wake up main CPU. + * Sets bit (1 << shift) of register reg to value val. + * This instruction can access RTC_CNTL_, RTC_IO_, and SENS_ peripheral registers. */ -#define I_END(wake) { .end = { \ - .wakeup = wake, \ +#define I_WR_REG_BIT(reg, shift, val) I_WR_REG(reg, shift, shift, val) + +/** + * Wake the SoC from deep sleep. + * + * This instruction initiates wake up from deep sleep. + * Use esp_deep_sleep_enable_ulp_wakeup to enable deep sleep wakeup + * triggered by the ULP before going into deep sleep. + * Note that ULP program will still keep running until the I_HALT + * instruction, and it will still be restarted by timer at regular + * intervals, even when the SoC is woken up. + * + * To stop the ULP program, use I_HALT instruction. + * + * To disable the timer which start ULP program, use I_END() + * instruction. I_END instruction clears the + * RTC_CNTL_ULP_CP_SLP_TIMER_EN_S bit of RTC_CNTL_STATE0_REG + * register, which controls the ULP timer. + */ +#define I_WAKE() { .end = { \ + .wakeup = 1, \ .unused = 0, \ .sub_opcode = SUB_OPCODE_END, \ .opcode = OPCODE_END } } +/** + * Stop ULP program timer. + * + * This is a convenience macro which disables the ULP program timer. + * Once this instruction is used, ULP program will not be restarted + * anymore until ulp_run function is called. + * + * ULP program will continue running after this instruction. To stop + * the currently running program, use I_HALT(). + */ +#define I_END() \ + I_WR_REG_BIT(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN_S, 0) +/** + * Select the time interval used to run ULP program. + * + * This instructions selects which of the SENS_SLEEP_CYCLES_Sx + * registers' value is used by the ULP program timer. + * When the ULP program stops at I_HALT instruction, ULP program + * timer start counting. When the counter reaches the value of + * the selected SENS_SLEEP_CYCLES_Sx register, ULP program + * start running again from the start address (passed to the ulp_run + * function). + * There are 5 SENS_SLEEP_CYCLES_Sx registers, so 0 <= timer_idx < 5. + * + * By default, SENS_SLEEP_CYCLES_S0 register is used by the ULP + * program timer. + */ +#define I_SLEEP_CYCLE_SEL(timer_idx) { .sleep = { \ + .cycle_sel = timer_idx, \ + .unused = 0, \ + .sub_opcode = SUB_OPCODE_SLEEP, \ + .opcode = OPCODE_END } } + +/** + * Perform temperature sensor measurement and store it into reg_dest. + * + * Delay can be set between 1 and ((1 << 14) - 1). Higher values give + * higher measurement resolution. + */ +#define I_TSENS(reg_dest, delay) { .tsens = { \ + .dreg = reg_dest, \ + .wait_delay = delay, \ + .reserved = 0, \ + .opcode = OPCODE_TSENS } } + +/** + * Perform ADC measurement and store result in reg_dest. + * + * adc_idx selects ADC (0 or 1). + * pad_idx selects ADC pad (0 - 7). + */ +#define I_ADC(reg_dest, adc_idx, pad_idx) { .adc = {\ + .dreg = reg_dest, \ + .mux = pad_idx + 1, \ + .sar_sel = adc_idx, \ + .unused1 = 0, \ + .cycles = 0, \ + .unused2 = 0, \ + .opcode = OPCODE_ADC } } + /** * Store value from register reg_val into RTC memory. * * The value is written to an offset calculated by adding value of * reg_addr register and offset_ field (this offset is expressed in 32-bit words). * 32 bits written to RTC memory are built as follows: - * - 5 MSBs are zero - * - next 11 bits hold the PC of current instruction, expressed in 32-bit words - * - next 16 bits hold the actual value to be written + * - bits [31:21] hold the PC of current instruction, expressed in 32-bit words + * - bits [20:16] = 5'b1 + * - bits [15:0] are assigned the contents of reg_val * * RTC_SLOW_MEM[addr + offset_] = { 5'b0, insn_PC[10:0], val[15:0] } */ diff --git a/components/ulp/test/test_ulp.c b/components/ulp/test/test_ulp.c index f04178623c..b653c48a81 100644 --- a/components/ulp/test/test_ulp.c +++ b/components/ulp/test/test_ulp.c @@ -64,7 +64,7 @@ TEST_CASE("ulp add test", "[ulp]") TEST_ASSERT_EQUAL(10 + 11, RTC_SLOW_MEM[18] & 0xffff); } -TEST_CASE("ulp branch test", "[ulp][ignore]") +TEST_CASE("ulp branch test", "[ulp]") { assert(CONFIG_ULP_COPROC_RESERVE_MEM >= 260 && "this test needs ULP_COPROC_RESERVE_MEM option set in menuconfig"); memset(RTC_SLOW_MEM, 0, CONFIG_ULP_COPROC_RESERVE_MEM); @@ -112,7 +112,9 @@ TEST_CASE("ulp wakeup test", "[ulp][ignore]") I_MOVI(R2, 42), I_MOVI(R3, 15), I_ST(R2, R3, 0), - I_END(1) + I_WAKE(), + I_END(), + I_HALT() }; size_t size = sizeof(program)/sizeof(ulp_insn_t); ulp_process_macros_and_load(0, program, &size); @@ -121,6 +123,100 @@ TEST_CASE("ulp wakeup test", "[ulp][ignore]") esp_deep_sleep_start(); } +TEST_CASE("ulp can write and read peripheral registers", "[ulp]") +{ + assert(CONFIG_ULP_COPROC_RESERVE_MEM >= 260 && "this test needs ULP_COPROC_RESERVE_MEM option set in menuconfig"); + CLEAR_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN); + memset(RTC_SLOW_MEM, 0, CONFIG_ULP_COPROC_RESERVE_MEM); + REG_WRITE(RTC_CNTL_STORE1_REG, 0x89abcdef); + + const ulp_insn_t program[] = { + I_MOVI(R1, 64), + I_RD_REG(RTC_CNTL_STORE1_REG, 0, 15), + I_ST(R0, R1, 0), + I_RD_REG(RTC_CNTL_STORE1_REG, 4, 11), + I_ST(R0, R1, 1), + I_RD_REG(RTC_CNTL_STORE1_REG, 16, 31), + I_ST(R0, R1, 2), + I_RD_REG(RTC_CNTL_STORE1_REG, 20, 27), + I_ST(R0, R1, 3), + I_WR_REG(RTC_CNTL_STORE0_REG, 0, 7, 0x89), + I_WR_REG(RTC_CNTL_STORE0_REG, 8, 15, 0xab), + I_WR_REG(RTC_CNTL_STORE0_REG, 16, 23, 0xcd), + I_WR_REG(RTC_CNTL_STORE0_REG, 24, 31, 0xef), + I_LD(R0, R1, 4), + I_ADDI(R0, R0, 1), + I_ST(R0, R1, 4), + I_END(), + I_HALT() + }; + size_t size = sizeof(program)/sizeof(ulp_insn_t); + TEST_ESP_OK(ulp_process_macros_and_load(0, program, &size)); + TEST_ESP_OK(ulp_run(0)); + vTaskDelay(100/portTICK_PERIOD_MS); + + TEST_ASSERT_EQUAL_HEX32(0xefcdab89, REG_READ(RTC_CNTL_STORE0_REG)); + TEST_ASSERT_EQUAL_HEX16(0xcdef, RTC_SLOW_MEM[64] & 0xffff); + TEST_ASSERT_EQUAL_HEX16(0xde, RTC_SLOW_MEM[65] & 0xffff); + TEST_ASSERT_EQUAL_HEX16(0x89ab, RTC_SLOW_MEM[66] & 0xffff); + TEST_ASSERT_EQUAL_HEX16(0x9a, RTC_SLOW_MEM[67] & 0xffff); + TEST_ASSERT_EQUAL_HEX32(1 | (15 << 21) | (1 << 16), RTC_SLOW_MEM[68]); +} + +TEST_CASE("ULP I_WR_REG instruction test", "[ulp]") +{ + assert(CONFIG_ULP_COPROC_RESERVE_MEM >= 260 && "this test needs ULP_COPROC_RESERVE_MEM option set in menuconfig"); + memset(RTC_SLOW_MEM, 0, CONFIG_ULP_COPROC_RESERVE_MEM); + typedef struct { + int low; + int width; + } wr_reg_test_item_t; + + const wr_reg_test_item_t test_items[] = { + {0, 1}, {0, 2}, {0, 3}, {0, 4}, {0, 5}, {0, 6}, {0, 7}, {0, 8}, + {3, 1}, {3, 2}, {3, 3}, {3, 4}, {3, 5}, {3, 6}, {3, 7}, {3, 8}, + {15, 1}, {15, 2}, {15, 3}, {15, 4}, {15, 5}, {15, 6}, {15, 7}, {15, 8}, + {16, 1}, {16, 2}, {16, 3}, {16, 4}, {16, 5}, {16, 6}, {16, 7}, {16, 8}, + {18, 1}, {18, 2}, {18, 3}, {18, 4}, {18, 5}, {18, 6}, {18, 7}, {18, 8}, + {24, 1}, {24, 2}, {24, 3}, {24, 4}, {24, 5}, {24, 6}, {24, 7}, {24, 8}, + }; + + const size_t test_items_count = + sizeof(test_items)/sizeof(test_items[0]); + for (size_t i = 0; i < test_items_count; ++i) { + const uint32_t mask = (uint32_t) (((1ULL << test_items[i].width) - 1) << test_items[i].low); + const uint32_t not_mask = ~mask; + printf("#%2d: low: %2d width: %2d mask: %08x expected: %08x ", i, + test_items[i].low, test_items[i].width, + mask, not_mask); + + REG_WRITE(RTC_CNTL_STORE0_REG, 0xffffffff); + REG_WRITE(RTC_CNTL_STORE1_REG, 0x00000000); + const ulp_insn_t program[] = { + I_WR_REG(RTC_CNTL_STORE0_REG, + test_items[i].low, + test_items[i].low + test_items[i].width - 1, + 0), + I_WR_REG(RTC_CNTL_STORE1_REG, + test_items[i].low, + test_items[i].low + test_items[i].width - 1, + 0xff & ((1 << test_items[i].width) - 1)), + I_END(), + I_HALT() + }; + size_t size = sizeof(program)/sizeof(ulp_insn_t); + ulp_process_macros_and_load(0, program, &size); + ulp_run(0); + vTaskDelay(10/portTICK_PERIOD_MS); + uint32_t clear = REG_READ(RTC_CNTL_STORE0_REG); + uint32_t set = REG_READ(RTC_CNTL_STORE1_REG); + printf("clear: %08x set: %08x\n", clear, set); + TEST_ASSERT_EQUAL_HEX32(not_mask, clear); + TEST_ASSERT_EQUAL_HEX32(mask, set); + } +} + + TEST_CASE("ulp controls RTC_IO", "[ulp][ignore]") { assert(CONFIG_ULP_COPROC_RESERVE_MEM >= 260 && "this test needs ULP_COPROC_RESERVE_MEM option set in menuconfig"); @@ -149,7 +245,9 @@ TEST_CASE("ulp controls RTC_IO", "[ulp][ignore]") M_LABEL(5), M_BX(4), M_LABEL(6), - I_END(1) // wake up the SoC + I_WAKE(), // wake up the SoC + I_END(), // stop ULP program timer + I_HALT() }; const gpio_num_t led_gpios[] = { GPIO_NUM_2, @@ -168,3 +266,198 @@ TEST_CASE("ulp controls RTC_IO", "[ulp][ignore]") esp_deep_sleep_start(); } +TEST_CASE("ulp power consumption in deep sleep", "[ulp][ignore]") +{ + assert(CONFIG_ULP_COPROC_RESERVE_MEM >= 4 && "this test needs ULP_COPROC_RESERVE_MEM option set in menuconfig"); + ulp_insn_t insn = I_HALT(); + RTC_SLOW_MEM[0] = *(uint32_t*) &insn; + + REG_WRITE(SENS_ULP_CP_SLEEP_CYC0_REG, 0x8000); + + ulp_run(0); + + esp_deep_sleep_enable_ulp_wakeup(); + esp_deep_sleep_enable_timer_wakeup(10 * 1000000); + esp_deep_sleep_start(); +} + +TEST_CASE("ulp timer setting", "[ulp]") +{ + /* + * Run a simple ULP program which increments the counter, for one second. + * Program calls I_HALT each time and gets restarted by the timer. + * Compare the expected number of times the program runs with the actual. + */ + assert(CONFIG_ULP_COPROC_RESERVE_MEM >= 32 && "this test needs ULP_COPROC_RESERVE_MEM option set in menuconfig"); + memset(RTC_SLOW_MEM, 0, CONFIG_ULP_COPROC_RESERVE_MEM); + + const int offset = 6; + const ulp_insn_t program[] = { + I_MOVI(R1, offset), // r1 <- offset + I_LD(R2, R1, 0), // load counter + I_ADDI(R2, R2, 1), // counter += 1 + I_ST(R2, R1, 0), // save counter + I_HALT(), + }; + + size_t size = sizeof(program)/sizeof(ulp_insn_t); + TEST_ESP_OK(ulp_process_macros_and_load(0, program, &size)); + assert(offset >= size && "data offset needs to be greater or equal to program size"); + TEST_ESP_OK(ulp_run(0)); + // disable the ULP program timer — we will enable it later + CLEAR_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN); + + const uint32_t cycles_to_test[] = {0x80, 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000}; + const size_t tests_count = sizeof(cycles_to_test) / sizeof(cycles_to_test[0]); + for (size_t i = 0; i < tests_count; ++i) { + // zero out the counter + RTC_SLOW_MEM[offset] = 0; + // set the number of slow clock cycles + REG_WRITE(SENS_ULP_CP_SLEEP_CYC0_REG, cycles_to_test[i]); + // enable the timer and wait for a second + SET_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN); + vTaskDelay(1000 / portTICK_PERIOD_MS); + // get the counter value and stop the timer + uint32_t counter = RTC_SLOW_MEM[offset] & 0xffff; + CLEAR_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN); + // compare the actual and expected numbers of iterations of ULP program + float expected_period = (cycles_to_test[i] + 16) / (float) RTC_CNTL_SLOWCLK_FREQ + 5 / 8e6f; + float error = 1.0f - counter * expected_period; + printf("%u\t%u\t%.01f\t%.04f\n", cycles_to_test[i], counter, 1.0f / expected_period, error); + // Should be within 15% + TEST_ASSERT_INT_WITHIN(15, 0, (int) error * 100); + // Note: currently RTC_CNTL_SLOWCLK_FREQ is ballpark value — we need to determine it + // Precisely by running calibration similar to the one done in deep sleep. + // This may cause the test to fail on some chips which have the slow clock frequency + // way off. + } +} + +TEST_CASE("ulp can use TSENS in deep sleep", "[ulp][ignore]") +{ + assert(CONFIG_ULP_COPROC_RESERVE_MEM >= 260 && "this test needs ULP_COPROC_RESERVE_MEM option set in menuconfig"); + + hexdump(RTC_SLOW_MEM, CONFIG_ULP_COPROC_RESERVE_MEM / 4); + printf("\n\n"); + memset(RTC_SLOW_MEM, 0, CONFIG_ULP_COPROC_RESERVE_MEM); + + // Allow TSENS to be controlled by the ULP + SET_PERI_REG_BITS(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_CLK_DIV, 10, SENS_TSENS_CLK_DIV_S); + SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR, 3, SENS_FORCE_XPD_SAR_S); + CLEAR_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_POWER_UP); + CLEAR_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_DUMP_OUT); + CLEAR_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_POWER_UP_FORCE); + + // data start offset + size_t offset = 20; + // number of samples to collect + RTC_SLOW_MEM[offset] = (CONFIG_ULP_COPROC_RESERVE_MEM) / 4 - offset - 8; + // sample counter + RTC_SLOW_MEM[offset + 1] = 0; + + const ulp_insn_t program[] = { + I_MOVI(R1, offset), // r1 <- offset + I_LD(R2, R1, 1), // r2 <- counter + I_LD(R3, R1, 0), // r3 <- length + I_SUBI(R3, R3, 1), // end = length - 1 + I_SUBR(R3, R3, R2), // r3 = length - counter + M_BXF(1), // if overflow goto 1: + I_WR_REG(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR_S, SENS_FORCE_XPD_SAR_S + 1, 3), + I_TSENS(R0, 16383), // r0 <- tsens + I_WR_REG(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR_S, SENS_FORCE_XPD_SAR_S + 1, 0), + I_ST(R0, R2, offset + 4), + I_ADDI(R2, R2, 1), // counter += 1 + I_ST(R2, R1, 1), // save counter + I_HALT(), // enter sleep + M_LABEL(1), // done with measurements + I_END(), // stop ULP timer + I_WAKE(), // initiate wakeup + I_HALT() + }; + + size_t size = sizeof(program)/sizeof(ulp_insn_t); + TEST_ESP_OK(ulp_process_macros_and_load(0, program, &size)); + assert(offset >= size); + + TEST_ESP_OK(ulp_run(0)); + esp_deep_sleep_enable_timer_wakeup(4000000); + esp_deep_sleep_enable_ulp_wakeup(); + esp_deep_sleep_start(); +} + +TEST_CASE("can use ADC in deep sleep", "[ulp][ignore]") +{ + assert(CONFIG_ULP_COPROC_RESERVE_MEM >= 260 && "this test needs ULP_COPROC_RESERVE_MEM option set in menuconfig"); + + hexdump(RTC_SLOW_MEM, CONFIG_ULP_COPROC_RESERVE_MEM / 4); + printf("\n\n"); + memset(RTC_SLOW_MEM, 0, CONFIG_ULP_COPROC_RESERVE_MEM); + + SET_PERI_REG_BITS(SENS_SAR_START_FORCE_REG, SENS_SAR1_BIT_WIDTH, 3, SENS_SAR1_BIT_WIDTH_S); + SET_PERI_REG_BITS(SENS_SAR_START_FORCE_REG, SENS_SAR2_BIT_WIDTH, 3, SENS_SAR2_BIT_WIDTH_S); + + SET_PERI_REG_BITS(SENS_SAR_READ_CTRL_REG, SENS_SAR1_SAMPLE_BIT, 0x3, SENS_SAR1_SAMPLE_BIT_S); + SET_PERI_REG_BITS(SENS_SAR_READ_CTRL2_REG, SENS_SAR2_SAMPLE_BIT, 0x3, SENS_SAR2_SAMPLE_BIT_S); + + CLEAR_PERI_REG_MASK(SENS_SAR_MEAS_START2_REG, SENS_MEAS2_START_FORCE); + CLEAR_PERI_REG_MASK(SENS_SAR_MEAS_START1_REG, SENS_MEAS1_START_FORCE); + + SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR, 0, SENS_FORCE_XPD_SAR_S); + SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_AMP, 2, SENS_FORCE_XPD_AMP_S); + +// SAR1 invert result + SET_PERI_REG_MASK(SENS_SAR_READ_CTRL_REG, SENS_SAR1_DATA_INV); + SET_PERI_REG_MASK(SENS_SAR_READ_CTRL_REG, SENS_SAR2_DATA_INV); + + +// const int adc = 1; +// const int channel = 1; +// const int atten = 3; +// const int gpio_num = 0; + + const int adc = 0; + const int channel = 0; + const int atten = 0; + const int gpio_num = 36; + + rtc_gpio_init(gpio_num); + + CLEAR_PERI_REG_MASK(SENS_SAR_MEAS_START1_REG, SENS_SAR1_EN_PAD_FORCE_M); + CLEAR_PERI_REG_MASK(SENS_SAR_MEAS_START2_REG, SENS_SAR2_EN_PAD_FORCE_M); + + SET_PERI_REG_BITS(SENS_SAR_ATTEN1_REG, 3, atten, 2 * channel); //set SAR1 attenuation + SET_PERI_REG_BITS(SENS_SAR_ATTEN2_REG, 3, atten, 2 * channel); //set SAR2 attenuation + + // data start offset + size_t offset = 20; + // number of samples to collect + RTC_SLOW_MEM[offset] = (CONFIG_ULP_COPROC_RESERVE_MEM) / 4 - offset - 8; + // sample counter + RTC_SLOW_MEM[offset + 1] = 0; + + const ulp_insn_t program[] = { + I_MOVI(R1, offset), // r1 <- offset + I_LD(R2, R1, 1), // r2 <- counter + I_LD(R3, R1, 0), // r3 <- length + I_SUBI(R3, R3, 1), // end = length - 1 + I_SUBR(R3, R3, R2), // r3 = length - counter + M_BXF(1), // if overflow goto 1: + I_ADC(R0, adc, channel), // r0 <- ADC + I_ST(R0, R2, offset + 4), + I_ADDI(R2, R2, 1), // counter += 1 + I_ST(R2, R1, 1), // save counter + I_HALT(), + M_LABEL(1), // done with measurements + I_END(), // stop ULP program timer + I_HALT() + }; + + size_t size = sizeof(program)/sizeof(ulp_insn_t); + TEST_ESP_OK(ulp_process_macros_and_load(0, program, &size)); + assert(offset >= size); + + TEST_ESP_OK(ulp_run(0)); + esp_deep_sleep_enable_timer_wakeup(4000000); + esp_deep_sleep_start(); +} + diff --git a/components/ulp/ulp.c b/components/ulp/ulp.c index 60fa292f7c..a6e462d1bf 100644 --- a/components/ulp/ulp.c +++ b/components/ulp/ulp.c @@ -265,12 +265,16 @@ esp_err_t ulp_run(uint32_t entry_point) { // disable ULP timer CLEAR_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN); + // wait for at least 1 RTC_SLOW_CLK cycle + ets_delay_us(10); // set entry point SET_PERI_REG_BITS(SENS_SAR_START_FORCE_REG, SENS_PC_INIT_V, entry_point, SENS_PC_INIT_S); // disable force start CLEAR_PERI_REG_MASK(SENS_SAR_START_FORCE_REG, SENS_ULP_CP_FORCE_START_TOP_M); // make sure voltage is raised when RTC 8MCLK is enabled SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BIAS_I2C_FOLW_8M); + SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BIAS_CORE_FOLW_8M); + SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BIAS_SLEEP_FOLW_8M); // enable ULP timer SET_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN); return ESP_OK; diff --git a/docs/api/bluetooth/esp_gap_ble.rst b/docs/api/bluetooth/esp_gap_ble.rst index fea9376304..41bffa0956 100644 --- a/docs/api/bluetooth/esp_gap_ble.rst +++ b/docs/api/bluetooth/esp_gap_ble.rst @@ -93,6 +93,12 @@ Structures .. doxygenstruct:: esp_ble_gap_cb_param_t::ble_scan_rsp_data_raw_cmpl_evt_param :members: +.. doxygenstruct:: esp_ble_gap_cb_param_t::ble_adv_start_cmpl_evt_param + :members: + +.. doxygenstruct:: esp_ble_gap_cb_param_t::ble_scan_start_cmpl_evt_param + :members: + Functions ^^^^^^^^^ diff --git a/docs/api/storage/fatfs.rst b/docs/api/storage/fatfs.rst index d2efc87abf..6dd04ae3e6 100644 --- a/docs/api/storage/fatfs.rst +++ b/docs/api/storage/fatfs.rst @@ -8,7 +8,7 @@ Additionally, FatFs has been modified to support run-time pluggable disk IO laye Using FatFs with VFS -------------------- -``esp_vfs_fat.h`` header file defines functions to connect FatFs with VFS. ``esp_vfs_fat_register`` function allocates a ``FATFS`` structure, and registers a given path prefix in VFS. Subsequent operations on files starting with this prefix are forwarded to FatFs APIs. ``esp_vfs_fat_unregister`` function deletes the registration with VFS, and frees the ``FATFS`` structure. +``esp_vfs_fat.h`` header file defines functions to connect FatFs with VFS. ``esp_vfs_fat_register`` function allocates a ``FATFS`` structure, and registers a given path prefix in VFS. Subsequent operations on files starting with this prefix are forwarded to FatFs APIs. ``esp_vfs_fat_unregister_path`` function deletes the registration with VFS, and frees the ``FATFS`` structure. Most applications will use the following flow when working with ``esp_vfs_fat_`` functions: @@ -28,12 +28,12 @@ Most applications will use the following flow when working with ``esp_vfs_fat_`` 8. Call ``ff_diskio_register`` with NULL ``ff_diskio_impl_t*`` argument and the same drive number. -9. Call ``esp_vfs_fat_unregister`` to remove FatFs from VFS, and free the ``FATFS`` structure allocated on step 1. +9. Call ``esp_vfs_fat_unregister_path`` with the path where the file system is mounted to remove FatFs from VFS, and free the ``FATFS`` structure allocated on step 1. Convenience functions, ``esp_vfs_fat_sdmmc_mount`` and ``esp_vfs_fat_sdmmc_unmount``, which wrap these steps and also handle SD card initialization, are described in the next section. .. doxygenfunction:: esp_vfs_fat_register -.. doxygenfunction:: esp_vfs_fat_unregister +.. doxygenfunction:: esp_vfs_fat_unregister_path Using FatFs with VFS and SD cards diff --git a/docs/api/storage/sdmmc.rst b/docs/api/storage/sdmmc.rst index fe2e5c27f8..4ea2f62606 100644 --- a/docs/api/storage/sdmmc.rst +++ b/docs/api/storage/sdmmc.rst @@ -79,6 +79,7 @@ Of all the funtions listed below, only ``sdmmc_host_init``, ``sdmmc_host_init_sl .. doxygendefine:: SDMMC_HOST_SLOT_0 .. doxygendefine:: SDMMC_HOST_SLOT_1 .. doxygendefine:: SDMMC_HOST_DEFAULT +.. doxygendefine:: SDMMC_SLOT_WIDTH_DEFAULT .. doxygenfunction:: sdmmc_host_init_slot diff --git a/docs/api/storage/spi_flash.rst b/docs/api/storage/spi_flash.rst index 5725ceb0c9..c42aedb4b0 100644 --- a/docs/api/storage/spi_flash.rst +++ b/docs/api/storage/spi_flash.rst @@ -1,5 +1,12 @@ .. include:: ../../../components/spi_flash/README.rst +See also +-------- + +- :doc:`Partition Table documentation ` +- :doc:`Over The Air Update (OTA) API ` provides high-level API for updating app firmware stored in flash. +- :doc:`Non-Volatile Storage (NVS) API ` provides a structured API for storing small items of data in SPI flash. + API Reference ------------- @@ -19,6 +26,7 @@ Macros .. doxygendefine:: SPI_FLASH_SEC_SIZE .. doxygendefine:: SPI_FLASH_MMU_PAGE_SIZE .. doxygendefine:: ESP_PARTITION_SUBTYPE_OTA +.. doxygendefine:: SPI_FLASH_CACHE2PHYS_FAIL Type Definitions ^^^^^^^^^^^^^^^^ @@ -52,6 +60,9 @@ Functions .. doxygenfunction:: spi_flash_mmap .. doxygenfunction:: spi_flash_munmap .. doxygenfunction:: spi_flash_mmap_dump +.. doxygenfunction:: spi_flash_cache2phys +.. doxygenfunction:: spi_flash_phys2cache +.. doxygenfunction:: spi_flash_cache_enabled .. doxygenfunction:: esp_partition_find .. doxygenfunction:: esp_partition_find_first .. doxygenfunction:: esp_partition_get @@ -63,3 +74,37 @@ Functions .. doxygenfunction:: esp_partition_mmap .. doxygenfunction:: esp_flash_encryption_enabled +.. _spi-flash-implementation-details: + +Implementation details +---------------------- + +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/docs/api/system/deep_sleep.rst b/docs/api/system/deep_sleep.rst index 87986026df..407b14bae0 100644 --- a/docs/api/system/deep_sleep.rst +++ b/docs/api/system/deep_sleep.rst @@ -26,6 +26,15 @@ The following function can be used to enable deep sleep wakeup using a timer. .. doxygenfunction:: esp_deep_sleep_enable_timer_wakeup +Touch pad +^^^^^^^^^ + +RTC IO module contains logic to trigger wakeup when a touch sensor interrupt occurs. You need to configure the touch pad interrupt before the chip starts deep sleep. + +Revisions 0 and 1 of the ESP32 only support this wakeup mode when RTC peripherals are not forced to be powered on (i.e. ESP_PD_DOMAIN_RTC_PERIPH should be set to ESP_PD_OPTION_AUTO). + +.. doxygenfunction:: esp_deep_sleep_enable_touchpad_wakeup + External wakeup (ext0) ^^^^^^^^^^^^^^^^^^^^^^ @@ -34,6 +43,10 @@ RTC IO module contains logic to trigger wakeup when one of RTC GPIOs is set to a Because RTC IO module is enabled in this mode, internal pullup or pulldown resistors can also be used. They need to be configured by the application using ``rtc_gpio_pullup_en`` and ``rtc_gpio_pulldown_en`` functions, before calling ``esp_deep_sleep_start``. +In revisions 0 and 1 of the ESP32, this wakeup source is incompatible with ULP and touch wakeup sources. + +.. warning:: After wake up from deep sleep, IO pad used for wakeup will be configured as RTC IO. Before using this pad as digital GPIO, reconfigure it using ``rtc_gpio_deinit(gpio_num)`` function. + .. doxygenfunction:: esp_deep_sleep_enable_ext0_wakeup External wakeup (ext1) @@ -41,14 +54,16 @@ External wakeup (ext1) RTC controller contains logic to trigger wakeup using multiple RTC GPIOs. One of the two logic functions can be used to trigger wakeup: - - wake up if any of the selected pins is low - - wake up if all the selected pins are high + - wake up if any of the selected pins is high (``ESP_EXT1_WAKEUP_ANY_HIGH``) + - wake up if all the selected pins are low (``ESP_EXT1_WAKEUP_ALL_LOW``) This wakeup source is implemented by the RTC controller. As such, RTC peripherals and RTC memories can be powered off in this mode. However, if RTC peripherals are powered down, internal pullup and pulldown resistors will be disabled. To use internal pullup or pulldown resistors, request RTC peripherals power domain to be kept on during deep sleep, and configure pullup/pulldown resistors using ``rtc_gpio_`` functions, before entering deep sleep:: esp_deep_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); gpio_pullup_dis(gpio_num); gpio_pulldown_en(gpio_num); + +.. warning:: After wake up from deep sleep, IO pad(s) used for wakeup will be configured as RTC IO. Before using these pads as digital GPIOs, reconfigure them using ``rtc_gpio_deinit(gpio_num)`` function. The following function can be used to enable this wakeup mode: @@ -60,7 +75,9 @@ The following function can be used to enable this wakeup mode: ULP coprocessor wakeup ^^^^^^^^^^^^^^^^^^^^^^ -ULP coprocessor can run while the chip is in deep sleep, and may be used to poll sensors, monitor ADC or touch sensor values, and wake up the chip when a specific event is detected. ULP coprocessor is part of RTC peripherals power domain, and it runs the program stored in RTC slow memeory. Therefore RTC peripherals and RTC slow memory will be powered on during deep sleep if this wakeup mode is requested. +ULP coprocessor can run while the chip is in deep sleep, and may be used to poll sensors, monitor ADC or touch sensor values, and wake up the chip when a specific event is detected. ULP coprocessor is part of RTC peripherals power domain, and it runs the program stored in RTC slow memeory. RTC slow memory will be powered on during deep sleep if this wakeup mode is requested. RTC peripherals will be automatically powered on before ULP coprocessor starts running the program; once the program stops running, RTC peripherals are automatically powered down again. + +Revisions 0 and 1 of the ESP32 only support this wakeup mode when RTC peripherals are not forced to be powered on (i.e. ESP_PD_DOMAIN_RTC_PERIPH should be set to ESP_PD_OPTION_AUTO). The following function can be used to enable this wakeup mode: @@ -71,7 +88,7 @@ Power-down of RTC peripherals and memories By default, ``esp_deep_sleep_start`` function will power down all RTC power domains which are not needed by the enabled wakeup sources. To override this behaviour, the following function is provided: -Note: on the first revision of the ESP32, RTC fast memory will always be kept enabled in deep sleep, so that the deep sleep stub can run after reset. This can be overriden, if the application doesn't need clean reset behaviour after deep sleep. +Note: in revision 0 of the ESP32, RTC fast memory will always be kept enabled in deep sleep, so that the deep sleep stub can run after reset. This can be overriden, if the application doesn't need clean reset behaviour after deep sleep. If some variables in the program are placed into RTC slow memory (for example, using ``RTC_DATA_ATTR`` attribute), RTC slow memory will be kept powered on by default. This can be overriden using ``esp_deep_sleep_pd_config`` function, if desired. @@ -87,8 +104,20 @@ The following function can be used to enter deep sleep once wakeup sources are c .. doxygenfunction:: esp_deep_sleep_start +Checking deep sleep wakeup cause +-------------------------------- + +The following function can be used to check which wakeup source has triggered wakeup from deep sleep mode. For touch pad and ext1 wakeup sources, it is possible to identify pin or touch pad which has caused wakeup. + +.. doxygenfunction:: esp_deep_sleep_get_wakeup_cause +.. doxygenenum:: esp_deep_sleep_wakeup_cause_t +.. doxygenfunction:: esp_deep_sleep_get_touchpad_wakeup_status +.. doxygenfunction:: esp_deep_sleep_get_ext1_wakeup_status + + Application Example ------------------- Implementation of basic functionality of deep sleep is shown in :example:`protocols/sntp` example, where ESP module is periodically waken up to retrive time from NTP server. +More extensive example in :example:`system/deep_sleep` illustrates usage of various deep sleep wakeup triggers and ULP coprocessor programming. diff --git a/docs/api/system/index.rst b/docs/api/system/index.rst index a7d5d39018..321dc70d31 100644 --- a/docs/api/system/index.rst +++ b/docs/api/system/index.rst @@ -7,7 +7,7 @@ System API Memory Allocation Interrupt Allocation Watchdogs - OTA + Over The Air Updates (OTA) Deep Sleep Logging diff --git a/docs/api/system/intr_alloc.rst b/docs/api/system/intr_alloc.rst index 015cc32cd2..8abe47e0fe 100644 --- a/docs/api/system/intr_alloc.rst +++ b/docs/api/system/intr_alloc.rst @@ -32,30 +32,56 @@ even when the int for DevB was cleared) the interrupt is never serviced.) Multicore issues ---------------- -Peripherals that can generate interrupts can be divided in two types: external peripherals, outside the Xtensa -cores in the ESP32, and internal peripherals, inside the ESP32. Interrupt handling differs slightly between -these two types of peripherals. +Peripherals that can generate interrupts can be divided in two types: -Each Xtensa core has its own set of internal peripherals: three timer comparators, a performance monitor and two -software interrupts. These peripherals can only be configured from the core they are associated with. When -generating an interrupt, the interrupt they generate is hard-wired to their associated core; it's not possible -to have e.g. an internal timer comparator of one core generate an interrupt on another core. That is why these -sources can only be managed using a task running on that specific core. Internal interrupt sources are still -allocatable using esp_intr_alloc as normal, but they cannot be shared and will always have a fixed interrupt -level (namely, the one associated in hardware with the peripheral). Internal interrupt sources are defined -in esp_intr_alloc.h as ETS_INTERNAL_*_INTR_SOURCE. + - External peripherals, within the ESP32 but outside the Xtensa cores themselves. Most ESP32 peripherals are of this type. + - Internal peripherals, part of the Xtensa CPU cores themselves. -The remaining interrupt slots in both cores are wired to an interrupt multiplexer, which can be used to -route any external interrupt source to any of these interrupt slots. Allocating an external interrupt will always -allocate it on the core that does the allocation, and freeing the interrupt should always happen on the same -core. Disabling and enabling the interrupt from another core is allowed, however. External interrupts can -share an interrupt slot bu passing ESP_INTR_FLAG_SHARED as a flag to esp_intr_alloc. External interrupt sources -are defined in soc/soc.h as ETS_*_INTR_SOURCE. +Interrupt handling differs slightly between these two types of peripherals. -Care should be taken when allocating an interrupt using a task not pinned to a certain core; while running -code not in a critical secion, these tasks can migrate between cores at any moment, possibly making an -interrupt operation fail because of the reasons mentioned above. It is advised to always use -xTaskCreatePinnedToCore with a specific CoreID argument to create tasks that will handle interrupts. +Internal peripheral interrupts +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Each Xtensa CPU core has its own set of six internal peripherals: + + - Three timer comparators + - A performance monitor + - Two software interrupts. + +Internal interrupt sources are defined in esp_intr_alloc.h as ``ETS_INTERNAL_*_INTR_SOURCE``. + +These peripherals can only be configured from the core they are associated with. When generating an interrupt, +the interrupt they generate is hard-wired to their associated core; it's not possible to have e.g. an internal +timer comparator of one core generate an interrupt on another core. That is why these sources can only be managed +using a task running on that specific core. Internal interrupt sources are still allocatable using esp_intr_alloc +as normal, but they cannot be shared and will always have a fixed interrupt level (namely, the one associated in +hardware with the peripheral). + +External Peripheral Interrupts +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The remaining interrupt sources are from external peripherals. These are defined in soc/soc.h as ``ETS_*_INTR_SOURCE``. + +Non-internal interrupt slots in both CPU cores are wired to an interrupt multiplexer, which can be used to +route any external interrupt source to any of these interrupt slots. + +- Allocating an external interrupt will always allocate it on the core that does the allocation. +- Freeing an external interrupt must always happen on the same core it was allocated on. +- Disabling and enabling external interrupts from another core is allowed. +- Multiple external interrupt sources can share an interrupt slot by passing ``ESP_INTR_FLAG_SHARED`` as a flag to esp_intr_alloc(). + +Care should be taken when calling esp_intr_alloc() from a task which is not pinned to a core. During task switching, these tasks can migrate between cores. Therefore it is impossible to tell which CPU the interrupt is allocated on, which makes it difficult to free the interrupt handle and may also cause debugging difficulties. It is advised to use xTaskCreatePinnedToCore() with a specific CoreID argument to create tasks that will allocate interrupts. In the case of internal interrupt sources, this is required. + +IRAM-Safe Interrupt Handlers +---------------------------- + +The ``ESP_INTR_FLAG_IRAM`` flag registers an interrupt handler that always runs from IRAM (and reads all its data from DRAM), and therefore does not need to be disabled during flash erase and write operations. + +This is useful for interrupts which need a guaranteed minimum execution latency, as flash write and erase operations can be slow (erases can take tens or hundreds of milliseconds to complete). + +It can also be useful to keep an interrupt handler in IRAM if it is called very frequently, to avoid flash cache misses. + +Refer to the :ref:`SPI flash API documentation ` for more details. Application Example ------------------- @@ -86,15 +112,6 @@ Macros .. doxygendefine:: ESP_INTR_FLAG_IRAM .. doxygendefine:: ESP_INTR_FLAG_INTRDISABLED -Type Definitions -^^^^^^^^^^^^^^^^ - -Enumerations -^^^^^^^^^^^^ - -Structures -^^^^^^^^^^ - Functions ^^^^^^^^^ diff --git a/docs/api/system/ota.rst b/docs/api/system/ota.rst index 6ea4ea1f85..eaabeae3a8 100644 --- a/docs/api/system/ota.rst +++ b/docs/api/system/ota.rst @@ -1,10 +1,47 @@ -OTA -=== +Over The Air Updates (OTA) +========================== + +OTA Process Overview +^^^^^^^^^^^^^^^^^^^^ + +The OTA update mechanism allows a device to update itself based on data received while the normal firmware is running +(for example, over WiFi or Bluetooth.) + +OTA requires configuring the :doc:`Partition Table ` of the device with at least two "OTA app slot" +partitions (ie `ota_0` and `ota_1`) and an "OTA Data Partition". + +The OTA operation functions write a new app firmware image to whichever OTA app slot is not currently being used for +booting. Once the image is verified, the OTA Data partition is updated to specify that this image should be used for the +next boot. + +.. _ota_data_partition: + +OTA Data Partition +^^^^^^^^^^^^^^^^^^ + +An OTA data partition (type ``data``, subtype ``ota``) must be included in the :doc:`Partition Table ` +of any project which uses the OTA functions. + +For factory boot settings, the OTA data partition should contain no data (all bytes erased to 0xFF). In this case the +esp-idf software bootloader will boot the factory app if it is present in the the partition table. If no factory app is +included in the partition table, the first available OTA slot (usually ``ota_0``) is booted. + +After the first OTA update, the OTA data partition is updated to specify which OTA app slot partition should be booted next. + +The OTA data partition is two flash sectors (0x2000 bytes) in size, to prevent problems if there is a power failure +while it is being written. Sectors are independently erased and written with matching data, and if they disagree a +counter field is used to determine which sector was written more recently. + +See also +-------- + +* :doc:`Partition Table documentation ` +* :doc:`Lower-Level SPI Flash/Partition API ` Application Example ------------------- - -Demonstration of OTA (over the air) firmware update workflow: :example:`system/ota`. + +End-to-end example of OTA firmware update workflow: :example:`system/ota`. API Reference ------------- @@ -21,6 +58,7 @@ Macros .. doxygendefine:: ESP_ERR_OTA_PARTITION_CONFLICT .. doxygendefine:: ESP_ERR_OTA_SELECT_INFO_INVALID .. doxygendefine:: ESP_ERR_OTA_VALIDATE_FAILED +.. doxygendefine:: OTA_SIZE_UNKNOWN Type Definitions ^^^^^^^^^^^^^^^^ @@ -33,5 +71,7 @@ Functions .. doxygenfunction:: esp_ota_begin .. doxygenfunction:: esp_ota_write .. doxygenfunction:: esp_ota_end +.. doxygenfunction:: esp_ota_get_running_partition .. doxygenfunction:: esp_ota_set_boot_partition .. doxygenfunction:: esp_ota_get_boot_partition +.. doxygenfunction:: esp_ota_get_next_update_partition diff --git a/docs/build-system.rst b/docs/build-system.rst index b3abbd28f5..ea4925f74e 100644 --- a/docs/build-system.rst +++ b/docs/build-system.rst @@ -144,7 +144,7 @@ The minimal ``component.mk`` file is an empty file(!). If the file is empty, the See `example component makefiles` for more complete component makefile examples. -Note that there is a different between an empty ``component.mk`` file (which invokes default component build behaviour) and no ``component.mk`` file (which means no default component build behaviour will occur.) It is possible for a component to have no `component.mk` file, if it only contains other files which influence the project configuration or build process. +Note that there is a difference between an empty ``component.mk`` file (which invokes default component build behaviour) and no ``component.mk`` file (which means no default component build behaviour will occur.) It is possible for a component to have no `component.mk` file, if it only contains other files which influence the project configuration or build process. .. component variables: diff --git a/docs/eclipse-setup.rst b/docs/eclipse-setup.rst index 140e81e173..aa53d171eb 100644 --- a/docs/eclipse-setup.rst +++ b/docs/eclipse-setup.rst @@ -49,7 +49,9 @@ Project Properties * Click "Add..." again, and enter name ``IDF_PATH``. The value should be the full path where ESP-IDF is installed. -*All users, continue with these steps:* +* Edit the ``PATH`` environment variable. Keep the current value, and append the path to the Xtensa toolchain that will installed as part of IDF setup (``something/xtensa-esp32-elf/bin``) if this is not already listed on the PATH. + +* On macOS, add a ``PYTHONPATH`` environment variable and set it to ``/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages``. This is so that the system Python, which has pyserial installed as part of the setup steps, overrides any built-in Eclipse Python. Navigate to "C/C++ General" -> "Preprocessor Include Paths" property page: diff --git a/docs/general-notes.rst b/docs/general-notes.rst index 6597337a1e..8f3c030451 100644 --- a/docs/general-notes.rst +++ b/docs/general-notes.rst @@ -55,6 +55,7 @@ While PRO CPU does initialization in ``start_cpu0`` function, APP CPU spins in ` Main task is the task which runs ``app_main`` function. Main task stack size and priority can be configured in ``menuconfig``. Application can use this task for initial application-specific setup, for example to launch other tasks. Application can also use main task for event loops and other general purpose activities. If ``app_main`` function returns, main task is deleted. +.. _memory-layout: Application memory layout ------------------------- diff --git a/docs/linux-setup.rst b/docs/linux-setup.rst index 94d77f3605..d3b8597cd4 100644 --- a/docs/linux-setup.rst +++ b/docs/linux-setup.rst @@ -77,12 +77,18 @@ If you can't think of a reason why you need to build it yourself, then probably In any case, here are the steps to compile the toolchain yourself. +(Note: You will also need the prerequisite packages mentioned in step 0, above.) + - Install dependencies: - - Ubuntu:: + - Ubuntu pre-16.04:: sudo apt-get install gawk gperf grep gettext libncurses-dev python python-dev automake bison flex texinfo help2man libtool + - Ubuntu 16.04:: + + sudo apt-get install gawk gperf grep gettext python python-dev automake bison flex texinfo help2man libtool libtool-bin + - Debian:: TODO @@ -96,7 +102,7 @@ Download ``crosstool-NG`` and build it:: cd ~/esp git clone -b xtensa-1.22.x https://github.com/espressif/crosstool-NG.git cd crosstool-NG - ./bootstrap && ./configure --prefix=$PWD && make install + ./bootstrap && ./configure --enable-local && make install Build the toolchain:: diff --git a/docs/macos-setup.rst b/docs/macos-setup.rst index eeed9e4e26..e8f8257b28 100644 --- a/docs/macos-setup.rst +++ b/docs/macos-setup.rst @@ -89,7 +89,7 @@ Download ``crosstool-NG`` and build it:: cd ~/esp git clone -b xtensa-1.22.x https://github.com/espressif/crosstool-NG.git cd crosstool-NG - ./bootstrap && ./configure --prefix=$PWD && make install + ./bootstrap && ./configure --enable-local && make install Build the toolchain:: diff --git a/docs/partition-tables.rst b/docs/partition-tables.rst index d45a540c66..afe880a2ba 100644 --- a/docs/partition-tables.rst +++ b/docs/partition-tables.rst @@ -46,7 +46,6 @@ Here is the summary printed for the "Factory app, two OTA definitions" configura * The type of all three are set as "app", but the subtype varies between the factory app at 0x10000 and the next two "OTA" apps. * There is also a new "ota data" slot, which holds the data for OTA updates. The bootloader consults this data in order to know which app to execute. If "ota data" is empty, it will execute the factory app. - Creating Custom Tables ---------------------- @@ -61,7 +60,7 @@ The CSV format is the same format as printed in the summaries shown above. Howev factory, app, factory, 0x10000, 1M ota_0, app, ota_0, , 1M ota_1, app, ota_1, , 1M - + * Whitespace between fields is ignored, and so is any line starting with # (comments). * Each non-comment line in the CSV file is a partition definition. * Only the offset for the first partition is supplied. The gen_esp32part.py tool fills in each remaining offset to start after the preceding partition. @@ -74,16 +73,50 @@ Name field can be any meaningful name. It is not significant to the ESP32. Names Type field ~~~~~~~~~~ -Type field can be specified as app (0) or data (1). Or it can be a number 0-254 (or as hex 0x00-0xFE). Types 0x00-0x3F are reserved for Espressif. If your application needs to store data, please add a custom partition type in the range 0x40-0xFE. +Partition type field can be specified as app (0) or data (1). Or it can be a number 0-254 (or as hex 0x00-0xFE). Types 0x00-0x3F are reserved for esp-idf core functions. -The bootloader ignores any types other than 0 & 1. +If your application needs to store data, please add a custom partition type in the range 0x40-0xFE. + +The bootloader ignores any partition types other than app (0) & data (1). Subtype ~~~~~~~ -When type is "app", the subtype field can be specified as factory (0), ota_0 (0x10) ... ota_15 (0x1F) and test (0x20). Or it can be any number 0-255 (0x00-0xFF). The bootloader will execute the factory app unless there it sees a partition of type data/ota, in which case it reads this partition to determine which OTA image to boot +The 8-bit subtype field is specific to a given partition type. -When type is "data", the subtype field can be specified as ota (0), phy (1), nvs (2). Or it can be a number 0x00-0xFF. The bootloader ignores all data subtypes except for ota. Subtypes 0-0x7f are reserved for Espressif use. To create custom data partition subtypes use "data" type, and choose any unused subtype in 0x80-0xFF range. If you are porting a filesystem to the ESP-IDF, consider opening a PR to add the new subtype to esp_partition.h file. +esp-idf currently only specifies the meaning of the subtype field for "app" and "data" partition types. + +App Subtypes +~~~~~~~~~~~~ + +When type is "app", the subtype field can be specified as factory (0), ota_0 (0x10) ... ota_15 (0x1F) or test (0x20). + +- factory (0) is the default app partition. The bootloader will execute the factory app unless there it sees a partition of type data/ota, in which case it reads this partition to determine which OTA image to boot. + + - OTA never updates the factory partition. + - If you want to conserve flash usage in an OTA project, you can remove the factory partition and use ota_0 instead. +- ota_0 (0x10) ... ota_15 (0x1F) are the OTA app slots. Refer to the :doc:`OTA documentation ` for more details, which then use the OTA data partition to configure which app slot the bootloader should boot. If using OTA, an application should have at least two OTA application slots (ota_0 & ota_1). Refer to the :doc:`OTA documentation ` for more details. +- test (0x2) is a reserved subtype for factory test procedures. It is not currently supported by the esp-idf bootloader. + +Data Subtypes +~~~~~~~~~~~~~ + +When type is "data", the subtype field can be specified as ota (0), phy (1), nvs (2). + +- ota (0) is the :ref:`OTA data partition ` which stores information about the currently selected OTA application. This partition should be 0x2000 bytes in size. Refer to the :ref:`OTA documentation ` for more details. +- phy (1) is for storing PHY initialisation data. This allows PHY to be configured per-device, instead of in firmware. + + - In the default configuration, the phy partition is not used and PHY initialisation data is compiled into the app itself. As such, this partition can be removed from the partition table to save space. + - To load PHY data from this partition, run ``make menuconfig`` and enable "Component Config" -> "PHY" -> "Use a partition to store PHY init data". You will also need to flash your devices with phy init data as the esp-idf build system does not do this automatically. +- nvs (2) is for the :doc:`Non-Volatile Storage (NVS) API `. + + - NVS is used to store per-device PHY calibration data (different to initialisation data). + - NVS is used to store WiFi data if the :doc:`esp_wifi_set_storage(WIFI_STORAGE_FLASH) ` initialisation function is used. + - The NVS API can also be used for other application data. + - It is strongly recommended that you include an NVS partition of at least 0x3000 bytes in your project. + - If using NVS API to store a lot of data, increase the NVS partition size from the default 0x6000 bytes. + +Other data subtypes are reserved for future esp-idf uses. Offset & Size ~~~~~~~~~~~~~ @@ -92,28 +125,26 @@ Only the first offset field is required (we recommend using 0x10000). Partitions App partitions have to be at offsets aligned to 0x10000 (64K). If you leave the offset field blank, the tool will automatically align the partition. If you specify an unaligned offset for an app partition, the tool will return an error. -Sizes and offsets can be specified as decimal numbers, hex numbers with the prefix 0x, or size multipliers M or K (1024 and 1024*1024 bytes). - -NVS data partition has to be at least 0x3000 bytes long, and OTA data parition has to be 0x2000 bytes long. If you are using NVS in your application to store a lot of data, consider using a custom partition table with larger NVS partition. +Sizes and offsets can be specified as decimal numbers, hex numbers with the prefix 0x, or size multipliers K or M (1024 and 1024*1024 bytes). Generating Binary Partition Table --------------------------------- -The partition table which is flashed to the ESP32 is in a binary format, not CSV. The tool bin/gen_esp32part.py is used to convert between CSV and binary formats. +The partition table which is flashed to the ESP32 is in a binary format, not CSV. The tool :component_file:`partition_table/gen_esp32part.py` is used to convert between CSV and binary formats. -If you configure the partition table CSV name in ``make menuconfig`` and then ``make partition_table``, this conversion is done for you. +If you configure the partition table CSV name in ``make menuconfig`` and then ``make partition_table``, this conversion is done as part of the build process. To convert CSV to Binary manually:: - python bin/gen_esp32part.py --verify input_partitions.csv binary_partitions.bin + python gen_esp32part.py --verify input_partitions.csv binary_partitions.bin To convert binary format back to CSV:: - python bin/gen_esp32part.py --verify binary_partitions.bin input_partitions.csv + python gen_esp32part.py --verify binary_partitions.bin input_partitions.csv To display the contents of a binary partition table on stdout (this is how the summaries displayed when running `make partition_table` are generated:: - python bin/gen_esp32part.py binary_partitions.bin + python gen_esp32part.py binary_partitions.bin ``gen_esp32part.py`` takes one optional argument, ``--verify``, which will also verify the partition table during conversion (checking for overlapping partitions, unaligned partitions, etc.) diff --git a/examples/bluetooth/gatt_client/main/gattc_demo.c b/examples/bluetooth/gatt_client/main/gattc_demo.c index 4a2fa0051d..2904484913 100644 --- a/examples/bluetooth/gatt_client/main/gattc_demo.c +++ b/examples/bluetooth/gatt_client/main/gattc_demo.c @@ -295,6 +295,12 @@ static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *par esp_ble_gap_start_scanning(duration); break; } + case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT: + //scan start complete event to indicate scan start successfully or failed + if (param->scan_start_cmpl.status != ESP_BT_STATUS_SUCCESS) { + ESP_LOGE(GATTC_TAG, "Scan start failed\n"); + } + break; case ESP_GAP_BLE_SCAN_RESULT_EVT: { esp_ble_gap_cb_param_t *scan_result = (esp_ble_gap_cb_param_t *)param; switch (scan_result->scan_rst.search_evt) { diff --git a/examples/bluetooth/gatt_server/main/gatts_demo.c b/examples/bluetooth/gatt_server/main/gatts_demo.c index acbf9195a0..39d33286a0 100644 --- a/examples/bluetooth/gatt_server/main/gatts_demo.c +++ b/examples/bluetooth/gatt_server/main/gatts_demo.c @@ -151,6 +151,12 @@ static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param case ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT: esp_ble_gap_start_advertising(&test_adv_params); break; + case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: + //advertising start complete event to indicate advertising start successfully or failed + if (param->adv_start_cmpl.status != ESP_BT_STATUS_SUCCESS) { + ESP_LOGE(GATTS_TAG, "Advertising start failed\n"); + } + break; default: break; } diff --git a/examples/bluetooth/gatt_server_service_table/main/gatts_table_creat_demo.c b/examples/bluetooth/gatt_server_service_table/main/gatts_table_creat_demo.c index 71baf1842c..9bb03e7e89 100644 --- a/examples/bluetooth/gatt_server_service_table/main/gatts_table_creat_demo.c +++ b/examples/bluetooth/gatt_server_service_table/main/gatts_table_creat_demo.c @@ -203,6 +203,12 @@ static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: esp_ble_gap_start_advertising(&heart_rate_adv_params); break; + case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: + //advertising start complete event to indicate advertising start successfully or failed + if (param->adv_start_cmpl.status != ESP_BT_STATUS_SUCCESS) { + ESP_LOGE(GATTS_TABLE_TAG, "Advertising start failed\n"); + } + break; default: break; } diff --git a/examples/ethernet/ethernet/main/ethernet_main.c b/examples/ethernet/ethernet/main/ethernet_main.c index 6419c41843..f76bd1d76e 100644 --- a/examples/ethernet/ethernet/main/ethernet_main.c +++ b/examples/ethernet/ethernet/main/ethernet_main.c @@ -45,7 +45,7 @@ void phy_tlk110_check_phy_init(void) { while((esp_eth_smi_read(BASIC_MODE_STATUS_REG) & AUTO_NEGOTIATION_COMPLETE ) != AUTO_NEGOTIATION_COMPLETE) {}; - while((esp_eth_smi_read(PHY_STATUS_REG) & AUTO_NEGTIATION_STATUS ) != AUTO_NEGTIATION_STATUS) + while((esp_eth_smi_read(PHY_STATUS_REG) & AUTO_NEGOTIATION_STATUS ) != AUTO_NEGOTIATION_STATUS) {}; while((esp_eth_smi_read(CABLE_DIAGNOSTIC_CONTROL_REG) & DIAGNOSTIC_DONE ) != DIAGNOSTIC_DONE) {}; @@ -108,7 +108,7 @@ void phy_tlk110_init(void) while (esp_eth_smi_read(PHY_IDENTIFIER_REG) != OUI_MSB_21TO6_DEF) { } - esp_eth_smi_write(SOFTWARE_STRAP_CONTROL_REG, DEFAULT_PHY_CONFIG |SW_STRAP_CONFIG_DONE); + esp_eth_smi_write(SW_STRAP_CONTROL_REG, DEFAULT_PHY_CONFIG | SW_STRAP_CONFIG_DONE); ets_delay_us(300); diff --git a/examples/ethernet/ethernet/main/tlk110_phy.h b/examples/ethernet/ethernet/main/tlk110_phy.h index a21bc57aea..63236edebd 100644 --- a/examples/ethernet/ethernet/main/tlk110_phy.h +++ b/examples/ethernet/ethernet/main/tlk110_phy.h @@ -13,7 +13,7 @@ #define PARTNER_ASM_DIR BIT(11) #define PARTNER_PAUSE BIT(10) -#define SOFTWARE_STRAP_CONTROL_REG (0x9) +#define SW_STRAP_CONTROL_REG (0x9) #define SW_STRAP_CONFIG_DONE BIT(15) #define AUTO_MDIX_ENABLE BIT(14) #define AUTO_NEGOTIATION_ENABLE BIT(13) @@ -23,7 +23,7 @@ #define RMII_ENHANCED_MODE BIT(9) #define PHY_STATUS_REG (0x10) -#define AUTO_NEGTIATION_STATUS BIT(4) +#define AUTO_NEGOTIATION_STATUS BIT(4) #define DUPLEX_STATUS BIT(2) #define SPEED_STATUS BIT(1) diff --git a/examples/ethernet/ethernet/sdkconfig.defaults b/examples/ethernet/ethernet/sdkconfig.defaults new file mode 100644 index 0000000000..b4f4c3c1c5 --- /dev/null +++ b/examples/ethernet/ethernet/sdkconfig.defaults @@ -0,0 +1,2 @@ +# CONFIG_WIFI_ENABLED is not set +CONFIG_ETHERNET=y diff --git a/examples/peripherals/touch_pad_interrupt/Makefile b/examples/peripherals/touch_pad_interrupt/Makefile new file mode 100644 index 0000000000..b4a0d859e3 --- /dev/null +++ b/examples/peripherals/touch_pad_interrupt/Makefile @@ -0,0 +1,8 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := touch_pad_interrupt + +include $(IDF_PATH)/make/project.mk diff --git a/examples/peripherals/touch_pad_interrupt/README.md b/examples/peripherals/touch_pad_interrupt/README.md new file mode 100644 index 0000000000..341a86855a --- /dev/null +++ b/examples/peripherals/touch_pad_interrupt/README.md @@ -0,0 +1,30 @@ +# Touch Pad Interrupt Example + +Demonstrates how to set up ESP32's capacitive touch pad peripheral to trigger interrupt when a pad is touched. + +Application has been developed and tested using [ESP32 Demo Board V2](https://dl.espressif.com/dl/schematics/ESP32-Demo-Board-V2_sch.pdf) that has ten capacitive sensor pads T0 to T9 exposed. + +![alt text](http://dl.espressif.com/dl/schematics/pictures/esp32-demo-board-v2.jpg "ESP32 Demo Board V2") + +The following output is shown when a pad is touched: + +``` +I (6303) Touch pad: Waiting for any pad being touched... +I (6733) Touch pad: T6 activated! +I (7333) Touch pad: T5 activated! +I (7723) Touch pad: T3 activated! +I (8043) Touch pad: T2 activated! +I (8883) Touch pad: T4 activated! +I (9523) Touch pad: T7 activated! +I (12503) Touch pad: Waiting for any pad being touched... +I (15483) Touch pad: T6 activated! +I (16253) Touch pad: T5 activated! +I (17903) Touch pad: Waiting for any pad being touched... +I (22903) Touch pad: Waiting for any pad being touched... +``` + +Note: Sensing threshold is set up automatically at start up by performing simple calibration. Application is reading current value for each pad and assuming half of this value as the sensing threshold. Do not touch pads on application start up, otherwise sensing may not work correctly. + +For a simpler example how to configure and read capacitive touch pads, please refer to [touch_pad_read](../touch_pad_read). + +See the README.md file in the upper level 'examples' directory for more information about examples. diff --git a/examples/peripherals/touch_pad_interrupt/main/component.mk b/examples/peripherals/touch_pad_interrupt/main/component.mk new file mode 100644 index 0000000000..dfe125be79 --- /dev/null +++ b/examples/peripherals/touch_pad_interrupt/main/component.mk @@ -0,0 +1,5 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) + diff --git a/examples/peripherals/touch_pad_interrupt/main/tp_interrupt_main.c b/examples/peripherals/touch_pad_interrupt/main/tp_interrupt_main.c new file mode 100644 index 0000000000..fd4fe5b276 --- /dev/null +++ b/examples/peripherals/touch_pad_interrupt/main/tp_interrupt_main.c @@ -0,0 +1,109 @@ +/* Touch Pad Interrupt Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_system.h" +#include "nvs_flash.h" +#include "esp_log.h" + +#include "driver/touch_pad.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/sens_reg.h" + +static const char* TAG = "Touch pad"; + +static bool touch_pad_activated[TOUCH_PAD_MAX]; + + +/* + Read values sensed at all available touch pads. + Use half of read value as the threshold + to trigger interrupt when the pad is touched. + Note: this routine demonstrates a simple way + to configure activation threshold for the touch pads. + Do not touch any pads when this routine + is running (on application start). + */ +static void touch_pad_set_thresholds(void) +{ + uint16_t touch_value; + for (int i=0; i> i) & 0x01) { + touch_pad_activated[i] = true; + } + } + } +} + + +void app_main() +{ + ESP_LOGI(TAG, "Starting"); + nvs_flash_init(); + + // Initialize touch pad peripheral + ESP_LOGI(TAG, "Initializing touch pad"); + touch_pad_init(); + touch_pad_set_thresholds(); + touch_pad_isr_handler_register(touch_pad_rtc_intr, NULL, 0, NULL); + + // Start a task to show what pads have been touched + xTaskCreate(&touch_pad_read_task, "touch_pad_read_task", 2048, NULL, 5, NULL); +} diff --git a/examples/peripherals/touch_pad_read/Makefile b/examples/peripherals/touch_pad_read/Makefile new file mode 100644 index 0000000000..6116edf6ea --- /dev/null +++ b/examples/peripherals/touch_pad_read/Makefile @@ -0,0 +1,8 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := touch_pad_read + +include $(IDF_PATH)/make/project.mk diff --git a/examples/peripherals/touch_pad_read/README.md b/examples/peripherals/touch_pad_read/README.md new file mode 100644 index 0000000000..6ce92cf4af --- /dev/null +++ b/examples/peripherals/touch_pad_read/README.md @@ -0,0 +1,24 @@ +# Touch Pad Read Example + +Read and display raw values from capacitive touch pad sensors. + +Once configured, ESP32 is continuously measuring capacitance of touch pad sensors. Measurement is reflected as numeric value inversely related to sensor's capacitance. The capacitance is bigger when sensor is touched with a finger and the measured value smaller. In opposite situation, when finger is released, capacitance is smaller and the measured value bigger. + +To detect when a sensor is touched and when not, each particular design should be calibrated by obtaining both measurements for each individual sensor. Then a threshold between both values should be established. Using specific threshold, API is then able to distinguish whether specific sensor is touched or released. + +ESP32 supports reading up to ten capacitive touch pad sensors T0 - T9, connected to specific GPIO pins. For information on available pins please refer to [Technical Reference Manual](http://espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf). Application initializes all ten sensor pads. Then in a loop reads sensors T0 - T9 and displays obtained values (after a colon) on a serial terminal: + +``` +T0: 486 T1: 1 T2: 559 T3: 567 T4: 871 T5: 522 T6:1174 T7:1531 T8:1508 T9:1640 +T0: 485 T1: 1 T2: 559 T3: 568 T4: 871 T5: 521 T6:1176 T7:1536 T8:1509 T9:1635 +T0: 485 T1: 1 T2: 559 T3: 568 T4: 871 T5: 521 T6:1172 T7:1536 T8:1507 T9:1640 +T0: 11 T1: 1 T2: 558 T3: 568 T4: 871 T5: 522 T6:1172 T7:1535 T8:1507 T9:1638 +``` + +Above log is prepared using [ESP32 Demo Board V2](https://dl.espressif.com/dl/schematics/ESP32-Demo-Board-V2_sch.pdf) that has all ten pad sensors exposed. Values will be different depending on layout of pads on particular board. + +![alt text](http://dl.espressif.com/dl/schematics/pictures/esp32-demo-board-v2.jpg "ESP32 Demo Board V2") + +There is another similar example that demonstrates how to perform simple calibration and trigger an interrupt when a pat is touched - see [touch_pad_interrupt](../touch_pad_interrupt). + +See the README.md file in the upper level 'examples' directory for more information about examples. diff --git a/examples/peripherals/touch_pad_read/main/component.mk b/examples/peripherals/touch_pad_read/main/component.mk new file mode 100644 index 0000000000..dfe125be79 --- /dev/null +++ b/examples/peripherals/touch_pad_read/main/component.mk @@ -0,0 +1,5 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) + diff --git a/examples/peripherals/touch_pad_read/main/tp_read_main.c b/examples/peripherals/touch_pad_read/main/tp_read_main.c new file mode 100644 index 0000000000..accfe5027a --- /dev/null +++ b/examples/peripherals/touch_pad_read/main/tp_read_main.c @@ -0,0 +1,45 @@ +/* Touch Pad Read Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_system.h" +#include "nvs_flash.h" + +#include "driver/touch_pad.h" + + +/* + Read values sensed at all available touch pads. + Print out values in a loop on a serial monitor. + */ +void touch_pad_read_task(void *pvParameter) +{ + while (1) { + uint16_t touch_value; + for (int i=0; i +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_deep_sleep.h" +#include "esp_log.h" +#include "esp32/ulp.h" +#include "driver/touch_pad.h" +#include "driver/adc.h" +#include "driver/rtc_io.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/sens_reg.h" + +static RTC_DATA_ATTR struct timeval sleep_enter_time; + +#ifdef CONFIG_ENABLE_ULP_TEMPERATURE_WAKEUP + +/* + * Offset (in 32-bit words) in RTC Slow memory where the data is placed + * by the ULP coprocessor. It can be chosen to be any value greater or equal + * to ULP program size, and less than the CONFIG_ULP_COPROC_RESERVE_MEM/4 - 6, + * where 6 is the number of words used by the ULP coprocessor. + */ +#define ULP_DATA_OFFSET 36 + +_Static_assert(ULP_DATA_OFFSET < CONFIG_ULP_COPROC_RESERVE_MEM/4 - 6, + "ULP_DATA_OFFSET is set too high, or CONFIG_ULP_COPROC_RESERVE_MEM is not sufficient"); + +/** + * @brief Start ULP temperature monitoring program + * + * This function loads a program into the RTC Slow memory and starts up the ULP. + * The program monitors on-chip temperature sensor and wakes up the SoC when + * the temperature goes lower or higher than certain thresholds. + */ +static void start_ulp_temperature_monitoring(); + +/** + * @brief Utility function which reads data written by ULP program + * + * @param offset offset from ULP_DATA_OFFSET in RTC Slow memory, in words + * @return lower 16-bit part of the word writable by the ULP + */ +static inline uint16_t ulp_data_read(size_t offset) +{ + return RTC_SLOW_MEM[ULP_DATA_OFFSET + offset] & 0xffff; +} + +/** + * @brief Utility function which writes data to be ready by ULP program + * + * @param offset offset from ULP_DATA_OFFSET in RTC Slow memory, in words + * @param value lower 16-bit part of the word to be stored + */ +static inline void ulp_data_write(size_t offset, uint16_t value) +{ + RTC_SLOW_MEM[ULP_DATA_OFFSET + offset] = value; +} + +#endif // CONFIG_ENABLE_ULP_TEMPERATURE_WAKEUP + +#ifdef CONFIG_ENABLE_TOUCH_WAKEUP +static void calibrate_touch_pad(touch_pad_t pad); +#endif + +void app_main() +{ + struct timeval now; + gettimeofday(&now, NULL); + int sleep_time_ms = (now.tv_sec - sleep_enter_time.tv_sec) * 1000 + (now.tv_usec - sleep_enter_time.tv_usec) / 1000; + + switch (esp_deep_sleep_get_wakeup_cause()) { + case ESP_DEEP_SLEEP_WAKEUP_EXT1: { + uint64_t wakeup_pin_mask = esp_deep_sleep_get_ext1_wakeup_status(); + if (wakeup_pin_mask != 0) { + int pin = __builtin_ffsll(wakeup_pin_mask) - 1; + printf("Wake up from GPIO %d\n", pin); + } else { + printf("Wake up from GPIO\n"); + } + break; + } + case ESP_DEEP_SLEEP_WAKEUP_TIMER: { + printf("Wake up from timer. Time spent in deep sleep: %dms\n", sleep_time_ms); + break; + } +#ifdef CONFIG_ENABLE_TOUCH_WAKEUP + case ESP_DEEP_SLEEP_WAKEUP_TOUCHPAD: { + printf("Wake up from touch on pad %d\n", esp_deep_sleep_get_touchpad_wakeup_status()); + break; + } +#endif // CONFIG_ENABLE_TOUCH_WAKEUP +#ifdef CONFIG_ENABLE_ULP_TEMPERATURE_WAKEUP + case ESP_DEEP_SLEEP_WAKEUP_ULP: { + printf("Wake up from ULP\n"); + int16_t diff_high = (int16_t) ulp_data_read(3); + int16_t diff_low = (int16_t) ulp_data_read(4); + if (diff_high < 0) { + printf("High temperature alarm was triggered\n"); + } else if (diff_low < 0) { + printf("Low temperature alarm was triggered\n"); + } else { + assert(false && "temperature has stayed within limits, but got ULP wakeup\n"); + } + break; + } +#endif // CONFIG_ENABLE_ULP_TEMPERATURE_WAKEUP + case ESP_DEEP_SLEEP_WAKEUP_UNDEFINED: + default: + printf("Not a deep sleep reset\n"); + } + +#ifdef CONFIG_ENABLE_ULP_TEMPERATURE_WAKEUP + if (esp_deep_sleep_get_wakeup_cause() != ESP_DEEP_SLEEP_WAKEUP_UNDEFINED) { + printf("ULP did %d temperature measurements in %d ms\n", ulp_data_read(1), sleep_time_ms); + printf("Initial T=%d, latest T=%d\n", ulp_data_read(0), ulp_data_read(2)); + } +#endif // CONFIG_ENABLE_ULP_TEMPERATURE_WAKEUP + + vTaskDelay(1000 / portTICK_PERIOD_MS); + + const int wakeup_time_sec = 20; + printf("Enabling timer wakeup, %ds\n", wakeup_time_sec); + esp_deep_sleep_enable_timer_wakeup(wakeup_time_sec * 1000000); + + const int ext_wakeup_pin_1 = 25; + const uint64_t ext_wakeup_pin_1_mask = 1ULL << ext_wakeup_pin_1; + const int ext_wakeup_pin_2 = 26; + const uint64_t ext_wakeup_pin_2_mask = 1ULL << ext_wakeup_pin_2; + + printf("Enabling EXT1 wakeup on pins GPIO%d, GPIO%d\n", ext_wakeup_pin_1, ext_wakeup_pin_2); + esp_deep_sleep_enable_ext1_wakeup(ext_wakeup_pin_1_mask | ext_wakeup_pin_2_mask, ESP_EXT1_WAKEUP_ANY_HIGH); + +#ifdef CONFIG_ENABLE_TOUCH_WAKEUP + touch_pad_init(); + calibrate_touch_pad(TOUCH_PAD_NUM8); + calibrate_touch_pad(TOUCH_PAD_NUM9); + printf("Enabling touch pad wakeup\n"); + esp_deep_sleep_enable_touchpad_wakeup(); +#endif // CONFIG_ENABLE_TOUCH_WAKEUP + +#ifdef CONFIG_ENABLE_ULP_TEMPERATURE_WAKEUP + printf("Enabling ULP wakeup\n"); + esp_deep_sleep_enable_ulp_wakeup(); +#endif + + printf("Entering deep sleep\n"); + gettimeofday(&sleep_enter_time, NULL); + +#ifdef CONFIG_ENABLE_ULP_TEMPERATURE_WAKEUP + start_ulp_temperature_monitoring(); +#endif + + esp_deep_sleep_start(); +} + +#ifdef CONFIG_ENABLE_TOUCH_WAKEUP +static void calibrate_touch_pad(touch_pad_t pad) +{ + touch_pad_config(pad, 1000); + + int avg = 0; + const size_t calibration_count = 128; + for (int i = 0; i < calibration_count; ++i) { + uint16_t val; + touch_pad_read(pad, &val); + avg += val; + } + avg /= calibration_count; + const int min_reading = 300; + if (avg < min_reading) { + printf("Touch pad #%d average reading is too low: %d (expecting at least %d). " + "Not using for deep sleep wakeup.\n", pad, avg, min_reading); + touch_pad_config(pad, 0); + } else { + int threshold = avg - 100; + printf("Touch pad #%d average: %d, wakeup threshold set to %d.\n", pad, avg, threshold); + touch_pad_config(pad, threshold); + } +} +#endif // CONFIG_ENABLE_TOUCH_WAKEUP + +#ifdef CONFIG_ENABLE_ULP_TEMPERATURE_WAKEUP +static void start_ulp_temperature_monitoring() +{ + /* + * This ULP program monitors the on-chip temperature sensor and wakes the chip up when + * the temperature goes outside of certain window. + * When the program runs for the first time, it saves the temperature measurement, + * it is considered initial temperature (T0). + * + * On each subsequent run, temperature measured and compared to T0. + * If the measured value is higher than T0 + max_temp_diff or lower than T0 - max_temp_diff, + * the chip is woken up from deep sleep. + */ + + /* Temperature difference threshold which causes wakeup + * With settings here (TSENS_CLK_DIV=2, 8000 cycles), + * TSENS measurement is done in units of 0.73 degrees Celsius. + * Therefore, max_temp_diff below is equivalent to ~2.2 degrees Celsius. + */ + const int16_t max_temp_diff = 3; + + // Number of measurements ULP should do per second + const uint32_t measurements_per_sec = 5; + + // Allow TSENS to be controlled by the ULP + SET_PERI_REG_BITS(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_CLK_DIV, 2, SENS_TSENS_CLK_DIV_S); + SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR, 3, SENS_FORCE_XPD_SAR_S); + CLEAR_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_POWER_UP); + CLEAR_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_DUMP_OUT); + CLEAR_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_POWER_UP_FORCE); + + // Clear the part of RTC_SLOW_MEM reserved for the ULP. Makes debugging easier. + memset(RTC_SLOW_MEM, 0, CONFIG_ULP_COPROC_RESERVE_MEM); + + // The first word of memory (at data offset) is used to store the initial temperature (T0) + // Zero it out here, then ULP will update it on the first run. + ulp_data_write(0, 0); + // The second word is used to store measurement count, zero it out as well. + ulp_data_write(1, 0); + + const ulp_insn_t program[] = { + // load data offset into R2 + I_MOVI(R2, ULP_DATA_OFFSET), + // load/increment/store measurement counter using R1 + I_LD(R1, R2, 1), + I_ADDI(R1, R1, 1), + I_ST(R1, R2, 1), + // enable temperature sensor + I_WR_REG(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR_S, SENS_FORCE_XPD_SAR_S + 1, 3), + // do temperature measurement and store result in R3 + I_TSENS(R3, 8000), + // disable temperature sensor + I_WR_REG(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR_S, SENS_FORCE_XPD_SAR_S + 1, 0), + // Save current measurement at offset+2 + I_ST(R3, R2, 2), + // load initial value into R0 + I_LD(R0, R2, 0), + // if threshold value >=1 (i.e. initialized), goto 1 + M_BGE(1, 1), + // otherwise, save the current value as initial (T0) + I_MOVR(R0, R3), + I_ST(R0, R2, 0), + M_LABEL(1), + // check if the temperature is greater or equal (T0 + max_temp_diff) + // uses R1 as scratch register, difference is saved at offset + 3 + I_ADDI(R1, R0, max_temp_diff - 1), + I_SUBR(R1, R1, R3), + I_ST(R1, R2, 3), + M_BXF(2), + // check if the temperature is less or equal (T0 - max_temp_diff) + // uses R1 as scratch register, difference is saved at offset + 4 + I_SUBI(R1, R0, max_temp_diff - 1), + I_SUBR(R1, R3, R1), + I_ST(R1, R2, 4), + M_BXF(2), + // temperature is within (T0 - max_temp_diff; T0 + max_temp_diff) + // stop ULP until the program timer starts it again + I_HALT(), + M_LABEL(2), + // temperature is out of bounds + // disable ULP program timer + I_WR_REG_BIT(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN_S, 0), + // initiate wakeup of the SoC + I_WAKE(), + // stop the ULP program + I_HALT() + }; + + // Load ULP program into RTC_SLOW_MEM, at offset 0 + size_t size = sizeof(program)/sizeof(ulp_insn_t); + ESP_ERROR_CHECK( ulp_process_macros_and_load(0, program, &size) ); + assert(size < ULP_DATA_OFFSET && "ULP_DATA_OFFSET needs to be greater or equal to the program size"); + + // Set ULP wakeup period + const uint32_t sleep_cycles = RTC_CNTL_SLOWCLK_FREQ / measurements_per_sec; + REG_WRITE(SENS_ULP_CP_SLEEP_CYC0_REG, sleep_cycles); + + // Start ULP + ESP_ERROR_CHECK( ulp_run(0) ); +} +#endif // CONFIG_ENABLE_ULP_TEMPERATURE_WAKEUP + diff --git a/examples/system/deep_sleep/sdkconfig.defaults b/examples/system/deep_sleep/sdkconfig.defaults new file mode 100644 index 0000000000..dd7da3f88d --- /dev/null +++ b/examples/system/deep_sleep/sdkconfig.defaults @@ -0,0 +1,7 @@ +CONFIG_ESP32_DEFAULT_CPU_FREQ_80=y +CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=80 +CONFIG_ULP_COPROC_ENABLED=y +CONFIG_ULP_COPROC_RESERVE_MEM=512 +CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1=y +CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC=y +CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY=500 diff --git a/examples/system/ota/main/ota.c b/examples/system/ota/main/ota_example.c similarity index 78% rename from examples/system/ota/main/ota.c rename to examples/system/ota/main/ota_example.c index 2dfda1c828..06ed81e638 100644 --- a/examples/system/ota/main/ota.c +++ b/examples/system/ota/main/ota_example.c @@ -33,17 +33,14 @@ static const char *TAG = "ota"; /*an ota data write buffer ready to write to the flash*/ -char ota_write_data[BUFFSIZE + 1] = { 0 }; +static char ota_write_data[BUFFSIZE + 1] = { 0 }; /*an packet receive buffer*/ -char text[BUFFSIZE + 1] = { 0 }; +static char text[BUFFSIZE + 1] = { 0 }; /* an image total length*/ -int binary_file_length = 0; +static int binary_file_length = 0; /*socket id*/ -int socket_id = -1; -char http_request[64] = {0}; -/* operate handle : uninitialized value is zero ,every ota begin would exponential growth*/ -esp_ota_handle_t out_handle = 0; -esp_partition_t operate_partition; +static int socket_id = -1; +static char http_request[64] = {0}; /* FreeRTOS event group to signal when we are connected & ready to make a request */ static EventGroupHandle_t wifi_event_group; @@ -109,7 +106,7 @@ static int read_until(char *buffer, char delim, int len) * return true if packet including \r\n\r\n that means http packet header finished,start to receive packet body * otherwise return false * */ -static bool read_past_http_header(char text[], int total_len, esp_ota_handle_t out_handle) +static bool read_past_http_header(char text[], int total_len, esp_ota_handle_t update_handle) { /* i means current position */ int i = 0, i_read_len = 0; @@ -122,7 +119,7 @@ static bool read_past_http_header(char text[], int total_len, esp_ota_handle_t o /*copy first http packet body to write buffer*/ memcpy(ota_write_data, &(text[i + 2]), i_write_len); - esp_err_t err = esp_ota_write( out_handle, (const void *)ota_write_data, i_write_len); + esp_err_t err = esp_ota_write( update_handle, (const void *)ota_write_data, i_write_len); if (err != ESP_OK) { ESP_LOGE(TAG, "Error: esp_ota_write failed! err=0x%x", err); return false; @@ -170,48 +167,6 @@ bool connect_to_http_server() return false; } -bool ota_init() -{ - esp_err_t err; - const esp_partition_t *esp_current_partition = esp_ota_get_boot_partition(); - if (esp_current_partition->type != ESP_PARTITION_TYPE_APP) { - ESP_LOGE(TAG, "Error: esp_current_partition->type != ESP_PARTITION_TYPE_APP"); - return false; - } - - esp_partition_t find_partition; - memset(&operate_partition, 0, sizeof(esp_partition_t)); - /*choose which OTA image should we write to*/ - switch (esp_current_partition->subtype) { - case ESP_PARTITION_SUBTYPE_APP_FACTORY: - find_partition.subtype = ESP_PARTITION_SUBTYPE_APP_OTA_0; - break; - case ESP_PARTITION_SUBTYPE_APP_OTA_0: - find_partition.subtype = ESP_PARTITION_SUBTYPE_APP_OTA_1; - break; - case ESP_PARTITION_SUBTYPE_APP_OTA_1: - find_partition.subtype = ESP_PARTITION_SUBTYPE_APP_OTA_0; - break; - default: - break; - } - find_partition.type = ESP_PARTITION_TYPE_APP; - - const esp_partition_t *partition = esp_partition_find_first(find_partition.type, find_partition.subtype, NULL); - assert(partition != NULL); - memset(&operate_partition, 0, sizeof(esp_partition_t)); - err = esp_ota_begin( partition, OTA_SIZE_UNKNOWN, &out_handle); - if (err != ESP_OK) { - ESP_LOGE(TAG, "esp_ota_begin failed err=0x%x!", err); - return false; - } else { - memcpy(&operate_partition, partition, sizeof(esp_partition_t)); - ESP_LOGI(TAG, "esp_ota_begin init OK"); - return true; - } - return false; -} - void __attribute__((noreturn)) task_fatal_error() { ESP_LOGE(TAG, "Exiting task due to fatal error..."); @@ -226,7 +181,19 @@ void __attribute__((noreturn)) task_fatal_error() void main_task(void *pvParameter) { esp_err_t err; + /* update handle : set by esp_ota_begin(), must be freed via esp_ota_end() */ + esp_ota_handle_t update_handle = 0 ; + const esp_partition_t *update_partition = NULL; + ESP_LOGI(TAG, "Starting OTA example..."); + + const esp_partition_t *configured = esp_ota_get_boot_partition(); + const esp_partition_t *running = esp_ota_get_running_partition(); + + assert(configured == running); /* fresh from reset, should be running from configured boot partition */ + ESP_LOGI(TAG, "Running partition type %d subtype %d (offset 0x%08x)", + configured->type, configured->subtype, configured->address); + /* Wait for the callback to set the CONNECTED_BIT in the event group. */ @@ -252,12 +219,17 @@ void main_task(void *pvParameter) ESP_LOGI(TAG, "Send GET request to server succeeded"); } - if ( ota_init() ) { - ESP_LOGI(TAG, "OTA Init succeeded"); - } else { - ESP_LOGE(TAG, "OTA Init failed"); + update_partition = esp_ota_get_next_update_partition(NULL); + ESP_LOGI(TAG, "Writing to partition subtype %d at offset 0x%x", + update_partition->subtype, update_partition->address); + assert(update_partition != NULL); + + err = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_ota_begin failed, error=%d", err); task_fatal_error(); } + ESP_LOGI(TAG, "esp_ota_begin succeeded"); bool resp_body_start = false, flag = true; /*deal with all receive packet*/ @@ -270,10 +242,10 @@ void main_task(void *pvParameter) task_fatal_error(); } else if (buff_len > 0 && !resp_body_start) { /*deal with response header*/ memcpy(ota_write_data, text, buff_len); - resp_body_start = read_past_http_header(text, buff_len, out_handle); + resp_body_start = read_past_http_header(text, buff_len, update_handle); } else if (buff_len > 0 && resp_body_start) { /*deal with response body*/ memcpy(ota_write_data, text, buff_len); - err = esp_ota_write( out_handle, (const void *)ota_write_data, buff_len); + err = esp_ota_write( update_handle, (const void *)ota_write_data, buff_len); if (err != ESP_OK) { ESP_LOGE(TAG, "Error: esp_ota_write failed! err=0x%x", err); task_fatal_error(); @@ -291,11 +263,11 @@ void main_task(void *pvParameter) ESP_LOGI(TAG, "Total Write binary data length : %d", binary_file_length); - if (esp_ota_end(out_handle) != ESP_OK) { + if (esp_ota_end(update_handle) != ESP_OK) { ESP_LOGE(TAG, "esp_ota_end failed!"); task_fatal_error(); } - err = esp_ota_set_boot_partition(&operate_partition); + err = esp_ota_set_boot_partition(update_partition); if (err != ESP_OK) { ESP_LOGE(TAG, "esp_ota_set_boot_partition failed! err=0x%x", err); task_fatal_error(); diff --git a/make/project.mk b/make/project.mk index 04015a8a8c..d5a72c5d1c 100644 --- a/make/project.mk +++ b/make/project.mk @@ -134,7 +134,7 @@ endif # If TEST_COMPONENTS is set, create variables for building unit tests ifdef TEST_COMPONENTS -override TEST_COMPONENTS := $(foreach comp,$(TEST_COMPONENTS),$(wildcard $(IDF_PATH)/components/$(comp)/test)) +override TEST_COMPONENTS := $(foreach comp,$(TEST_COMPONENTS),$(firstword $(foreach dir,$(COMPONENT_DIRS),$(wildcard $(dir)/$(comp)/test)))) TEST_COMPONENT_PATHS := $(TEST_COMPONENTS) TEST_COMPONENT_NAMES := $(foreach comp,$(TEST_COMPONENTS),$(lastword $(subst /, ,$(dir $(comp))))_test) endif diff --git a/make/test_build_system.sh b/make/test_build_system.sh index 5d24e2a948..cc7cc9e128 100755 --- a/make/test_build_system.sh +++ b/make/test_build_system.sh @@ -29,7 +29,9 @@ [ -z ${TMP} ] && TMP="/tmp" # override ESP_IDF_TEMPLATE_GIT to point to a local dir if you're testing and want fast iterations [ -z ${ESP_IDF_TEMPLATE_GIT} ] && ESP_IDF_TEMPLATE_GIT=https://github.com/espressif/esp-idf-template.git -export V=1 + +# uncomment next line to produce a lot more debug output +#export V=1 function run_tests() { @@ -133,7 +135,7 @@ function run_tests() # make a copy of esp-idf and CRLFify it CRLF_ESPIDF=${TESTDIR}/esp-idf-crlf mkdir -p ${CRLF_ESPIDF} - cp -rv ${IDF_PATH}/* ${CRLF_ESPIDF} + cp -r ${IDF_PATH}/* ${CRLF_ESPIDF} # don't CRLFify executable files, as Linux will fail to execute them find ${CRLF_ESPIDF} -type f ! -perm 755 -exec unix2dos {} \; make IDF_PATH=${CRLF_ESPIDF} || failure "Failed to build with CRLFs in source" @@ -148,9 +150,9 @@ function run_tests() make assert_rebuilt ${APP_BINS} ${BOOTLOADER_BINS} - print_status "Touching peripherals ld file should only re-link app" + print_status "Touching app-only ld file should only re-link app" take_build_snapshot - touch ${IDF_PATH}/components/esp32/ld/esp32.peripherals.ld + touch ${IDF_PATH}/components/esp32/ld/esp32.common.ld make assert_rebuilt ${APP_BINS} assert_not_rebuilt ${BOOTLOADER_BINS} diff --git a/tools/unit-test-app/UnitTestParser.py b/tools/unit-test-app/UnitTestParser.py index 26f90a0262..d3fa1efb02 100644 --- a/tools/unit-test-app/UnitTestParser.py +++ b/tools/unit-test-app/UnitTestParser.py @@ -1,5 +1,6 @@ import yaml import os +import os.path import re import sys import shutil @@ -41,7 +42,7 @@ class Parser(object): def parse_test_folders(cls): test_folder_paths = list() os.chdir(os.path.join(IDF_PATH, "components")) - component_dirs = os.listdir(".") + component_dirs = [d for d in os.listdir(".") if os.path.isdir(d)] for dir in component_dirs: os.chdir(dir) if "test" in os.listdir("."): diff --git a/components/spi_flash/test/test_config.h b/tools/unit-test-app/components/unity/include/test_utils.h similarity index 63% rename from components/spi_flash/test/test_config.h rename to tools/unit-test-app/components/unity/include/test_utils.h index 45e73661bb..2fad41a10a 100644 --- a/components/spi_flash/test/test_config.h +++ b/tools/unit-test-app/components/unity/include/test_utils.h @@ -1,4 +1,4 @@ -// Copyright 2010-2016 Espressif Systems (Shanghai) PTE LTD +// 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. @@ -11,14 +11,14 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - -// Common header for SPI flash test data #pragma once -/* Define a region of flash we can mess up for testing... +// Utilities for esp-idf unit tests + +#include + +/* Return the 'flash_test' custom data partition (type 0x55) + defined in the custom partition table. +*/ +const esp_partition_t *get_test_data_partition(); - This is pretty ugly, better to do something with a partition but - this is OK for now. - */ -#define TEST_REGION_START 0x180000 -#define TEST_REGION_END 0x1E0000 diff --git a/tools/unit-test-app/components/unity/test_utils.c b/tools/unit-test-app/components/unity/test_utils.c new file mode 100644 index 0000000000..416853a7f3 --- /dev/null +++ b/tools/unit-test-app/components/unity/test_utils.c @@ -0,0 +1,25 @@ +// 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 "unity.h" +#include "test_utils.h" + +const esp_partition_t *get_test_data_partition() +{ + /* This user type/subtype (0x55) is set in + partition_table_unit_test_app.csv */ + const esp_partition_t *result = esp_partition_find_first(0x55, 0x55, NULL); + TEST_ASSERT_NOT_NULL(result); /* means partition table set wrong */ + return result; +} diff --git a/tools/unit-test-app/partition_table_unit_test_app.csv b/tools/unit-test-app/partition_table_unit_test_app.csv new file mode 100644 index 0000000000..b6e57b4ed3 --- /dev/null +++ b/tools/unit-test-app/partition_table_unit_test_app.csv @@ -0,0 +1,12 @@ +# Special partition table for unit test app +# +# Name, Type, SubType, Offset, Size, Flags +# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild +nvs, data, nvs, 0x9000, 0x4000 +otadata, data, ota, 0xd000, 0x2000 +phy_init, data, phy, 0xf000, 0x1000 +factory, 0, 0, 0x10000, 1M +ota_0, 0, ota_0, , 1M +ota_1, 0, ota_1, , 1M +# flash_test partition used for SPI flash tests +flash_test, 0x55, 0x55, , 512K diff --git a/tools/unit-test-app/sdkconfig b/tools/unit-test-app/sdkconfig index 14b31e0d18..e9ad8e6374 100644 --- a/tools/unit-test-app/sdkconfig +++ b/tools/unit-test-app/sdkconfig @@ -21,12 +21,10 @@ CONFIG_LOG_BOOTLOADER_LEVEL_WARN=y CONFIG_LOG_BOOTLOADER_LEVEL=2 # -# Secure boot configuration +# Security features # -CONFIG_SECURE_BOOTLOADER_DISABLED=y -# CONFIG_SECURE_BOOTLOADER_ONE_TIME_FLASH is not set -# CONFIG_SECURE_BOOTLOADER_REFLASHABLE is not set -# CONFIG_SECURE_BOOTLOADER_ENABLED is not set +# CONFIG_SECURE_BOOT_ENABLED is not set +# CONFIG_FLASH_ENCRYPTION_ENABLED is not set # # Serial flasher config @@ -40,10 +38,10 @@ CONFIG_ESPTOOLPY_BAUD_921600B=y CONFIG_ESPTOOLPY_BAUD_OTHER_VAL=115200 CONFIG_ESPTOOLPY_BAUD=921600 CONFIG_ESPTOOLPY_COMPRESSED=y -# CONFIG_ESPTOOLPY_FLASHMODE_QIO is not set -# CONFIG_ESPTOOLPY_FLASHMODE_QOUT is not set -CONFIG_ESPTOOLPY_FLASHMODE_DIO=y -# CONFIG_ESPTOOLPY_FLASHMODE_DOUT is not set +# CONFIG_FLASHMODE_QIO is not set +# CONFIG_FLASHMODE_QOUT is not set +CONFIG_FLASHMODE_DIO=y +# CONFIG_FLASHMODE_DOUT is not set CONFIG_ESPTOOLPY_FLASHMODE="dio" # CONFIG_ESPTOOLPY_FLASHFREQ_80M is not set CONFIG_ESPTOOLPY_FLASHFREQ_40M=y @@ -51,44 +49,66 @@ CONFIG_ESPTOOLPY_FLASHFREQ_40M=y # CONFIG_ESPTOOLPY_FLASHFREQ_20M is not set CONFIG_ESPTOOLPY_FLASHFREQ="40m" # CONFIG_ESPTOOLPY_FLASHSIZE_1MB is not set -CONFIG_ESPTOOLPY_FLASHSIZE_2MB=y -# CONFIG_ESPTOOLPY_FLASHSIZE_4MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_2MB is not set +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y # CONFIG_ESPTOOLPY_FLASHSIZE_8MB is not set # CONFIG_ESPTOOLPY_FLASHSIZE_16MB is not set -CONFIG_ESPTOOLPY_FLASHSIZE="2MB" +CONFIG_ESPTOOLPY_FLASHSIZE="4MB" +CONFIG_ESPTOOLPY_FLASHSIZE_DETECT=y +CONFIG_ESPTOOLPY_BEFORE_RESET=y +# CONFIG_ESPTOOLPY_BEFORE_NORESET is not set +# CONFIG_ESPTOOLPY_BEFORE_ESP32R0 is not set +CONFIG_ESPTOOLPY_BEFORE="default_reset" +CONFIG_ESPTOOLPY_AFTER_RESET=y +# CONFIG_ESPTOOLPY_AFTER_NORESET is not set +CONFIG_ESPTOOLPY_AFTER="hard_reset" +# CONFIG_MONITOR_BAUD_9600B is not set +# CONFIG_MONITOR_BAUD_57600B is not set +CONFIG_MONITOR_BAUD_115200B=y +# CONFIG_MONITOR_BAUD_230400B is not set +# CONFIG_MONITOR_BAUD_921600B is not set +# CONFIG_MONITOR_BAUD_2MB is not set +# CONFIG_MONITOR_BAUD_OTHER is not set +CONFIG_MONITOR_BAUD_OTHER_VAL=115200 +CONFIG_MONITOR_BAUD=115200 # # Partition Table # -CONFIG_PARTITION_TABLE_SINGLE_APP=y +# CONFIG_PARTITION_TABLE_SINGLE_APP is not set # CONFIG_PARTITION_TABLE_TWO_OTA is not set -# CONFIG_PARTITION_TABLE_CUSTOM is not set -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partition_table_unit_test_app.csv" CONFIG_PARTITION_TABLE_CUSTOM_APP_BIN_OFFSET=0x10000 -CONFIG_PARTITION_TABLE_FILENAME="partitions_singleapp.csv" +CONFIG_PARTITION_TABLE_FILENAME="partition_table_unit_test_app.csv" CONFIG_APP_OFFSET=0x10000 -CONFIG_PHY_DATA_OFFSET=0xf000 +CONFIG_PHY_DATA_OFFSET= CONFIG_OPTIMIZATION_LEVEL_DEBUG=y # CONFIG_OPTIMIZATION_LEVEL_RELEASE is not set # # Component config # -CONFIG_BTC_TASK_STACK_SIZE=2048 +# CONFIG_BT_ENABLED is not set CONFIG_BT_RESERVE_DRAM=0 # -# ESP32-specific config +# ESP32-specific # # CONFIG_ESP32_DEFAULT_CPU_FREQ_80 is not set # CONFIG_ESP32_DEFAULT_CPU_FREQ_160 is not set CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240 -# CONFIG_ESP32_ENABLE_STACK_WIFI is not set -# CONFIG_ESP32_ENABLE_STACK_BT is not set CONFIG_MEMMAP_SMP=y # CONFIG_MEMMAP_TRACEMEM is not set CONFIG_TRACEMEM_RESERVE_DRAM=0x0 +# CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH is not set +# CONFIG_ESP32_ENABLE_COREDUMP_TO_UART is not set +CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE=y +# CONFIG_ESP32_ENABLE_COREDUMP is not set +# CONFIG_TWO_MAC_ADDRESS_FROM_EFUSE is not set +CONFIG_FOUR_MAC_ADDRESS_FROM_EFUSE=y +CONFIG_NUMBER_OF_MAC_ADDRESS_GENERATED_FROM_EFUSE=4 CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32 CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2048 CONFIG_MAIN_TASK_STACK_SIZE=4096 @@ -115,8 +135,21 @@ CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1=y # CONFIG_ESP32_TIME_SYSCALL_USE_FRC1 is not set # CONFIG_ESP32_TIME_SYSCALL_USE_NONE is not set CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC=y -CONFIG_ESP32_PHY_AUTO_INIT=y +CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY=0 +CONFIG_WIFI_ENABLED=y +CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=10 +CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=0 +CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM=32 +CONFIG_ESP32_WIFI_AMPDU_ENABLED=y +CONFIG_ESP32_WIFI_NVS_ENABLED=y +CONFIG_PHY_ENABLED=y + +# +# PHY +# +CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE=y # CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION is not set +CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER=20 CONFIG_ESP32_PHY_MAX_TX_POWER=20 # CONFIG_ETHERNET is not set @@ -126,12 +159,12 @@ CONFIG_ESP32_PHY_MAX_TX_POWER=20 # CONFIG_FREERTOS_UNICORE is not set CONFIG_FREERTOS_CORETIMER_0=y # CONFIG_FREERTOS_CORETIMER_1 is not set -# CONFIG_FREERTOS_CORETIMER_2 is not set CONFIG_FREERTOS_HZ=1000 CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION=y # CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE is not set # CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL is not set CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY=y +# CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK is not set CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=3 CONFIG_FREERTOS_ASSERT_FAIL_ABORT=y # CONFIG_FREERTOS_ASSERT_FAIL_PRINT_CONTINUE is not set @@ -140,6 +173,7 @@ CONFIG_FREERTOS_BREAK_ON_SCHEDULER_START_JTAG=y # CONFIG_ENABLE_MEMORY_DEBUG is not set CONFIG_FREERTOS_ISR_STACKSIZE=1536 # CONFIG_FREERTOS_LEGACY_HOOKS is not set +CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16 # CONFIG_FREERTOS_DEBUG_INTERNALS is not set # @@ -161,7 +195,13 @@ CONFIG_LOG_COLORS=y CONFIG_LWIP_MAX_SOCKETS=4 CONFIG_LWIP_THREAD_LOCAL_STORAGE_INDEX=0 # CONFIG_LWIP_SO_REUSE is not set +# CONFIG_LWIP_SO_RCVBUF is not set CONFIG_LWIP_DHCP_MAX_NTP_SERVERS=1 +# CONFIG_LWIP_IP_FRAG is not set +# CONFIG_LWIP_IP_REASSEMBLY is not set +CONFIG_TCP_MAXRTX=12 +CONFIG_TCP_SYNMAXRTX=6 +# CONFIG_LWIP_DHCP_DOES_ARP_CHECK is not set # # mbedTLS @@ -175,6 +215,13 @@ CONFIG_MBEDTLS_HARDWARE_SHA=y CONFIG_MBEDTLS_HAVE_TIME=y # CONFIG_MBEDTLS_HAVE_TIME_DATE is not set +# +# OpenSSL +# +# CONFIG_OPENSSL_DEBUG is not set +CONFIG_OPENSSL_ASSERT_DO_NOTHING=y +# CONFIG_OPENSSL_ASSERT_EXIT is not set + # # SPI Flash driver #