diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5bf67bb9c..14921f4bb 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -315,7 +315,7 @@ test_fatfs_on_host: - cd components/fatfs/test_fatfs_host/ - make test -test_mdns_fuzzer_on_host: +.host_fuzzer_test_template: &host_fuzzer_test_template stage: host_test image: $CI_DOCKER_REGISTRY/afl-fuzzer-test tags: @@ -324,21 +324,47 @@ test_mdns_fuzzer_on_host: artifacts: when: always paths: - - components/mdns/test_afl_fuzz_host/out/crashes + - ${FUZZER_TEST_DIR}/out/crashes + - ${FUZZER_TEST_DIR}/fuzz_output.log expire_in: 1 mos only: # can only be triggered - triggers - variables: - BOT_NEEDS_TRIGGER_BY_NAME: 1 script: - export AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 && export AFL_SKIP_CPUFREQ=1 - - cd components/mdns/test_afl_fuzz_host/ + - cd ${FUZZER_TEST_DIR} # run AFL fuzzer for one hour - - ( make fuzz || pkill sleep ) & + - ( ( make ${FUZZER_PARAMS} fuzz | tee fuzz_output.log | grep -v '\(Fuzzing test case\|Entering queue cycle\)' ) || pkill sleep ) & - ( sleep 3600 || mkdir -p out/crashes/env_failed ) && pkill afl-fuz # check no crashes found - - "[ -z `ls out/crashes/` ] || exit 1" + - test -z "$(ls out/crashes/)" || exit 1 + +test_mdns_fuzzer_on_host: + <<: *host_fuzzer_test_template + variables: + BOT_NEEDS_TRIGGER_BY_NAME: 1 + FUZZER_TEST_DIR: components/mdns/test_afl_fuzz_host + +test_lwip_dns_fuzzer_on_host: + <<: *host_fuzzer_test_template + variables: + BOT_NEEDS_TRIGGER_BY_NAME: 1 + FUZZER_TEST_DIR: components/lwip/test_afl_host + FUZZER_PARAMS: MODE=dns + +test_lwip_dhcp_fuzzer_on_host: + <<: *host_fuzzer_test_template + variables: + BOT_NEEDS_TRIGGER_BY_NAME: 1 + FUZZER_TEST_DIR: components/lwip/test_afl_host + FUZZER_PARAMS: MODE=dhcp_client + +test_lwip_dhcps_fuzzer_on_host: + <<: *host_fuzzer_test_template + variables: + BOT_NEEDS_TRIGGER_BY_NAME: 1 + FUZZER_TEST_DIR: components/lwip/test_afl_host + FUZZER_PARAMS: MODE=dhcp_server test_spiffs_on_host: <<: *host_test_template @@ -385,14 +411,30 @@ test_idf_monitor: expire_in: 1 week script: - cd ${IDF_PATH}/tools/test_idf_monitor + - source /opt/pyenv/activate + - pyenv global 2.7.15 - ./run_test_idf_monitor.py + - pyenv global 3.4.8 + - ./run_test_idf_monitor.py + - pyenv global system test_esp_err_to_name_on_host: <<: *host_test_template + artifacts: + when: on_failure + paths: + - components/esp32/esp_err_to_name.c + expire_in: 1 week script: - cd tools/ + - source /opt/pyenv/activate + - pyenv global 2.7.15 - ./gen_esp_err_to_name.py - git diff --exit-code -- ../components/esp32/esp_err_to_name.c || (echo 'Differences found. Please run gen_esp_err_to_name.py and commit the changes.'; exit 1) + - pyenv global 3.4.8 + - ./gen_esp_err_to_name.py + - git diff --exit-code -- ../components/esp32/esp_err_to_name.c || (echo 'Differences found between running under Python 2 and 3.'; exit 1) + - pyenv global system push_to_github: stage: deploy @@ -931,6 +973,18 @@ UT_001_34: - ESP32_IDF - UT_T1_1 +UT_001_35: + <<: *unit_test_template + tags: + - ESP32_IDF + - UT_T1_1 + +UT_001_36: + <<: *unit_test_template + tags: + - ESP32_IDF + - UT_T1_1 + UT_002_01: <<: *unit_test_template tags: diff --git a/components/app_trace/include/esp_app_trace_util.h b/components/app_trace/include/esp_app_trace_util.h index e689d4502..6376008c2 100644 --- a/components/app_trace/include/esp_app_trace_util.h +++ b/components/app_trace/include/esp_app_trace_util.h @@ -41,6 +41,7 @@ static inline void esp_apptrace_tmo_init(esp_apptrace_tmo_t *tmo, uint32_t user_ { tmo->start = portGET_RUN_TIME_COUNTER_VALUE(); tmo->tmo = user_tmo; + tmo->elapsed = 0; } /** diff --git a/components/app_update/Makefile.projbuild b/components/app_update/Makefile.projbuild index 29d223dc5..a21e047b8 100644 --- a/components/app_update/Makefile.projbuild +++ b/components/app_update/Makefile.projbuild @@ -1,22 +1,55 @@ -# # Generate partition binary # -.PHONY: erase_ota blank_ota_data +.PHONY: dump_otadata erase_ota blank_ota_data GEN_EMPTY_PART := $(PYTHON) $(COMPONENT_PATH)/gen_empty_partition.py BLANK_OTA_DATA_FILE = $(BUILD_DIR_BASE)/ota_data_initial.bin +PARTITION_TABLE_LEN := 0xC00 +OTADATA_LEN := 0x2000 + +PARTITION_TABLE_ONCHIP_BIN_PATH := $(call dequote,$(abspath $(BUILD_DIR_BASE))) +PARTITION_TABLE_ONCHIP_BIN_NAME := "onchip_partition.bin" +OTADATA_ONCHIP_BIN_NAME := "onchip_otadata.bin" + +PARTITION_TABLE_ONCHIP_BIN := $(PARTITION_TABLE_ONCHIP_BIN_PATH)/$(call dequote,$(PARTITION_TABLE_ONCHIP_BIN_NAME)) +OTADATA_ONCHIP_BIN := $(PARTITION_TABLE_ONCHIP_BIN_PATH)/$(call dequote,$(OTADATA_ONCHIP_BIN_NAME)) + +PARTITION_TABLE_GET_BIN_CMD = $(ESPTOOLPY_SERIAL) read_flash $(PARTITION_TABLE_OFFSET) $(PARTITION_TABLE_LEN) $(PARTITION_TABLE_ONCHIP_BIN) +OTADATA_GET_BIN_CMD = $(ESPTOOLPY_SERIAL) read_flash $(OTADATA_OFFSET) $(OTADATA_LEN) $(OTADATA_ONCHIP_BIN) + +GEN_OTADATA = $(IDF_PATH)/components/app_update/dump_otadata.py +ERASE_OTADATA_CMD = $(ESPTOOLPY_SERIAL) erase_region $(OTADATA_OFFSET) $(OTADATA_LEN) + # If there is no otadata partition, both OTA_DATA_OFFSET and BLANK_OTA_DATA_FILE # expand to empty values. ESPTOOL_ALL_FLASH_ARGS += $(OTA_DATA_OFFSET) $(BLANK_OTA_DATA_FILE) +$(PARTITION_TABLE_ONCHIP_BIN): + $(PARTITION_TABLE_GET_BIN_CMD) + +onchip_otadata_get_info: $(PARTITION_TABLE_ONCHIP_BIN) + $(eval OTADATA_OFFSET:=$(shell $(GET_PART_INFO) --type data --subtype ota --offset $(PARTITION_TABLE_ONCHIP_BIN))) + @echo $(if $(OTADATA_OFFSET), $(shell export OTADATA_OFFSET), $(shell rm -f $(PARTITION_TABLE_ONCHIP_BIN));$(error "ERROR: ESP32 does not have otadata partition.")) + +$(OTADATA_ONCHIP_BIN): + $(OTADATA_GET_BIN_CMD) + +dump_otadata: onchip_otadata_get_info $(OTADATA_ONCHIP_BIN) $(PARTITION_TABLE_ONCHIP_BIN) + @echo "otadata retrieved. Contents:" + @echo $(SEPARATOR) + $(GEN_OTADATA) $(OTADATA_ONCHIP_BIN) + @echo $(SEPARATOR) + rm -f $(PARTITION_TABLE_ONCHIP_BIN) + rm -f $(OTADATA_ONCHIP_BIN) + $(BLANK_OTA_DATA_FILE): partition_table_get_info $(GEN_EMPTY_PART) --size $(OTA_DATA_SIZE) $(BLANK_OTA_DATA_FILE) $(eval BLANK_OTA_DATA_FILE = $(shell if [ $(OTA_DATA_SIZE) != 0 ]; then echo $(BLANK_OTA_DATA_FILE); else echo " "; fi) ) blank_ota_data: $(BLANK_OTA_DATA_FILE) -erase_ota: partition_table_get_info +erase_ota: partition_table_get_info check_python_dependencies @echo $(if $(OTA_DATA_OFFSET), "Erase ota_data [addr=$(OTA_DATA_OFFSET) size=$(OTA_DATA_SIZE)] ...", $(error "ERROR: Partition table does not have ota_data partition.")) $(ESPTOOLPY_SERIAL) erase_region $(OTA_DATA_OFFSET) $(OTA_DATA_SIZE) diff --git a/components/app_update/dump_otadata.py b/components/app_update/dump_otadata.py new file mode 100755 index 000000000..040bd2b03 --- /dev/null +++ b/components/app_update/dump_otadata.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python +# +# gen_otadata prints info about the otadata partition. +# +# Copyright 2018 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. +from __future__ import print_function, division +import argparse +import os +import re +import struct +import sys +import hashlib +import binascii + +__version__ = '1.0' + +quiet = False + +def status(msg): + """ Print status message to stderr """ + if not quiet: + critical(msg) + +def critical(msg): + """ Print critical message to stderr """ + if not quiet: + sys.stderr.write(msg) + sys.stderr.write('\n') + +def little_endian(buff, offset): + data = buff[offset:offset+4] + data.reverse() + data = ''.join(data) + return data + +def main(): + global quiet + parser = argparse.ArgumentParser(description='Prints otadata partition in human readable form.') + + parser.add_argument('--quiet', '-q', help="Don't print status messages to stderr", action='store_true') + + search_type = parser.add_mutually_exclusive_group() + + parser.add_argument('input', help='Path to binary file containing otadata partition to parse.', + type=argparse.FileType('rb')) + + args = parser.parse_args() + + quiet = args.quiet + + input = args.input.read() + + hex_input_0 = binascii.hexlify(input) + hex_input_0 = map(''.join, zip(*[iter(hex_input_0)]*2)) + hex_input_1 = binascii.hexlify(input[4096:]) + hex_input_1 = map(''.join, zip(*[iter(hex_input_1)]*2)) + + print("\t%11s\t%8s |\t%8s\t%8s" %("OTA_SEQ", "CRC", "OTA_SEQ", "CRC")) + print("Firmware: 0x%s \t 0x%s |\t0x%s \t 0x%s" % (little_endian(hex_input_0, 0), little_endian(hex_input_0, 28), \ + little_endian(hex_input_1, 0), little_endian(hex_input_1, 28))) +class InputError(RuntimeError): + def __init__(self, e): + super(InputError, self).__init__(e) + +class ValidationError(InputError): + def __init__(self, partition, message): + super(ValidationError, self).__init__( + "Partition %s invalid: %s" % (partition.name, message)) + +if __name__ == '__main__': + try: + r = main() + sys.exit(r) + except InputError as e: + print(e, file=sys.stderr) + sys.exit(2) diff --git a/components/app_update/test/test_switch_ota.c b/components/app_update/test/test_switch_ota.c new file mode 100644 index 000000000..28248ced0 --- /dev/null +++ b/components/app_update/test/test_switch_ota.c @@ -0,0 +1,478 @@ +/* + * Tests for switching between partitions: factory, OTAx, test. + */ + +#include +#include +#include "string.h" + +#include "rom/spi_flash.h" +#include "rom/rtc.h" +#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 "bootloader_common.h" +#include "../include_bootloader/bootloader_flash.h" + +#include "esp_log.h" +#include "esp_ota_ops.h" +#include "esp_partition.h" +#include "esp_flash_partitions.h" +#include "esp_image_format.h" +#include "nvs_flash.h" + +#include "driver/gpio.h" + +#include "sdkconfig.h" + +RTC_DATA_ATTR static int boot_count = 0; +static const char *TAG = "ota_test"; + +/* @brief Copies a current app to next partition using handle. + * + * @param[in] update_handle - Handle of API ota. + * @param[in] cur_app - Current app. + */ +static void copy_app_partition(esp_ota_handle_t update_handle, const esp_partition_t *curr_app) +{ + const void *partition_bin = NULL; + spi_flash_mmap_handle_t data_map; + TEST_ESP_OK(esp_partition_mmap(curr_app, 0, curr_app->size, SPI_FLASH_MMAP_DATA, &partition_bin, &data_map)); + TEST_ESP_OK(esp_ota_write(update_handle, (const void *)partition_bin, curr_app->size)); + spi_flash_munmap(data_map); +} + +#if defined(CONFIG_BOOTLOADER_FACTORY_RESET) || defined(CONFIG_BOOTLOADER_APP_TEST) +/* @brief Copies partition from source partition to destination partition. + * + * Partitions can be of any types and subtypes. + * @param[in] dst_partition - Destination partition + * @param[in] src_partition - Source partition + */ +static void copy_partition(const esp_partition_t *dst_partition, const esp_partition_t *src_partition) +{ + const void *partition_bin = NULL; + spi_flash_mmap_handle_t data_map; + TEST_ESP_OK(esp_partition_mmap(src_partition, 0, src_partition->size, SPI_FLASH_MMAP_DATA, &partition_bin, &data_map)); + TEST_ESP_OK(esp_partition_erase_range(dst_partition, 0, dst_partition->size)); + TEST_ESP_OK(esp_partition_write(dst_partition, 0, (const void *)partition_bin, dst_partition->size)); + spi_flash_munmap(data_map); +} +#endif + +/* @brief Get the next partition of OTA for the update. + * + * @return The next partition of OTA(OTA0-15). + */ +static const esp_partition_t * get_next_update_partition(void) +{ + const esp_partition_t *update_partition = esp_ota_get_next_update_partition(NULL); + TEST_ASSERT_NOT_EQUAL(NULL, update_partition); + ESP_LOGI(TAG, "Writing to partition subtype %d at offset 0x%x", update_partition->subtype, update_partition->address); + return update_partition; +} + +/* @brief Copies a current app to next partition (OTA0-15) and then configure OTA data for a new boot partition. + * + * @param[in] cur_app_partition - Current app. + * @param[in] next_app_partition - Next app for boot. + */ +static void copy_current_app_to_next_part(const esp_partition_t *cur_app_partition, const esp_partition_t *next_app_partition) +{ + esp_ota_get_next_update_partition(NULL); + TEST_ASSERT_NOT_EQUAL(NULL, next_app_partition); + ESP_LOGI(TAG, "Writing to partition subtype %d at offset 0x%x", next_app_partition->subtype, next_app_partition->address); + + esp_ota_handle_t update_handle = 0; + TEST_ESP_OK(esp_ota_begin(next_app_partition, OTA_SIZE_UNKNOWN, &update_handle)); + + copy_app_partition(update_handle, cur_app_partition); + + TEST_ESP_OK(esp_ota_end(update_handle)); + TEST_ESP_OK(esp_ota_set_boot_partition(next_app_partition)); +} + +/* @brief Erase otadata partition + */ +static void erase_ota_data(void) +{ + const esp_partition_t *data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_OTA, NULL); + TEST_ASSERT_NOT_EQUAL(NULL, data_partition); + TEST_ESP_OK(esp_partition_erase_range(data_partition, 0, 2 * SPI_FLASH_SEC_SIZE)); +} + +/* @brief Reboots ESP using mode deep sleep. This mode guaranty that RTC_DATA_ATTR variables is not reset. + */ +static void reboot_as_deep_sleep(void) +{ + esp_sleep_enable_timer_wakeup(2000); + esp_deep_sleep_start(); +} + +/* @brief Copies a current app to next partition (OTA0-15), after that ESP is rebooting and run this (the next) OTAx. + */ +static void copy_current_app_to_next_part_and_reboot() +{ + const esp_partition_t *cur_app = esp_ota_get_running_partition(); + copy_current_app_to_next_part(cur_app, get_next_update_partition()); + reboot_as_deep_sleep(); +} + +/* @brief Get running app. + * + * @return The next partition of OTA(OTA0-15). + */ +static const esp_partition_t* get_running_firmware(void) +{ + const esp_partition_t *configured = esp_ota_get_boot_partition(); + const esp_partition_t *running = esp_ota_get_running_partition(); + ESP_LOGI(TAG, "Running partition type %d subtype %d (offset 0x%08x)", + running->type, running->subtype, running->address); + ESP_LOGI(TAG, "Configured partition type %d subtype %d (offset 0x%08x)", + configured->type, configured->subtype, configured->address); + TEST_ASSERT_NOT_EQUAL(NULL, configured); + TEST_ASSERT_NOT_EQUAL(NULL, running); + if (running->subtype != ESP_PARTITION_SUBTYPE_APP_TEST) { + TEST_ASSERT_EQUAL_PTR(running, configured); + } + return running; +} + +// type of a corrupt ota_data +typedef enum { + CORR_CRC_1_SECTOR_OTA_DATA = (1 << 0), /*!< Corrupt CRC only 1 sector of ota_data */ + CORR_CRC_2_SECTOR_OTA_DATA = (1 << 1), /*!< Corrupt CRC only 2 sector of ota_data */ +} corrupt_ota_data_t; + +/* @brief Get two copies ota_data from otadata partition. + * + * @param[in] otadata_partition - otadata partition. + * @param[out] ota_data_0 - First copy from otadata_partition. + * @param[out] ota_data_1 - Second copy from otadata_partition. + */ +static void get_ota_data(const esp_partition_t *otadata_partition, esp_ota_select_entry_t *ota_data_0, esp_ota_select_entry_t *ota_data_1) +{ + uint32_t offset = otadata_partition->address; + uint32_t size = otadata_partition->size; + if (offset != 0) { + const esp_ota_select_entry_t *ota_select_map; + ota_select_map = bootloader_mmap(offset, size); + TEST_ASSERT_NOT_EQUAL(NULL, ota_select_map); + + memcpy(ota_data_0, ota_select_map, sizeof(esp_ota_select_entry_t)); + memcpy(ota_data_1, (uint8_t *)ota_select_map + SPI_FLASH_SEC_SIZE, sizeof(esp_ota_select_entry_t)); + bootloader_munmap(ota_select_map); + } +} + +/* @brief Writes a ota_data into required sector of otadata_partition. + * + * @param[in] otadata_partition - Partition information otadata. + * @param[in] ota_data - otadata structure. + * @param[in] sec_id - Sector number 0 or 1. + */ +static void write_ota_data(const esp_partition_t *otadata_partition, esp_ota_select_entry_t *ota_data, int sec_id) +{ + esp_partition_write(otadata_partition, SPI_FLASH_SEC_SIZE * sec_id, &ota_data[sec_id], sizeof(esp_ota_select_entry_t)); +} + +/* @brief Makes a corrupt of ota_data. + * @param[in] err - type error + */ +static void corrupt_ota_data(corrupt_ota_data_t err) +{ + esp_ota_select_entry_t ota_data[2]; + + const esp_partition_t *otadata_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_OTA, NULL); + TEST_ASSERT_NOT_EQUAL(NULL, otadata_partition); + get_ota_data(otadata_partition, &ota_data[0], &ota_data[1]); + + if (err & CORR_CRC_1_SECTOR_OTA_DATA) { + ota_data[0].crc = 0; + } + if (err & CORR_CRC_2_SECTOR_OTA_DATA) { + ota_data[1].crc = 0; + } + TEST_ESP_OK(esp_partition_erase_range(otadata_partition, 0, otadata_partition->size)); + write_ota_data(otadata_partition, &ota_data[0], 0); + write_ota_data(otadata_partition, &ota_data[1], 1); +} + +#if defined(CONFIG_BOOTLOADER_FACTORY_RESET) || defined(CONFIG_BOOTLOADER_APP_TEST) +/* @brief Sets the pin number to output and sets output level as low. After reboot (deep sleep) this pin keep the same level. + * + * The output level of the pad will be force locked and can not be changed. + * Power down or call gpio_hold_dis will disable this function. + * + * @param[in] num_pin - Pin number + */ +static void set_output_pin(uint32_t num_pin) +{ + TEST_ESP_OK(gpio_hold_dis(num_pin)); + + gpio_config_t io_conf; + io_conf.intr_type = GPIO_PIN_INTR_DISABLE; + io_conf.mode = GPIO_MODE_OUTPUT; + io_conf.pin_bit_mask = (1ULL << num_pin); + io_conf.pull_down_en = 0; + io_conf.pull_up_en = 0; + TEST_ESP_OK(gpio_config(&io_conf)); + + TEST_ESP_OK(gpio_set_level(num_pin, 0)); + TEST_ESP_OK(gpio_hold_en(num_pin)); +} + +/* @brief Unset the pin number hold function. + */ +static void reset_output_pin(uint32_t num_pin) +{ + TEST_ESP_OK(gpio_hold_dis(num_pin)); + TEST_ESP_OK(gpio_reset_pin(num_pin)); +} +#endif + + +/* @brief Checks and prepares the partition so that the factory app is launched after that. + */ +static void start_test(void) +{ + ESP_LOGI(TAG, "boot count 1 - reset"); + boot_count = 1; + erase_ota_data(); + reboot_as_deep_sleep(); +} + +static void test_flow1(void) +{ + boot_count++; + ESP_LOGI(TAG, "boot count %d", boot_count); + const esp_partition_t *cur_app = get_running_firmware(); + switch (boot_count) { + case 2: + ESP_LOGI(TAG, "Factory"); + TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_FACTORY, cur_app->subtype); + copy_current_app_to_next_part_and_reboot(cur_app); + break; + case 3: + ESP_LOGI(TAG, "OTA0"); + TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_0, cur_app->subtype); + copy_current_app_to_next_part_and_reboot(cur_app); + break; + case 4: + ESP_LOGI(TAG, "OTA1"); + TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_1, cur_app->subtype); + copy_current_app_to_next_part_and_reboot(cur_app); + break; + case 5: + ESP_LOGI(TAG, "OTA0"); + TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_0, cur_app->subtype); + erase_ota_data(); + break; + default: + erase_ota_data(); + TEST_FAIL_MESSAGE("Unexpected stage"); + break; + } +} + +// 1 Stage: After POWER_RESET erase OTA_DATA for this test -> reboot through deep sleep. +// 2 Stage: run factory -> check it -> copy factory to OTA0 -> reboot --//-- +// 3 Stage: run OTA0 -> check it -> copy OTA0 to OTA1 -> reboot --//-- +// 4 Stage: run OTA1 -> check it -> copy OTA1 to OTA0 -> reboot --//-- +// 5 Stage: run OTA0 -> check it -> erase OTA_DATA for next tests -> PASS +TEST_CASE_MULTIPLE_STAGES("Switching between factory, OTA0, OTA1, OTA0", "[app_update][reset=DEEPSLEEP_RESET, DEEPSLEEP_RESET, DEEPSLEEP_RESET, DEEPSLEEP_RESET]", start_test, test_flow1, test_flow1, test_flow1, test_flow1); + +static void test_flow2(void) +{ + boot_count++; + ESP_LOGI(TAG, "boot count %d", boot_count); + const esp_partition_t *cur_app = get_running_firmware(); + switch (boot_count) { + case 2: + ESP_LOGI(TAG, "Factory"); + TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_FACTORY, cur_app->subtype); + copy_current_app_to_next_part_and_reboot(cur_app); + break; + case 3: + ESP_LOGI(TAG, "OTA0"); + TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_0, cur_app->subtype); + copy_current_app_to_next_part(cur_app, get_next_update_partition()); + corrupt_ota_data(CORR_CRC_1_SECTOR_OTA_DATA); + reboot_as_deep_sleep(); + break; + case 4: + ESP_LOGI(TAG, "Factory"); + TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_FACTORY, cur_app->subtype); + erase_ota_data(); + break; + default: + erase_ota_data(); + TEST_FAIL_MESSAGE("Unexpected stage"); + break; + } +} + +// 1 Stage: After POWER_RESET erase OTA_DATA for this test -> reboot through deep sleep. +// 2 Stage: run factory -> check it -> copy factory to OTA0 -> reboot --//-- +// 3 Stage: run OTA0 -> check it -> corrupt ota data -> reboot --//-- +// 4 Stage: run factory -> check it -> erase OTA_DATA for next tests -> PASS +TEST_CASE_MULTIPLE_STAGES("Switching between factory, OTA0, corrupt ota_sec1, factory", "[app_update][reset=DEEPSLEEP_RESET, DEEPSLEEP_RESET, DEEPSLEEP_RESET]", start_test, test_flow2, test_flow2, test_flow2); + +static void test_flow3(void) +{ + boot_count++; + ESP_LOGI(TAG, "boot count %d", boot_count); + const esp_partition_t *cur_app = get_running_firmware(); + switch (boot_count) { + case 2: + ESP_LOGI(TAG, "Factory"); + TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_FACTORY, cur_app->subtype); + copy_current_app_to_next_part_and_reboot(cur_app); + break; + case 3: + ESP_LOGI(TAG, "OTA0"); + TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_0, cur_app->subtype); + copy_current_app_to_next_part_and_reboot(cur_app); + break; + case 4: + ESP_LOGI(TAG, "OTA1"); + TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_1, cur_app->subtype); + copy_current_app_to_next_part(cur_app, get_next_update_partition()); + corrupt_ota_data(CORR_CRC_2_SECTOR_OTA_DATA); + reboot_as_deep_sleep(); + break; + case 5: + ESP_LOGI(TAG, "OTA0"); + TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_0, cur_app->subtype); + erase_ota_data(); + break; + default: + erase_ota_data(); + TEST_FAIL_MESSAGE("Unexpected stage"); + break; + } +} + +// 1 Stage: After POWER_RESET erase OTA_DATA for this test -> reboot through deep sleep. +// 2 Stage: run factory -> check it -> copy factory to OTA0 -> reboot --//-- +// 3 Stage: run OTA0 -> check it -> copy OTA0 to OTA1 -> reboot --//-- +// 3 Stage: run OTA1 -> check it -> corrupt ota sector2 -> reboot --//-- +// 4 Stage: run OTA0 -> check it -> erase OTA_DATA for next tests -> PASS +TEST_CASE_MULTIPLE_STAGES("Switching between factory, OTA0, OTA1, currupt ota_sec2, OTA0", "[app_update][reset=DEEPSLEEP_RESET, DEEPSLEEP_RESET, DEEPSLEEP_RESET, DEEPSLEEP_RESET]", start_test, test_flow3, test_flow3, test_flow3, test_flow3); + +#ifdef CONFIG_BOOTLOADER_FACTORY_RESET +#define STORAGE_NAMESPACE "update_ota" + +static void test_flow4(void) +{ + boot_count++; + ESP_LOGI(TAG, "boot count %d", boot_count); + const esp_partition_t *cur_app = get_running_firmware(); + nvs_handle handle = 0; + int boot_count_nvs = 0; + switch (boot_count) { + case 2: + ESP_LOGI(TAG, "Factory"); + TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_FACTORY, cur_app->subtype); + + TEST_ESP_OK(nvs_flash_erase()); + TEST_ESP_OK(nvs_flash_init()); + TEST_ESP_OK(nvs_open(STORAGE_NAMESPACE, NVS_READWRITE, &handle)); + TEST_ESP_OK(nvs_set_i32(handle, "boot_count", boot_count)); + TEST_ESP_OK(nvs_commit(handle)); + nvs_close(handle); + nvs_flash_deinit(); + + copy_current_app_to_next_part_and_reboot(cur_app); + break; + case 3: + ESP_LOGI(TAG, "OTA0"); + TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_0, cur_app->subtype); + + TEST_ESP_OK(nvs_flash_init()); + TEST_ESP_OK(nvs_open(STORAGE_NAMESPACE, NVS_READWRITE, &handle)); + TEST_ESP_OK(nvs_get_i32(handle, "boot_count", &boot_count_nvs)); + TEST_ASSERT_EQUAL(boot_count_nvs + 1, boot_count); + nvs_close(handle); + nvs_flash_deinit(); + + set_output_pin(CONFIG_BOOTLOADER_NUM_PIN_FACTORY_RESET); + + reboot_as_deep_sleep(); + break; + case 4: + reset_output_pin(CONFIG_BOOTLOADER_NUM_PIN_FACTORY_RESET); + ESP_LOGI(TAG, "Factory"); + TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_FACTORY, cur_app->subtype); + + int boot_count_nvs; + TEST_ESP_OK(nvs_flash_init()); + TEST_ESP_OK(nvs_open(STORAGE_NAMESPACE, NVS_READWRITE, &handle)); + TEST_ESP_ERR(ESP_ERR_NVS_NOT_FOUND, nvs_get_i32(handle, "boot_count", &boot_count_nvs)); + nvs_close(handle); + nvs_flash_deinit(); + + erase_ota_data(); + break; + default: + reset_output_pin(CONFIG_BOOTLOADER_NUM_PIN_FACTORY_RESET); + erase_ota_data(); + TEST_FAIL_MESSAGE("Unexpected stage"); + break; + } +} +// 1 Stage: After POWER_RESET erase OTA_DATA for this test -> reboot through deep sleep. +// 2 Stage: run factory -> check it -> copy factory to OTA0 -> reboot --//-- +// 3 Stage: run OTA0 -> check it -> set_pin_factory_reset -> reboot --//-- +// 4 Stage: run factory -> check it -> erase OTA_DATA for next tests -> PASS +TEST_CASE_MULTIPLE_STAGES("Switching between factory, OTA0, sets pin_factory_reset, factory", "[app_update][reset=DEEPSLEEP_RESET, DEEPSLEEP_RESET, DEEPSLEEP_RESET]", start_test, test_flow4, test_flow4, test_flow4); +#endif + +#ifdef CONFIG_BOOTLOADER_APP_TEST +static void test_flow5(void) +{ + boot_count++; + ESP_LOGI(TAG, "boot count %d", boot_count); + const esp_partition_t *cur_app = get_running_firmware(); + switch (boot_count) { + case 2: + ESP_LOGI(TAG, "Factory"); + TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_FACTORY, cur_app->subtype); + + set_output_pin(CONFIG_BOOTLOADER_NUM_PIN_APP_TEST); + + copy_partition(esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_TEST, NULL), cur_app); + esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_OTA, NULL); + reboot_as_deep_sleep(); + break; + case 3: + reset_output_pin(CONFIG_BOOTLOADER_NUM_PIN_APP_TEST); + ESP_LOGI(TAG, "Test"); + TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_TEST, cur_app->subtype); + reboot_as_deep_sleep(); + break; + case 4: + ESP_LOGI(TAG, "Factory"); + TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_FACTORY, cur_app->subtype); + erase_ota_data(); + break; + default: + reset_output_pin(CONFIG_BOOTLOADER_NUM_PIN_APP_TEST); + erase_ota_data(); + TEST_FAIL_MESSAGE("Unexpected stage"); + break; + } +} + +// 1 Stage: After POWER_RESET erase OTA_DATA for this test -> reboot through deep sleep. +// 2 Stage: run factory -> check it -> copy factory to Test and set pin_test_app -> reboot --//-- +// 3 Stage: run test -> check it -> reset pin_test_app -> reboot --//-- +// 4 Stage: run factory -> check it -> erase OTA_DATA for next tests -> PASS +TEST_CASE_MULTIPLE_STAGES("Switching between factory, test, factory", "[app_update][reset=DEEPSLEEP_RESET, DEEPSLEEP_RESET, DEEPSLEEP_RESET]", start_test, test_flow5, test_flow5, test_flow5); +#endif diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index b283c9044..d8fa2a5ca 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -49,14 +49,14 @@ ifndef CONFIG_SECURE_BOOT_ENABLED # If secure boot disabled, bootloader flashing is integrated # with 'make flash' and no warnings are printed. -bootloader: $(BOOTLOADER_BIN) +bootloader: $(BOOTLOADER_BIN) | check_python_dependencies @echo $(SEPARATOR) @echo "Bootloader built. Default flash command is:" @echo "$(ESPTOOLPY_WRITE_FLASH) $(BOOTLOADER_OFFSET) $^" ESPTOOL_ALL_FLASH_ARGS += $(BOOTLOADER_OFFSET) $(BOOTLOADER_BIN) -bootloader-flash: $(BOOTLOADER_BIN) $(call prereq_if_explicit,erase_flash) +bootloader-flash: $(BOOTLOADER_BIN) $(call prereq_if_explicit,erase_flash) check_python_dependencies $(ESPTOOLPY_WRITE_FLASH) 0x1000 $^ else ifdef CONFIG_SECURE_BOOTLOADER_ONE_TIME_FLASH @@ -67,7 +67,7 @@ else ifdef CONFIG_SECURE_BOOTLOADER_ONE_TIME_FLASH # The flashing command is deliberately printed without an auto-reset # step, so the device doesn't immediately reset to flash itself. -bootloader: $(BOOTLOADER_BIN) +bootloader: $(BOOTLOADER_BIN) | check_python_dependencies @echo $(SEPARATOR) @echo "Bootloader built. One-time flash command is:" @echo "$(subst hard_reset,no_reset,$(ESPTOOLPY_WRITE_FLASH)) $(BOOTLOADER_OFFSET) $(BOOTLOADER_BIN)" @@ -82,7 +82,7 @@ BOOTLOADER_DIGEST_BIN := $(BOOTLOADER_BUILD_DIR)/bootloader-reflash-digest.bin SECURE_BOOTLOADER_KEY := $(BOOTLOADER_BUILD_DIR)/secure-bootloader-key.bin ifdef CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES -$(SECURE_BOOTLOADER_KEY): $(SECURE_BOOT_SIGNING_KEY) +$(SECURE_BOOTLOADER_KEY): $(SECURE_BOOT_SIGNING_KEY) | check_python_dependencies $(ESPSECUREPY) digest_private_key -k $< $@ else $(SECURE_BOOTLOADER_KEY): @@ -105,7 +105,7 @@ bootloader: $(BOOTLOADER_DIGEST_BIN) @echo "* After first boot, only re-flashes of this kind (with same key) will be accepted." @echo "* Not recommended to re-use the same secure boot keyfile on multiple production devices." -$(BOOTLOADER_DIGEST_BIN): $(BOOTLOADER_BIN) $(SECURE_BOOTLOADER_KEY) +$(BOOTLOADER_DIGEST_BIN): $(BOOTLOADER_BIN) $(SECURE_BOOTLOADER_KEY) | check_python_dependencies @echo "DIGEST $(notdir $@)" $(ESPSECUREPY) digest_secure_bootloader -k $(SECURE_BOOTLOADER_KEY) -o $@ $< diff --git a/components/bootloader_support/include/esp_flash_encrypt.h b/components/bootloader_support/include/esp_flash_encrypt.h index 31b77a734..45fce4465 100644 --- a/components/bootloader_support/include/esp_flash_encrypt.h +++ b/components/bootloader_support/include/esp_flash_encrypt.h @@ -101,4 +101,14 @@ esp_err_t esp_flash_encrypt_check_and_update(void); */ esp_err_t esp_flash_encrypt_region(uint32_t src_addr, size_t data_length); +/** @brief Write protect FLASH_CRYPT_CNT + * + * Intended to be called as a part of boot process if flash encryption + * is enabled but secure boot is not used. This should protect against + * serial re-flashing of an unauthorised code in absence of secure boot. + * + * @return + */ +void esp_flash_write_protect_crypt_cnt(); + #endif diff --git a/components/bootloader_support/src/bootloader_clock.c b/components/bootloader_support/src/bootloader_clock.c index 64ef9c233..d5a1e52ab 100644 --- a/components/bootloader_support/src/bootloader_clock.c +++ b/components/bootloader_support/src/bootloader_clock.c @@ -29,7 +29,7 @@ void bootloader_clock_configure() uart_tx_wait_idle(0); /* Set CPU to 80MHz. Keep other clocks unmodified. */ - rtc_cpu_freq_t cpu_freq = RTC_CPU_FREQ_80M; + int cpu_freq_mhz = 80; /* On ESP32 rev 0, switching to 80MHz if clock was previously set to * 240 MHz may cause the chip to lock up (see section 3.5 of the errata @@ -39,12 +39,12 @@ void bootloader_clock_configure() uint32_t chip_ver_reg = REG_READ(EFUSE_BLK0_RDATA3_REG); if ((chip_ver_reg & EFUSE_RD_CHIP_VER_REV1_M) == 0 && CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ == 240) { - cpu_freq = RTC_CPU_FREQ_240M; + cpu_freq_mhz = 240; } rtc_clk_config_t clk_cfg = RTC_CLK_CONFIG_DEFAULT(); clk_cfg.xtal_freq = CONFIG_ESP32_XTAL_FREQ; - clk_cfg.cpu_freq = cpu_freq; + clk_cfg.cpu_freq_mhz = cpu_freq_mhz; clk_cfg.slow_freq = rtc_clk_slow_freq_get(); clk_cfg.fast_freq = rtc_clk_fast_freq_get(); rtc_clk_init(clk_cfg); diff --git a/components/bootloader_support/src/flash_encrypt.c b/components/bootloader_support/src/flash_encrypt.c index 2b229c00b..7b17dd451 100644 --- a/components/bootloader_support/src/flash_encrypt.c +++ b/components/bootloader_support/src/flash_encrypt.c @@ -337,3 +337,13 @@ esp_err_t esp_flash_encrypt_region(uint32_t src_addr, size_t data_length) ESP_LOGE(TAG, "flash operation failed: 0x%x", err); return err; } + +void esp_flash_write_protect_crypt_cnt() +{ + uint32_t efuse_blk0 = REG_READ(EFUSE_BLK0_RDATA0_REG); + bool flash_crypt_wr_dis = efuse_blk0 & EFUSE_WR_DIS_FLASH_CRYPT_CNT; + if(!flash_crypt_wr_dis) { + REG_WRITE(EFUSE_BLK0_WDATA0_REG, EFUSE_WR_DIS_FLASH_CRYPT_CNT); + esp_efuse_burn_new_values(); + } +} diff --git a/components/bt/Kconfig b/components/bt/Kconfig index 187cdd73c..149b45c83 100644 --- a/components/bt/Kconfig +++ b/components/bt/Kconfig @@ -6,6 +6,68 @@ config BT_ENABLED help Select this option to enable Bluetooth and show the submenu with Bluetooth configuration choices. +menu "Bluetooth controller" + visible if BT_ENABLED + +choice BTDM_CONTROLLER_MODE + prompt "Bluetooth controller mode (BR/EDR/BLE/DUALMODE)" + depends on BT_ENABLED + help + Specify the bluetooth controller mode (BR/EDR, BLE or dual mode). + +config BTDM_CONTROLLER_MODE_BLE_ONLY + bool "BLE Only" + +config BTDM_CONTROLLER_MODE_BR_EDR_ONLY + bool "BR/EDR Only" + +config BTDM_CONTROLLER_MODE_BTDM + bool "Bluetooth Dual Mode" + +endchoice + +config BTDM_CONTROLLER_BLE_MAX_CONN + int "BLE Max Connections" + depends on BTDM_CONTROLLER_MODE_BLE_ONLY || BTDM_CONTROLLER_MODE_BTDM + default 3 + range 1 9 + help + BLE maximum connections of bluetooth controller. + Each connection uses 1KB static DRAM whenever the BT controller is enabled. + +config BTDM_CONTROLLER_BR_EDR_MAX_ACL_CONN + int "BR/EDR ACL Max Connections" + depends on BTDM_CONTROLLER_MODE_BR_EDR_ONLY || BTDM_CONTROLLER_MODE_BTDM + default 2 + range 1 7 + help + BR/EDR ACL maximum connections of bluetooth controller. + Each connection uses 1.2KB static DRAM whenever the BT controller is enabled. + +config BTDM_CONTROLLER_BR_EDR_MAX_SYNC_CONN + int "BR/EDR Sync(SCO/eSCO) Max Connections" + depends on BTDM_CONTROLLER_MODE_BR_EDR_ONLY || BTDM_CONTROLLER_MODE_BTDM + default 0 + range 0 3 + help + BR/EDR Synchronize maximum connections of bluetooth controller. + Each connection uses 2KB static DRAM whenever the BT controller is enabled. + +config BTDM_CONTROLLER_BLE_MAX_CONN_EFF + int + default BTDM_CONTROLLER_BLE_MAX_CONN if BTDM_CONTROLLER_MODE_BLE_ONLY || BTDM_CONTROLLER_MODE_BTDM + default 0 + +config BTDM_CONTROLLER_BR_EDR_MAX_ACL_CONN_EFF + int + default BTDM_CONTROLLER_BR_EDR_MAX_ACL_CONN if BTDM_CONTROLLER_MODE_BR_EDR_ONLY || BTDM_CONTROLLER_MODE_BTDM + default 0 + +config BTDM_CONTROLLER_BR_EDR_MAX_SYNC_CONN_EFF + int + default BTDM_CONTROLLER_BR_EDR_MAX_SYNC_CONN if BTDM_CONTROLLER_MODE_BR_EDR_ONLY || BTDM_CONTROLLER_MODE_BTDM + default 0 + choice BTDM_CONTROLLER_PINNED_TO_CORE_CHOICE prompt "The cpu core which bluetooth controller run" depends on BT_ENABLED && !FREERTOS_UNICORE @@ -104,6 +166,42 @@ config BTDM_LPCLK_SEL_EXT_32K_XTAL depends on ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL endchoice +endmenu + +config BLE_SCAN_DUPLICATE + bool "BLE Scan Duplicate Options" + depends on (BTDM_CONTROLLER_MODE_BTDM || BTDM_CONTROLLER_MODE_BLE_ONLY) + default y + help + This select enables parameters setting of BLE scan duplicate. + +config DUPLICATE_SCAN_CACHE_SIZE + int "Maximum number of devices in scan duplicate filter" + depends on BLE_SCAN_DUPLICATE + range 10 200 + default 20 + help + Maximum number of devices which can be recorded in scan duplicate filter. + When the maximum amount of device in the filter is reached, the cache will be refreshed. + +config BLE_MESH_SCAN_DUPLICATE_EN + bool "Special duplicate scan mechanism for BLE Mesh scan" + depends on BLE_SCAN_DUPLICATE + default n + help + This enables the BLE scan duplicate for special BLE Mesh scan. + +config MESH_DUPLICATE_SCAN_CACHE_SIZE + int "Maximum number of Mesh adv packets in scan duplicate filter" + depends on BLE_MESH_SCAN_DUPLICATE_EN + range 10 200 + default 50 + help + Maximum number of adv packets which can be recorded in duplicate scan cache for BLE Mesh. + When the maximum amount of device in the filter is reached, the cache will be refreshed. + + + endmenu menuconfig BLUEDROID_ENABLED @@ -213,28 +311,28 @@ endchoice config GATTS_ENABLE bool "Include GATT server module(GATTS)" - depends on BLUEDROID_ENABLED + depends on BLUEDROID_ENABLED && (BTDM_CONTROLLER_MODE_BTDM || BTDM_CONTROLLER_MODE_BLE_ONLY) default y help This option can be disabled when the app work only on gatt client mode config GATTC_ENABLE bool "Include GATT client module(GATTC)" - depends on BLUEDROID_ENABLED + depends on BLUEDROID_ENABLED && (BTDM_CONTROLLER_MODE_BTDM || BTDM_CONTROLLER_MODE_BLE_ONLY) default y help This option can be close when the app work only on gatt server mode config GATTC_CACHE_NVS_FLASH bool "Save gattc cache data to nvs flash" - depends on GATTC_ENABLE + depends on GATTC_ENABLE && (BTDM_CONTROLLER_MODE_BTDM || BTDM_CONTROLLER_MODE_BLE_ONLY) default n help This select can save gattc cache data to nvs flash config BLE_SMP_ENABLE bool "Include BLE security module(SMP)" - depends on BLUEDROID_ENABLED + depends on BLUEDROID_ENABLED && (BTDM_CONTROLLER_MODE_BTDM || BTDM_CONTROLLER_MODE_BLE_ONLY) default y help This option can be close when the app not used the ble security connect. @@ -1018,38 +1116,6 @@ config BLE_HOST_QUEUE_CONGESTION_CHECK handling adv packets is slow, it will cause the controller memory to run out. if enabled, adv packets will be lost when host queue is congested. -config BLE_SCAN_DUPLICATE - bool "BLE Scan Duplicate Options" - depends on BLUEDROID_ENABLED - default y - help - This select enables parameters setting of BLE scan duplicate. - -config DUPLICATE_SCAN_CACHE_SIZE - int "Maximum number of devices in scan duplicate filter" - depends on BLE_SCAN_DUPLICATE - range 10 1000 - default 50 - help - Maximum number of devices which can be recorded in scan duplicate filter. - When the maximum amount of device in the filter is reached, the cache will be refreshed. - -config BLE_MESH_SCAN_DUPLICATE_EN - bool "Special duplicate scan mechanism for BLE Mesh scan" - depends on BLE_SCAN_DUPLICATE - default n - help - This enables the BLE scan duplicate for special BLE Mesh scan. - -config MESH_DUPLICATE_SCAN_CACHE_SIZE - int "Maximum number of Mesh adv packets in scan duplicate filter" - depends on BLE_MESH_SCAN_DUPLICATE_EN - range 10 1000 - default 100 - help - Maximum number of adv packets which can be recorded in duplicate scan cache for BLE Mesh. - When the maximum amount of device in the filter is reached, the cache will be refreshed. - config SMP_ENABLE bool depends on BLUEDROID_ENABLED @@ -1058,7 +1124,7 @@ config SMP_ENABLE # Memory reserved at start of DRAM for Bluetooth stack config BT_RESERVE_DRAM hex - default 0x10000 if BT_ENABLED + default 0xdb5c if BT_ENABLED default 0 endmenu diff --git a/components/bt/bluedroid/bta/gatt/bta_gattc_cache.c b/components/bt/bluedroid/bta/gatt/bta_gattc_cache.c index 90a9d1350..2d6e9ee15 100644 --- a/components/bt/bluedroid/bta/gatt/bta_gattc_cache.c +++ b/components/bt/bluedroid/bta/gatt/bta_gattc_cache.c @@ -1291,7 +1291,9 @@ void bta_gattc_get_db_with_opration(UINT16 conn_id, tBTA_GATTC_CLCB *p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id); if (p_clcb == NULL) { - return NULL; + *count = 0; + *char_db = NULL; + return; } tBTA_GATTC_SERV *p_srcb = p_clcb->p_srcb; @@ -1671,7 +1673,8 @@ void bta_gattc_get_db_size_with_type_handle(UINT16 conn_id, bt_gatt_db_attribute tBTA_GATTC_CLCB *p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id); if (p_clcb == NULL) { - return NULL; + *count = 0; + return; } tBTA_GATTC_SERV *p_srcb = p_clcb->p_srcb; diff --git a/components/bt/bluedroid/bta/gatt/bta_gattc_utils.c b/components/bt/bluedroid/bta/gatt/bta_gattc_utils.c index 774134478..37193438a 100644 --- a/components/bt/bluedroid/bta/gatt/bta_gattc_utils.c +++ b/components/bt/bluedroid/bta/gatt/bta_gattc_utils.c @@ -578,13 +578,14 @@ void bta_gattc_clear_notif_registration(tBTA_GATTC_SERV *p_srcb, UINT16 conn_id, for (i = 0 ; i < BTA_GATTC_NOTIF_REG_MAX; i ++) { if (p_clrcb->notif_reg[i].in_use && !bdcmp(p_clrcb->notif_reg[i].remote_bda, remote_bda)) - + { /* It's enough to get service or characteristic handle, as * clear boundaries are always around service. */ handle = p_clrcb->notif_reg[i].handle; if (handle >= start_handle && handle <= end_handle) memset(&p_clrcb->notif_reg[i], 0, sizeof(tBTA_GATTC_NOTIF_REG)); + } } } } else { diff --git a/components/bt/bluedroid/bta/gatt/bta_gatts_act.c b/components/bt/bluedroid/bta/gatt/bta_gatts_act.c index c72cd6328..cf3bb5dbd 100644 --- a/components/bt/bluedroid/bta/gatt/bta_gatts_act.c +++ b/components/bt/bluedroid/bta/gatt/bta_gatts_act.c @@ -702,6 +702,10 @@ void bta_gatts_indicate_handle (tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg) APPL_TRACE_ERROR("%s, malloc failed", __func__); } (*p_rcb->p_cback)(BTA_GATTS_CONF_EVT, &cb_data); + if (cb_data.req_data.value != NULL) { + osi_free(cb_data.req_data.value); + cb_data.req_data.value = NULL; + } } } else { APPL_TRACE_ERROR("Not an registered servce attribute ID: 0x%04x", diff --git a/components/bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c b/components/bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c index 3bfa6afbc..56be5fb61 100644 --- a/components/bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c +++ b/components/bt/bluedroid/btc/profile/esp/blufi/blufi_prf.c @@ -209,10 +209,7 @@ static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) blufi_env.frag_size = p_data->req_data.p_data->mtu - BLUFI_MTU_RESERVED_SIZE; break; case BTA_GATTS_CONF_EVT: - BLUFI_TRACE_DEBUG("CONIRM EVT\n"); - if (p_data && p_data->req_data.value){ - osi_free(p_data->req_data.value); - } + BLUFI_TRACE_DEBUG("CONFIRM EVT\n"); /* Nothing */ break; case BTA_GATTS_CREATE_EVT: diff --git a/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink.c b/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink.c index faf23eefc..729237ed7 100644 --- a/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink.c +++ b/components/bt/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink.c @@ -586,7 +586,7 @@ static void btc_a2dp_sink_handle_inc_media(tBT_SBC_HDR *p_msg) OI_STATUS status; int num_sbc_frames = p_msg->num_frames_to_be_processed; UINT32 sbc_frame_len = p_msg->len - 1; - availPcmBytes = 2 * sizeof(pcmData); + availPcmBytes = sizeof(pcmData); /* XXX: Check if the below check is correct, we are checking for peer to be sink when we are sink */ if (btc_av_get_peer_sep() == AVDT_TSEP_SNK || (btc_aa_snk_cb.rx_flush)) { @@ -617,7 +617,7 @@ static void btc_a2dp_sink_handle_inc_media(tBT_SBC_HDR *p_msg) p_msg->len = sbc_frame_len + 1; } - btc_a2d_data_cb_to_app((uint8_t *)pcmData, (2 * sizeof(pcmData) - availPcmBytes)); + btc_a2d_data_cb_to_app((uint8_t *)pcmData, (sizeof(pcmData) - availPcmBytes)); } /******************************************************************************* 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 fc043914e..bfe9cc04e 100644 --- a/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c +++ b/components/bt/bluedroid/btc/profile/std/gatt/btc_gatts.c @@ -530,9 +530,6 @@ static void btc_gatts_cb_param_copy_free(btc_msg_t *msg, tBTA_GATTS *p_data) } break; case BTA_GATTS_CONF_EVT: - if (p_data && p_data->req_data.value){ - osi_free(p_data->req_data.value); - } break; default: break; diff --git a/components/bt/bluedroid/hci/hci_hal_h4.c b/components/bt/bluedroid/hci/hci_hal_h4.c index e910aa7b7..0a0ea85bd 100644 --- a/components/bt/bluedroid/hci/hci_hal_h4.c +++ b/components/bt/bluedroid/hci/hci_hal_h4.c @@ -112,8 +112,9 @@ static bool hal_open(const hci_hal_callbacks_t *upper_callbacks) xTaskCreatePinnedToCore(hci_hal_h4_rx_handler, HCI_H4_TASK_NAME, HCI_H4_TASK_STACK_SIZE, NULL, HCI_H4_TASK_PRIO, &xHciH4TaskHandle, HCI_H4_TASK_PINNED_TO_CORE); //register vhci host cb - esp_vhci_host_register_callback(&vhci_host_cb); - + if (esp_vhci_host_register_callback(&vhci_host_cb) != ESP_OK) { + return false; + } return true; } diff --git a/components/bt/bluedroid/osi/config.c b/components/bt/bluedroid/osi/config.c index 55e782ca6..55a3b3d4b 100644 --- a/components/bt/bluedroid/osi/config.c +++ b/components/bt/bluedroid/osi/config.c @@ -336,7 +336,8 @@ static int get_config_size_from_flash(nvs_handle fp) assert(fp != 0); esp_err_t err; - char *keyname = osi_calloc(sizeof(CONFIG_KEY) + 1); + const size_t keyname_bufsz = sizeof(CONFIG_KEY) + 5 + 1; // including log10(sizeof(i)) + char *keyname = osi_calloc(keyname_bufsz); if (!keyname){ OSI_TRACE_ERROR("%s, malloc error\n", __func__); return 0; @@ -344,7 +345,7 @@ static int get_config_size_from_flash(nvs_handle fp) size_t length = CONFIG_FILE_DEFAULE_LENGTH; size_t total_length = 0; uint16_t i = 0; - snprintf(keyname, sizeof(CONFIG_KEY) + 1, "%s%d", CONFIG_KEY, 0); + snprintf(keyname, keyname_bufsz, "%s%d", CONFIG_KEY, 0); err = nvs_get_blob(fp, keyname, NULL, &length); if (err == ESP_ERR_NVS_NOT_FOUND) { osi_free(keyname); @@ -358,7 +359,7 @@ static int get_config_size_from_flash(nvs_handle fp) total_length += length; while (length == CONFIG_FILE_MAX_SIZE) { length = CONFIG_FILE_DEFAULE_LENGTH; - snprintf(keyname, sizeof(CONFIG_KEY) + 1, "%s%d", CONFIG_KEY, ++i); + snprintf(keyname, keyname_bufsz, "%s%d", CONFIG_KEY, ++i); err = nvs_get_blob(fp, keyname, NULL, &length); if (err == ESP_ERR_NVS_NOT_FOUND) { @@ -385,7 +386,8 @@ bool config_save(const config_t *config, const char *filename) int err_code = 0; nvs_handle fp; char *line = osi_calloc(1024); - char *keyname = osi_calloc(sizeof(CONFIG_KEY) + 1); + const size_t keyname_bufsz = sizeof(CONFIG_KEY) + 5 + 1; // including log10(sizeof(i)) + char *keyname = osi_calloc(keyname_bufsz); int config_size = get_config_size(config); char *buf = osi_calloc(config_size + 100); if (!line || !buf || !keyname) { @@ -430,7 +432,7 @@ bool config_save(const config_t *config, const char *filename) } buf[w_cnt_total] = '\0'; if (w_cnt_total < CONFIG_FILE_MAX_SIZE) { - snprintf(keyname, sizeof(CONFIG_KEY)+1, "%s%d", CONFIG_KEY, 0); + snprintf(keyname, keyname_bufsz, "%s%d", CONFIG_KEY, 0); err = nvs_set_blob(fp, keyname, buf, w_cnt_total); if (err != ESP_OK) { nvs_close(fp); @@ -441,7 +443,7 @@ bool config_save(const config_t *config, const char *filename) uint count = (w_cnt_total / CONFIG_FILE_MAX_SIZE); for (int i = 0; i <= count; i++) { - snprintf(keyname, sizeof(CONFIG_KEY)+1, "%s%d", CONFIG_KEY, i); + snprintf(keyname, keyname_bufsz, "%s%d", CONFIG_KEY, i); if (i == count) { err = nvs_set_blob(fp, keyname, buf + i*CONFIG_FILE_MAX_SIZE, w_cnt_total - i*CONFIG_FILE_MAX_SIZE); OSI_TRACE_DEBUG("save keyname = %s, i = %d, %d\n", keyname, i, w_cnt_total - i*CONFIG_FILE_MAX_SIZE); @@ -518,14 +520,15 @@ static void config_parse(nvs_handle fp, config_t *config) size_t total_length = 0; char *line = osi_calloc(1024); char *section = osi_calloc(1024); - char *keyname = osi_calloc(sizeof(CONFIG_KEY) + 1); + const size_t keyname_bufsz = sizeof(CONFIG_KEY) + 5 + 1; // including log10(sizeof(i)) + char *keyname = osi_calloc(keyname_bufsz); int buf_size = get_config_size_from_flash(fp); char *buf = osi_calloc(buf_size + 100); if (!line || !section || !buf || !keyname) { err_code |= 0x01; goto error; } - snprintf(keyname, sizeof(CONFIG_KEY)+1, "%s%d", CONFIG_KEY, 0); + snprintf(keyname, keyname_bufsz, "%s%d", CONFIG_KEY, 0); err = nvs_get_blob(fp, keyname, buf, &length); if (err == ESP_ERR_NVS_NOT_FOUND) { goto error; @@ -537,7 +540,7 @@ static void config_parse(nvs_handle fp, config_t *config) total_length += length; while (length == CONFIG_FILE_MAX_SIZE) { length = CONFIG_FILE_DEFAULE_LENGTH; - snprintf(keyname, sizeof(CONFIG_KEY) + 1, "%s%d", CONFIG_KEY, ++i); + snprintf(keyname, keyname_bufsz, "%s%d", CONFIG_KEY, ++i); err = nvs_get_blob(fp, keyname, buf + CONFIG_FILE_MAX_SIZE * i, &length); if (err == ESP_ERR_NVS_NOT_FOUND) { diff --git a/components/bt/bluedroid/osi/list.c b/components/bt/bluedroid/osi/list.c index eb46cda45..ede10976b 100644 --- a/components/bt/bluedroid/osi/list.c +++ b/components/bt/bluedroid/osi/list.c @@ -99,13 +99,13 @@ list_node_t *list_back_node(const list_t *list) { } bool list_insert_after(list_t *list, list_node_t *prev_node, void *data) { - assert(list != NULL); - assert(prev_node != NULL); - assert(data != NULL); + assert(list != NULL); + assert(prev_node != NULL); + assert(data != NULL); - list_node_t *node = (list_node_t *)list->allocator->alloc(sizeof(list_node_t)); - if (!node) - return false; + list_node_t *node = (list_node_t *)list->allocator->alloc(sizeof(list_node_t)); + if (!node) + return false; node->next = prev_node->next; node->data = data; diff --git a/components/bt/bluedroid/stack/a2dp/a2d_api.c b/components/bt/bluedroid/stack/a2dp/a2d_api.c index 73b174e61..937e46b1e 100644 --- a/components/bt/bluedroid/stack/a2dp/a2d_api.c +++ b/components/bt/bluedroid/stack/a2dp/a2d_api.c @@ -352,8 +352,8 @@ UINT8 A2D_SetTraceLevel (UINT8 new_level) ******************************************************************************/ UINT8 A2D_BitsSet(UINT8 num) { - UINT8 count; - BOOLEAN res; + UINT8 count; + UINT8 res; if (num == 0) { res = A2D_SET_ZERO_BIT; } else { diff --git a/components/bt/bluedroid/stack/btm/btm_ble_adv_filter.c b/components/bt/bluedroid/stack/btm/btm_ble_adv_filter.c index ae55635b5..ed56459e4 100644 --- a/components/bt/bluedroid/stack/btm/btm_ble_adv_filter.c +++ b/components/bt/bluedroid/stack/btm/btm_ble_adv_filter.c @@ -331,8 +331,8 @@ void btm_ble_scan_pf_cmpl_cback(tBTM_VSC_CMPL *p_params) break; } + BTM_TRACE_DEBUG("btm_ble_scan_pf_cmpl_cback: calling the cback: %d", cb_evt); switch (cb_evt) { - BTM_TRACE_DEBUG("btm_ble_scan_pf_cmpl_cback: calling the cback: %d", cb_evt); case BTM_BLE_FILT_CFG: if (NULL != p_scan_cfg_cback) { p_scan_cfg_cback(action, cond_type, num_avail, status, ref_value); diff --git a/components/bt/bluedroid/stack/btm/btm_ble_gap.c b/components/bt/bluedroid/stack/btm/btm_ble_gap.c index d36e32a11..3caab465a 100644 --- a/components/bt/bluedroid/stack/btm/btm_ble_gap.c +++ b/components/bt/bluedroid/stack/btm/btm_ble_gap.c @@ -1439,7 +1439,8 @@ void BTM_BleSetScanFilterParams(tGATT_IF client_if, UINT32 scan_interval, UINT32 if (BTM_BLE_ISVALID_PARAM(scan_interval, BTM_BLE_SCAN_INT_MIN, max_scan_interval) && BTM_BLE_ISVALID_PARAM(scan_window, BTM_BLE_SCAN_WIN_MIN, max_scan_window) && - (scan_mode == BTM_BLE_SCAN_MODE_ACTI || scan_mode == BTM_BLE_SCAN_MODE_PASS)) { + (scan_mode == BTM_BLE_SCAN_MODE_ACTI || scan_mode == BTM_BLE_SCAN_MODE_PASS) && + (scan_duplicate_filter < BTM_BLE_SCAN_DUPLICATE_MAX)) { p_cb->scan_type = scan_mode; p_cb->scan_interval = scan_interval; p_cb->scan_window = scan_window; @@ -2637,7 +2638,7 @@ static void btm_ble_parse_adv_data(tBTM_INQ_INFO *p_info, UINT8 *p_data, ** Returns void ** *******************************************************************************/ -void btm_ble_cache_adv_data(tBTM_INQ_RESULTS *p_cur, UINT8 data_len, UINT8 *p, UINT8 evt_type) +void btm_ble_cache_adv_data(BD_ADDR bda, tBTM_INQ_RESULTS *p_cur, UINT8 data_len, UINT8 *p, UINT8 evt_type) { tBTM_BLE_INQ_CB *p_le_inq_cb = &btm_cb.ble_ctr_cb.inq_var; UINT8 *p_cache; @@ -2649,6 +2650,15 @@ void btm_ble_cache_adv_data(tBTM_INQ_RESULTS *p_cur, UINT8 data_len, UINT8 *p, U memset(p_le_inq_cb->adv_data_cache, 0, BTM_BLE_CACHE_ADV_DATA_MAX); p_cur->adv_data_len = 0; p_cur->scan_rsp_len = 0; + } + + //Clear the adv cache if the addresses are not equal + if(memcmp(bda, p_le_inq_cb->adv_addr, BD_ADDR_LEN) != 0) { + p_le_inq_cb->adv_len = 0; + memcpy(p_le_inq_cb->adv_addr, bda, BD_ADDR_LEN); + memset(p_le_inq_cb->adv_data_cache, 0, BTM_BLE_CACHE_ADV_DATA_MAX); + p_cur->adv_data_len = 0; + p_cur->scan_rsp_len = 0; } if (data_len > 0) { @@ -2878,7 +2888,7 @@ static void btm_ble_appearance_to_cod(UINT16 appearance, UINT8 *dev_class) ** Returns void ** *******************************************************************************/ -BOOLEAN btm_ble_update_inq_result(tINQ_DB_ENT *p_i, UINT8 addr_type, UINT8 evt_type, UINT8 *p) +BOOLEAN btm_ble_update_inq_result(BD_ADDR bda, tINQ_DB_ENT *p_i, UINT8 addr_type, UINT8 evt_type, UINT8 *p) { BOOLEAN to_report = TRUE; tBTM_INQ_RESULTS *p_cur = &p_i->inq_info.results; @@ -2896,7 +2906,7 @@ BOOLEAN btm_ble_update_inq_result(tINQ_DB_ENT *p_i, UINT8 addr_type, UINT8 evt_t BTM_TRACE_WARNING("EIR data too long %d. discard", data_len); return FALSE; } - btm_ble_cache_adv_data(p_cur, data_len, p, evt_type); + btm_ble_cache_adv_data(bda, p_cur, data_len, p, evt_type); p1 = (p + data_len); STREAM_TO_UINT8 (rssi, p1); @@ -3120,6 +3130,71 @@ void btm_ble_process_adv_pkt (UINT8 *p_data) } } +/******************************************************************************* +** +** Function btm_ble_process_last_adv_pkt +** +** Description This function is called to report last adv packet +** +** Parameters +** +** Returns void +** +*******************************************************************************/ + +static void btm_ble_process_last_adv_pkt(void) +{ + UINT8 result = 0; + UINT8 null_bda[6] = {0}; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + tBTM_INQ_RESULTS_CB *p_inq_results_cb = p_inq->p_inq_results_cb; + tBTM_INQ_RESULTS_CB *p_obs_results_cb = btm_cb.ble_ctr_cb.p_obs_results_cb; + tBTM_INQ_RESULTS_CB *p_scan_results_cb = btm_cb.ble_ctr_cb.p_scan_results_cb; + tBTM_BLE_INQ_CB *p_le_inq_cb = &btm_cb.ble_ctr_cb.inq_var; + tINQ_DB_ENT *p_i = btm_inq_db_find (p_le_inq_cb->adv_addr); + + if(memcmp(null_bda, p_le_inq_cb->adv_addr, BD_ADDR_LEN) == 0) { + return; + } + + if(p_i == NULL) { + BTM_TRACE_DEBUG("no last adv"); + return; + } + + if ((result = btm_ble_is_discoverable(p_le_inq_cb->adv_addr, p_i->inq_info.results.ble_evt_type, NULL)) == 0) { + BTM_TRACE_WARNING("%s device is no longer discoverable so discarding advertising packet pkt", + __func__); + return; + } + /* background connection in selective connection mode */ + if (btm_cb.ble_ctr_cb.bg_conn_type == BTM_BLE_CONN_SELECTIVE) { + //do nothing + } else { + if (p_inq_results_cb && (result & BTM_BLE_INQ_RESULT)) { + (p_inq_results_cb)((tBTM_INQ_RESULTS *) &p_i->inq_info.results, p_le_inq_cb->adv_data_cache); + p_le_inq_cb->adv_len = 0; + memset(p_le_inq_cb->adv_addr, 0, BD_ADDR_LEN); + p_i->inq_info.results.adv_data_len = 0; + p_i->inq_info.results.scan_rsp_len = 0; + } + if (p_obs_results_cb && (result & BTM_BLE_OBS_RESULT)) { + (p_obs_results_cb)((tBTM_INQ_RESULTS *) &p_i->inq_info.results, p_le_inq_cb->adv_data_cache); + p_le_inq_cb->adv_len = 0; + memset(p_le_inq_cb->adv_addr, 0, BD_ADDR_LEN); + p_i->inq_info.results.adv_data_len = 0; + p_i->inq_info.results.scan_rsp_len = 0; + } + if (p_scan_results_cb && (result & BTM_BLE_DISCO_RESULT)) { + (p_scan_results_cb)((tBTM_INQ_RESULTS *) &p_i->inq_info.results, p_le_inq_cb->adv_data_cache); + p_le_inq_cb->adv_len = 0; + memset(p_le_inq_cb->adv_addr, 0, BD_ADDR_LEN); + p_i->inq_info.results.adv_data_len = 0; + p_i->inq_info.results.scan_rsp_len = 0; + } + } +} + /******************************************************************************* ** ** Function btm_ble_process_adv_pkt_cont @@ -3144,6 +3219,13 @@ static void btm_ble_process_adv_pkt_cont(BD_ADDR bda, UINT8 addr_type, UINT8 evt BOOLEAN update = TRUE; UINT8 result = 0; + //if scan duplicate is enabled, the adv packet without scan response is allowed to report to higgher layer + if(p_le_inq_cb->scan_duplicate_filter == BTM_BLE_SCAN_DUPLICATE_ENABLE) { + if(memcmp(bda, p_le_inq_cb->adv_addr, BD_ADDR_LEN) != 0) { + btm_ble_process_last_adv_pkt(); + } + } + p_i = btm_inq_db_find (bda); /* Check if this address has already been processed for this inquiry */ @@ -3172,7 +3254,7 @@ static void btm_ble_process_adv_pkt_cont(BD_ADDR bda, UINT8 addr_type, UINT8 evt p_inq->inq_cmpl_info.num_resp++; } /* update the LE device information in inquiry database */ - if (!btm_ble_update_inq_result(p_i, addr_type, evt_type, p)) { + if (!btm_ble_update_inq_result(bda, p_i, addr_type, evt_type, p)) { return; } @@ -3216,12 +3298,24 @@ static void btm_ble_process_adv_pkt_cont(BD_ADDR bda, UINT8 addr_type, UINT8 evt } else { if (p_inq_results_cb && (result & BTM_BLE_INQ_RESULT)) { (p_inq_results_cb)((tBTM_INQ_RESULTS *) &p_i->inq_info.results, p_le_inq_cb->adv_data_cache); + p_le_inq_cb->adv_len = 0; + memset(p_le_inq_cb->adv_addr, 0, BD_ADDR_LEN); + p_i->inq_info.results.adv_data_len = 0; + p_i->inq_info.results.scan_rsp_len = 0; } if (p_obs_results_cb && (result & BTM_BLE_OBS_RESULT)) { (p_obs_results_cb)((tBTM_INQ_RESULTS *) &p_i->inq_info.results, p_le_inq_cb->adv_data_cache); + p_le_inq_cb->adv_len = 0; + memset(p_le_inq_cb->adv_addr, 0, BD_ADDR_LEN); + p_i->inq_info.results.adv_data_len = 0; + p_i->inq_info.results.scan_rsp_len = 0; } if (p_scan_results_cb && (result & BTM_BLE_DISCO_RESULT)) { (p_scan_results_cb)((tBTM_INQ_RESULTS *) &p_i->inq_info.results, p_le_inq_cb->adv_data_cache); + p_le_inq_cb->adv_len = 0; + memset(p_le_inq_cb->adv_addr, 0, BD_ADDR_LEN); + p_i->inq_info.results.adv_data_len = 0; + p_i->inq_info.results.scan_rsp_len = 0; } } } diff --git a/components/bt/bluedroid/stack/btm/include/btm_ble_int.h b/components/bt/bluedroid/stack/btm/include/btm_ble_int.h index 1a8aed28e..c080c9e7e 100644 --- a/components/bt/bluedroid/stack/btm/include/btm_ble_int.h +++ b/components/bt/bluedroid/stack/btm/include/btm_ble_int.h @@ -162,7 +162,7 @@ typedef struct { UINT8 adv_len; UINT8 adv_data_cache[BTM_BLE_CACHE_ADV_DATA_MAX]; - + BD_ADDR adv_addr; /* inquiry BD addr database */ UINT8 num_bd_entries; UINT8 max_bd_entries; diff --git a/components/bt/bluedroid/stack/btu/btu_task.c b/components/bt/bluedroid/stack/btu/btu_task.c index 5d6b82a0f..644731504 100644 --- a/components/bt/bluedroid/stack/btu/btu_task.c +++ b/components/bt/bluedroid/stack/btu/btu_task.c @@ -242,23 +242,6 @@ void btu_task_thread_handler(void *arg) case SIG_BTU_ONESHOT_ALARM: { TIMER_LIST_ENT *p_tle = (TIMER_LIST_ENT *)e.par; btu_general_alarm_process(p_tle); - - switch (p_tle->event) { -#if (defined(BLE_INCLUDED) && BLE_INCLUDED == TRUE) - case BTU_TTYPE_BLE_RANDOM_ADDR: - btm_ble_timeout(p_tle); - break; -#endif - case BTU_TTYPE_USER_FUNC: { - tUSER_TIMEOUT_FUNC *p_uf = (tUSER_TIMEOUT_FUNC *)p_tle->param; - (*p_uf)(p_tle); - break; - } - default: - // FAIL - HCI_TRACE_ERROR("Received unexpected oneshot timer event:0x%x\n", p_tle->event); - break; - } break; } case SIG_BTU_L2CAP_ALARM: diff --git a/components/bt/bluedroid/stack/gatt/gatt_cl.c b/components/bt/bluedroid/stack/gatt/gatt_cl.c index ba0202007..daa911e8c 100644 --- a/components/bt/bluedroid/stack/gatt/gatt_cl.c +++ b/components/bt/bluedroid/stack/gatt/gatt_cl.c @@ -1038,7 +1038,10 @@ BOOLEAN gatt_cl_send_next_cmd_inq(tGATT_TCB *p_tcb) if (att_ret == GATT_SUCCESS || att_ret == GATT_CONGESTED) { sent = TRUE; p_cmd->to_send = FALSE; - p_cmd->p_cmd = NULL; + if(p_cmd->p_cmd) { + osi_free(p_cmd->p_cmd); + p_cmd->p_cmd = NULL; + } /* dequeue the request if is write command or sign write */ if (p_cmd->op_code != GATT_CMD_WRITE && p_cmd->op_code != GATT_SIGN_CMD_WRITE) { diff --git a/components/bt/bluedroid/stack/gatt/gatt_utils.c b/components/bt/bluedroid/stack/gatt/gatt_utils.c index 12a2e1dba..5dd43a2fe 100644 --- a/components/bt/bluedroid/stack/gatt/gatt_utils.c +++ b/components/bt/bluedroid/stack/gatt/gatt_utils.c @@ -2258,6 +2258,7 @@ void gatt_cleanup_upon_disc(BD_ADDR bda, UINT16 reason, tBT_TRANSPORT transport) GATT_TRACE_DEBUG ("exit gatt_cleanup_upon_disc "); BTM_Recovery_Pre_State(); } + gatt_delete_dev_from_srv_chg_clt_list(bda); } /******************************************************************************* ** diff --git a/components/bt/bluedroid/stack/include/stack/btm_ble_api.h b/components/bt/bluedroid/stack/include/stack/btm_ble_api.h index b3d254df0..0c96eaa5e 100644 --- a/components/bt/bluedroid/stack/include/stack/btm_ble_api.h +++ b/components/bt/bluedroid/stack/include/stack/btm_ble_api.h @@ -582,6 +582,12 @@ typedef struct { tBTM_BLE_REF_VALUE ref_value; } tBTM_BLE_BATCH_SCAN_CB; +/// Ble scan duplicate type +enum { + BTM_BLE_SCAN_DUPLICATE_DISABLE = 0x0, /*!< the Link Layer should generate advertising reports to the host for each packet received */ + BTM_BLE_SCAN_DUPLICATE_ENABLE = 0x1, /*!< the Link Layer should filter out duplicate advertising reports to the Host */ + BTM_BLE_SCAN_DUPLICATE_MAX = 0x2, /*!< 0x02 – 0xFF, Reserved for future use */ +}; /* filter selection bit index */ #define BTM_BLE_PF_ADDR_FILTER 0 #define BTM_BLE_PF_SRVC_DATA 1 diff --git a/components/bt/bluedroid/stack/smp/p_256_curvepara.c b/components/bt/bluedroid/stack/smp/p_256_curvepara.c index 1761d5848..0b7977056 100644 --- a/components/bt/bluedroid/stack/smp/p_256_curvepara.c +++ b/components/bt/bluedroid/stack/smp/p_256_curvepara.c @@ -41,8 +41,8 @@ void p_256_init_curve(UINT32 keyLength) ec->p[1] = 0xFFFFFFFF; ec->p[0] = 0xFFFFFFFF; - memset(ec->omega, 0, KEY_LENGTH_DWORDS_P256); - memset(ec->a, 0, KEY_LENGTH_DWORDS_P256); + memset(ec->omega, 0, KEY_LENGTH_DWORDS_P256 * sizeof(ec->omega[0])); + memset(ec->a, 0, KEY_LENGTH_DWORDS_P256 * sizeof(ec->a[0])); ec->a_minus3 = TRUE; diff --git a/components/bt/bluedroid/stack/smp/smp_l2c.c b/components/bt/bluedroid/stack/smp/smp_l2c.c index 7c5efa7ea..a3ac356ac 100644 --- a/components/bt/bluedroid/stack/smp/smp_l2c.c +++ b/components/bt/bluedroid/stack/smp/smp_l2c.c @@ -105,7 +105,10 @@ static void smp_connect_callback (UINT16 channel, BD_ADDR bd_addr, BOOLEAN conne if (transport == BT_TRANSPORT_BR_EDR || memcmp(bd_addr, dummy_bda, BD_ADDR_LEN) == 0) { return; } - + if(!connected && &p_cb->rsp_timer_ent) { + //free timer + btu_free_timer(&p_cb->rsp_timer_ent); + } if (memcmp(bd_addr, p_cb->pairing_bda, BD_ADDR_LEN) == 0) { SMP_TRACE_EVENT ("%s() for pairing BDA: %08x%04x Event: %s\n", __FUNCTION__, diff --git a/components/bt/bt.c b/components/bt/bt.c index 12ca8e3cf..a5d5e1424 100644 --- a/components/bt/bt.c +++ b/components/bt/bt.c @@ -41,11 +41,16 @@ #include "driver/periph_ctrl.h" #include "soc/rtc.h" #include "soc/rtc_cntl_reg.h" +#include "soc/soc_memory_layout.h" #include "esp_clk.h" #if CONFIG_BT_ENABLED +/* Macro definition + ************************************************************************ + */ + #define BTDM_LOG_TAG "BTDM_INIT" #define BTDM_INIT_PERIOD (5000) /* ms */ @@ -56,65 +61,20 @@ #define BTDM_CFG_CONTROLLER_RUN_APP_CPU (1<<2) #define BTDM_CFG_SCAN_DUPLICATE_OPTIONS (1<<3) #define BTDM_CFG_SEND_ADV_RESERVED_SIZE (1<<4) -/* 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 int btdm_controller_init(uint32_t config_mask, esp_bt_controller_config_t *config_opts); -extern void btdm_controller_deinit(void); -extern int btdm_controller_enable(esp_bt_mode_t mode); -extern void btdm_controller_disable(void); -extern uint8_t btdm_controller_get_mode(void); -extern const char *btdm_controller_get_compile_version(void); -extern void btdm_rf_bb_init(void); -extern void btdm_controller_enable_sleep(bool enable); +/* Sleep mode */ #define BTDM_MODEM_SLEEP_MODE_NONE (0) #define BTDM_MODEM_SLEEP_MODE_ORIG (1) #define BTDM_MODEM_SLEEP_MODE_EVED (2) -extern void btdm_controller_set_sleep_mode(uint8_t mode); -extern uint8_t btdm_controller_get_sleep_mode(void); -extern bool btdm_power_state_active(void); -extern void btdm_wakeup_request(void); +/* Low Power Clock Selection */ #define BTDM_LPCLK_SEL_XTAL (0) #define BTDM_LPCLK_SEL_XTAL32K (1) #define BTDM_LPCLK_SEL_RTC_SLOW (2) #define BTDM_LPCLK_SEL_8M (3) -extern bool btdm_lpclk_select_src(uint32_t sel); -extern bool btdm_lpclk_set_div(uint32_t div); +/* Sleep duration */ #define BTDM_MIN_SLEEP_DURATION (20) -/* VHCI function interface */ -typedef struct vhci_host_callback { - void (*notify_host_send_available)(void); /*!< callback used to notify that the host can send packet to controller */ - int (*notify_host_recv)(uint8_t *data, uint16_t len); /*!< callback used to notify that the controller has a packet to send to the host*/ -} vhci_host_callback_t; - -extern bool API_vhci_host_check_send_available(void); -extern void API_vhci_host_send_packet(uint8_t *data, uint16_t len); -extern void API_vhci_host_register_callback(const vhci_host_callback_t *callback); - -extern int ble_txpwr_set(int power_type, int power_level); -extern int ble_txpwr_get(int power_type); -extern int bredr_txpwr_set(int min_power_level, int max_power_level); -extern int bredr_txpwr_get(int *min_power_level, int *max_power_level); -extern void bredr_sco_datapath_set(uint8_t data_path); - -extern char _bss_start_btdm; -extern char _bss_end_btdm; -extern uint32_t _bt_bss_start; -extern uint32_t _bt_bss_end; -extern uint32_t _btdm_bss_start; -extern uint32_t _btdm_bss_end; -extern uint32_t _bt_data_start; -extern uint32_t _bt_data_end; -extern uint32_t _btdm_data_start; -extern uint32_t _btdm_data_end; -extern char _data_start_btdm; -extern char _data_end_btdm; -extern uint32_t _data_start_btdm_rom; -extern uint32_t _data_end_btdm_rom; #define BT_DEBUG(...) #define BT_API_CALL_CHECK(info, api_call, ret) \ @@ -127,45 +87,43 @@ do{\ } while(0) #define OSI_FUNCS_TIME_BLOCKING 0xffffffff +#define OSI_VERSION 0x00010001 +#define OSI_MAGIC_VALUE 0xFADEBEAD +/* SPIRAM Configuration */ +#if CONFIG_SPIRAM_USE_MALLOC +#define BTDM_MAX_QUEUE_NUM (5) +#endif + +/* Types definition + ************************************************************************ + */ + +/* VHCI function interface */ +typedef struct vhci_host_callback { + void (*notify_host_send_available)(void); /*!< callback used to notify that the host can send packet to controller */ + int (*notify_host_recv)(uint8_t *data, uint16_t len); /*!< callback used to notify that the controller has a packet to send to the host*/ +} vhci_host_callback_t; + +/* Dram region */ typedef struct { esp_bt_mode_t mode; intptr_t start; intptr_t end; } btdm_dram_available_region_t; -/* the mode column will be modified by release function to indicate the available region */ -static btdm_dram_available_region_t btdm_dram_available_region[] = { - //following is .data - {ESP_BT_MODE_BTDM, 0x3ffae6e0, 0x3ffaff10}, - //following is memory which HW will use - {ESP_BT_MODE_BTDM, 0x3ffb0000, 0x3ffb09a8}, - {ESP_BT_MODE_BLE, 0x3ffb09a8, 0x3ffb1ddc}, - {ESP_BT_MODE_BTDM, 0x3ffb1ddc, 0x3ffb2730}, - {ESP_BT_MODE_CLASSIC_BT, 0x3ffb2730, 0x3ffb8000}, - //following is .bss - {ESP_BT_MODE_BTDM, 0x3ffb8000, 0x3ffbbb28}, - {ESP_BT_MODE_CLASSIC_BT, 0x3ffbbb28, 0x3ffbdb28}, - {ESP_BT_MODE_BTDM, 0x3ffbdb28, 0x3ffc0000}, -}; - -/* Reserve the full memory region used by Bluetooth Controller, - some may be released later at runtime. */ -SOC_RESERVE_MEMORY_REGION(0x3ffb0000, 0x3ffc0000, bt_hardware_shared_mem); -SOC_RESERVE_MEMORY_REGION(0x3ffae6e0, 0x3ffaff10, rom_bt_data); - +/* PSRAM configuration */ #if CONFIG_SPIRAM_USE_MALLOC typedef struct { QueueHandle_t handle; void *storage; void *buffer; } btdm_queue_item_t; -#define BTDM_MAX_QUEUE_NUM (5) -static btdm_queue_item_t btdm_queue_table[BTDM_MAX_QUEUE_NUM]; -SemaphoreHandle_t btdm_queue_table_mux = NULL; -#endif /* #if CONFIG_SPIRAM_USE_MALLOC */ +#endif +/* OSI function */ struct osi_funcs_t { + uint32_t _version; xt_handler (*_set_isr)(int n, xt_handler f, void *arg); void (*_ints_on)(unsigned int mask); void (*_interrupt_disable)(void); @@ -193,6 +151,7 @@ struct osi_funcs_t { bool (* _is_in_isr)(void); int (* _cause_sw_intr_to_core)(int core_id, int intr_no); void *(* _malloc)(uint32_t size); + void *(* _malloc_internal)(uint32_t size); void (* _free)(void *p); int32_t (* _read_efuse_mac)(uint8_t mac[6]); void (* _srand)(unsigned int seed); @@ -202,9 +161,176 @@ struct osi_funcs_t { bool (* _btdm_sleep_check_duration)(uint32_t *slot_cnt); void (* _btdm_sleep_enter)(void); void (* _btdm_sleep_exit)(void); /* called from ISR */ + uint32_t _magic; }; +/* External functions or values + ************************************************************************ + */ + +/* not for user call, so don't put to include file */ +/* OSI */ +extern int btdm_osi_funcs_register(void *osi_funcs); +/* Initialise and De-initialise */ +extern int btdm_controller_init(uint32_t config_mask, esp_bt_controller_config_t *config_opts); +extern void btdm_controller_deinit(void); +extern int btdm_controller_enable(esp_bt_mode_t mode); +extern void btdm_controller_disable(void); +extern uint8_t btdm_controller_get_mode(void); +extern const char *btdm_controller_get_compile_version(void); +extern void btdm_rf_bb_init(void); +/* Sleep */ +extern void btdm_controller_enable_sleep(bool enable); +extern void btdm_controller_set_sleep_mode(uint8_t mode); +extern uint8_t btdm_controller_get_sleep_mode(void); +extern bool btdm_power_state_active(void); +extern void btdm_wakeup_request(void); +/* Low Power Clock */ +extern bool btdm_lpclk_select_src(uint32_t sel); +extern bool btdm_lpclk_set_div(uint32_t div); +/* VHCI */ +extern bool API_vhci_host_check_send_available(void); +extern void API_vhci_host_send_packet(uint8_t *data, uint16_t len); +extern int API_vhci_host_register_callback(const vhci_host_callback_t *callback); +/* TX power */ +extern int ble_txpwr_set(int power_type, int power_level); +extern int ble_txpwr_get(int power_type); +extern int bredr_txpwr_set(int min_power_level, int max_power_level); +extern int bredr_txpwr_get(int *min_power_level, int *max_power_level); +extern void bredr_sco_datapath_set(uint8_t data_path); + +extern char _bss_start_btdm; +extern char _bss_end_btdm; +extern char _data_start_btdm; +extern char _data_end_btdm; +extern uint32_t _data_start_btdm_rom; +extern uint32_t _data_end_btdm_rom; + +extern uint32_t _bt_bss_start; +extern uint32_t _bt_bss_end; +extern uint32_t _btdm_bss_start; +extern uint32_t _btdm_bss_end; +extern uint32_t _bt_data_start; +extern uint32_t _bt_data_end; +extern uint32_t _btdm_data_start; +extern uint32_t _btdm_data_end; + +/* Local Function Declare + ********************************************************************* + */ +#if CONFIG_SPIRAM_USE_MALLOC +static bool IRAM_ATTR btdm_queue_generic_register(const btdm_queue_item_t *queue); +static bool IRAM_ATTR btdm_queue_generic_deregister(btdm_queue_item_t *queue); +#endif /* CONFIG_SPIRAM_USE_MALLOC */ +static void IRAM_ATTR interrupt_disable(void); +static void IRAM_ATTR interrupt_restore(void); +static void IRAM_ATTR task_yield_from_isr(void); +static void *IRAM_ATTR semphr_create_wrapper(uint32_t max, uint32_t init); +static void IRAM_ATTR semphr_delete_wrapper(void *semphr); +static int32_t IRAM_ATTR semphr_take_from_isr_wrapper(void *semphr, void *hptw); +static int32_t IRAM_ATTR semphr_give_from_isr_wrapper(void *semphr, void *hptw); +static int32_t IRAM_ATTR semphr_take_wrapper(void *semphr, uint32_t block_time_ms); +static int32_t IRAM_ATTR semphr_give_wrapper(void *semphr); +static void *IRAM_ATTR mutex_create_wrapper(void); +static void IRAM_ATTR mutex_delete_wrapper(void *mutex); +static int32_t IRAM_ATTR mutex_lock_wrapper(void *mutex); +static int32_t IRAM_ATTR mutex_unlock_wrapper(void *mutex); +static void *IRAM_ATTR queue_create_wrapper(uint32_t queue_len, uint32_t item_size); +static void IRAM_ATTR queue_delete_wrapper(void *queue); +static int32_t IRAM_ATTR queue_send_wrapper(void *queue, void *item, uint32_t block_time_ms); +static int32_t IRAM_ATTR queue_send_from_isr_wrapper(void *queue, void *item, void *hptw); +static int32_t IRAM_ATTR queue_recv_wrapper(void *queue, void *item, uint32_t block_time_ms); +static int32_t IRAM_ATTR queue_recv_from_isr_wrapper(void *queue, void *item, void *hptw); +static int32_t IRAM_ATTR task_create_wrapper(void *task_func, const char *name, uint32_t stack_depth, void *param, uint32_t prio, void *task_handle, uint32_t core_id); +static void IRAM_ATTR task_delete_wrapper(void *task_handle); +static bool IRAM_ATTR is_in_isr_wrapper(void); +static void IRAM_ATTR cause_sw_intr(void *arg); +static int IRAM_ATTR cause_sw_intr_to_core_wrapper(int core_id, int intr_no); +static void * IRAM_ATTR malloc_internal_wrapper(size_t size); +static int32_t IRAM_ATTR read_mac_wrapper(uint8_t mac[6]); +static void IRAM_ATTR srand_wrapper(unsigned int seed); +static int IRAM_ATTR rand_wrapper(void); +static uint32_t IRAM_ATTR btdm_lpcycles_2_us(uint32_t cycles); +static uint32_t IRAM_ATTR btdm_us_2_lpcycles(uint32_t us); +static bool IRAM_ATTR btdm_sleep_check_duration(uint32_t *slot_cnt); +static void IRAM_ATTR btdm_sleep_enter_wrapper(void); +static void IRAM_ATTR btdm_sleep_exit_wrapper(void); + +/* Local variable definition + *************************************************************************** + */ +/* OSI funcs */ +static const struct osi_funcs_t osi_funcs_ro = { + ._version = OSI_VERSION, + ._set_isr = xt_set_interrupt_handler, + ._ints_on = xt_ints_on, + ._interrupt_disable = interrupt_disable, + ._interrupt_restore = interrupt_restore, + ._task_yield = vPortYield, + ._task_yield_from_isr = task_yield_from_isr, + ._semphr_create = semphr_create_wrapper, + ._semphr_delete = semphr_delete_wrapper, + ._semphr_take_from_isr = semphr_take_from_isr_wrapper, + ._semphr_give_from_isr = semphr_give_from_isr_wrapper, + ._semphr_take = semphr_take_wrapper, + ._semphr_give = semphr_give_wrapper, + ._mutex_create = mutex_create_wrapper, + ._mutex_delete = mutex_delete_wrapper, + ._mutex_lock = mutex_lock_wrapper, + ._mutex_unlock = mutex_unlock_wrapper, + ._queue_create = queue_create_wrapper, + ._queue_delete = queue_delete_wrapper, + ._queue_send = queue_send_wrapper, + ._queue_send_from_isr = queue_send_from_isr_wrapper, + ._queue_recv = queue_recv_wrapper, + ._queue_recv_from_isr = queue_recv_from_isr_wrapper, + ._task_create = task_create_wrapper, + ._task_delete = task_delete_wrapper, + ._is_in_isr = is_in_isr_wrapper, + ._cause_sw_intr_to_core = cause_sw_intr_to_core_wrapper, + ._malloc = malloc, + ._malloc_internal = malloc_internal_wrapper, + ._free = free, + ._read_efuse_mac = read_mac_wrapper, + ._srand = srand_wrapper, + ._rand = rand_wrapper, + ._btdm_lpcycles_2_us = btdm_lpcycles_2_us, + ._btdm_us_2_lpcycles = btdm_us_2_lpcycles, + ._btdm_sleep_check_duration = btdm_sleep_check_duration, + ._btdm_sleep_enter = btdm_sleep_enter_wrapper, + ._btdm_sleep_exit = btdm_sleep_exit_wrapper, + ._magic = OSI_MAGIC_VALUE, +}; + +/* the mode column will be modified by release function to indicate the available region */ +static btdm_dram_available_region_t btdm_dram_available_region[] = { + //following is .data + {ESP_BT_MODE_BTDM, SOC_MEM_BT_DATA_START, SOC_MEM_BT_DATA_END }, + //following is memory which HW will use + {ESP_BT_MODE_BTDM, SOC_MEM_BT_EM_BTDM0_START, SOC_MEM_BT_EM_BTDM0_END }, + {ESP_BT_MODE_BLE, SOC_MEM_BT_EM_BLE_START, SOC_MEM_BT_EM_BLE_END }, + {ESP_BT_MODE_BTDM, SOC_MEM_BT_EM_BTDM1_START, SOC_MEM_BT_EM_BTDM1_END }, + {ESP_BT_MODE_CLASSIC_BT, SOC_MEM_BT_EM_BREDR_START, SOC_MEM_BT_EM_BREDR_REAL_END}, + //following is .bss + {ESP_BT_MODE_BTDM, SOC_MEM_BT_BSS_START, SOC_MEM_BT_BSS_END }, + {ESP_BT_MODE_BTDM, SOC_MEM_BT_MISC_START, SOC_MEM_BT_MISC_END }, +}; + +/* Reserve the full memory region used by Bluetooth Controller, + * some may be released later at runtime. */ +SOC_RESERVE_MEMORY_REGION(SOC_MEM_BT_EM_START, SOC_MEM_BT_EM_BREDR_REAL_END, rom_bt_em); +SOC_RESERVE_MEMORY_REGION(SOC_MEM_BT_BSS_START, SOC_MEM_BT_BSS_END, rom_bt_bss); +SOC_RESERVE_MEMORY_REGION(SOC_MEM_BT_MISC_START, SOC_MEM_BT_MISC_END, rom_bt_misc); +SOC_RESERVE_MEMORY_REGION(SOC_MEM_BT_DATA_START, SOC_MEM_BT_DATA_END, rom_bt_data); + +static struct osi_funcs_t *osi_funcs_p; + +#if CONFIG_SPIRAM_USE_MALLOC +static btdm_queue_item_t btdm_queue_table[BTDM_MAX_QUEUE_NUM]; +SemaphoreHandle_t btdm_queue_table_mux = NULL; +#endif /* #if CONFIG_SPIRAM_USE_MALLOC */ + /* Static variable declare */ static bool btdm_bb_init_flag = false; static esp_bt_controller_status_t btdm_controller_status = ESP_BT_CONTROLLER_STATUS_IDLE; @@ -220,7 +346,7 @@ static esp_pm_lock_handle_t s_pm_lock; #endif #if CONFIG_SPIRAM_USE_MALLOC -bool IRAM_ATTR btdm_queue_generic_register(const btdm_queue_item_t *queue) +static bool IRAM_ATTR btdm_queue_generic_register(const btdm_queue_item_t *queue) { if (!btdm_queue_table_mux || !queue) { return NULL; @@ -241,7 +367,7 @@ bool IRAM_ATTR btdm_queue_generic_register(const btdm_queue_item_t *queue) return ret; } -bool IRAM_ATTR btdm_queue_generic_deregister(btdm_queue_item_t *queue) +static bool IRAM_ATTR btdm_queue_generic_deregister(btdm_queue_item_t *queue) { if (!btdm_queue_table_mux || !queue) { return false; @@ -579,6 +705,11 @@ static int IRAM_ATTR cause_sw_intr_to_core_wrapper(int core_id, int intr_no) return err; } +static void * IRAM_ATTR malloc_internal_wrapper(size_t size) +{ + return heap_caps_malloc(size, MALLOC_CAP_DEFAULT|MALLOC_CAP_INTERNAL); +} + static int32_t IRAM_ATTR read_mac_wrapper(uint8_t mac[6]) { return esp_read_mac(mac, ESP_MAC_BT); @@ -650,45 +781,6 @@ static void IRAM_ATTR btdm_sleep_exit_wrapper(void) } } -static struct osi_funcs_t osi_funcs = { - ._set_isr = xt_set_interrupt_handler, - ._ints_on = xt_ints_on, - ._interrupt_disable = interrupt_disable, - ._interrupt_restore = interrupt_restore, - ._task_yield = vPortYield, - ._task_yield_from_isr = task_yield_from_isr, - ._semphr_create = semphr_create_wrapper, - ._semphr_delete = semphr_delete_wrapper, - ._semphr_take_from_isr = semphr_take_from_isr_wrapper, - ._semphr_give_from_isr = semphr_give_from_isr_wrapper, - ._semphr_take = semphr_take_wrapper, - ._semphr_give = semphr_give_wrapper, - ._mutex_create = mutex_create_wrapper, - ._mutex_delete = mutex_delete_wrapper, - ._mutex_lock = mutex_lock_wrapper, - ._mutex_unlock = mutex_unlock_wrapper, - ._queue_create = queue_create_wrapper, - ._queue_delete = queue_delete_wrapper, - ._queue_send = queue_send_wrapper, - ._queue_send_from_isr = queue_send_from_isr_wrapper, - ._queue_recv = queue_recv_wrapper, - ._queue_recv_from_isr = queue_recv_from_isr_wrapper, - ._task_create = task_create_wrapper, - ._task_delete = task_delete_wrapper, - ._is_in_isr = is_in_isr_wrapper, - ._cause_sw_intr_to_core = cause_sw_intr_to_core_wrapper, - ._malloc = malloc, - ._free = free, - ._read_efuse_mac = read_mac_wrapper, - ._srand = srand_wrapper, - ._rand = rand_wrapper, - ._btdm_lpcycles_2_us = btdm_lpcycles_2_us, - ._btdm_us_2_lpcycles = btdm_us_2_lpcycles, - ._btdm_sleep_check_duration = btdm_sleep_check_duration, - ._btdm_sleep_enter = btdm_sleep_enter_wrapper, - ._btdm_sleep_exit = btdm_sleep_exit_wrapper, -}; - bool esp_vhci_host_check_send_available(void) { return API_vhci_host_check_send_available(); @@ -702,19 +794,15 @@ void esp_vhci_host_send_packet(uint8_t *data, uint16_t len) API_vhci_host_send_packet(data, len); } -void esp_vhci_host_register_callback(const esp_vhci_host_callback_t *callback) +esp_err_t esp_vhci_host_register_callback(const esp_vhci_host_callback_t *callback) { - API_vhci_host_register_callback((const vhci_host_callback_t *)callback); + return API_vhci_host_register_callback((const vhci_host_callback_t *)callback) == 0 ? ESP_OK : ESP_FAIL; } static uint32_t btdm_config_mask_load(void) { uint32_t mask = 0x0; - if (btdm_dram_available_region[0].mode == ESP_BT_MODE_BLE) { - mask |= BTDM_CFG_BT_DATA_RELEASE; - } - #if CONFIG_BTDM_CONTROLLER_HCI_MODE_UART_H4 mask |= BTDM_CFG_HCI_UART; #endif @@ -730,10 +818,11 @@ static uint32_t btdm_config_mask_load(void) static void btdm_controller_mem_init(void) { - /* initialise .bss, .data and .etc section */ + /* initialise .data section */ memcpy(&_data_start_btdm, (void *)_data_start_btdm_rom, &_data_end_btdm - &_data_start_btdm); ESP_LOGD(BTDM_LOG_TAG, ".data initialise [0x%08x] <== [0x%08x]\n", (uint32_t)&_data_start_btdm, _data_start_btdm_rom); + //initial em, .bss section for (int i = 1; i < sizeof(btdm_dram_available_region)/sizeof(btdm_dram_available_region_t); i++) { if (btdm_dram_available_region[i].mode != ESP_BT_MODE_IDLE) { memset((void *)btdm_dram_available_region[i].start, 0x0, btdm_dram_available_region[i].end - btdm_dram_available_region[i].start); @@ -796,12 +885,16 @@ esp_err_t esp_bt_controller_mem_release(esp_bt_mode_t mode) if (mode == ESP_BT_MODE_BTDM) { mem_start = (intptr_t)&_btdm_bss_start; mem_end = (intptr_t)&_btdm_bss_end; - ESP_LOGD(BTDM_LOG_TAG, "Release BTDM BSS [0x%08x] - [0x%08x]\n", mem_start, mem_end); - ESP_ERROR_CHECK(heap_caps_add_region(mem_start, mem_end)); + if (mem_start != mem_end) { + ESP_LOGD(BTDM_LOG_TAG, "Release BTDM BSS [0x%08x] - [0x%08x]\n", mem_start, mem_end); + ESP_ERROR_CHECK(heap_caps_add_region(mem_start, mem_end)); + } mem_start = (intptr_t)&_btdm_data_start; mem_end = (intptr_t)&_btdm_data_end; - ESP_LOGD(BTDM_LOG_TAG, "Release BTDM Data [0x%08x] - [0x%08x]\n", mem_start, mem_end); - ESP_ERROR_CHECK(heap_caps_add_region(mem_start, mem_end)); + if (mem_start != mem_end) { + ESP_LOGD(BTDM_LOG_TAG, "Release BTDM Data [0x%08x] - [0x%08x]\n", mem_start, mem_end); + ESP_ERROR_CHECK(heap_caps_add_region(mem_start, mem_end)); + } } return ESP_OK; } @@ -819,12 +912,16 @@ esp_err_t esp_bt_mem_release(esp_bt_mode_t mode) if (mode == ESP_BT_MODE_BTDM) { mem_start = (intptr_t)&_bt_bss_start; mem_end = (intptr_t)&_bt_bss_end; - ESP_LOGD(BTDM_LOG_TAG, "Release BT BSS [0x%08x] - [0x%08x]\n", mem_start, mem_end); - ESP_ERROR_CHECK(heap_caps_add_region(mem_start, mem_end)); + if (mem_start != mem_end) { + ESP_LOGD(BTDM_LOG_TAG, "Release BT BSS [0x%08x] - [0x%08x]\n", mem_start, mem_end); + ESP_ERROR_CHECK(heap_caps_add_region(mem_start, mem_end)); + } mem_start = (intptr_t)&_bt_data_start; mem_end = (intptr_t)&_bt_data_end; - ESP_LOGD(BTDM_LOG_TAG, "Release BT Data [0x%08x] - [0x%08x]\n", mem_start, mem_end); - ESP_ERROR_CHECK(heap_caps_add_region(mem_start, mem_end)); + if (mem_start != mem_end) { + ESP_LOGD(BTDM_LOG_TAG, "Release BT Data [0x%08x] - [0x%08x]\n", mem_start, mem_end); + ESP_ERROR_CHECK(heap_caps_add_region(mem_start, mem_end)); + } } return ESP_OK; } @@ -834,6 +931,16 @@ esp_err_t esp_bt_controller_init(esp_bt_controller_config_t *cfg) BaseType_t ret; uint32_t btdm_cfg_mask = 0; + osi_funcs_p = (struct osi_funcs_t *)malloc_internal_wrapper(sizeof(struct osi_funcs_t)); + if (osi_funcs_p == NULL) { + return ESP_ERR_NO_MEM; + } + + memcpy(osi_funcs_p, &osi_funcs_ro, sizeof(struct osi_funcs_t)); + if (btdm_osi_funcs_register(osi_funcs_p) != 0) { + return ESP_ERR_INVALID_ARG; + } + if (btdm_controller_status != ESP_BT_CONTROLLER_STATUS_IDLE) { return ESP_ERR_INVALID_STATE; } @@ -852,6 +959,16 @@ esp_err_t esp_bt_controller_init(esp_bt_controller_config_t *cfg) return ESP_ERR_INVALID_ARG; } + //overwrite some parameters + cfg->bt_max_sync_conn = CONFIG_BTDM_CONTROLLER_BR_EDR_MAX_SYNC_CONN_EFF; + cfg->magic = ESP_BT_CONTROLLER_CONFIG_MAGIC_VAL; + + if (((cfg->mode & ESP_BT_MODE_BLE) && (cfg->ble_max_conn <= 0 || cfg->ble_max_conn > BTDM_CONTROLLER_BLE_MAX_CONN_LIMIT)) + || ((cfg->mode & ESP_BT_MODE_CLASSIC_BT) && (cfg->bt_max_acl_conn <= 0 || cfg->bt_max_acl_conn > BTDM_CONTROLLER_BR_EDR_MAX_ACL_CONN_LIMIT)) + || ((cfg->mode & ESP_BT_MODE_CLASSIC_BT) && (cfg->bt_max_sync_conn > BTDM_CONTROLLER_BR_EDR_MAX_SYNC_CONN_LIMIT))) { + return ESP_ERR_INVALID_ARG; + } + #ifdef CONFIG_PM_ENABLE esp_err_t err = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "bt", &s_pm_lock); if (err != ESP_OK) { @@ -873,8 +990,6 @@ esp_err_t esp_bt_controller_init(esp_bt_controller_config_t *cfg) memset(btdm_queue_table, 0, sizeof(btdm_queue_item_t) * BTDM_MAX_QUEUE_NUM); #endif - btdm_osi_funcs_register(&osi_funcs); - btdm_controller_mem_init(); periph_module_enable(PERIPH_BT_MODULE); @@ -936,6 +1051,9 @@ esp_err_t esp_bt_controller_deinit(void) memset(btdm_queue_table, 0, sizeof(btdm_queue_item_t) * BTDM_MAX_QUEUE_NUM); #endif + free(osi_funcs_p); + osi_funcs_p = NULL; + btdm_controller_status = ESP_BT_CONTROLLER_STATUS_IDLE; btdm_lpcycle_us = 0; @@ -956,8 +1074,8 @@ esp_err_t esp_bt_controller_enable(esp_bt_mode_t mode) return ESP_ERR_INVALID_STATE; } - //check the mode is available mode - if (mode & ~btdm_dram_available_region[0].mode) { + //As the history reason, mode should be equal to the mode which set in esp_bt_controller_init() + if (mode != btdm_controller_get_mode()) { return ESP_ERR_INVALID_ARG; } diff --git a/components/bt/include/esp_bt.h b/components/bt/include/esp_bt.h index 6ab606ff6..0376b5d38 100644 --- a/components/bt/include/esp_bt.h +++ b/components/bt/include/esp_bt.h @@ -25,83 +25,7 @@ extern "C" { #endif -/** - * @brief Controller config options, depend on config mask. - * Config mask indicate which functions enabled, this means - * some options or parameters of some functions enabled by config mask. - */ -typedef struct { - uint16_t controller_task_stack_size; /*!< Bluetooth controller task stack size */ - uint8_t controller_task_prio; /*!< Bluetooth controller task priority */ - uint8_t hci_uart_no; /*!< If use UART1/2 as HCI IO interface, indicate UART number */ - uint32_t hci_uart_baudrate; /*!< If use UART1/2 as HCI IO interface, indicate UART baudrate */ - uint8_t scan_duplicate_mode; /*!< If use UART1/2 as HCI IO interface, indicate UART baudrate */ - uint16_t normal_adv_size; /*!< Normal adv size for scan duplicate */ - uint16_t mesh_adv_size; /*!< Mesh adv size for scan duplicate */ - uint16_t send_adv_reserved_size; /*!< Controller minimum memory value */ - uint32_t controller_debug_flag; /*!< Controller debug log flag */ -} esp_bt_controller_config_t; - -#ifdef CONFIG_BT_ENABLED -/* While scanning, if the free memory value in controller is less than SCAN_SEND_ADV_RESERVED_SIZE, -the adv packet will be discarded until the memory is restored. */ -#define SCAN_SEND_ADV_RESERVED_SIZE 1000 -/* enable controller log debug when adv lost */ -#define CONTROLLER_ADV_LOST_DEBUG_BIT (0<<0) - -#ifdef CONFIG_BT_HCI_UART_NO -#define BT_HCI_UART_NO_DEFAULT CONFIG_BT_HCI_UART_NO -#else -#define BT_HCI_UART_NO_DEFAULT 1 -#endif /* BT_HCI_UART_NO_DEFAULT */ - -#ifdef CONFIG_BT_HCI_UART_BAUDRATE -#define BT_HCI_UART_BAUDRATE_DEFAULT CONFIG_BT_HCI_UART_BAUDRATE -#else -#define BT_HCI_UART_BAUDRATE_DEFAULT 921600 -#endif /* BT_HCI_UART_BAUDRATE_DEFAULT */ - -/* normal adv cache size */ -#ifdef CONFIG_DUPLICATE_SCAN_CACHE_SIZE -#define NORMAL_SCAN_DUPLICATE_CACHE_SIZE CONFIG_DUPLICATE_SCAN_CACHE_SIZE -#else -#define NORMAL_SCAN_DUPLICATE_CACHE_SIZE 20 -#endif - -#ifndef CONFIG_BLE_MESH_SCAN_DUPLICATE_EN -#define CONFIG_BLE_MESH_SCAN_DUPLICATE_EN FALSE -#endif - -#define SCAN_DUPLICATE_MODE_NORMAL_ADV_ONLY 0 -#define SCAN_DUPLICATE_MODE_NORMAL_ADV_MESH_ADV 1 - -#if CONFIG_BLE_MESH_SCAN_DUPLICATE_EN - #define SCAN_DUPLICATE_MODE SCAN_DUPLICATE_MODE_NORMAL_ADV_MESH_ADV - #ifdef CONFIG_MESH_DUPLICATE_SCAN_CACHE_SIZE - #define MESH_DUPLICATE_SCAN_CACHE_SIZE CONFIG_MESH_DUPLICATE_SCAN_CACHE_SIZE - #else - #define MESH_DUPLICATE_SCAN_CACHE_SIZE 50 - #endif -#else - #define SCAN_DUPLICATE_MODE SCAN_DUPLICATE_MODE_NORMAL_ADV_ONLY - #define MESH_DUPLICATE_SCAN_CACHE_SIZE 0 -#endif - -#define BT_CONTROLLER_INIT_CONFIG_DEFAULT() { \ - .controller_task_stack_size = ESP_TASK_BT_CONTROLLER_STACK, \ - .controller_task_prio = ESP_TASK_BT_CONTROLLER_PRIO, \ - .hci_uart_no = BT_HCI_UART_NO_DEFAULT, \ - .hci_uart_baudrate = BT_HCI_UART_BAUDRATE_DEFAULT, \ - .scan_duplicate_mode = SCAN_DUPLICATE_MODE, \ - .normal_adv_size = NORMAL_SCAN_DUPLICATE_CACHE_SIZE, \ - .mesh_adv_size = MESH_DUPLICATE_SCAN_CACHE_SIZE, \ - .send_adv_reserved_size = SCAN_SEND_ADV_RESERVED_SIZE, \ - .controller_debug_flag = CONTROLLER_ADV_LOST_DEBUG_BIT, \ -}; - -#else -#define BT_CONTROLLER_INIT_CONFIG_DEFAULT() {0}; _Static_assert(0, "please enable bluetooth in menuconfig to use bt.h"); -#endif +#define ESP_BT_CONTROLLER_CONFIG_MAGIC_VAL 0x5A5AA5A5 /** * @brief Bluetooth mode for controller enable/disable @@ -113,6 +37,114 @@ typedef enum { ESP_BT_MODE_BTDM = 0x03, /*!< Run dual mode */ } esp_bt_mode_t; +#ifdef CONFIG_BT_ENABLED +/* While scanning, if the free memory value in controller is less than SCAN_SEND_ADV_RESERVED_SIZE, +the adv packet will be discarded until the memory is restored. */ +#define SCAN_SEND_ADV_RESERVED_SIZE 1000 +/* enable controller log debug when adv lost */ +#define CONTROLLER_ADV_LOST_DEBUG_BIT (0<<0) + +#ifdef CONFIG_BT_HCI_UART_NO +#define BT_HCI_UART_NO_DEFAULT CONFIG_BT_HCI_UART_NO +#else +#define BT_HCI_UART_NO_DEFAULT 1 +#endif /* BT_HCI_UART_NO_DEFAULT */ + +#ifdef CONFIG_BT_HCI_UART_BAUDRATE +#define BT_HCI_UART_BAUDRATE_DEFAULT CONFIG_BT_HCI_UART_BAUDRATE +#else +#define BT_HCI_UART_BAUDRATE_DEFAULT 921600 +#endif /* BT_HCI_UART_BAUDRATE_DEFAULT */ + +/* normal adv cache size */ +#ifdef CONFIG_DUPLICATE_SCAN_CACHE_SIZE +#define NORMAL_SCAN_DUPLICATE_CACHE_SIZE CONFIG_DUPLICATE_SCAN_CACHE_SIZE +#else +#define NORMAL_SCAN_DUPLICATE_CACHE_SIZE 20 +#endif + +#ifndef CONFIG_BLE_MESH_SCAN_DUPLICATE_EN +#define CONFIG_BLE_MESH_SCAN_DUPLICATE_EN FALSE +#endif + +#define SCAN_DUPLICATE_MODE_NORMAL_ADV_ONLY 0 +#define SCAN_DUPLICATE_MODE_NORMAL_ADV_MESH_ADV 1 + +#if CONFIG_BLE_MESH_SCAN_DUPLICATE_EN + #define SCAN_DUPLICATE_MODE SCAN_DUPLICATE_MODE_NORMAL_ADV_MESH_ADV + #ifdef CONFIG_MESH_DUPLICATE_SCAN_CACHE_SIZE + #define MESH_DUPLICATE_SCAN_CACHE_SIZE CONFIG_MESH_DUPLICATE_SCAN_CACHE_SIZE + #else + #define MESH_DUPLICATE_SCAN_CACHE_SIZE 50 + #endif +#else + #define SCAN_DUPLICATE_MODE SCAN_DUPLICATE_MODE_NORMAL_ADV_ONLY + #define MESH_DUPLICATE_SCAN_CACHE_SIZE 0 +#endif + +#if defined(CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY) +#define BTDM_CONTROLLER_MODE_EFF ESP_BT_MODE_BLE +#elif defined(CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY) +#define BTDM_CONTROLLER_MODE_EFF ESP_BT_MODE_CLASSIC_BT +#else +#define BTDM_CONTROLLER_MODE_EFF ESP_BT_MODE_BTDM +#endif + +#define BTDM_CONTROLLER_BLE_MAX_CONN_LIMIT 9 //Maximum BLE connection limitation +#define BTDM_CONTROLLER_BR_EDR_MAX_ACL_CONN_LIMIT 7 //Maximum ACL connection limitation +#define BTDM_CONTROLLER_BR_EDR_MAX_SYNC_CONN_LIMIT 3 //Maximum SCO/eSCO connection limitation + +#define BT_CONTROLLER_INIT_CONFIG_DEFAULT() { \ + .controller_task_stack_size = ESP_TASK_BT_CONTROLLER_STACK, \ + .controller_task_prio = ESP_TASK_BT_CONTROLLER_PRIO, \ + .hci_uart_no = BT_HCI_UART_NO_DEFAULT, \ + .hci_uart_baudrate = BT_HCI_UART_BAUDRATE_DEFAULT, \ + .scan_duplicate_mode = SCAN_DUPLICATE_MODE, \ + .normal_adv_size = NORMAL_SCAN_DUPLICATE_CACHE_SIZE, \ + .mesh_adv_size = MESH_DUPLICATE_SCAN_CACHE_SIZE, \ + .send_adv_reserved_size = SCAN_SEND_ADV_RESERVED_SIZE, \ + .controller_debug_flag = CONTROLLER_ADV_LOST_DEBUG_BIT, \ + .mode = BTDM_CONTROLLER_MODE_EFF, \ + .ble_max_conn = CONFIG_BTDM_CONTROLLER_BLE_MAX_CONN_EFF, \ + .bt_max_acl_conn = CONFIG_BTDM_CONTROLLER_BR_EDR_MAX_ACL_CONN_EFF, \ + .bt_max_sync_conn = CONFIG_BTDM_CONTROLLER_BR_EDR_MAX_SYNC_CONN_EFF, \ + .magic = ESP_BT_CONTROLLER_CONFIG_MAGIC_VAL, \ +}; + +#else +#define BT_CONTROLLER_INIT_CONFIG_DEFAULT() {0}; _Static_assert(0, "please enable bluetooth in menuconfig to use bt.h"); +#endif + +/** + * @brief Controller config options, depend on config mask. + * Config mask indicate which functions enabled, this means + * some options or parameters of some functions enabled by config mask. + */ +typedef struct { + /* + * Following parameters can be configured runtime, when call esp_bt_controller_init() + */ + uint16_t controller_task_stack_size; /*!< Bluetooth controller task stack size */ + uint8_t controller_task_prio; /*!< Bluetooth controller task priority */ + uint8_t hci_uart_no; /*!< If use UART1/2 as HCI IO interface, indicate UART number */ + uint32_t hci_uart_baudrate; /*!< If use UART1/2 as HCI IO interface, indicate UART baudrate */ + uint8_t scan_duplicate_mode; /*!< If use UART1/2 as HCI IO interface, indicate UART baudrate */ + uint16_t normal_adv_size; /*!< Normal adv size for scan duplicate */ + uint16_t mesh_adv_size; /*!< Mesh adv size for scan duplicate */ + uint16_t send_adv_reserved_size; /*!< Controller minimum memory value */ + uint32_t controller_debug_flag; /*!< Controller debug log flag */ + uint8_t mode; /*!< Controller mode: BR/EDR, BLE or Dual Mode */ + uint8_t ble_max_conn; /*!< BLE maximum connection numbers */ + uint8_t bt_max_acl_conn; /*!< BR/EDR maximum ACL connection numbers */ + /* + * Following parameters can not be configured runtime when call esp_bt_controller_init() + * It will be overwrite with a constant value which in menuconfig or from a macro. + * So, do not modify the value when esp_bt_controller_init() + */ + uint8_t bt_max_sync_conn; /*!< BR/EDR maxium ACL connection numbers. Effective in menuconfig */ + uint32_t magic; /*!< Magic number */ +} esp_bt_controller_config_t; + /** * @brief Bluetooth controller enable/disable/initialised/de-initialised status */ @@ -123,7 +155,6 @@ typedef enum { ESP_BT_CONTROLLER_STATUS_NUM, } esp_bt_controller_status_t; - /** * @brief BLE tx power type * ESP_BLE_PWR_TYPE_CONN_HDL0-8: for each connection, and only be set after connection completed. @@ -231,10 +262,11 @@ esp_err_t esp_bredr_tx_power_get(esp_power_level_t *min_power_level, esp_power_l esp_err_t esp_bredr_sco_datapath_set(esp_sco_data_path_t data_path); /** - * @brief Initialize BT controller to allocate task and other resource. - * @param cfg: Initial configuration of BT controller. - * This function should be called only once, before any other BT functions are called. - * @return ESP_OK - success, other - failed + * @brief Initialize BT controller to allocate task and other resource. + * This function should be called only once, before any other BT functions are called. + * @param cfg: Initial configuration of BT controller. Different from previous version, there's a mode and some + * connection configuration in "cfg" to configure controller work mode and allocate the resource which is needed. + * @return ESP_OK - success, other - failed */ esp_err_t esp_bt_controller_init(esp_bt_controller_config_t *cfg); @@ -252,7 +284,8 @@ esp_err_t esp_bt_controller_deinit(void); * Due to a known issue, you cannot call esp_bt_controller_enable() a second time * to change the controller mode dynamically. To change controller mode, call * esp_bt_controller_disable() and then call esp_bt_controller_enable() with the new mode. - * @param mode : the mode(BLE/BT/BTDM) to enable. + * @param mode : the mode(BLE/BT/BTDM) to enable. For compatible of API, retain this argument. This mode must be + * equal as the mode in "cfg" of esp_bt_controller_init(). * @return ESP_OK - success, other - failed */ esp_err_t esp_bt_controller_enable(esp_bt_mode_t mode); @@ -294,8 +327,9 @@ void esp_vhci_host_send_packet(uint8_t *data, uint16_t len); * register the vhci referece callback, the call back * struct defined by vhci_host_callback structure. * @param callback esp_vhci_host_callback type variable + * @return ESP_OK - success, ESP_FAIL - failed */ -void esp_vhci_host_register_callback(const esp_vhci_host_callback_t *callback); +esp_err_t esp_vhci_host_register_callback(const esp_vhci_host_callback_t *callback); /** @brief esp_bt_controller_mem_release * release the controller memory as per the mode diff --git a/components/bt/lib b/components/bt/lib index d3c834d66..17e29e6ed 160000 --- a/components/bt/lib +++ b/components/bt/lib @@ -1 +1 @@ -Subproject commit d3c834d66153b1acd6b4e5e744a66aaa55ae5c40 +Subproject commit 17e29e6ed4e2c952a24e4097e9f41bb89bac6128 diff --git a/components/bt/test/component.mk b/components/bt/test/component.mk index 5dd172bdb..55e05e423 100644 --- a/components/bt/test/component.mk +++ b/components/bt/test/component.mk @@ -1,5 +1,5 @@ -# -#Component Makefile -# - +ifdef CONFIG_BT_ENABLED COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive +else +COMPONENT_CONFIG_ONLY := 1 +endif diff --git a/components/driver/Kconfig b/components/driver/Kconfig index bff45a46b..eaf61508d 100644 --- a/components/driver/Kconfig +++ b/components/driver/Kconfig @@ -6,8 +6,8 @@ config ADC_FORCE_XPD_FSM bool "Use the FSM to control ADC power" default n help - ADC power can be controlled by the FSM instead of software. This allows the ADC to - be shut off when it is not working leading to lower power consumption. However + ADC power can be controlled by the FSM instead of software. This allows the ADC to + be shut off when it is not working leading to lower power consumption. However using the FSM control ADC power will increase the noise of ADC. config ADC2_DISABLE_DAC @@ -20,7 +20,7 @@ config ADC2_DISABLE_DAC endmenu # ADC Configuration -menu "SPI master configuration" +menu "SPI configuration" config SPI_MASTER_IN_IRAM bool "Place transmitting functions of SPI master into IRAM" @@ -44,6 +44,26 @@ config SPI_MASTER_ISR_IN_IRAM Place the SPI master ISR in to IRAM to avoid possibly cache miss, or being disabled during flash writing access. -endmenu # SPI Master Configuration +config SPI_SLAVE_IN_IRAM + bool "Place transmitting functions of SPI slave into IRAM" + default n + select SPI_SLAVE_ISR_IN_IRAM + help + Normally only the ISR of SPI slave is placed in the IRAM, so that it + can work without the flash when interrupt is triggered. + For other functions, there's some possibility that the flash cache + miss when running inside and out of SPI functions, which may increase + the interval of SPI transactions. + Enable this to put ``queue_trans``, ``get_trans_result`` and + ``transmit`` functions into the IRAM to avoid possible cache miss. + +config SPI_SLAVE_ISR_IN_IRAM + bool "Place SPI slave ISR function into IRAM" + default y + help + Place the SPI slave ISR in to IRAM to avoid possibly cache miss, or + being disabled during flash writing access. + +endmenu # SPI Configuration endmenu # Driver configurations diff --git a/components/driver/spi_common.c b/components/driver/spi_common.c index aa5495a8c..06873f934 100644 --- a/components/driver/spi_common.c +++ b/components/driver/spi_common.c @@ -330,7 +330,7 @@ void spicommon_cs_free_io(int cs_gpio_num) } //Set up a list of dma descriptors. dmadesc is an array of descriptors. Data is the buffer to point to. -void spicommon_setup_dma_desc_links(lldesc_t *dmadesc, int len, const uint8_t *data, bool isrx) +void IRAM_ATTR spicommon_setup_dma_desc_links(lldesc_t *dmadesc, int len, const uint8_t *data, bool isrx) { int n = 0; while (len) { diff --git a/components/driver/spi_master.c b/components/driver/spi_master.c index 5728bd45e..1981ec3f2 100644 --- a/components/driver/spi_master.c +++ b/components/driver/spi_master.c @@ -177,7 +177,7 @@ esp_err_t spi_bus_initialize(spi_host_device_t host, const spi_bus_config_t *bus spihost[host]->dma_chan=dma_chan; if (dma_chan == 0) { - spihost[host]->max_transfer_sz = 32; + spihost[host]->max_transfer_sz = 64; } else { //See how many dma descriptors we need and allocate them int dma_desc_ct=(bus_config->max_transfer_sz+SPI_MAX_DMA_LEN-1)/SPI_MAX_DMA_LEN; @@ -213,6 +213,10 @@ esp_err_t spi_bus_initialize(spi_host_device_t host, const spi_bus_config_t *bus //Reset timing spihost[host]->hw->ctrl2.val=0; + //master use all 64 bytes of the buffer + spihost[host]->hw->user.usr_miso_highpart=0; + spihost[host]->hw->user.usr_mosi_highpart=0; + //Disable unneeded ints spihost[host]->hw->slave.rd_buf_done=0; spihost[host]->hw->slave.wr_buf_done=0; @@ -607,6 +611,8 @@ static void SPI_MASTER_ISR_ATTR spi_intr(void *arg) host->hw->dma_in_link.start=0; host->hw->dma_conf.val &= ~(SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST); host->hw->dma_conf.out_data_burst_en=1; + host->hw->dma_conf.indscr_burst_en=1; + host->hw->dma_conf.outdscr_burst_en=1; //Set up QIO/DIO if needed host->hw->ctrl.val &= ~(SPI_FREAD_DUAL|SPI_FREAD_QUAD|SPI_FREAD_DIO|SPI_FREAD_QIO); host->hw->user.val &= ~(SPI_FWRITE_DUAL|SPI_FWRITE_QUAD|SPI_FWRITE_DIO|SPI_FWRITE_QIO); @@ -633,7 +639,6 @@ static void SPI_MASTER_ISR_ATTR spi_intr(void *arg) //Fill DMA descriptors int extra_dummy=0; if (trans_buf->buffer_to_rcv) { - host->hw->user.usr_miso_highpart=0; if (host->dma_chan == 0) { //No need to setup anything; we'll copy the result out of the work registers directly later. } else { @@ -662,16 +667,13 @@ static void SPI_MASTER_ISR_ATTR spi_intr(void *arg) //Use memcpy to get around alignment issues for txdata uint32_t word; memcpy(&word, &trans_buf->buffer_to_send[x/32], 4); - host->hw->data_buf[(x/32)+8]=word; + host->hw->data_buf[(x/32)]=word; } - host->hw->user.usr_mosi_highpart=1; } else { spicommon_dmaworkaround_transfer_active(host->dma_chan); //mark channel as active spicommon_setup_dma_desc_links(host->dmadesc_tx, (trans->length+7)/8, (uint8_t*)trans_buf->buffer_to_send, false); - host->hw->user.usr_mosi_highpart=0; host->hw->dma_out_link.addr=(int)(&host->dmadesc_tx[0]) & 0xFFFFF; host->hw->dma_out_link.start=1; - host->hw->user.usr_mosi_highpart=0; } } diff --git a/components/driver/spi_slave.c b/components/driver/spi_slave.c index 7ff89e6c8..e563ef074 100644 --- a/components/driver/spi_slave.c +++ b/components/driver/spi_slave.c @@ -47,6 +47,18 @@ static const char *SPI_TAG = "spi_slave"; #define VALID_HOST(x) (x>SPI_HOST && x<=VSPI_HOST) +#ifdef CONFIG_SPI_SLAVE_ISR_IN_IRAM +#define SPI_SLAVE_ISR_ATTR IRAM_ATTR +#else +#define SPI_SLAVE_ISR_ATTR +#endif + +#ifdef CONFIG_SPI_SLAVE_IN_IRAM +#define SPI_SLAVE_ATTR IRAM_ATTR +#else +#define SPI_SLAVE_ATTR +#endif + typedef struct { spi_slave_interface_config_t cfg; intr_handle_t intr; @@ -79,7 +91,7 @@ esp_err_t spi_slave_initialize(spi_host_device_t host, const spi_bus_config_t *b spi_chan_claimed=spicommon_periph_claim(host); SPI_CHECK(spi_chan_claimed, "host already in use", ESP_ERR_INVALID_STATE); - + if ( dma_chan != 0 ) { dma_chan_claimed=spicommon_dma_chan_claim(dma_chan); if ( !dma_chan_claimed ) { @@ -138,7 +150,11 @@ esp_err_t spi_slave_initialize(spi_host_device_t host, const spi_bus_config_t *b goto cleanup; } - err = esp_intr_alloc(spicommon_irqsource_for_host(host), ESP_INTR_FLAG_INTRDISABLED, spi_intr, (void *)spihost[host], &spihost[host]->intr); + int flags = ESP_INTR_FLAG_INTRDISABLED; +#ifdef CONFIG_SPI_SLAVE_ISR_IN_IRAM + flags |= ESP_INTR_FLAG_IRAM; +#endif + err = esp_intr_alloc(spicommon_irqsource_for_host(host), flags, spi_intr, (void *)spihost[host], &spihost[host]->intr); if (err != ESP_OK) { ret = err; goto cleanup; @@ -250,14 +266,14 @@ esp_err_t spi_slave_free(spi_host_device_t host) } -esp_err_t spi_slave_queue_trans(spi_host_device_t host, const spi_slave_transaction_t *trans_desc, TickType_t ticks_to_wait) +esp_err_t SPI_SLAVE_ATTR spi_slave_queue_trans(spi_host_device_t host, const spi_slave_transaction_t *trans_desc, TickType_t ticks_to_wait) { BaseType_t r; SPI_CHECK(VALID_HOST(host), "invalid host", ESP_ERR_INVALID_ARG); SPI_CHECK(spihost[host], "host not slave", ESP_ERR_INVALID_ARG); - SPI_CHECK(spihost[host]->dma_chan == 0 || trans_desc->tx_buffer==NULL || esp_ptr_dma_capable(trans_desc->tx_buffer), + SPI_CHECK(spihost[host]->dma_chan == 0 || trans_desc->tx_buffer==NULL || esp_ptr_dma_capable(trans_desc->tx_buffer), "txdata not in DMA-capable memory", ESP_ERR_INVALID_ARG); - SPI_CHECK(spihost[host]->dma_chan == 0 || trans_desc->rx_buffer==NULL || esp_ptr_dma_capable(trans_desc->rx_buffer), + SPI_CHECK(spihost[host]->dma_chan == 0 || trans_desc->rx_buffer==NULL || esp_ptr_dma_capable(trans_desc->rx_buffer), "rxdata not in DMA-capable memory", ESP_ERR_INVALID_ARG); SPI_CHECK(trans_desc->length <= spihost[host]->max_transfer_sz * 8, "data transfer > host maximum", ESP_ERR_INVALID_ARG); @@ -268,7 +284,7 @@ esp_err_t spi_slave_queue_trans(spi_host_device_t host, const spi_slave_transact } -esp_err_t spi_slave_get_trans_result(spi_host_device_t host, spi_slave_transaction_t **trans_desc, TickType_t ticks_to_wait) +esp_err_t SPI_SLAVE_ATTR spi_slave_get_trans_result(spi_host_device_t host, spi_slave_transaction_t **trans_desc, TickType_t ticks_to_wait) { BaseType_t r; SPI_CHECK(VALID_HOST(host), "invalid host", ESP_ERR_INVALID_ARG); @@ -279,7 +295,7 @@ esp_err_t spi_slave_get_trans_result(spi_host_device_t host, spi_slave_transacti } -esp_err_t spi_slave_transmit(spi_host_device_t host, spi_slave_transaction_t *trans_desc, TickType_t ticks_to_wait) +esp_err_t SPI_SLAVE_ATTR spi_slave_transmit(spi_host_device_t host, spi_slave_transaction_t *trans_desc, TickType_t ticks_to_wait) { esp_err_t ret; spi_slave_transaction_t *ret_trans; @@ -316,7 +332,7 @@ static void dumpll(lldesc_t *ll) } #endif -static void IRAM_ATTR spi_slave_restart_after_dmareset(void *arg) +static void SPI_SLAVE_ISR_ATTR spi_slave_restart_after_dmareset(void *arg) { spi_slave_t *host = (spi_slave_t *)arg; esp_intr_enable(host->intr); @@ -325,7 +341,7 @@ static void IRAM_ATTR spi_slave_restart_after_dmareset(void *arg) //This is run in interrupt context and apart from initialization and destruction, this is the only code //touching the host (=spihost[x]) variable. The rest of the data arrives in queues. That is why there are //no muxes in this code. -static void IRAM_ATTR spi_intr(void *arg) +static void SPI_SLAVE_ISR_ATTR spi_intr(void *arg) { BaseType_t r; BaseType_t do_yield = pdFALSE; @@ -342,7 +358,7 @@ static void IRAM_ATTR spi_intr(void *arg) if (host->cur_trans) { //when data of cur_trans->length are all sent, the slv_rdata_bit - //will be the length sent-1 (i.e. cur_trans->length-1 ), otherwise + //will be the length sent-1 (i.e. cur_trans->length-1 ), otherwise //the length sent. host->cur_trans->trans_len = host->hw->slv_rd_bit.slv_rdata_bit; if ( host->cur_trans->trans_len == host->cur_trans->length - 1 ) { diff --git a/components/driver/test/test_rmt.c b/components/driver/test/test_rmt.c index de9937e3a..9ee803dd9 100644 --- a/components/driver/test/test_rmt.c +++ b/components/driver/test/test_rmt.c @@ -88,7 +88,7 @@ static void fill_item_end(rmt_item32_t* item) /** * @brief Check whether duration is around target_us */ -inline bool check_in_range(int duration_ticks, int target_us, int margin_us) +static inline bool check_in_range(int duration_ticks, int target_us, int margin_us) { if(( ITEM_DURATION(duration_ticks) < (target_us + margin_us)) && ( ITEM_DURATION(duration_ticks) > (target_us - margin_us))) { diff --git a/components/driver/test/test_spi_master.c b/components/driver/test/test_spi_master.c index 8887d5cef..9b82477d0 100644 --- a/components/driver/test/test_spi_master.c +++ b/components/driver/test/test_spi_master.c @@ -234,6 +234,10 @@ TEST_CASE("SPI Master test", "[spi]") success &= spi_test(handle, 4); //aligned success &= spi_test(handle, 16); //small success &= spi_test(handle, 21); //small, unaligned + success &= spi_test(handle, 32); //small + success &= spi_test(handle, 47); //small, unaligned + success &= spi_test(handle, 63); //small + success &= spi_test(handle, 64); //small, unaligned destroy_spi_bus(handle); @@ -575,7 +579,7 @@ TEST_CASE("SPI Master DMA test, TX and RX in different regions", "[spi]") ESP_LOGI(TAG, "iram: %p, dram: %p", data_iram, data_dram); ESP_LOGI(TAG, "drom: %p, malloc: %p", data_drom, data_malloc); - memset(trans, 0, 6*sizeof(spi_transaction_t)); + memset(trans, 0, sizeof(trans)); trans[0].length = 320*8, trans[0].tx_buffer = data_iram; @@ -693,8 +697,22 @@ TEST_CASE("SPI Master DMA test: length, start, not aligned", "[spi]") static const char MASTER_TAG[] = "test_master"; static const char SLAVE_TAG[] = "test_slave"; -DRAM_ATTR static uint8_t master_send[] = {0x93, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0xaa, 0xcc, 0xff, 0xee, 0x55, 0x77, 0x88, 0x43}; -DRAM_ATTR static uint8_t slave_send[] = { 0xaa, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x13, 0x57, 0x9b, 0xdf, 0x24, 0x68, 0xac, 0xe0 }; +DRAM_ATTR static uint8_t master_send[] = { + 0x93, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0xaa, 0xcc, 0xff, 0xee, 0x55, 0x77, 0x88, 0x43, + 0x74, + 0x93, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0xaa, 0xcc, 0xff, 0xee, 0x55, 0x77, 0x88, 0x43, + 0x74, + 0x93, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0xaa, 0xcc, 0xff, 0xee, 0x55, 0x77, 0x88, 0x43, + 0x74, + }; +DRAM_ATTR static uint8_t slave_send[] = { + 0xaa, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x13, 0x57, 0x9b, 0xdf, 0x24, 0x68, 0xac, 0xe0, + 0xda, + 0xaa, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x13, 0x57, 0x9b, 0xdf, 0x24, 0x68, 0xac, 0xe0, + 0xda, + 0xaa, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x13, 0x57, 0x9b, 0xdf, 0x24, 0x68, 0xac, 0xe0, + 0xda, + }; static void master_deinit(spi_device_handle_t spi) @@ -1006,6 +1024,8 @@ esp_err_t check_data(spi_transaction_t *t, spi_dup_t dup, slave_rxdata_t *slave_ return ESP_OK; } +int test_len[] = {1, 3, 5, 7, 9, 11, 33, 64}; + static void timing_init_transactions(spi_dup_t dup, timing_context_t* context) { spi_transaction_t* trans = context->master_trans; @@ -1014,7 +1034,7 @@ static void timing_init_transactions(spi_dup_t dup, timing_context_t* context) for (int i = 0; i < 8; i++ ) { trans[i] = (spi_transaction_t) { .flags = 0, - .rxlength = 8*(i*2+1), + .rxlength = 8*test_len[i], .rx_buffer = rx_buf_ptr, }; rx_buf_ptr += ((context->master_trans[i].rxlength + 31)/8)&(~3); @@ -1023,7 +1043,7 @@ static void timing_init_transactions(spi_dup_t dup, timing_context_t* context) for (int i = 0; i < 8; i++ ) { trans[i] = (spi_transaction_t) { .flags = 0, - .length = 8*(i*2+1), + .length = 8*test_len[i], .tx_buffer = master_send+i, }; } @@ -1031,7 +1051,7 @@ static void timing_init_transactions(spi_dup_t dup, timing_context_t* context) for (int i = 0; i < 8; i++ ) { trans[i] = (spi_transaction_t) { .flags = 0, - .length = 8*(i*2+1), + .length = 8*test_len[i], .tx_buffer = master_send+i, .rx_buffer = rx_buf_ptr, }; @@ -1042,7 +1062,7 @@ static void timing_init_transactions(spi_dup_t dup, timing_context_t* context) for (int i = 0; i < 8; i ++) { context->slave_trans[i] = (slave_txdata_t) { .start = slave_send + 4*(i%3), - .len = 256, + .len = 512, }; } } @@ -1073,7 +1093,7 @@ typedef struct { #define ESP_SPI_SLAVE_MAX_FREQ_SYNC SPI_MASTER_FREQ_40M -static test_timing_config_t timing_master_conf_t[] = {/**/ +static test_timing_config_t timing_master_conf_t[] = { { .cfg_name = "FULL_DUP, MASTER IOMUX", .freq_limit = SPI_MASTER_FREQ_13M, .dup = FULL_DUPLEX, diff --git a/components/esp-tls/esp_tls.c b/components/esp-tls/esp_tls.c index 44b1b1671..33f3a0e1a 100644 --- a/components/esp-tls/esp_tls.c +++ b/components/esp-tls/esp_tls.c @@ -32,6 +32,8 @@ static const char *TAG = "esp-tls"; #define ESP_LOGE(TAG, ...) printf(__VA_ARGS__); #endif +#define DEFAULT_TIMEOUT_MS -1 + static struct addrinfo *resolve_host_name(const char *host, size_t hostlen) { struct addrinfo hints; @@ -74,7 +76,13 @@ static ssize_t tls_read(esp_tls_t *tls, char *data, size_t datalen) return ret; } -static int esp_tcp_connect(const char *host, int hostlen, int port) +static void ms_to_timeval(int timeout_ms, struct timeval *tv) +{ + tv->tv_sec = timeout_ms / 1000; + tv->tv_usec = (timeout_ms % 1000) * 1000; +} + +static int esp_tcp_connect(const char *host, int hostlen, int port, int timeout_ms) { struct addrinfo *res = resolve_host_name(host, hostlen); if (!res) { @@ -103,6 +111,12 @@ static int esp_tcp_connect(const char *host, int hostlen, int port) goto err_freesocket; } + if (timeout_ms >= 0) { + struct timeval tv; + ms_to_timeval(timeout_ms, &tv); + setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + } + ret = connect(fd, addr_ptr, res->ai_addrlen); if (ret < 0) { ESP_LOGE(TAG, "Failed to connnect to host (errno %d)", errno); @@ -266,7 +280,13 @@ static ssize_t tls_write(esp_tls_t *tls, const char *data, size_t datalen) */ esp_tls_t *esp_tls_conn_new(const char *hostname, int hostlen, int port, const esp_tls_cfg_t *cfg) { - int sockfd = esp_tcp_connect(hostname, hostlen, port); + int sockfd; + if (cfg) { + sockfd = esp_tcp_connect(hostname, hostlen, port, cfg->timeout_ms); + } else { + sockfd = esp_tcp_connect(hostname, hostlen, port, DEFAULT_TIMEOUT_MS); + } + if (sockfd < 0) { return NULL; } @@ -324,3 +344,12 @@ esp_tls_t *esp_tls_conn_http_new(const char *url, const esp_tls_cfg_t *cfg) return esp_tls_conn_new(&url[u.field_data[UF_HOST].off], u.field_data[UF_HOST].len, get_port(url, &u), cfg); } + +size_t esp_tls_get_bytes_avail(esp_tls_t *tls) +{ + if (!tls) { + ESP_LOGE(TAG, "empty arg passed to esp_tls_get_bytes_avail()"); + return ESP_FAIL; + } + return mbedtls_ssl_get_bytes_avail(&tls->ssl); +} \ No newline at end of file diff --git a/components/esp-tls/esp_tls.h b/components/esp-tls/esp_tls.h index 92b0734eb..d5975dc19 100644 --- a/components/esp-tls/esp_tls.h +++ b/components/esp-tls/esp_tls.h @@ -18,7 +18,6 @@ #include #include - #include "mbedtls/platform.h" #include "mbedtls/net_sockets.h" #include "mbedtls/esp_debug.h" @@ -48,12 +47,14 @@ typedef struct esp_tls_cfg { const unsigned char *cacert_pem_buf; /*!< Certificate Authority's certificate in a buffer */ - const unsigned int cacert_pem_bytes; /*!< Size of Certificate Authority certificate + unsigned int cacert_pem_bytes; /*!< Size of Certificate Authority certificate pointed to by cacert_pem_buf */ bool non_block; /*!< Configure non-blocking mode. If set to true the underneath socket will be configured in non blocking mode after tls session is established */ + + int timeout_ms; /*!< Network timeout in milliseconds */ } esp_tls_cfg_t; /** @@ -165,6 +166,21 @@ static inline ssize_t esp_tls_conn_read(esp_tls_t *tls, void *data, size_t data */ void esp_tls_conn_delete(esp_tls_t *tls); +/** + * @brief Return the number of application data bytes remaining to be + * read from the current record + * + * This API is a wrapper over mbedtls's mbedtls_ssl_get_bytes_avail() API. + * + * @param[in] tls pointer to esp-tls as esp-tls handle. + * + * @return + * - -1 in case of invalid arg + * - bytes available in the application data + * record read buffer + */ +size_t esp_tls_get_bytes_avail(esp_tls_t *tls); + #ifdef __cplusplus } #endif diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index dc897dff2..2f7d4c868 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -687,18 +687,39 @@ choice ESP32_RTC_CLOCK_SOURCE default ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC help Choose which clock is used as RTC clock source. + + - "Internal 150kHz oscillator" option provides lowest deep sleep current + consumption, and does not require extra external components. However + frequency stability with respect to temperature is poor, so time may + drift in deep/light sleep modes. + - "External 32kHz crystal" provides better frequency stability, at the + expense of slightly higher (1uA) deep sleep current consumption. + - "External 32kHz oscillator" allows using 32kHz clock generated by an + external circuit. In this case, external clock signal must be connected + to 32K_XP pin. Amplitude should be <1.2V in case of sine wave signal, + and <1V in case of square wave signal. Common mode voltage should be + 0.1 < Vcm < 0.5Vamp, where Vamp is the signal amplitude. + Additionally, 1nF capacitor must be connected between 32K_XN pin and + ground. 32K_XN pin can not be used as a GPIO in this case. + - "Internal 8.5MHz oscillator divided by 256" option results in higher + deep sleep current (by 5uA) but has better frequency stability than + the internal 150kHz oscillator. It does not require external components. config ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC bool "Internal 150kHz RC oscillator" config ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL bool "External 32kHz crystal" +config ESP32_RTC_CLOCK_SOURCE_EXTERNAL_OSC + bool "External 32kHz oscillator at 32K_XP pin" +config ESP32_RTC_CLOCK_SOURCE_INTERNAL_8MD256 + bool "Internal 8.5MHz oscillator, divided by 256 (~33kHz)" endchoice config ESP32_RTC_CLK_CAL_CYCLES int "Number of cycles for RTC_SLOW_CLK calibration" default 3000 if ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL default 1024 if ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC - range 0 27000 if ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL + range 0 27000 if ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL || ESP32_RTC_CLOCK_SOURCE_EXTERNAL_OSC || ESP32_RTC_CLOCK_SOURCE_INTERNAL_8MD256 range 0 32766 if ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC help When the startup code initializes RTC_SLOW_CLK, it can perform diff --git a/components/esp32/brownout.c b/components/esp32/brownout.c index 50d8ac044..1d7800614 100644 --- a/components/esp32/brownout.c +++ b/components/esp32/brownout.c @@ -21,7 +21,7 @@ #include "soc/cpu.h" #include "soc/rtc_cntl_reg.h" #include "rom/ets_sys.h" -#include "esp_system.h" +#include "esp_system_internal.h" #include "driver/rtc_cntl.h" #include "freertos/FreeRTOS.h" @@ -42,6 +42,7 @@ static void rtc_brownout_isr_handler() * at the same time as the following ets_printf. */ esp_cpu_stall(!xPortGetCoreID()); + esp_reset_reason_set_hint(ESP_RST_BROWNOUT); ets_printf("\r\nBrownout detector was triggered\r\n\r\n"); esp_restart_noos(); } diff --git a/components/esp32/clk.c b/components/esp32/clk.c index 4b1f0369c..17afaa1b3 100644 --- a/components/esp32/clk.c +++ b/components/esp32/clk.c @@ -40,7 +40,23 @@ #define MHZ (1000000) -static void select_rtc_slow_clk(rtc_slow_freq_t slow_clk); +/* Indicates that this 32k oscillator gets input from external oscillator, rather + * than a crystal. + */ +#define EXT_OSC_FLAG BIT(3) + +/* This is almost the same as rtc_slow_freq_t, except that we define + * an extra enum member for the external 32k oscillator. + * For convenience, lower 2 bits should correspond to rtc_slow_freq_t values. + */ +typedef enum { + SLOW_CLK_150K = RTC_SLOW_FREQ_RTC, //!< Internal 150 kHz RC oscillator + SLOW_CLK_32K_XTAL = RTC_SLOW_FREQ_32K_XTAL, //!< External 32 kHz XTAL + SLOW_CLK_8MD256 = RTC_SLOW_FREQ_8MD256, //!< Internal 8 MHz RC oscillator, divided by 256 + SLOW_CLK_32K_EXT_OSC = RTC_SLOW_FREQ_32K_XTAL | EXT_OSC_FLAG //!< External 32k oscillator connected to 32K_XP pin +} slow_clk_sel_t; + +static void select_rtc_slow_clk(slow_clk_sel_t slow_clk); // g_ticks_us defined in ROMs for PRO and APP CPU extern uint32_t g_ticks_per_us_pro; @@ -71,50 +87,47 @@ void esp_clk_init(void) rtc_clk_fast_freq_set(RTC_FAST_FREQ_8M); -#ifdef CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL - select_rtc_slow_clk(RTC_SLOW_FREQ_32K_XTAL); +#if defined(CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL) + select_rtc_slow_clk(SLOW_CLK_32K_XTAL); +#elif defined(CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_OSC) + select_rtc_slow_clk(SLOW_CLK_32K_EXT_OSC); +#elif defined(CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_8MD256) + select_rtc_slow_clk(SLOW_CLK_8MD256); #else select_rtc_slow_clk(RTC_SLOW_FREQ_RTC); #endif - uint32_t freq_mhz = CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ; - rtc_cpu_freq_t freq = RTC_CPU_FREQ_80M; - switch(freq_mhz) { - case 240: - freq = RTC_CPU_FREQ_240M; - break; - case 160: - freq = RTC_CPU_FREQ_160M; - break; - default: - freq_mhz = 80; - /* falls through */ - case 80: - freq = RTC_CPU_FREQ_80M; - break; - } + rtc_cpu_freq_config_t old_config, new_config; + rtc_clk_cpu_freq_get_config(&old_config); + const uint32_t old_freq_mhz = old_config.freq_mhz; + const uint32_t new_freq_mhz = CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ; + + bool res = rtc_clk_cpu_freq_mhz_to_config(new_freq_mhz, &new_config); + assert(res); // Wait for UART TX to finish, otherwise some UART output will be lost // when switching APB frequency uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM); - uint32_t freq_before = rtc_clk_cpu_freq_value(rtc_clk_cpu_freq_get()) / MHZ ; - - rtc_clk_cpu_freq_set(freq); + rtc_clk_cpu_freq_set_config(&new_config); // Re calculate the ccount to make time calculation correct. - uint32_t freq_after = CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ; - XTHAL_SET_CCOUNT( XTHAL_GET_CCOUNT() * freq_after / freq_before ); + XTHAL_SET_CCOUNT( XTHAL_GET_CCOUNT() * new_freq_mhz / old_freq_mhz ); } int IRAM_ATTR esp_clk_cpu_freq(void) { - return g_ticks_per_us_pro * 1000000; + return g_ticks_per_us_pro * MHZ; } int IRAM_ATTR esp_clk_apb_freq(void) { - return MIN(g_ticks_per_us_pro, 80) * 1000000; + return MIN(g_ticks_per_us_pro, 80) * MHZ; +} + +int IRAM_ATTR esp_clk_xtal_freq(void) +{ + return rtc_clk_xtal_freq_get() * MHZ; } void IRAM_ATTR ets_update_cpu_frequency(uint32_t ticks_per_us) @@ -124,16 +137,12 @@ void IRAM_ATTR ets_update_cpu_frequency(uint32_t ticks_per_us) g_ticks_per_us_app = ticks_per_us; } -static void select_rtc_slow_clk(rtc_slow_freq_t slow_clk) +static void select_rtc_slow_clk(slow_clk_sel_t slow_clk) { + rtc_slow_freq_t rtc_slow_freq = slow_clk & RTC_CNTL_ANA_CLK_RTC_SEL_V; uint32_t cal_val = 0; - uint32_t wait = 0; - uint32_t freq_hz = ((slow_clk == RTC_SLOW_FREQ_32K_XTAL) ? 32768 : 150000); - uint32_t warning_timeout = 3 /* sec */ * freq_hz /* Hz */ / (SLOW_CLK_CAL_CYCLES + 1); - warning_timeout = ((warning_timeout == 0) ? 3 /* sec */ : warning_timeout); - bool changing_clock_to_150k = false; do { - if (slow_clk == RTC_SLOW_FREQ_32K_XTAL) { + if (rtc_slow_freq == RTC_SLOW_FREQ_32K_XTAL) { /* 32k XTAL oscillator needs to be enabled and running before it can * be used. Hardware doesn't have a direct way of checking if the * oscillator is running. Here we use rtc_clk_cal function to count @@ -142,25 +151,23 @@ static void select_rtc_slow_clk(rtc_slow_freq_t slow_clk) * will time out, returning 0. */ ESP_EARLY_LOGD(TAG, "waiting for 32k oscillator to start up"); - rtc_clk_32k_enable(true); + if (slow_clk == SLOW_CLK_32K_XTAL) { + rtc_clk_32k_enable(true); + } else if (slow_clk == SLOW_CLK_32K_EXT_OSC) { + rtc_clk_32k_enable_external(); + } // When SLOW_CLK_CAL_CYCLES is set to 0, clock calibration will not be performed at startup. if (SLOW_CLK_CAL_CYCLES > 0) { cal_val = rtc_clk_cal(RTC_CAL_32K_XTAL, SLOW_CLK_CAL_CYCLES); if (cal_val == 0 || cal_val < 15000000L) { - ESP_EARLY_LOGE(TAG, "RTC: Not found External 32 kHz XTAL. Switching to Internal 150 kHz RC chain"); - slow_clk = RTC_SLOW_FREQ_RTC; - changing_clock_to_150k = true; + ESP_EARLY_LOGW(TAG, "32 kHz XTAL not found, switching to internal 150 kHz oscillator"); + rtc_slow_freq = RTC_SLOW_FREQ_RTC; } } + } else if (rtc_slow_freq == RTC_SLOW_FREQ_8MD256) { + rtc_clk_8m_enable(true, true); } - rtc_clk_slow_freq_set(slow_clk); - if (changing_clock_to_150k == true && wait > 1){ - // This helps when there are errors when switching the clock from External 32 kHz XTAL to Internal 150 kHz RC chain. - rtc_clk_32k_enable(false); - uint32_t min_bootstrap = 5; // Min bootstrapping for continue switching the clock. - rtc_clk_32k_bootstrap(min_bootstrap); - rtc_clk_32k_enable(true); - } + rtc_clk_slow_freq_set(rtc_slow_freq); if (SLOW_CLK_CAL_CYCLES > 0) { /* TODO: 32k XTAL oscillator has some frequency drift at startup. @@ -171,9 +178,6 @@ static void select_rtc_slow_clk(rtc_slow_freq_t slow_clk) const uint64_t cal_dividend = (1ULL << RTC_CLK_CAL_FRACT) * 1000000ULL; cal_val = (uint32_t) (cal_dividend / rtc_clk_slow_freq_get_hz()); } - if (++wait % warning_timeout == 0) { - ESP_EARLY_LOGW(TAG, "still waiting for source selection RTC"); - } } while (cal_val == 0); ESP_EARLY_LOGD(TAG, "RTC_SLOW_CLK calibration value: %d", cal_val); esp_clk_slowclk_cal_set(cal_val); diff --git a/components/esp32/esp_err_to_name.c b/components/esp32/esp_err_to_name.c index 7b74704e0..efa2370dd 100644 --- a/components/esp32/esp_err_to_name.c +++ b/components/esp32/esp_err_to_name.c @@ -125,11 +125,11 @@ static const esp_err_msg_t esp_err_msg_table[] = { ERR_TBL_IT(ESP_ERR_NVS_INVALID_HANDLE), /* 4359 0x1107 Handle has been closed or is NULL */ # endif # ifdef ESP_ERR_NVS_REMOVE_FAILED - ERR_TBL_IT(ESP_ERR_NVS_REMOVE_FAILED), /* 4360 0x1108 The value wasn’t updated because flash - write operation has failed. The value was - written however, and update will be finished - after re-initialization of nvs, provided - that flash operation doesn’t fail again. */ + ERR_TBL_IT(ESP_ERR_NVS_REMOVE_FAILED), /* 4360 0x1108 The value wasn’t updated because flash write + operation has failed. The value was written + however, and update will be finished after + re-initialization of nvs, provided that + flash operation doesn’t fail again. */ # endif # ifdef ESP_ERR_NVS_KEY_TOO_LONG ERR_TBL_IT(ESP_ERR_NVS_KEY_TOO_LONG), /* 4361 0x1109 Key name is too long */ diff --git a/components/esp32/esp_system_internal.h b/components/esp32/esp_system_internal.h new file mode 100644 index 000000000..5bda73fb7 --- /dev/null +++ b/components/esp32/esp_system_internal.h @@ -0,0 +1,56 @@ +// Copyright 2018 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 + +#include "esp_system.h" + +/** + * @brief Internal function to restart PRO and APP CPUs. + * + * @note This function should not be called from FreeRTOS applications. + * Use esp_restart instead. + * + * This is an internal function called by esp_restart. It is called directly + * by the panic handler and brownout detector interrupt. + */ +void esp_restart_noos() __attribute__ ((noreturn)); + +/** + * @brief Internal function to set reset reason hint + * + * The hint is used do distinguish different reset reasons when software reset + * is performed. + * + * The hint is stored in RTC store register, RTC_RESET_CAUSE_REG. + * + * @param hint Desired esp_reset_reason_t value for the real reset reason + */ +void esp_reset_reason_set_hint(esp_reset_reason_t hint); + +/** + * @brief Internal function to get the reset hint value + * @return - Reset hint value previously stored into RTC_RESET_CAUSE_REG using + * esp_reset_reason_set_hint function + * - ESP_RST_UNKNOWN if the value in RTC_RESET_CAUSE_REG is invalid + */ +esp_reset_reason_t esp_reset_reason_get_hint(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp32/include/esp32/pm.h b/components/esp32/include/esp32/pm.h index a7cbf0eac..f64045fb3 100644 --- a/components/esp32/include/esp32/pm.h +++ b/components/esp32/include/esp32/pm.h @@ -31,8 +31,10 @@ extern "C" { * Pass a pointer to this structure as an argument to esp_pm_configure function. */ typedef struct { - rtc_cpu_freq_t max_cpu_freq; /*!< Maximum CPU frequency to use */ - rtc_cpu_freq_t min_cpu_freq; /*!< Minimum CPU frequency to use when no frequency locks are taken */ + rtc_cpu_freq_t max_cpu_freq __attribute__((deprecated)); /*!< Maximum CPU frequency to use. Deprecated, use max_freq_mhz instead. */ + int max_freq_mhz; /*!< Maximum CPU frequency, in MHz */ + rtc_cpu_freq_t min_cpu_freq __attribute__((deprecated)); /*!< Minimum CPU frequency to use when no frequency locks are taken. Deprecated, use min_freq_mhz instead. */ + int min_freq_mhz; /*!< Minimum CPU frequency to use when no locks are taken, in MHz */ bool light_sleep_enable; /*!< Enter light sleep when no locks are taken */ } esp_pm_config_esp32_t; diff --git a/components/esp32/include/esp_clk.h b/components/esp32/include/esp_clk.h index 6526aa927..1a91d26f9 100644 --- a/components/esp32/include/esp_clk.h +++ b/components/esp32/include/esp_clk.h @@ -62,6 +62,17 @@ int esp_clk_cpu_freq(void); */ int esp_clk_apb_freq(void); +/** + * @brief Return frequency of the main XTAL + * + * Frequency of the main XTAL can be either auto-detected or set at compile + * time (see CONFIG_ESP32_XTAL_FREQ_SEL sdkconfig option). In both cases, this + * function returns the actual value at run time. + * + * @return XTAL frequency, in Hz + */ +int esp_clk_xtal_freq(void); + /** * @brief Read value of RTC counter, converting it to microseconds diff --git a/components/esp32/include/esp_mesh.h b/components/esp32/include/esp_mesh.h index 8fea167de..0adb59124 100644 --- a/components/esp32/include/esp_mesh.h +++ b/components/esp32/include/esp_mesh.h @@ -166,7 +166,7 @@ typedef enum { MESH_EVENT_ROUTING_TABLE_REMOVE, /**< routing table is changed by removing leave children */ MESH_EVENT_PARENT_CONNECTED, /**< parent is connected on station interface */ MESH_EVENT_PARENT_DISCONNECTED, /**< parent is disconnected on station interface */ - MESH_EVENT_NO_PARNET_FOUND, /**< no parent found */ + MESH_EVENT_NO_PARENT_FOUND, /**< no parent found */ MESH_EVENT_LAYER_CHANGE, /**< layer changes over the mesh network */ MESH_EVENT_TODS_STATE, /**< state represents if root is able to access external IP network */ MESH_EVENT_VOTE_STARTED, /**< the process of voting a new root is started either by children or by root */ @@ -771,9 +771,13 @@ esp_err_t esp_mesh_set_id(const mesh_addr_t *id); esp_err_t esp_mesh_get_id(mesh_addr_t *id); /** - * @brief set device type over the mesh network(Unimplemented) + * @brief specify device type over the mesh network + * - MESH_ROOT: designates the root node for a mesh network + * - MESH_LEAF: designates a device as a standalone Wi-Fi station * - * @param type device type + * @attention This API shall be called before esp_mesh_start(). + * + * @param type device type (only support MESH_ROOT, MESH_LEAF) * * @return * - ESP_OK @@ -782,9 +786,7 @@ esp_err_t esp_mesh_get_id(mesh_addr_t *id); esp_err_t esp_mesh_set_type(mesh_type_t type); /** - * @brief get device type over mesh network - * - * @attention This API shall be called after having received the event MESH_EVENT_PARENT_CONNECTED. + * @brief get device type over the mesh network * * @return mesh type * @@ -792,7 +794,7 @@ esp_err_t esp_mesh_set_type(mesh_type_t type); mesh_type_t esp_mesh_get_type(void); /** - * @brief set max layer configuration(max:15, default:15) + * @brief set max layer configuration(max:25, default:25) * * @attention This API shall be called before esp_mesh_start(). * @@ -1320,6 +1322,33 @@ esp_err_t esp_mesh_scan_get_ap_record(wifi_ap_record_t *ap_record, void *buffer) */ esp_err_t esp_mesh_flush_upstream_packets(void); +/** + * @brief get the number of nodes in the subnet of a specific child + * + * @param child_mac an associated child address of this device + * @param nodes_num pointer to the number of nodes in the subnet of a specific child + * + * @return + * - ESP_OK + * - ESP_ERR_MESH_NOT_START + * - ESP_ERR_MESH_ARGUMENT + */ +esp_err_t esp_mesh_get_subnet_nodes_num(const mesh_addr_t *child_mac, int *nodes_num); + +/** + * @brief get nodes in the subnet of a specific child + * + * @param child_mac an associated child address of this device + * @param nodes pointer to nodes in the subnet of a specific child + * @param nodes_num the number of nodes in the subnet of a specific child + * + * @return + * - ESP_OK + * - ESP_ERR_MESH_NOT_START + * - ESP_ERR_MESH_ARGUMENT + */ +esp_err_t esp_mesh_get_subnet_nodes_list(const mesh_addr_t *child_mac, mesh_addr_t *nodes, int nodes_num); + #ifdef __cplusplus } #endif diff --git a/components/esp32/include/esp_system.h b/components/esp32/include/esp_system.h index 0d57d84d8..b43dbebb8 100644 --- a/components/esp32/include/esp_system.h +++ b/components/esp32/include/esp_system.h @@ -31,12 +31,32 @@ typedef enum { ESP_MAC_ETH, } esp_mac_type_t; +/** @cond */ #define TWO_UNIVERSAL_MAC_ADDR 2 #define FOUR_UNIVERSAL_MAC_ADDR 4 #define UNIVERSAL_MAC_ADDR_NUM CONFIG_NUMBER_OF_UNIVERSAL_MAC_ADDRESS +/** @endcond */ /** - * @attention application don't need to call this function anymore. It do nothing and will + * @brief Reset reasons + */ +typedef enum { + ESP_RST_UNKNOWN, //!< Reset reason can not be determined + ESP_RST_POWERON, //!< Reset due to power-on event + ESP_RST_EXT, //!< Reset by external pin (not applicable for ESP32) + ESP_RST_SW, //!< Software reset via esp_restart + ESP_RST_PANIC, //!< Software reset due to exception/panic + ESP_RST_INT_WDT, //!< Reset (software or hardware) due to interrupt watchdog + ESP_RST_TASK_WDT, //!< Reset due to task watchdog + ESP_RST_WDT, //!< Reset due to other watchdogs + ESP_RST_DEEPSLEEP, //!< Reset after exiting deep sleep mode + ESP_RST_BROWNOUT, //!< Brownout reset (software or hardware) + ESP_RST_SDIO, //!< Reset over SDIO +} esp_reset_reason_t; + +/** @cond */ +/** + * @attention Applications don't need to call this function anymore. It does nothing and will * be removed in future version. */ void system_init(void) __attribute__ ((deprecated)); @@ -48,13 +68,18 @@ void system_init(void) __attribute__ ((deprecated)); * This name will be removed in a future release. */ void system_restore(void) __attribute__ ((deprecated)); +/** @endcond */ +/** + * Shutdown handler type + */ typedef void (*shutdown_handler_t)(void); + /** * @brief Register shutdown handler * - * This function allows you to register a handler that gets invoked before a - * systematic shutdown of the chip. + * This function allows you to register a handler that gets invoked before + * the application is restarted using esp_restart function. */ esp_err_t esp_register_shutdown_handler(shutdown_handler_t handle); @@ -68,17 +93,7 @@ esp_err_t esp_register_shutdown_handler(shutdown_handler_t handle); */ void esp_restart(void) __attribute__ ((noreturn)); -/** - * @brief Internal function to restart PRO and APP CPUs. - * - * @note This function should not be called from FreeRTOS applications. - * Use esp_restart instead. - * - * This is an internal function called by esp_restart. It is called directly - * by the panic handler and brownout detector interrupt. - */ -void esp_restart_noos() __attribute__ ((noreturn)); - +/** @cond */ /** * @brief Restart system. * @@ -86,7 +101,15 @@ void esp_restart_noos() __attribute__ ((noreturn)); * This name will be removed in a future release. */ void system_restart(void) __attribute__ ((deprecated, noreturn)); +/** @endcond */ +/** + * @brief Get reason of last reset + * @return See description of esp_reset_reason_t for explanation of each value. + */ +esp_reset_reason_t esp_reset_reason(void); + +/** @cond */ /** * @brief Get system time, unit: microsecond. * @@ -94,6 +117,7 @@ void system_restart(void) __attribute__ ((deprecated, noreturn)); * This definition will be removed in a future release. */ uint32_t system_get_time(void) __attribute__ ((deprecated)); +/** @endcond */ /** * @brief Get the size of available heap. @@ -105,6 +129,7 @@ uint32_t system_get_time(void) __attribute__ ((deprecated)); */ uint32_t esp_get_free_heap_size(void); +/** @cond */ /** * @brief Get the size of available heap. * @@ -114,6 +139,7 @@ uint32_t esp_get_free_heap_size(void); * @return Available heap size, in bytes. */ uint32_t system_get_free_heap_size(void) __attribute__ ((deprecated)); +/** @endcond */ /** * @brief Get the minimum heap that has ever been available @@ -187,6 +213,7 @@ esp_err_t esp_efuse_mac_get_custom(uint8_t *mac); */ esp_err_t esp_efuse_mac_get_default(uint8_t *mac); +/** @cond */ /** * @brief Read hardware MAC address from efuse. * @@ -209,6 +236,7 @@ esp_err_t esp_efuse_read_mac(uint8_t *mac) __attribute__ ((deprecated)); * @return ESP_OK on success */ esp_err_t system_efuse_read_mac(uint8_t *mac) __attribute__ ((deprecated)); +/** @endcond */ /** * @brief Read base MAC address and set MAC address of the interface. @@ -240,6 +268,7 @@ esp_err_t esp_read_mac(uint8_t* mac, esp_mac_type_t type); */ esp_err_t esp_derive_local_mac(uint8_t* local_mac, const uint8_t* universal_mac); +/** @cond */ /** * Get SDK version * @@ -248,6 +277,7 @@ esp_err_t esp_derive_local_mac(uint8_t* local_mac, const uint8_t* universal_mac) * @return constant string "master" */ const char* system_get_sdk_version(void) __attribute__ ((deprecated)); +/** @endcond */ /** * Get IDF version @@ -264,13 +294,11 @@ typedef enum { CHIP_ESP32 = 1, //!< ESP32 } esp_chip_model_t; -/** - * Chip feature flags, used in esp_chip_info_t - */ -#define CHIP_FEATURE_EMB_FLASH BIT(0) -#define CHIP_FEATURE_WIFI_BGN BIT(1) -#define CHIP_FEATURE_BLE BIT(4) -#define CHIP_FEATURE_BT BIT(5) +/* Chip feature flags, used in esp_chip_info_t */ +#define CHIP_FEATURE_EMB_FLASH BIT(0) //!< Chip has embedded flash memory +#define CHIP_FEATURE_WIFI_BGN BIT(1) //!< Chip has 2.4GHz WiFi +#define CHIP_FEATURE_BLE BIT(4) //!< Chip has Bluetooth LE +#define CHIP_FEATURE_BT BIT(5) //!< Chip has Bluetooth Classic /** * @brief The structure represents information about the chip diff --git a/components/esp32/include/esp_wifi.h b/components/esp32/include/esp_wifi.h index d4c11c387..04b999803 100644 --- a/components/esp32/include/esp_wifi.h +++ b/components/esp32/include/esp_wifi.h @@ -303,7 +303,13 @@ esp_err_t esp_wifi_restore(void); * * @attention 1. This API only impact WIFI_MODE_STA or WIFI_MODE_APSTA mode * @attention 2. If the ESP32 is connected to an AP, call esp_wifi_disconnect to disconnect. - * + * @attention 3. The scanning triggered by esp_wifi_start_scan() will not be effective until connection between ESP32 and the AP is established. + * If ESP32 is scanning and connecting at the same time, ESP32 will abort scanning and return a warning message and error + * number ESP_ERR_WIFI_STATE. + * If you want to do reconnection after ESP32 received disconnect event, remember to add the maximum retry time, otherwise the called + * scan will not work. This is especially true when the AP doesn't exist, and you still try reconnection after ESP32 received disconnect + * event with the reason code WIFI_REASON_NO_AP_FOUND. + * * @return * - ESP_OK: succeed * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by esp_wifi_init @@ -366,6 +372,7 @@ esp_err_t esp_wifi_deauth_sta(uint16_t aid); * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by esp_wifi_init * - ESP_ERR_WIFI_NOT_STARTED: WiFi was not started by esp_wifi_start * - ESP_ERR_WIFI_TIMEOUT: blocking scan is timeout + * - ESP_ERR_WIFI_STATE: wifi still connecting when invoke esp_wifi_scan_start * - others: refer to error code in esp_err.h */ esp_err_t esp_wifi_scan_start(const wifi_scan_config_t *config, bool block); diff --git a/components/esp32/include/rom/rtc.h b/components/esp32/include/rom/rtc.h index 08d8ace09..9ebd1020b 100644 --- a/components/esp32/include/rom/rtc.h +++ b/components/esp32/include/rom/rtc.h @@ -68,6 +68,7 @@ extern "C" { #define RTC_XTAL_FREQ_REG RTC_CNTL_STORE4_REG #define RTC_APB_FREQ_REG RTC_CNTL_STORE5_REG #define RTC_ENTRY_ADDR_REG RTC_CNTL_STORE6_REG +#define RTC_RESET_CAUSE_REG RTC_CNTL_STORE6_REG #define RTC_MEMORY_CRC_REG RTC_CNTL_STORE7_REG diff --git a/components/esp32/ld/esp32.common.ld b/components/esp32/ld/esp32.common.ld index 71ae22095..cf1a3a7a9 100644 --- a/components/esp32/ld/esp32.common.ld +++ b/components/esp32/ld/esp32.common.ld @@ -32,6 +32,7 @@ SECTIONS _rtc_bss_start = ABSOLUTE(.); *rtc_wake_stub*.*(.bss .bss.*) *rtc_wake_stub*.*(COMMON) + *(.rtc.bss) _rtc_bss_end = ABSOLUTE(.); } > rtc_slow_seg diff --git a/components/esp32/ld/esp32.rom.ld b/components/esp32/ld/esp32.rom.ld index fdeda525f..82007fec4 100644 --- a/components/esp32/ld/esp32.rom.ld +++ b/components/esp32/ld/esp32.rom.ld @@ -176,6 +176,7 @@ PROVIDE ( lld_evt_elt_wait_get = 0x400468e4 ); PROVIDE ( lld_evt_get_next_free_slot = 0x4004692c ); PROVIDE ( lld_pdu_adv_pk_desc_tab = 0x3ff98c70 ); PROVIDE ( lld_pdu_llcp_pk_desc_tab = 0x3ff98b68 ); +PROVIDE ( lld_pdu_tx_flush_list = 0x4004a760 ); PROVIDE ( lld_pdu_pack = 0x4004ab14 ); PROVIDE ( LLM_AA_CT1 = 0x3ff98d8a ); PROVIDE ( LLM_AA_CT2 = 0x3ff98d88 ); @@ -1620,6 +1621,7 @@ PROVIDE ( hci_tl_env = 0x3ffb8154 ); PROVIDE ( ld_acl_env = 0x3ffb8258 ); PROVIDE ( ea_env = 0x3ffb80ec ); PROVIDE ( lc_sco_data_path_config = 0x3ffb81f8 ); +PROVIDE ( lc_sco_env = 0x3ffb81fc ); PROVIDE ( ld_active_ch_map = 0x3ffb8334 ); PROVIDE ( ld_bcst_acl_env = 0x3ffb8274 ); PROVIDE ( ld_csb_rx_env = 0x3ffb8278 ); diff --git a/components/esp32/lib b/components/esp32/lib index 3a57e7198..4c69c1ad8 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit 3a57e719887cfa3f72abc27e7d3102800cd7eb65 +Subproject commit 4c69c1ad8da7a9cbe8e27598b8c91780ac0b5068 diff --git a/components/esp32/panic.c b/components/esp32/panic.c index 5a000574b..6217c65e8 100644 --- a/components/esp32/panic.c +++ b/components/esp32/panic.c @@ -40,7 +40,7 @@ #include "esp_spi_flash.h" #include "esp_cache_err_int.h" #include "esp_app_trace.h" -#include "esp_system.h" +#include "esp_system_internal.h" #include "sdkconfig.h" #if CONFIG_SYSVIEW_ENABLE #include "SEGGER_RTT.h" @@ -121,6 +121,20 @@ void __attribute__((weak)) vApplicationStackOverflowHook( TaskHandle_t xTask, s abort(); } +/* These two weak stubs for esp_reset_reason_{get,set}_hint are used when + * the application does not call esp_reset_reason() function, and + * reset_reason.c is not linked into the output file. + */ +void __attribute__((weak)) esp_reset_reason_set_hint(esp_reset_reason_t hint) +{ +} + +esp_reset_reason_t __attribute__((weak)) esp_reset_reason_get_hint(void) +{ + return ESP_RST_UNKNOWN; +} + + static bool abort_called; static __attribute__((noreturn)) inline void invoke_abort() @@ -147,6 +161,12 @@ void abort() #if !CONFIG_ESP32_PANIC_SILENT_REBOOT ets_printf("abort() was called at PC 0x%08x on core %d\r\n", (intptr_t)__builtin_return_address(0) - 3, xPortGetCoreID()); #endif + /* Calling code might have set other reset reason hint (such as Task WDT), + * don't overwrite that. + */ + if (esp_reset_reason_get_hint() == ESP_RST_UNKNOWN) { + esp_reset_reason_set_hint(ESP_RST_PANIC); + } invoke_abort(); } @@ -234,6 +254,10 @@ void panicHandler(XtExcFrame *frame) } #endif //!CONFIG_FREERTOS_UNICORE + if (frame->exccause == PANIC_RSN_INTWDT_CPU0 || frame->exccause == PANIC_RSN_INTWDT_CPU1) { + esp_reset_reason_set_hint(ESP_RST_INT_WDT); + } + haltOtherCore(); esp_dport_access_int_abort(); panicPutStr("Guru Meditation Error: Core "); @@ -333,6 +357,7 @@ void xt_unhandled_exception(XtExcFrame *frame) return; } panicPutStr(". Exception was unhandled.\r\n"); + esp_reset_reason_set_hint(ESP_RST_PANIC); } commonErrorHandler(frame); } @@ -382,7 +407,7 @@ static void esp_panic_dig_reset() // make sure all the panic handler output is sent from UART FIFO uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM); // switch to XTAL (otherwise we will keep running from the PLL) - rtc_clk_cpu_freq_set(RTC_CPU_FREQ_XTAL); + rtc_clk_cpu_freq_set_xtal(); // reset the digital part esp_cpu_unstall(PRO_CPU_NUM); SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_SYS_RST); diff --git a/components/esp32/pm_esp32.c b/components/esp32/pm_esp32.c index f75b3fe81..5d77dd8db 100644 --- a/components/esp32/pm_esp32.c +++ b/components/esp32/pm_esp32.c @@ -51,6 +51,11 @@ */ #define LIGHT_SLEEP_EARLY_WAKEUP_US 100 +/* Minimal divider at which REF_CLK_FREQ can be obtained */ +#define REF_CLK_DIV_MIN 10 + +#define MHZ 1000000 + #ifdef CONFIG_PM_PROFILING #define WITH_PROFILING #endif @@ -80,44 +85,20 @@ static uint32_t s_ccount_mul; */ static volatile bool s_need_update_ccompare[portNUM_PROCESSORS]; -/* When no RTOS tasks are active, these locks are released to allow going into - * a lower power mode. Used by ISR hook and idle hook. - */ -static esp_pm_lock_handle_t s_rtos_lock_handle[portNUM_PROCESSORS]; - /* A flag indicating that Idle hook has run on a given CPU; * Next interrupt on the same CPU will take s_rtos_lock_handle. */ static bool s_core_idle[portNUM_PROCESSORS]; -/* g_ticks_us defined in ROM for PRO CPU */ -extern uint32_t g_ticks_per_us_pro; +/* When no RTOS tasks are active, these locks are released to allow going into + * a lower power mode. Used by ISR hook and idle hook. + */ +static esp_pm_lock_handle_t s_rtos_lock_handle[portNUM_PROCESSORS]; -/* Lookup table of CPU frequencies to be used in each mode. +/* Lookup table of CPU frequency configs to be used in each mode. * Initialized by esp_pm_impl_init and modified by esp_pm_configure. */ -rtc_cpu_freq_t s_cpu_freq_by_mode[PM_MODE_COUNT]; - -/* Lookup table of CPU ticks per microsecond for each RTC_CPU_FREQ_ value. - * Essentially the same as returned by rtc_clk_cpu_freq_value(), but without - * the function call. Not const because XTAL frequency is only known at run time. - */ -static uint32_t s_cpu_freq_to_ticks[] = { - [RTC_CPU_FREQ_XTAL] = 0, /* This is set by esp_pm_impl_init */ - [RTC_CPU_FREQ_80M] = 80, - [RTC_CPU_FREQ_160M] = 160, - [RTC_CPU_FREQ_240M] = 240, - [RTC_CPU_FREQ_2M] = 2 -}; - -/* Lookup table of names for each RTC_CPU_FREQ_ value. Used for logging only. */ -static const char* s_freq_names[] __attribute__((unused)) = { - [RTC_CPU_FREQ_XTAL] = "XTAL", - [RTC_CPU_FREQ_80M] = "80", - [RTC_CPU_FREQ_160M] = "160", - [RTC_CPU_FREQ_240M] = "240", - [RTC_CPU_FREQ_2M] = "2" -}; +rtc_cpu_freq_config_t s_cpu_freq_by_mode[PM_MODE_COUNT]; /* Whether automatic light sleep is enabled */ static bool s_light_sleep_en = false; @@ -167,21 +148,6 @@ pm_mode_t esp_pm_impl_get_mode(esp_pm_lock_type_t type, int arg) } } -/* rtc_cpu_freq_t enum is not ordered by frequency, so convert to MHz, - * figure out the maximum value, then convert back to rtc_cpu_freq_t. - */ -static rtc_cpu_freq_t max_freq_of(rtc_cpu_freq_t f1, rtc_cpu_freq_t f2) -{ - int f1_hz = rtc_clk_cpu_freq_value(f1); - int f2_hz = rtc_clk_cpu_freq_value(f2); - int f_max_hz = MAX(f1_hz, f2_hz); - rtc_cpu_freq_t result = RTC_CPU_FREQ_XTAL; - if (!rtc_clk_cpu_freq_from_mhz(f_max_hz/1000000, &result)) { - assert(false && "unsupported frequency"); - } - return result; -} - esp_err_t esp_pm_configure(const void* vconfig) { #ifndef CONFIG_PM_ENABLE @@ -195,46 +161,66 @@ esp_err_t esp_pm_configure(const void* vconfig) } #endif - if (config->min_cpu_freq == RTC_CPU_FREQ_2M) { - /* Minimal APB frequency to achieve 1MHz REF_TICK frequency is 5 MHz */ - return ESP_ERR_NOT_SUPPORTED; + int min_freq_mhz = config->min_freq_mhz; + int max_freq_mhz = config->max_freq_mhz; + + if (min_freq_mhz == 0 && max_freq_mhz == 0) { + /* For compatibility, handle deprecated fields, min_cpu_freq and max_cpu_freq. */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + min_freq_mhz = rtc_clk_cpu_freq_value(config->min_cpu_freq) / MHZ; + max_freq_mhz = rtc_clk_cpu_freq_value(config->max_cpu_freq) / MHZ; +#pragma GCC diagnostic pop } - rtc_cpu_freq_t min_freq = config->min_cpu_freq; - rtc_cpu_freq_t max_freq = config->max_cpu_freq; - int min_freq_mhz = rtc_clk_cpu_freq_value(min_freq); - int max_freq_mhz = rtc_clk_cpu_freq_value(max_freq); if (min_freq_mhz > max_freq_mhz) { return ESP_ERR_INVALID_ARG; } - rtc_cpu_freq_t apb_max_freq = max_freq; /* CPU frequency in APB_MAX mode */ - if (max_freq == RTC_CPU_FREQ_240M) { + rtc_cpu_freq_config_t freq_config; + if (!rtc_clk_cpu_freq_mhz_to_config(min_freq_mhz, &freq_config)) { + ESP_LOGW(TAG, "invalid min_freq_mhz value (%d)", min_freq_mhz); + return ESP_ERR_INVALID_ARG; + } + + int xtal_freq_mhz = (int) rtc_clk_xtal_freq_get(); + if (min_freq_mhz < xtal_freq_mhz && min_freq_mhz * MHZ / REF_CLK_FREQ < REF_CLK_DIV_MIN) { + ESP_LOGW(TAG, "min_freq_mhz should be >= %d", REF_CLK_FREQ * REF_CLK_DIV_MIN / MHZ); + return ESP_ERR_INVALID_ARG; + } + + if (!rtc_clk_cpu_freq_mhz_to_config(max_freq_mhz, &freq_config)) { + ESP_LOGW(TAG, "invalid max_freq_mhz value (%d)", max_freq_mhz); + return ESP_ERR_INVALID_ARG; + } + + int apb_max_freq = max_freq_mhz; /* CPU frequency in APB_MAX mode */ + if (max_freq_mhz == 240) { /* We can't switch between 240 and 80/160 without disabling PLL, * so use 240MHz CPU frequency when 80MHz APB frequency is requested. */ - apb_max_freq = RTC_CPU_FREQ_240M; - } else if (max_freq == RTC_CPU_FREQ_160M || max_freq == RTC_CPU_FREQ_80M) { + apb_max_freq = 240; + } else if (max_freq_mhz == 160 || max_freq_mhz == 80) { /* Otherwise, can use 80MHz * CPU frequency when 80MHz APB frequency is requested. */ - apb_max_freq = RTC_CPU_FREQ_80M; + apb_max_freq = 80; } - apb_max_freq = max_freq_of(apb_max_freq, min_freq); + apb_max_freq = MAX(apb_max_freq, min_freq_mhz); ESP_LOGI(TAG, "Frequency switching config: " - "CPU_MAX: %s, APB_MAX: %s, APB_MIN: %s, Light sleep: %s", - s_freq_names[max_freq], - s_freq_names[apb_max_freq], - s_freq_names[min_freq], + "CPU_MAX: %d, APB_MAX: %d, APB_MIN: %d, Light sleep: %s", + max_freq_mhz, + apb_max_freq, + min_freq_mhz, config->light_sleep_enable ? "ENABLED" : "DISABLED"); portENTER_CRITICAL(&s_switch_lock); - s_cpu_freq_by_mode[PM_MODE_CPU_MAX] = max_freq; - s_cpu_freq_by_mode[PM_MODE_APB_MAX] = apb_max_freq; - s_cpu_freq_by_mode[PM_MODE_APB_MIN] = min_freq; - s_cpu_freq_by_mode[PM_MODE_LIGHT_SLEEP] = min_freq; + rtc_clk_cpu_freq_mhz_to_config(max_freq_mhz, &s_cpu_freq_by_mode[PM_MODE_CPU_MAX]); + rtc_clk_cpu_freq_mhz_to_config(apb_max_freq, &s_cpu_freq_by_mode[PM_MODE_APB_MAX]); + rtc_clk_cpu_freq_mhz_to_config(min_freq_mhz, &s_cpu_freq_by_mode[PM_MODE_APB_MIN]); + s_cpu_freq_by_mode[PM_MODE_LIGHT_SLEEP] = s_cpu_freq_by_mode[PM_MODE_APB_MIN]; s_light_sleep_en = config->light_sleep_enable; s_config_changed = true; portEXIT_CRITICAL(&s_switch_lock); @@ -310,7 +296,7 @@ static void IRAM_ATTR on_freq_update(uint32_t old_ticks_per_us, uint32_t ticks_p } /* Calculate new tick divisor */ - _xt_tick_divisor = ticks_per_us * 1000000 / XT_TICK_PER_SEC; + _xt_tick_divisor = ticks_per_us * MHZ / XT_TICK_PER_SEC; int core_id = xPortGetCoreID(); if (s_rtos_lock_handle[core_id] != NULL) { @@ -375,17 +361,18 @@ static void IRAM_ATTR do_switch(pm_mode_t new_mode) s_config_changed = false; portEXIT_CRITICAL_ISR(&s_switch_lock); - rtc_cpu_freq_t new_freq = s_cpu_freq_by_mode[new_mode]; - rtc_cpu_freq_t old_freq; + rtc_cpu_freq_config_t new_config = s_cpu_freq_by_mode[new_mode]; + rtc_cpu_freq_config_t old_config; + if (!config_changed) { - old_freq = s_cpu_freq_by_mode[s_mode]; + old_config = s_cpu_freq_by_mode[s_mode]; } else { - old_freq = rtc_clk_cpu_freq_get(); + rtc_clk_cpu_freq_get_config(&old_config); } - if (new_freq != old_freq) { - uint32_t old_ticks_per_us = g_ticks_per_us_pro; - uint32_t new_ticks_per_us = s_cpu_freq_to_ticks[new_freq]; + if (new_config.freq_mhz != old_config.freq_mhz) { + uint32_t old_ticks_per_us = old_config.freq_mhz; + uint32_t new_ticks_per_us = new_config.freq_mhz; bool switch_down = new_ticks_per_us < old_ticks_per_us; @@ -393,7 +380,7 @@ static void IRAM_ATTR do_switch(pm_mode_t new_mode) if (switch_down) { on_freq_update(old_ticks_per_us, new_ticks_per_us); } - rtc_clk_cpu_freq_set_fast(new_freq); + rtc_clk_cpu_freq_set_config_fast(&new_config); if (!switch_down) { on_freq_update(old_ticks_per_us, new_ticks_per_us); } @@ -536,9 +523,9 @@ void esp_pm_impl_dump_stats(FILE* out) /* don't display light sleep mode if it's not enabled */ continue; } - fprintf(out, "%8s %6s %12lld %2d%%\n", + fprintf(out, "%8s %3dM %12lld %2d%%\n", s_mode_names[i], - s_freq_names[s_cpu_freq_by_mode[i]], + s_cpu_freq_by_mode[i].freq_mhz, time_in_mode[i], (int) (time_in_mode[i] * 100 / now)); } @@ -547,7 +534,6 @@ void esp_pm_impl_dump_stats(FILE* out) void esp_pm_impl_init() { - s_cpu_freq_to_ticks[RTC_CPU_FREQ_XTAL] = rtc_clk_xtal_freq_get(); #ifdef CONFIG_PM_TRACE esp_pm_trace_init(); #endif @@ -563,11 +549,11 @@ void esp_pm_impl_init() /* Configure all modes to use the default CPU frequency. * This will be modified later by a call to esp_pm_configure. */ - rtc_cpu_freq_t default_freq; - if (!rtc_clk_cpu_freq_from_mhz(CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ, &default_freq)) { + rtc_cpu_freq_config_t default_config; + if (!rtc_clk_cpu_freq_mhz_to_config(CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ, &default_config)) { assert(false && "unsupported frequency"); } for (size_t i = 0; i < PM_MODE_COUNT; ++i) { - s_cpu_freq_by_mode[i] = default_freq; + s_cpu_freq_by_mode[i] = default_config; } } diff --git a/components/esp32/reset_reason.c b/components/esp32/reset_reason.c new file mode 100644 index 000000000..b43bc8bc7 --- /dev/null +++ b/components/esp32/reset_reason.c @@ -0,0 +1,126 @@ +// Copyright 2018 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "esp_system.h" +#include "esp_system_internal.h" +#include "rom/rtc.h" +#include "soc/rtc_cntl_reg.h" + +static void esp_reset_reason_clear_hint(); + +static esp_reset_reason_t s_reset_reason; + +static esp_reset_reason_t get_reset_reason(RESET_REASON rtc_reset_reason, esp_reset_reason_t reset_reason_hint) +{ + switch (rtc_reset_reason) { + case POWERON_RESET: + return ESP_RST_POWERON; + + /* For ESP32, ESP_RST_EXT is never returned */ + + + case SW_CPU_RESET: + case SW_RESET: + case EXT_CPU_RESET: /* unused */ + if (reset_reason_hint == ESP_RST_PANIC || + reset_reason_hint == ESP_RST_BROWNOUT || + reset_reason_hint == ESP_RST_TASK_WDT || + reset_reason_hint == ESP_RST_INT_WDT) { + return reset_reason_hint; + } + return ESP_RST_SW; + + case DEEPSLEEP_RESET: + return ESP_RST_DEEPSLEEP; + + case TG0WDT_SYS_RESET: + return ESP_RST_TASK_WDT; + + case TG1WDT_SYS_RESET: + return ESP_RST_INT_WDT; + + case OWDT_RESET: + case RTCWDT_SYS_RESET: + case RTCWDT_RTC_RESET: + case RTCWDT_CPU_RESET: /* unused */ + case TGWDT_CPU_RESET: /* unused */ + return ESP_RST_WDT; + + case RTCWDT_BROWN_OUT_RESET: /* unused */ + return ESP_RST_BROWNOUT; + + case SDIO_RESET: + return ESP_RST_SDIO; + + case INTRUSION_RESET: /* unused */ + default: + return ESP_RST_UNKNOWN; + } +} + +static void __attribute__((constructor)) esp_reset_reason_init(void) +{ + esp_reset_reason_t hint = esp_reset_reason_get_hint(); + s_reset_reason = get_reset_reason(rtc_get_reset_reason(PRO_CPU_NUM), + hint); + if (hint != ESP_RST_UNKNOWN) { + esp_reset_reason_clear_hint(); + } +} + +esp_reset_reason_t esp_reset_reason(void) +{ + return s_reset_reason; +} + +/* Reset reason hint is stored in RTC_RESET_CAUSE_REG, a.k.a. RTC_CNTL_STORE6_REG, + * a.k.a. RTC_ENTRY_ADDR_REG. It is safe to use this register both for the + * deep sleep wake stub entry address and for reset reason hint, since wake stub + * is only used for deep sleep reset, and in this case the reason provided by + * rtc_get_reset_reason is unambiguous. + * + * Same layout is used as for RTC_APB_FREQ_REG (a.k.a. RTC_CNTL_STORE5_REG): + * the value is replicated in low and high half-words. In addition to that, + * MSB is set to 1, which doesn't happen when RTC_CNTL_STORE6_REG contains + * deep sleep wake stub address. + */ + +#define RST_REASON_BIT 0x80000000 +#define RST_REASON_MASK 0x7FFF +#define RST_REASON_SHIFT 16 + +/* in IRAM, can be called from panic handler */ +void IRAM_ATTR esp_reset_reason_set_hint(esp_reset_reason_t hint) +{ + assert((hint & (~RST_REASON_MASK)) == 0); + uint32_t val = hint | (hint << RST_REASON_SHIFT) | RST_REASON_BIT; + REG_WRITE(RTC_RESET_CAUSE_REG, val); +} + +/* in IRAM, can be called from panic handler */ +esp_reset_reason_t IRAM_ATTR esp_reset_reason_get_hint(void) +{ + uint32_t reset_reason_hint = REG_READ(RTC_RESET_CAUSE_REG); + uint32_t high = (reset_reason_hint >> RST_REASON_SHIFT) & RST_REASON_MASK; + uint32_t low = reset_reason_hint & RST_REASON_MASK; + if ((reset_reason_hint & RST_REASON_BIT) == 0 || high != low) { + return ESP_RST_UNKNOWN; + } + return (esp_reset_reason_t) low; +} +static void esp_reset_reason_clear_hint() +{ + REG_WRITE(RTC_RESET_CAUSE_REG, 0); +} + diff --git a/components/esp32/sleep_modes.c b/components/esp32/sleep_modes.c index 91713ad6b..ac9ea70dc 100644 --- a/components/esp32/sleep_modes.c +++ b/components/esp32/sleep_modes.c @@ -103,11 +103,14 @@ esp_deep_sleep_wake_stub_fn_t esp_get_deep_sleep_wake_stub(void) REG_WRITE(RTC_MEMORY_CRC_REG, stored_crc); _lock_release(&lock_rtc_memory_crc); - if(stored_crc == calc_crc) { - return (esp_deep_sleep_wake_stub_fn_t)REG_READ(RTC_ENTRY_ADDR_REG); - } else { + if(stored_crc != calc_crc) { return NULL; } + esp_deep_sleep_wake_stub_fn_t stub_ptr = (esp_deep_sleep_wake_stub_fn_t) REG_READ(RTC_ENTRY_ADDR_REG); + if (!esp_ptr_executable(stub_ptr)) { + return NULL; + } + return stub_ptr; } void esp_set_deep_sleep_wake_stub(esp_deep_sleep_wake_stub_fn_t new_stub) @@ -178,8 +181,9 @@ static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags) } // Save current frequency and switch to XTAL - rtc_cpu_freq_t cpu_freq = rtc_clk_cpu_freq_get(); - rtc_clk_cpu_freq_set(RTC_CPU_FREQ_XTAL); + rtc_cpu_freq_config_t cpu_freq_config; + rtc_clk_cpu_freq_get_config(&cpu_freq_config); + rtc_clk_cpu_freq_set_xtal(); // Configure pins for external wakeup if (s_config.wakeup_triggers & RTC_EXT0_TRIG_EN) { @@ -205,7 +209,7 @@ static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags) uint32_t result = rtc_sleep_start(s_config.wakeup_triggers, 0); // Restore CPU frequency - rtc_clk_cpu_freq_set(cpu_freq); + rtc_clk_cpu_freq_set_config(&cpu_freq_config); // re-enable UART output resume_uarts(); diff --git a/components/esp32/system_api.c b/components/esp32/system_api.c index f56bf622e..5c80c8b7a 100644 --- a/components/esp32/system_api.c +++ b/components/esp32/system_api.c @@ -36,6 +36,7 @@ #include "freertos/task.h" #include "freertos/xtensa_api.h" #include "esp_heap_caps.h" +#include "esp_system_internal.h" static const char* TAG = "system_api"; @@ -333,7 +334,7 @@ void IRAM_ATTR esp_restart_noos() DPORT_REG_WRITE(DPORT_PERIP_RST_EN_REG, 0); // Set CPU back to XTAL source, no PLL, same as hard reset - rtc_clk_cpu_freq_set(RTC_CPU_FREQ_XTAL); + rtc_clk_cpu_freq_set_xtal(); // Clear entry point for APP CPU DPORT_REG_WRITE(DPORT_APPCPU_CTRL_D_REG, 0); diff --git a/components/esp32/task_wdt.c b/components/esp32/task_wdt.c index 0e0b87c65..fb10aac05 100644 --- a/components/esp32/task_wdt.c +++ b/components/esp32/task_wdt.c @@ -35,6 +35,7 @@ #include "driver/timer.h" #include "driver/periph_ctrl.h" #include "esp_task_wdt.h" +#include "esp_system_internal.h" //Assertion macro where, if 'cond' is false, will exit the critical section and return 'ret' #define ASSERT_EXIT_CRIT_RETURN(cond, ret) ({ \ @@ -155,6 +156,7 @@ static void task_wdt_isr(void *arg) if (twdt_config->panic){ //Trigger Panic if configured to do so ets_printf("Aborting.\n"); portEXIT_CRITICAL(&twdt_spinlock); + esp_reset_reason_set_hint(ESP_RST_TASK_WDT); abort(); } diff --git a/components/esp32/test/test_attr.c b/components/esp32/test/test_attr.c new file mode 100644 index 000000000..d6560b3cc --- /dev/null +++ b/components/esp32/test/test_attr.c @@ -0,0 +1,27 @@ +#include "unity.h" +#include "esp_attr.h" +#include "esp_log.h" + +static __NOINIT_ATTR uint32_t s_noinit; +static RTC_NOINIT_ATTR uint32_t s_rtc_noinit; +static RTC_DATA_ATTR uint32_t s_rtc_data; + +extern int _rtc_noinit_start; +extern int _rtc_noinit_end; +extern int _rtc_data_start; +extern int _rtc_data_end; +extern int _noinit_start; +extern int _noinit_end; + +static bool data_in_segment(void *ptr, int *seg_start, int *seg_end) +{ + return ((intptr_t)ptr < (intptr_t)seg_end) && \ + ((intptr_t)ptr >= (intptr_t)seg_start); +} + +TEST_CASE("Attributes place variables into correct sections", "[ld]") +{ + TEST_ASSERT(data_in_segment(&s_noinit, &_noinit_start, &_noinit_end)); + TEST_ASSERT(data_in_segment(&s_rtc_noinit, &_rtc_noinit_start, &_rtc_noinit_end)); + TEST_ASSERT(data_in_segment(&s_rtc_data, &_rtc_data_start, &_rtc_data_end)); +} diff --git a/components/esp32/test/test_dport.c b/components/esp32/test/test_dport.c index 6b5960bf4..96fa0b109 100644 --- a/components/esp32/test/test_dport.c +++ b/components/esp32/test/test_dport.c @@ -1,11 +1,12 @@ - -#include #include #include +#include "esp_types.h" +#include "esp_clk.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/semphr.h" +#include "freertos/xtensa_timer.h" #include "soc/cpu.h" #include "unity.h" #include "rom/uart.h" @@ -99,49 +100,52 @@ TEST_CASE("access DPORT and APB at same time", "[esp32]") { dport_test_result = false; apb_test_result = false; - printf("CPU_FREQ = %d MHz\n", rtc_clk_cpu_freq_value(rtc_clk_cpu_freq_get()) / MHZ); + printf("CPU_FREQ = %d MHz\n", esp_clk_cpu_freq()); run_tasks("accessDPORT", accessDPORT, "accessAPB", accessAPB, 10000); } -void run_tasks_with_change_freq_cpu (rtc_cpu_freq_t cpu_freq) +void run_tasks_with_change_freq_cpu(int cpu_freq_mhz) { - dport_test_result = false; - apb_test_result = false; - rtc_cpu_freq_t cur_freq = rtc_clk_cpu_freq_get(); - uint32_t freq_before_changed = rtc_clk_cpu_freq_value(cur_freq) / MHZ; - uint32_t freq_changed = freq_before_changed; - printf("CPU_FREQ = %d MHz\n", freq_before_changed); - - if (cur_freq != cpu_freq) { - uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM); - - rtc_clk_cpu_freq_set(cpu_freq); - - const int uart_num = CONFIG_CONSOLE_UART_NUM; - const int uart_baud = CONFIG_CONSOLE_UART_BAUDRATE; - uart_div_modify(uart_num, (rtc_clk_apb_freq_get() << 4) / uart_baud); - - freq_changed = rtc_clk_cpu_freq_value(rtc_clk_cpu_freq_get()) / MHZ; - printf("CPU_FREQ switching to %d MHz\n", freq_changed); - } - run_tasks("accessDPORT", accessDPORT, "accessAPB", accessAPB, 10000 / ((freq_before_changed <= freq_changed) ? 1 : (freq_before_changed / freq_changed))); - - // return old freq. - uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM); - rtc_clk_cpu_freq_set(cur_freq); const int uart_num = CONFIG_CONSOLE_UART_NUM; const int uart_baud = CONFIG_CONSOLE_UART_BAUDRATE; + dport_test_result = false; + apb_test_result = false; + rtc_cpu_freq_config_t old_config; + rtc_clk_cpu_freq_get_config(&old_config); + + printf("CPU_FREQ = %d MHz\n", old_config.freq_mhz); + + if (cpu_freq_mhz != old_config.freq_mhz) { + rtc_cpu_freq_config_t new_config; + bool res = rtc_clk_cpu_freq_mhz_to_config(cpu_freq_mhz, &new_config); + assert(res && "invalid frequency value"); + + uart_tx_wait_idle(uart_num); + rtc_clk_cpu_freq_set_config(&new_config); + uart_div_modify(uart_num, (rtc_clk_apb_freq_get() << 4) / uart_baud); + /* adjust RTOS ticks */ + _xt_tick_divisor = cpu_freq_mhz * 1000000 / XT_TICK_PER_SEC; + vTaskDelay(2); + + printf("CPU_FREQ switched to %d MHz\n", cpu_freq_mhz); + } + run_tasks("accessDPORT", accessDPORT, "accessAPB", accessAPB, 10000); + + // return old freq. + uart_tx_wait_idle(uart_num); + rtc_clk_cpu_freq_set_config(&old_config); uart_div_modify(uart_num, (rtc_clk_apb_freq_get() << 4) / uart_baud); + _xt_tick_divisor = old_config.freq_mhz * 1000000 / XT_TICK_PER_SEC; } TEST_CASE("access DPORT and APB at same time (Freq CPU and APB = 80 MHz)", "[esp32] [ignore]") { - run_tasks_with_change_freq_cpu(RTC_CPU_FREQ_80M); + run_tasks_with_change_freq_cpu(80); } TEST_CASE("access DPORT and APB at same time (Freq CPU and APB = 40 MHz (XTAL))", "[esp32]") { - run_tasks_with_change_freq_cpu(RTC_CPU_FREQ_XTAL); + run_tasks_with_change_freq_cpu((int) rtc_clk_xtal_freq_get()); } static uint32_t stall_other_cpu_counter; diff --git a/components/esp32/test/test_exception.c b/components/esp32/test/test_exception.c deleted file mode 100644 index 4e5d6dec2..000000000 --- a/components/esp32/test/test_exception.c +++ /dev/null @@ -1,9 +0,0 @@ -#include "unity.h" -#include "esp_system.h" -#include "string.h" - - -TEST_CASE("make exception", "[restart][reset=StoreProhibited,SW_CPU_RESET]") -{ - *(int *) NULL = 0; -} diff --git a/components/esp32/test/test_int_wdt.c b/components/esp32/test/test_int_wdt.c deleted file mode 100644 index 9bfde9cdc..000000000 --- a/components/esp32/test/test_int_wdt.c +++ /dev/null @@ -1,20 +0,0 @@ -/* - Tests for the interrupt watchdog -*/ - -#include -#include -#include "rom/ets_sys.h" -#include "unity.h" -#include "soc/dport_reg.h" -#include "soc/io_mux_reg.h" -#include "esp_intr_alloc.h" -#include "freertos/FreeRTOS.h" - - - -TEST_CASE("Int wdt test", "[esp32][reset=Interrupt wdt timeout on CPU0,SW_CPU_RESET]") -{ - portENTER_CRITICAL_NESTED(); - while(1); -} diff --git a/components/esp32/test/test_noinit.c b/components/esp32/test/test_noinit.c deleted file mode 100644 index bb1b42939..000000000 --- a/components/esp32/test/test_noinit.c +++ /dev/null @@ -1,123 +0,0 @@ -#include "unity.h" -#include "esp_system.h" -#include "rom/rtc.h" -#include "esp_log.h" - -// This is a test sequence to test behavior of .rtc_noinit and .noinit sections. -// The values placed into .rtc_noinit section go to RTC SLOW Memory segment and -// keep their value after reset and deep sleep. Use new added attribute macro -// RTC_NOINIT_ATTR for this behavior. The second macro - __NOINIT_ATTR places value -// into .noinit section which goes to SRAM and will not be initialized after reset. - -#define RTC_NOINIT_PATTERN 0xAAAAAAAA -#define _NOINIT_PATTERN 0x55555555 - -static __NOINIT_ATTR uint32_t noinit_data; -static RTC_NOINIT_ATTR uint32_t rtc_noinit_data; - -extern int _rtc_noinit_start; -extern int _rtc_noinit_end; -extern int _noinit_start; -extern int _noinit_end; - -// Pointers to the values -uint32_t *noinit_val_addr = (uint32_t*)&noinit_data; -uint32_t *rtc_noinit_val_addr = (uint32_t*)&rtc_noinit_data; - -static const char* tag = "noinit_UnitTestMain"; - -static esp_err_t check_data_seg(uint32_t *value_address, \ - uint32_t *seg_start, uint32_t *seg_end) -{ - esp_err_t result = ESP_FAIL; - if (((uint32_t)value_address <= (uint32_t)seg_end) && \ - ((uint32_t)value_address >= (uint32_t)seg_start)){ - result = ESP_OK; - } - return result; -} - -static void setup_attributes(void) -{ - rtc_noinit_data = RTC_NOINIT_PATTERN; - noinit_data = _NOINIT_PATTERN; -} - -static void init_attributes(void) -{ - setup_attributes(); - printf("noinit_data = 0x%X \n", (uint32_t)*noinit_val_addr); - printf("rtc_noinit_data = 0x%X \n", (uint32_t)*rtc_noinit_val_addr); - TEST_ASSERT(*noinit_val_addr == noinit_data); - TEST_ASSERT(*rtc_noinit_val_addr == rtc_noinit_data); -} - -static void reset_reason_power_on(void) -{ - printf("This test case checks behavior of noinit variables POWERON_RESET sequence. \n"); - RESET_REASON reason = rtc_get_reset_reason(0); - ESP_LOGI(tag, "POWERON_RESET reset values = (0x%X), (0x%X), reset reason=(%d)\n", \ - (uint32_t)*noinit_val_addr, (uint32_t)*rtc_noinit_val_addr, (uint16_t)reason); - TEST_ASSERT((reason == POWERON_RESET) || (reason == RTCWDT_RTC_RESET)); - - init_attributes(); - TEST_ASSERT(check_data_seg(noinit_val_addr, \ - (uint32_t*)&_noinit_start, \ - (uint32_t*)&_noinit_end) == ESP_OK); - TEST_ASSERT(check_data_seg(rtc_noinit_val_addr, \ - (uint32_t*)&_rtc_noinit_start, \ - (uint32_t*)&_rtc_noinit_end) == ESP_OK); - TEST_ASSERT(_NOINIT_PATTERN == *noinit_val_addr); - TEST_ASSERT(RTC_NOINIT_PATTERN == *rtc_noinit_val_addr); - - printf("Next test case will check SOFTWARE_RESET behavior. \n"); - esp_restart(); -} - -static void reset_reason_sw_reset(void) -{ - printf("This test case checks behavior of noinit variables after software reset sequence. \n"); - RESET_REASON reason = rtc_get_reset_reason(0); - ESP_LOGI(tag, "SW_CPU_RESET reset values = (0x%X), (0x%X), reset reason=(%d)\n", \ - (uint32_t)*noinit_val_addr, (uint32_t)*rtc_noinit_val_addr, (uint16_t)reason); - TEST_ASSERT(reason == SW_CPU_RESET); - TEST_ASSERT(check_data_seg(noinit_val_addr, \ - (uint32_t*)&_noinit_start, \ - (uint32_t*)&_noinit_end) == ESP_OK); - TEST_ASSERT(check_data_seg(rtc_noinit_val_addr, \ - (uint32_t*)&_rtc_noinit_start, \ - (uint32_t*)&_rtc_noinit_end) == ESP_OK); - // The ROM bootloader behavior may apply to this assert. - // TEST_ASSERT(0x55555555 == *noinit_val_addr); - TEST_ASSERT(RTC_NOINIT_PATTERN == *rtc_noinit_val_addr); - printf("Go to deep sleep to check DEEP_SLEEP_RESET behavior. \n"); - esp_sleep_enable_timer_wakeup(2000000); - esp_deep_sleep_start(); -} - -static void reset_reason_deep_sleep(void) -{ - printf("This test case checks behavior of noinit variables after deep sleep reset. \n"); - RESET_REASON reason = rtc_get_reset_reason(0); - ESP_LOGI(tag, "DEEP_SLEEP_RESET reset values = (0x%X), (0x%X), reset reason=(%d)\n", \ - (uint32_t)*noinit_val_addr, (uint32_t)*rtc_noinit_val_addr, (uint16_t)reason); - TEST_ASSERT(reason == DEEPSLEEP_RESET); - TEST_ASSERT(check_data_seg(noinit_val_addr, \ - (uint32_t*)&_noinit_start, \ - (uint32_t*)&_noinit_end) == ESP_OK); - TEST_ASSERT(check_data_seg(rtc_noinit_val_addr, \ - (uint32_t*)&_rtc_noinit_start, \ - (uint32_t*)&_rtc_noinit_end) == ESP_OK); - TEST_ASSERT(RTC_NOINIT_PATTERN == *rtc_noinit_val_addr); - printf("The noinit test cases are done.. \n"); -} - -// The lines below are required to suppress GCC warnings about casting of function pointers -// in unity macro expansion. These warnings may be treated as errors during automated test. -#pragma GCC diagnostic push // required for GCC -#pragma GCC diagnostic ignored "-Wdiscarded-qualifiers" -// The multiple stages test case to check values after certain reset reason -TEST_CASE_MULTIPLE_STAGES("NOINIT attributes behavior", - "[restart][reset=SW_CPU_RESET, DEEPSLEEP_RESET]", - reset_reason_power_on, reset_reason_sw_reset, reset_reason_deep_sleep); -#pragma GCC diagnostic pop // require GCC diff --git a/components/esp32/test/test_pm.c b/components/esp32/test/test_pm.c index fa432eddc..e34658b7e 100644 --- a/components/esp32/test/test_pm.c +++ b/components/esp32/test/test_pm.c @@ -2,6 +2,7 @@ #include #include #include +#include #include "unity.h" #include "esp_pm.h" #include "esp_clk.h" @@ -25,19 +26,17 @@ TEST_CASE("Can dump power management lock stats", "[pm]") static void switch_freq(int mhz) { - rtc_cpu_freq_t max_freq; - assert(rtc_clk_cpu_freq_from_mhz(mhz, &max_freq)); + int xtal_freq = rtc_clk_xtal_freq_get(); esp_pm_config_esp32_t pm_config = { - .max_cpu_freq = max_freq, - .min_cpu_freq = RTC_CPU_FREQ_XTAL, + .max_freq_mhz = mhz, + .min_freq_mhz = MIN(mhz, xtal_freq), }; ESP_ERROR_CHECK( esp_pm_configure(&pm_config) ); - printf("Waiting for frequency to be set to %d (%d MHz)...\n", max_freq, mhz); + printf("Waiting for frequency to be set to %d MHz...\n", mhz); while (esp_clk_cpu_freq() / 1000000 != mhz) { - vTaskDelay(pdMS_TO_TICKS(1000)); - printf("Frequency is %d MHz\n", esp_clk_cpu_freq()); + vTaskDelay(pdMS_TO_TICKS(200)); + printf("Frequency is %d MHz\n", esp_clk_cpu_freq() / 1000000); } - printf("Frequency is set to %d MHz\n", mhz); } TEST_CASE("Can switch frequency using esp_pm_configure", "[pm]") @@ -52,6 +51,10 @@ TEST_CASE("Can switch frequency using esp_pm_configure", "[pm]") switch_freq(240); switch_freq(40); switch_freq(80); + switch_freq(10); + switch_freq(80); + switch_freq(20); + switch_freq(40); switch_freq(orig_freq_mhz); } diff --git a/components/esp32/test/test_reset_reason.c b/components/esp32/test/test_reset_reason.c new file mode 100644 index 000000000..8a1d9a3b2 --- /dev/null +++ b/components/esp32/test/test_reset_reason.c @@ -0,0 +1,211 @@ +#include "unity.h" +#include "esp_system.h" +#include "esp_task_wdt.h" +#include "esp_attr.h" +#include "soc/rtc_cntl_reg.h" + +#define RTC_BSS_ATTR __attribute__((section(".rtc.bss"))) + +static __NOINIT_ATTR uint32_t s_noinit_val; +static RTC_NOINIT_ATTR uint32_t s_rtc_noinit_val; +static RTC_DATA_ATTR uint32_t s_rtc_data_val; +static RTC_BSS_ATTR uint32_t s_rtc_bss_val; + +#define CHECK_VALUE 0x89abcdef + +static void setup_values() +{ + s_noinit_val = CHECK_VALUE; + s_rtc_noinit_val = CHECK_VALUE; + s_rtc_data_val = CHECK_VALUE; + s_rtc_bss_val = CHECK_VALUE; +} + +/* This test needs special test runners: rev1 silicon, and SPI flash with + * fast start-up time. Otherwise reset reason will be RTCWDT_RESET. + */ +TEST_CASE("reset reason ESP_RST_POWERON", "[reset][ignore]") +{ + TEST_ASSERT_EQUAL(ESP_RST_POWERON, esp_reset_reason()); +} + +static void do_deep_sleep() +{ + setup_values(); + esp_sleep_enable_timer_wakeup(10000); + esp_deep_sleep_start(); +} + +static void check_reset_reason_deep_sleep() +{ + TEST_ASSERT_EQUAL(ESP_RST_DEEPSLEEP, esp_reset_reason()); + + TEST_ASSERT_EQUAL_HEX32(CHECK_VALUE, s_rtc_noinit_val); + TEST_ASSERT_EQUAL_HEX32(CHECK_VALUE, s_rtc_data_val); + TEST_ASSERT_EQUAL_HEX32(CHECK_VALUE, s_rtc_bss_val); +} + +TEST_CASE_MULTIPLE_STAGES("reset reason ESP_RST_DEEPSLEEP", "[reset_reason][reset=DEEPSLEEP_RESET]", + do_deep_sleep, + check_reset_reason_deep_sleep); + +static void do_exception() +{ + setup_values(); + *(int*) (0x40000001) = 0; +} + +static void do_abort() +{ + setup_values(); + abort(); +} + +static void check_reset_reason_panic() +{ + TEST_ASSERT_EQUAL(ESP_RST_PANIC, esp_reset_reason()); + + TEST_ASSERT_EQUAL_HEX32(CHECK_VALUE, s_noinit_val); + TEST_ASSERT_EQUAL_HEX32(CHECK_VALUE, s_rtc_noinit_val); + TEST_ASSERT_EQUAL_HEX32(0, s_rtc_data_val); + TEST_ASSERT_EQUAL_HEX32(0, s_rtc_bss_val); +} + +TEST_CASE_MULTIPLE_STAGES("reset reason ESP_RST_PANIC after exception", "[reset_reason][reset=LoadStoreError,SW_CPU_RESET]", + do_exception, + check_reset_reason_panic); + +TEST_CASE_MULTIPLE_STAGES("reset reason ESP_RST_PANIC after abort", "[reset_reason][reset=abort,SW_CPU_RESET]", + do_abort, + check_reset_reason_panic); + +static void do_restart() +{ + setup_values(); + esp_restart(); +} + +#if portNUM_PROCESSORS > 1 +static void do_restart_from_app_cpu() +{ + setup_values(); + xTaskCreatePinnedToCore((TaskFunction_t) &do_restart, "restart", 2048, NULL, 5, NULL, 1); + vTaskDelay(2); +} +#endif + +static void check_reset_reason_sw() +{ + TEST_ASSERT_EQUAL(ESP_RST_SW, esp_reset_reason()); + + TEST_ASSERT_EQUAL_HEX32(CHECK_VALUE, s_noinit_val); + TEST_ASSERT_EQUAL_HEX32(CHECK_VALUE, s_rtc_noinit_val); + TEST_ASSERT_EQUAL_HEX32(0, s_rtc_data_val); + TEST_ASSERT_EQUAL_HEX32(0, s_rtc_bss_val); +} + +TEST_CASE_MULTIPLE_STAGES("reset reason ESP_RST_SW after restart", "[reset_reason][reset=SW_CPU_RESET]", + do_restart, + check_reset_reason_sw); + +#if portNUM_PROCESSORS > 1 +TEST_CASE_MULTIPLE_STAGES("reset reason ESP_RST_SW after restart from APP CPU", "[reset_reason][reset=SW_CPU_RESET]", + do_restart_from_app_cpu, + check_reset_reason_sw); +#endif + + +static void do_int_wdt() +{ + portENTER_CRITICAL_NESTED(); + while(1); +} + +static void do_int_wdt_hw() +{ + XTOS_SET_INTLEVEL(XCHAL_NMILEVEL); + while(1); +} + +static void check_reset_reason_int_wdt() +{ + TEST_ASSERT_EQUAL(ESP_RST_INT_WDT, esp_reset_reason()); +} + +TEST_CASE_MULTIPLE_STAGES("reset reason ESP_RST_INT_WDT after interrupt watchdog (panic)", + "[reset_reason][reset=Interrupt wdt timeout on CPU0,SW_CPU_RESET]", + do_int_wdt, + check_reset_reason_int_wdt); + +TEST_CASE_MULTIPLE_STAGES("reset reason ESP_RST_INT_WDT after interrupt watchdog (hw)", + "[reset_reason][reset=TG1WDT_SYS_RESET]", + do_int_wdt_hw, + check_reset_reason_int_wdt); + +static void do_task_wdt() +{ + setup_values(); + esp_task_wdt_init(1, true); + esp_task_wdt_add(xTaskGetIdleTaskHandleForCPU(0)); + while(1); +} + +static void check_reset_reason_task_wdt() +{ + TEST_ASSERT_EQUAL(ESP_RST_TASK_WDT, esp_reset_reason()); + + TEST_ASSERT_EQUAL_HEX32(CHECK_VALUE, s_noinit_val); + TEST_ASSERT_EQUAL_HEX32(CHECK_VALUE, s_rtc_noinit_val); + TEST_ASSERT_EQUAL_HEX32(0, s_rtc_data_val); + TEST_ASSERT_EQUAL_HEX32(0, s_rtc_bss_val); +} + +TEST_CASE_MULTIPLE_STAGES("reset reason ESP_RST_TASK_WDT after task watchdog", + "[reset_reason][reset=abort,SW_CPU_RESET]", + do_task_wdt, + check_reset_reason_task_wdt); + +static void do_rtc_wdt() +{ + WRITE_PERI_REG(RTC_CNTL_WDTWPROTECT_REG, RTC_CNTL_WDT_WKEY_VALUE); + REG_SET_FIELD(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_SYS_RESET_LENGTH, 7); + REG_SET_FIELD(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_STG0, RTC_WDT_STG_SEL_RESET_SYSTEM); + WRITE_PERI_REG(RTC_CNTL_WDTCONFIG1_REG, 10000); + REG_SET_BIT(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_FLASHBOOT_MOD_EN); + while(1); +} + +static void check_reset_reason_any_wdt() +{ + TEST_ASSERT_EQUAL(ESP_RST_WDT, esp_reset_reason()); +} + +TEST_CASE_MULTIPLE_STAGES("reset reason ESP_RST_WDT after RTC watchdog", + "[reset_reason][reset=RTCWDT_RTC_RESET]", + do_rtc_wdt, + check_reset_reason_any_wdt); + + +static void do_brownout() +{ + setup_values(); + printf("Manual test: lower the supply voltage to cause brownout\n"); + vTaskSuspend(NULL); +} + +static void check_reset_reason_brownout() +{ + TEST_ASSERT_EQUAL(ESP_RST_BROWNOUT, esp_reset_reason()); + + TEST_ASSERT_EQUAL_HEX32(CHECK_VALUE, s_noinit_val); + TEST_ASSERT_EQUAL_HEX32(CHECK_VALUE, s_rtc_noinit_val); + TEST_ASSERT_EQUAL_HEX32(0, s_rtc_data_val); + TEST_ASSERT_EQUAL_HEX32(0, s_rtc_bss_val); +} + +TEST_CASE_MULTIPLE_STAGES("reset reason ESP_RST_BROWNOUT after brownout event", + "[reset_reason][ignore][reset=SW_CPU_RESET]", + do_brownout, + check_reset_reason_brownout); + +/* Not tested here: ESP_RST_SDIO */ diff --git a/components/esp32/test/test_restart.c b/components/esp32/test/test_restart.c deleted file mode 100644 index 6ad334da0..000000000 --- a/components/esp32/test/test_restart.c +++ /dev/null @@ -1,25 +0,0 @@ -#include "unity.h" -#include "esp_system.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" - - -TEST_CASE("restart from PRO CPU", "[restart][reset=SW_CPU_RESET]") -{ - esp_restart(); -} - -static void restart_task(void *arg) -{ - esp_restart(); -} - -#ifndef CONFIG_FREERTOS_UNICORE -TEST_CASE("restart from APP CPU", "[restart][reset=SW_CPU_RESET]") -{ - xTaskCreatePinnedToCore(&restart_task, "restart", 2048, NULL, 5, NULL, 1); - while (true) { - ; - } -} -#endif diff --git a/components/esp32/test/test_sleep.c b/components/esp32/test/test_sleep.c index abd5a2080..7eb504226 100644 --- a/components/esp32/test/test_sleep.c +++ b/components/esp32/test/test_sleep.c @@ -177,13 +177,16 @@ TEST_CASE("light sleep and frequency switching", "[deepsleep]") uart_div_modify(CONFIG_CONSOLE_UART_NUM, (uart_clk_freq << 4) / CONFIG_CONSOLE_UART_BAUDRATE); #endif + rtc_cpu_freq_config_t config_xtal, config_default; + rtc_clk_cpu_freq_get_config(&config_default); + rtc_clk_cpu_freq_mhz_to_config((int) rtc_clk_xtal_freq_get(), &config_xtal); + esp_sleep_enable_timer_wakeup(1000); - rtc_cpu_freq_t default_freq = rtc_clk_cpu_freq_get(); for (int i = 0; i < 1000; ++i) { if (i % 2 == 0) { - rtc_clk_cpu_freq_set_fast(RTC_CPU_FREQ_XTAL); + rtc_clk_cpu_freq_set_config_fast(&config_xtal); } else { - rtc_clk_cpu_freq_set_fast(default_freq); + rtc_clk_cpu_freq_set_config_fast(&config_default); } printf("%d\n", i); fflush(stdout); @@ -199,6 +202,74 @@ TEST_CASE("enter deep sleep on APP CPU and wake up using timer", "[deepsleep][re } #endif +static void do_deep_sleep() +{ + esp_sleep_enable_timer_wakeup(100000); + esp_deep_sleep_start(); +} + +static void check_sleep_reset_and_sleep() +{ + TEST_ASSERT_EQUAL(ESP_RST_DEEPSLEEP, esp_reset_reason()); + esp_sleep_enable_timer_wakeup(100000); + esp_deep_sleep_start(); +} + +static void check_sleep_reset() +{ + TEST_ASSERT_EQUAL(ESP_RST_DEEPSLEEP, esp_reset_reason()); +} + +TEST_CASE_MULTIPLE_STAGES("enter deep sleep more than once", "[deepsleep][reset=DEEPSLEEP_RESET,DEEPSLEEP_RESET,DEEPSLEEP_RESET]", + do_deep_sleep, + check_sleep_reset_and_sleep, + check_sleep_reset_and_sleep, + check_sleep_reset); + +static void do_abort() +{ + abort(); +} + +static void check_abort_reset_and_sleep() +{ + TEST_ASSERT_EQUAL(ESP_RST_PANIC, esp_reset_reason()); + esp_sleep_enable_timer_wakeup(100000); + esp_deep_sleep_start(); +} + +TEST_CASE_MULTIPLE_STAGES("enter deep sleep after abort", "[deepsleep][reset=abort,SW_CPU_RESET,DEEPSLEEP_RESET]", + do_abort, + check_abort_reset_and_sleep, + check_sleep_reset); + +static RTC_DATA_ATTR uint32_t s_wake_stub_var; + +static RTC_IRAM_ATTR void wake_stub() +{ + esp_default_wake_deep_sleep(); + s_wake_stub_var = (uint32_t) &wake_stub; +} + +static void prepare_wake_stub() +{ + esp_set_deep_sleep_wake_stub(&wake_stub); + esp_sleep_enable_timer_wakeup(100000); + esp_deep_sleep_start(); +} + +static void check_wake_stub() +{ + TEST_ASSERT_EQUAL(ESP_RST_DEEPSLEEP, esp_reset_reason()); + TEST_ASSERT_EQUAL_HEX32((uint32_t) &wake_stub, s_wake_stub_var); + /* ROM code clears wake stub entry address */ + TEST_ASSERT_NULL(esp_get_deep_sleep_wake_stub()); +} + +TEST_CASE_MULTIPLE_STAGES("can set sleep wake stub", "[deepsleep][reset=DEEPSLEEP_RESET]", + prepare_wake_stub, + check_wake_stub); + TEST_CASE("wake up using ext0 (13 high)", "[deepsleep][ignore]") { diff --git a/components/esp_http_client/CMakeLists.txt b/components/esp_http_client/CMakeLists.txt index f95c657ab..31bc1185e 100644 --- a/components/esp_http_client/CMakeLists.txt +++ b/components/esp_http_client/CMakeLists.txt @@ -3,6 +3,6 @@ set(COMPONENT_ADD_INCLUDEDIRS "include") set(COMPONENT_PRIV_INCLUDEDIRS "lib/include") set(COMPONENT_REQUIRES "nghttp") -set(COMPONENT_PRIV_REQUIRES "mbedtls" "lwip") +set(COMPONENT_PRIV_REQUIRES "mbedtls" "lwip" "esp-tls") register_component() diff --git a/components/esp_http_client/esp_http_client.c b/components/esp_http_client/esp_http_client.c index e21b93da2..1905d5f0c 100644 --- a/components/esp_http_client/esp_http_client.c +++ b/components/esp_http_client/esp_http_client.c @@ -878,7 +878,7 @@ esp_err_t esp_http_client_open(esp_http_client_handle_t client, int write_len) return ESP_ERR_HTTP_INVALID_TRANSPORT; } if (transport_connect(client->transport, client->connection_info.host, client->connection_info.port, client->timeout_ms) < 0) { - ESP_LOGE(TAG, "Connection failed, sock < 0"); + ESP_LOGE(TAG, "Connection failed"); return ESP_ERR_HTTP_CONNECT; } http_dispatch_event(client, HTTP_EVENT_ON_CONNECTED, NULL, 0); @@ -932,10 +932,17 @@ esp_err_t esp_http_client_open(esp_http_client_handle_t client, int write_len) } client->request->buffer->data[wlen] = 0; ESP_LOGD(TAG, "Write header[%d]: %s", header_index, client->request->buffer->data); - if (transport_write(client->transport, client->request->buffer->data, wlen, client->timeout_ms) <= 0) { - ESP_LOGE(TAG, "Error write request"); - esp_http_client_close(client); - return ESP_ERR_HTTP_WRITE_DATA; + + int widx = 0, wret = 0; + while (wlen > 0) { + wret = transport_write(client->transport, client->request->buffer->data + widx, wlen, client->timeout_ms); + if (wret <= 0) { + ESP_LOGE(TAG, "Error write request"); + esp_http_client_close(client); + return ESP_ERR_HTTP_WRITE_DATA; + } + widx += wret; + wlen -= wret; } wlen = client->buffer_size; } @@ -949,14 +956,10 @@ int esp_http_client_write(esp_http_client_handle_t client, const char *buffer, i if (client->state < HTTP_STATE_REQ_COMPLETE_HEADER) { return ESP_FAIL; } - int need_write; + int wlen = 0, widx = 0; while (len > 0) { - need_write = len; - if (need_write > client->buffer_size) { - need_write = client->buffer_size; - } - wlen = transport_write(client->transport, buffer + widx, need_write, client->timeout_ms); + wlen = transport_write(client->transport, buffer + widx, len, client->timeout_ms); if (wlen <= 0) { return wlen; } diff --git a/components/esp_http_client/lib/transport_ssl.c b/components/esp_http_client/lib/transport_ssl.c index bb07c9245..bd2260aaa 100644 --- a/components/esp_http_client/lib/transport_ssl.c +++ b/components/esp_http_client/lib/transport_ssl.c @@ -17,22 +17,7 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" -#include "lwip/err.h" -#include "lwip/sockets.h" -#include "lwip/sys.h" -#include "lwip/netdb.h" -#include "lwip/dns.h" - -#include "mbedtls/platform.h" -#include "mbedtls/net_sockets.h" -#include "mbedtls/esp_debug.h" -#include "mbedtls/ssl.h" -#include "mbedtls/entropy.h" -#include "mbedtls/ctr_drbg.h" -#include "mbedtls/error.h" -#include "mbedtls/certs.h" - - +#include "esp_tls.h" #include "esp_log.h" #include "esp_system.h" @@ -45,12 +30,7 @@ static const char *TAG = "TRANS_SSL"; * mbedtls specific transport data */ typedef struct { - mbedtls_entropy_context entropy; - mbedtls_ctr_drbg_context ctr_drbg; - mbedtls_ssl_context ctx; - mbedtls_x509_crt cacert; - mbedtls_ssl_config conf; - mbedtls_net_context client_fd; + esp_tls_t *tls; void *cert_pem_data; int cert_pem_len; bool ssl_initialized; @@ -61,108 +41,21 @@ static int ssl_close(transport_handle_t t); static int ssl_connect(transport_handle_t t, const char *host, int port, int timeout_ms) { - int ret = -1, flags; - struct timeval tv; transport_ssl_t *ssl = transport_get_context_data(t); - - if (!ssl) { + esp_tls_cfg_t cfg = { 0 }; + if (ssl->cert_pem_data) { + ssl->verify_server = true; + cfg.cacert_pem_buf = ssl->cert_pem_data; + cfg.cacert_pem_bytes = ssl->cert_pem_len + 1; + } + cfg.timeout_ms = timeout_ms; + ssl->ssl_initialized = true; + ssl->tls = esp_tls_conn_new(host, strlen(host), port, &cfg); + if (!ssl->tls) { + ESP_LOGE(TAG, "Failed to open a new connection"); return -1; } - ssl->ssl_initialized = true; - mbedtls_ssl_init(&ssl->ctx); - mbedtls_ctr_drbg_init(&ssl->ctr_drbg); - mbedtls_ssl_config_init(&ssl->conf); - mbedtls_entropy_init(&ssl->entropy); - - if ((ret = mbedtls_ssl_config_defaults(&ssl->conf, - MBEDTLS_SSL_IS_CLIENT, - MBEDTLS_SSL_TRANSPORT_STREAM, - MBEDTLS_SSL_PRESET_DEFAULT)) != 0) { - ESP_LOGE(TAG, "mbedtls_ssl_config_defaults returned %d", ret); - goto exit; - } - - if ((ret = mbedtls_ctr_drbg_seed(&ssl->ctr_drbg, mbedtls_entropy_func, &ssl->entropy, NULL, 0)) != 0) { - ESP_LOGE(TAG, "mbedtls_ctr_drbg_seed returned %d", ret); - goto exit; - } - - if (ssl->cert_pem_data) { - mbedtls_x509_crt_init(&ssl->cacert); - ssl->verify_server = true; - if ((ret = mbedtls_x509_crt_parse(&ssl->cacert, ssl->cert_pem_data, ssl->cert_pem_len + 1)) < 0) { - ESP_LOGE(TAG, "mbedtls_x509_crt_parse returned -0x%x\n\nDATA=%s,len=%d", -ret, (char*)ssl->cert_pem_data, ssl->cert_pem_len); - goto exit; - } - mbedtls_ssl_conf_ca_chain(&ssl->conf, &ssl->cacert, NULL); - mbedtls_ssl_conf_authmode(&ssl->conf, MBEDTLS_SSL_VERIFY_REQUIRED); - - if ((ret = mbedtls_ssl_set_hostname(&ssl->ctx, host)) != 0) { - ESP_LOGE(TAG, "mbedtls_ssl_set_hostname returned -0x%x", -ret); - goto exit; - } - } else { - mbedtls_ssl_conf_authmode(&ssl->conf, MBEDTLS_SSL_VERIFY_NONE); - } - - - mbedtls_ssl_conf_rng(&ssl->conf, mbedtls_ctr_drbg_random, &ssl->ctr_drbg); - -#ifdef CONFIG_MBEDTLS_DEBUG - mbedtls_esp_enable_debug_log(&ssl->conf, 4); -#endif - - if ((ret = mbedtls_ssl_setup(&ssl->ctx, &ssl->conf)) != 0) { - ESP_LOGE(TAG, "mbedtls_ssl_setup returned -0x%x\n\n", -ret); - goto exit; - } - - mbedtls_net_init(&ssl->client_fd); - - http_utils_ms_to_timeval(timeout_ms, &tv); - - setsockopt(ssl->client_fd.fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); - ESP_LOGD(TAG, "Connect to %s:%d", host, port); - char port_str[8] = {0}; - sprintf(port_str, "%d", port); - if ((ret = mbedtls_net_connect(&ssl->client_fd, host, port_str, MBEDTLS_NET_PROTO_TCP)) != 0) { - ESP_LOGE(TAG, "mbedtls_net_connect returned -%x", -ret); - goto exit; - } - - mbedtls_ssl_set_bio(&ssl->ctx, &ssl->client_fd, mbedtls_net_send, mbedtls_net_recv, NULL); - - if((ret = mbedtls_ssl_set_hostname(&ssl->ctx, host)) != 0) { - ESP_LOGE(TAG, " failed\n ! mbedtls_ssl_set_hostname returned %d\n\n", ret); - goto exit; - } - - ESP_LOGD(TAG, "Performing the SSL/TLS handshake..."); - - while ((ret = mbedtls_ssl_handshake(&ssl->ctx)) != 0) { - if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { - ESP_LOGE(TAG, "mbedtls_ssl_handshake returned -0x%x", -ret); - goto exit; - } - } - - ESP_LOGD(TAG, "Verifying peer X.509 certificate..."); - - if ((flags = mbedtls_ssl_get_verify_result(&ssl->ctx)) != 0) { - /* In real life, we probably want to close connection if ret != 0 */ - ESP_LOGW(TAG, "Failed to verify peer certificate!"); - if (ssl->cert_pem_data) { - goto exit; - } - } else { - ESP_LOGD(TAG, "Certificate verified."); - } - - ESP_LOGD(TAG, "Cipher suite is %s", mbedtls_ssl_get_ciphersuite(&ssl->ctx)); - return ret; -exit: - ssl_close(t); - return ret; + return 0; } static int ssl_poll_read(transport_handle_t t, int timeout_ms) @@ -170,11 +63,11 @@ static int ssl_poll_read(transport_handle_t t, int timeout_ms) transport_ssl_t *ssl = transport_get_context_data(t); fd_set readset; FD_ZERO(&readset); - FD_SET(ssl->client_fd.fd, &readset); + FD_SET(ssl->tls->sockfd, &readset); struct timeval timeout; http_utils_ms_to_timeval(timeout_ms, &timeout); - return select(ssl->client_fd.fd + 1, &readset, NULL, NULL, &timeout); + return select(ssl->tls->sockfd + 1, &readset, NULL, NULL, &timeout); } static int ssl_poll_write(transport_handle_t t, int timeout_ms) @@ -182,10 +75,10 @@ static int ssl_poll_write(transport_handle_t t, int timeout_ms) transport_ssl_t *ssl = transport_get_context_data(t); fd_set writeset; FD_ZERO(&writeset); - FD_SET(ssl->client_fd.fd, &writeset); + FD_SET(ssl->tls->sockfd, &writeset); struct timeval timeout; http_utils_ms_to_timeval(timeout_ms, &timeout); - return select(ssl->client_fd.fd + 1, NULL, &writeset, NULL, &timeout); + return select(ssl->tls->sockfd + 1, NULL, &writeset, NULL, &timeout); } static int ssl_write(transport_handle_t t, const char *buffer, int len, int timeout_ms) @@ -194,10 +87,10 @@ static int ssl_write(transport_handle_t t, const char *buffer, int len, int time transport_ssl_t *ssl = transport_get_context_data(t); if ((poll = transport_poll_write(t, timeout_ms)) <= 0) { - ESP_LOGW(TAG, "Poll timeout or error, errno=%s, fd=%d, timeout_ms=%d", strerror(errno), ssl->client_fd.fd, timeout_ms); + ESP_LOGW(TAG, "Poll timeout or error, errno=%s, fd=%d, timeout_ms=%d", strerror(errno), ssl->tls->sockfd, timeout_ms); return poll; } - ret = mbedtls_ssl_write(&ssl->ctx, (const unsigned char *) buffer, len); + ret = esp_tls_conn_write(ssl->tls, (const unsigned char *) buffer, len); if (ret <= 0) { ESP_LOGE(TAG, "mbedtls_ssl_write error, errno=%s", strerror(errno)); } @@ -206,14 +99,18 @@ static int ssl_write(transport_handle_t t, const char *buffer, int len, int time static int ssl_read(transport_handle_t t, char *buffer, int len, int timeout_ms) { - int poll = -1, ret; + int poll, ret; transport_ssl_t *ssl = transport_get_context_data(t); - if (mbedtls_ssl_get_bytes_avail(&ssl->ctx) <= 0) { + + if (esp_tls_get_bytes_avail(ssl->tls) <= 0) { if ((poll = transport_poll_read(t, timeout_ms)) <= 0) { return poll; } } - ret = mbedtls_ssl_read(&ssl->ctx, (unsigned char *)buffer, len); + ret = esp_tls_conn_read(ssl->tls, (unsigned char *)buffer, len); + if (ret <= 0) { + ESP_LOGE(TAG, "mbedtls_ssl_read error, errno=%s", strerror(errno)); + } return ret; } @@ -222,17 +119,7 @@ static int ssl_close(transport_handle_t t) int ret = -1; transport_ssl_t *ssl = transport_get_context_data(t); if (ssl->ssl_initialized) { - ESP_LOGD(TAG, "Cleanup mbedtls"); - mbedtls_ssl_close_notify(&ssl->ctx); - mbedtls_ssl_session_reset(&ssl->ctx); - mbedtls_net_free(&ssl->client_fd); - mbedtls_ssl_config_free(&ssl->conf); - if (ssl->verify_server) { - mbedtls_x509_crt_free(&ssl->cacert); - } - mbedtls_ctr_drbg_free(&ssl->ctr_drbg); - mbedtls_entropy_free(&ssl->entropy); - mbedtls_ssl_free(&ssl->ctx); + esp_tls_conn_delete(ssl->tls); ssl->ssl_initialized = false; ssl->verify_server = false; } @@ -261,7 +148,6 @@ transport_handle_t transport_ssl_init() transport_handle_t t = transport_init(); transport_ssl_t *ssl = calloc(1, sizeof(transport_ssl_t)); HTTP_MEM_CHECK(TAG, ssl, return NULL); - mbedtls_net_init(&ssl->client_fd); transport_set_context_data(t, ssl); transport_set_func(t, ssl_connect, ssl_read, ssl_write, ssl_close, ssl_poll_read, ssl_poll_write, ssl_destroy); return t; diff --git a/components/esptool_py/Makefile.projbuild b/components/esptool_py/Makefile.projbuild index 25f4b8e6f..5e082a2e9 100644 --- a/components/esptool_py/Makefile.projbuild +++ b/components/esptool_py/Makefile.projbuild @@ -55,17 +55,17 @@ endif # non-secure boot (or bootloader), both these files are the same APP_BIN_UNSIGNED ?= $(APP_BIN) -$(APP_BIN_UNSIGNED): $(APP_ELF) $(ESPTOOLPY_SRC) +$(APP_BIN_UNSIGNED): $(APP_ELF) $(ESPTOOLPY_SRC) | check_python_dependencies $(ESPTOOLPY) elf2image $(ESPTOOL_FLASH_OPTIONS) $(ESPTOOL_ELF2IMAGE_OPTIONS) -o $@ $< -flash: all_binaries $(ESPTOOLPY_SRC) $(call prereq_if_explicit,erase_flash) partition_table_get_info +flash: all_binaries $(ESPTOOLPY_SRC) $(call prereq_if_explicit,erase_flash) partition_table_get_info check_python_dependencies @echo "Flashing binaries to serial port $(ESPPORT) (app at offset $(APP_OFFSET))..." ifdef CONFIG_SECURE_BOOT_ENABLED @echo "(Secure boot enabled, so bootloader not flashed automatically. See 'make bootloader' output)" endif $(ESPTOOLPY_WRITE_FLASH) $(ESPTOOL_ALL_FLASH_ARGS) -app-flash: $(APP_BIN) $(ESPTOOLPY_SRC) $(call prereq_if_explicit,erase_flash) partition_table_get_info +app-flash: $(APP_BIN) $(ESPTOOLPY_SRC) $(call prereq_if_explicit,erase_flash) partition_table_get_info check_python_dependencies @echo "Flashing app to serial port $(ESPPORT), offset $(APP_OFFSET)..." $(ESPTOOLPY_WRITE_FLASH) $(APP_OFFSET) $(APP_BIN) @@ -73,7 +73,7 @@ app-flash: $(APP_BIN) $(ESPTOOLPY_SRC) $(call prereq_if_explicit,erase_flash) pa # at the project level as long as qualified path COMPONENT_SUBMODULES += $(COMPONENT_PATH)/esptool -erase_flash: +erase_flash: check_python_dependencies @echo "Erasing entire flash..." $(ESPTOOLPY_SERIAL) erase_flash @@ -90,14 +90,14 @@ endif # note: if you want to run miniterm from command line, can simply run # miniterm.py on the console. The '$(PYTHON) -m serial.tools.miniterm' # is to allow for the $(PYTHON) variable overriding the python path. -simple_monitor: $(call prereq_if_explicit,%flash) +simple_monitor: check_python_dependencies $(call prereq_if_explicit,%flash) $(MONITOR_PYTHON) -m serial.tools.miniterm --rts 0 --dtr 0 --raw $(ESPPORT) $(MONITORBAUD) PRINT_FILTER ?= MONITOR_OPTS := --baud $(MONITORBAUD) --port $(ESPPORT) --toolchain-prefix $(CONFIG_TOOLPREFIX) --make "$(MAKE)" --print_filter "$(PRINT_FILTER)" -monitor: $(call prereq_if_explicit,%flash) +monitor: check_python_dependencies $(call prereq_if_explicit,%flash) $(summary) MONITOR [ -f $(APP_ELF) ] || echo "*** 'make monitor' target requires an app to be compiled and flashed first." [ -f $(APP_ELF) ] || echo "*** Run 'make flash monitor' to build, flash and monitor" diff --git a/components/ethernet/Kconfig b/components/ethernet/Kconfig index f7281bc4c..5808c525d 100644 --- a/components/ethernet/Kconfig +++ b/components/ethernet/Kconfig @@ -26,15 +26,23 @@ config EMAC_L2_TO_L3_RX_BUF_MODE If this options is selected, a copy of each received buffer will be created when passing it from the Ethernet MAC (L2) to the IP stack (L3). Otherwise, IP stack will receive pointers to the DMA buffers used by Ethernet MAC. - + When Ethernet MAC doesn't have any unused buffers left, it will drop incomming packets (flow control may help with this problem, to some extent). - + The buffers for the IP stack are allocated from the heap, so the total number of receive buffers is limited by the available heap size, if this option is selected. - + If unsure, choose n. +config EMAC_CHECK_LINK_PERIOD_MS + int "Period(ms) of checking Ethernet linkup status" + range 1000 5000 + default 2000 + help + The emac driver uses an internal timer to check the ethernet linkup + status. Here you should choose a valid the interval time. + config EMAC_TASK_PRIORITY int "EMAC_TASK_PRIORITY" default 20 diff --git a/components/ethernet/emac_dev.c b/components/ethernet/emac_dev.c index cceea4f1b..9ec1822bb 100644 --- a/components/ethernet/emac_dev.c +++ b/components/ethernet/emac_dev.c @@ -98,7 +98,6 @@ void emac_dma_init(void) REG_SET_BIT(EMAC_DMAOPERATION_MODE_REG, EMAC_FWD_UNDER_GF); REG_SET_BIT(EMAC_DMAOPERATION_MODE_REG, EMAC_OPT_SECOND_FRAME); REG_SET_FIELD(EMAC_DMABUSMODE_REG, EMAC_PROG_BURST_LEN, 4); - REG_SET_BIT(EMAC_DMAOPERATION_MODE_REG, EMAC_DMAOPERATION_MODE_REG); } void emac_mac_enable_txrx(void) diff --git a/components/ethernet/emac_dev.h b/components/ethernet/emac_dev.h index 0a49f94cc..73bb627d0 100644 --- a/components/ethernet/emac_dev.h +++ b/components/ethernet/emac_dev.h @@ -31,13 +31,13 @@ struct dma_desc { uint32_t desc3; }; -struct dma_extended_desc { +typedef struct dma_extended_desc { struct dma_desc basic; uint32_t desc4; uint32_t desc5; uint32_t desc6; uint32_t desc7; -}; +}dma_extended_desc_t; void emac_enable_clk(bool enable); void emac_reset(void); @@ -54,54 +54,54 @@ void emac_enable_flowctrl(void); void emac_disable_flowctrl(void); void emac_mac_enable_txrx(void); -uint32_t inline emac_read_tx_cur_reg(void) +static inline uint32_t emac_read_tx_cur_reg(void) { return REG_READ(EMAC_DMATXCURRDESC_REG); } -uint32_t inline emac_read_rx_cur_reg(void) +static inline uint32_t emac_read_rx_cur_reg(void) { return REG_READ(EMAC_DMARXCURRDESC_REG); } -void inline emac_poll_tx_cmd(void) +static inline void emac_poll_tx_cmd(void) { //write any to wake up dma REG_WRITE(EMAC_DMATXPOLLDEMAND_REG, 1); } -void inline emac_poll_rx_cmd(void) +static inline void emac_poll_rx_cmd(void) { //write any to wake up dma REG_WRITE(EMAC_DMARXPOLLDEMAND_REG, 1); } -void inline emac_disable_rx_intr(void) +static inline void emac_disable_rx_intr(void) { REG_CLR_BIT(EMAC_DMAIN_EN_REG, EMAC_DMAIN_RIE); } -void inline emac_enable_rx_intr(void) +static inline void emac_enable_rx_intr(void) { REG_SET_BIT(EMAC_DMAIN_EN_REG, EMAC_DMAIN_RIE); } -void inline emac_disable_rx_unavail_intr(void) +static inline void emac_disable_rx_unavail_intr(void) { REG_CLR_BIT(EMAC_DMAIN_EN_REG, EMAC_DMAIN_RBUE); } -void inline emac_enable_rx_unavail_intr(void) +static inline void emac_enable_rx_unavail_intr(void) { REG_SET_BIT(EMAC_DMAIN_EN_REG, EMAC_DMAIN_RBUE); } -void IRAM_ATTR inline emac_send_pause_frame_enable(void) +static inline void IRAM_ATTR emac_send_pause_frame_enable(void) { REG_SET_BIT(EMAC_EX_PHYINF_CONF_REG, EMAC_EX_SBD_FLOWCTRL); } -void inline emac_send_pause_zero_frame_enable(void) +static inline void emac_send_pause_zero_frame_enable(void) { REG_CLR_BIT(EMAC_EX_PHYINF_CONF_REG, EMAC_EX_SBD_FLOWCTRL); } diff --git a/components/ethernet/emac_main.c b/components/ethernet/emac_main.c index f89c56952..f257d8b5a 100644 --- a/components/ethernet/emac_main.c +++ b/components/ethernet/emac_main.c @@ -13,6 +13,7 @@ // limitations under the License. #include +#include #include #include "rom/ets_sys.h" @@ -58,15 +59,15 @@ static struct emac_config_data emac_config; -static uint8_t emac_dma_rx_chain_buf[sizeof(struct dma_extended_desc) * DMA_RX_BUF_NUM]; -static uint8_t emac_dma_tx_chain_buf[sizeof(struct dma_extended_desc) * DMA_TX_BUF_NUM]; -static uint8_t emac_dma_rx_buf[DMA_RX_BUF_SIZE * DMA_RX_BUF_NUM]; -static uint8_t emac_dma_tx_buf[DMA_TX_BUF_SIZE * DMA_TX_BUF_NUM]; +static dma_extended_desc_t *emac_dma_rx_chain_buf[DMA_RX_BUF_NUM]; +static dma_extended_desc_t *emac_dma_tx_chain_buf[DMA_TX_BUF_NUM]; +static uint8_t *emac_dma_rx_buf[DMA_RX_BUF_NUM]; +static uint8_t *emac_dma_tx_buf[DMA_TX_BUF_NUM]; -static SemaphoreHandle_t emac_g_sem; +static SemaphoreHandle_t emac_g_sem = NULL; static portMUX_TYPE g_emac_mux = portMUX_INITIALIZER_UNLOCKED; -static xTaskHandle emac_task_hdl; -static xQueueHandle emac_xqueue; +static xTaskHandle emac_task_hdl = NULL; +static xQueueHandle emac_xqueue = NULL; static uint8_t emac_sig_cnt[EMAC_SIG_MAX] = {0}; static TimerHandle_t emac_timer = NULL; static SemaphoreHandle_t emac_rx_xMutex = NULL; @@ -92,19 +93,23 @@ void esp_eth_get_mac(uint8_t mac[6]) esp_err_t esp_eth_set_mac(const uint8_t mac[6]) { - if((mac[0] & 0x01) == 0) { - memcpy(&(emac_config.macaddr[0]),mac, 6); + if ((mac[0] & 0x01) == 0) { + memcpy(&(emac_config.macaddr[0]), mac, 6); return ESP_OK; } else { return ESP_ERR_INVALID_MAC; } } -static void emac_setup_tx_desc(struct dma_extended_desc *tx_desc , uint32_t size) +static void emac_setup_tx_desc(struct dma_extended_desc *tx_desc, uint32_t size) { tx_desc->basic.desc1 = size & 0xfff; - tx_desc->basic.desc0 = EMAC_DESC_INT_COMPL | EMAC_DESC_LAST_SEGMENT | EMAC_DESC_FIRST_SEGMENT | EMAC_DESC_SECOND_ADDR_CHAIN; - tx_desc->basic.desc0 = EMAC_DESC_TX_OWN | EMAC_DESC_INT_COMPL | EMAC_DESC_LAST_SEGMENT | EMAC_DESC_FIRST_SEGMENT | EMAC_DESC_SECOND_ADDR_CHAIN; + tx_desc->basic.desc0 = EMAC_DESC_INT_COMPL | EMAC_DESC_LAST_SEGMENT | + EMAC_DESC_FIRST_SEGMENT | + EMAC_DESC_SECOND_ADDR_CHAIN; + tx_desc->basic.desc0 = EMAC_DESC_TX_OWN | EMAC_DESC_INT_COMPL | + EMAC_DESC_LAST_SEGMENT | EMAC_DESC_FIRST_SEGMENT | + EMAC_DESC_SECOND_ADDR_CHAIN; } static void emac_clean_tx_desc(struct dma_extended_desc *tx_desc) @@ -113,7 +118,8 @@ static void emac_clean_tx_desc(struct dma_extended_desc *tx_desc) tx_desc->basic.desc0 = 0; } -static void emac_clean_rx_desc(struct dma_extended_desc *rx_desc , uint32_t buf_ptr) +static void emac_clean_rx_desc(struct dma_extended_desc *rx_desc, + uint32_t buf_ptr) { if (buf_ptr != 0) { rx_desc->basic.desc2 = buf_ptr; @@ -133,23 +139,34 @@ static void emac_set_rx_base_reg(void) } /* -* dirty_rx indicates the hardware has been fed with data packets and is the first node software needs to handle; +* dirty_rx indicates the hardware has been fed with data packets and is the +* first node software needs to handle; * -* cur_rx indicates the completion of software handling and is the last node hardware could use; +* cur_rx indicates the completion of software handling and is the last node +* hardware could use; * -* cnt_rx is to count the numbers of packets handled by software, passed to protocol stack and not been freed. +* cnt_rx is to count the numbers of packets handled by software, passed to +* protocol stack and not been freed. * -* (1) Initializing the Linked List. Connect the numerable nodes to a circular linked list, appoint one of the nodes as the head node, mark* the dirty_rx and cur_rx into the node, and mount the node on the hardware base address. Initialize cnt_rx into 0. +* (1) Initializing the Linked List. Connect the numerable nodes to a circular +* linked list, appoint one of the nodes as the head node, mark* the dirty_rx +* and cur_rx into the node, and mount the node on the hardware base address. +* Initialize cnt_rx into 0. * -* (2) When hardware receives packets, nodes of linked lists will be fed with data packets from the base address by turns, marks the node +* (2) When hardware receives packets, nodes of linked lists will be fed with +* data packets from the base address by turns, marks the node * of linked lists as “HARDWARE UNUSABLE” and reports interrupts. * -* (3) When the software receives the interrupts, it will handle the linked lists by turns from dirty_rx, send data packets to protocol +* (3) When the software receives the interrupts, it will handle the linked +* lists by turns from dirty_rx, send data packets to protocol * stack. dirty_rx will deviate backwards by turns and cnt_rx will by turns ++. * -* (4) After the protocol stack handles all the data and calls the free function, it will deviate backwards by turns from cur_rx, mark the * node of linked lists as “HARDWARE USABLE” and cnt_rx will by turns --. +* (4) After the protocol stack handles all the data and calls the free function, +* it will deviate backwards by turns from cur_rx, mark the * node of linked +* lists as “HARDWARE USABLE” and cnt_rx will by turns --. * -* (5) Cycle from Step 2 to Step 4 without break and build up circular linked list handling. +* (5) Cycle from Step 2 to Step 4 without break and build up circular linked +* list handling. */ static void emac_reset_dma_chain(void) { @@ -165,48 +182,46 @@ static void emac_reset_dma_chain(void) static void emac_init_dma_chain(void) { int i; - uint32_t dma_phy; - struct dma_extended_desc *p = NULL; + dma_extended_desc_t *p = NULL; //init tx chain - emac_config.dma_etx = (struct dma_extended_desc *)(&emac_dma_tx_chain_buf[0]); + emac_config.dma_etx = emac_dma_tx_chain_buf[0]; emac_config.cnt_tx = 0; emac_config.cur_tx = 0; emac_config.dirty_tx = 0; - dma_phy = (uint32_t)(emac_config.dma_etx); - p = emac_config.dma_etx; - - for (i = 0; i < (DMA_TX_BUF_NUM - 1); i++ ) { - dma_phy += sizeof(struct dma_extended_desc); + p = emac_dma_tx_chain_buf[0]; + for (i = 0; i < (DMA_TX_BUF_NUM - 1); i++) { emac_clean_tx_desc(p); - p->basic.desc3 = dma_phy; - p->basic.desc2 = (uint32_t)(&emac_dma_tx_buf[0]) + (i * DMA_TX_BUF_SIZE); - p++; + /* point to the buffer */ + p->basic.desc2 = (uint32_t)(emac_dma_tx_buf[i]); + /* point to next descriptor */ + p->basic.desc3 = (uint32_t)(emac_dma_tx_chain_buf[i + 1]); + p = emac_dma_tx_chain_buf[i + 1]; } - p->basic.desc3 = (uint32_t)(emac_config.dma_etx); - p->basic.desc2 = (uint32_t)(&emac_dma_tx_buf[0]) + (i * DMA_TX_BUF_SIZE); - - //init desc0 desc1 emac_clean_tx_desc(p); + /* point to the buffer */ + p->basic.desc2 = (uint32_t)(emac_dma_tx_buf[i]); + /* point to first descriptor */ + p->basic.desc3 = (uint32_t)(emac_config.dma_etx); //init rx chain - emac_config.dma_erx = (struct dma_extended_desc *)(&emac_dma_rx_chain_buf[0]); + emac_config.dma_erx = emac_dma_rx_chain_buf[0]; emac_config.cnt_rx = 0; emac_config.cur_rx = 0; emac_config.dirty_rx = 0; - dma_phy = (uint32_t)(emac_config.dma_erx); - p = emac_config.dma_erx; - - for (i = 0; i < (DMA_RX_BUF_NUM - 1); i++ ) { - dma_phy += sizeof(struct dma_extended_desc); - emac_clean_rx_desc(p, (uint32_t)(&emac_dma_rx_buf[0]) + (i * DMA_RX_BUF_SIZE)); - p->basic.desc3 = dma_phy; - p++; + p = emac_dma_rx_chain_buf[0]; + for (i = 0; i < (DMA_RX_BUF_NUM - 1); i++) { + emac_clean_rx_desc(p, (uint32_t)(emac_dma_rx_buf[i])); + /* point to the buffer */ + p->basic.desc3 = (uint32_t)(emac_dma_rx_chain_buf[i + 1]); + /* point to next descriptor */ + p = emac_dma_rx_chain_buf[i + 1]; } - - emac_clean_rx_desc(p, (uint32_t)(&emac_dma_rx_buf[0]) + (i * DMA_RX_BUF_SIZE)); + /* point to the buffer */ + emac_clean_rx_desc(p, (uint32_t)(emac_dma_rx_buf[i])); + /* point to first descriptor */ p->basic.desc3 = (uint32_t)(emac_config.dma_erx); } @@ -214,13 +229,14 @@ void esp_eth_smi_write(uint32_t reg_num, uint16_t value) { uint32_t phy_num = emac_config.phy_addr; - while (REG_GET_BIT(EMAC_GMIIADDR_REG, EMAC_MIIBUSY) == 1 ) { + while (REG_GET_BIT(EMAC_GMIIADDR_REG, EMAC_MIIBUSY) == 1) { } REG_WRITE(EMAC_MIIDATA_REG, value); - REG_WRITE(EMAC_GMIIADDR_REG, 0x3 | ((reg_num & 0x1f) << 6) | ((phy_num & 0x1f) << 11) | ((0x3) << 2)); + REG_WRITE(EMAC_GMIIADDR_REG, 0x3 | ((reg_num & 0x1f) << 6) | + ((phy_num & 0x1f) << 11) | ((0x3) << 2)); - while (REG_GET_BIT(EMAC_GMIIADDR_REG, EMAC_MIIBUSY) == 1 ) { + while (REG_GET_BIT(EMAC_GMIIADDR_REG, EMAC_MIIBUSY) == 1) { } } @@ -229,21 +245,24 @@ uint16_t esp_eth_smi_read(uint32_t reg_num) uint32_t phy_num = emac_config.phy_addr; uint16_t value = 0; - while (REG_GET_BIT(EMAC_GMIIADDR_REG, EMAC_MIIBUSY) == 1 ) { + while (REG_GET_BIT(EMAC_GMIIADDR_REG, EMAC_MIIBUSY) == 1) { } - REG_WRITE(EMAC_GMIIADDR_REG, 0x1 | ((reg_num & 0x1f) << 6) | ((phy_num & 0x1f) << 11) | (0x3 << 2)); - while (REG_GET_BIT(EMAC_GMIIADDR_REG, EMAC_MIIBUSY) == 1 ) { + REG_WRITE(EMAC_GMIIADDR_REG, 0x1 | ((reg_num & 0x1f) << 6) | + ((phy_num & 0x1f) << 11) | (0x3 << 2)); + while (REG_GET_BIT(EMAC_GMIIADDR_REG, EMAC_MIIBUSY) == 1) { } value = (REG_READ(EMAC_MIIDATA_REG) & 0xffff); return value; } -esp_err_t esp_eth_smi_wait_value(uint32_t reg_num, uint16_t value, uint16_t value_mask, int timeout_ms) +esp_err_t esp_eth_smi_wait_value(uint32_t reg_num, uint16_t value, + uint16_t value_mask, int timeout_ms) { unsigned start = xTaskGetTickCount(); - unsigned timeout_ticks = (timeout_ms + portTICK_PERIOD_MS - 1) / portTICK_PERIOD_MS; + unsigned timeout_ticks = (timeout_ms + portTICK_PERIOD_MS - 1) / + portTICK_PERIOD_MS; uint16_t current_value = 0; while (timeout_ticks == 0 || (xTaskGetTickCount() - start < timeout_ticks)) { @@ -253,13 +272,12 @@ esp_err_t esp_eth_smi_wait_value(uint32_t reg_num, uint16_t value, uint16_t valu } vTaskDelay(1); } - ESP_LOGE(TAG, "Timed out waiting for PHY register 0x%x to have value 0x%04x (mask 0x%04x). Current value 0x%04x", + ESP_LOGE(TAG, "Timed out waiting for PHY register 0x%x to have value 0x%04x(mask 0x%04x). Current value 0x%04x", reg_num, value, value_mask, current_value); return ESP_ERR_TIMEOUT; } - -static void emac_set_user_config_data(eth_config_t *config ) +static void emac_set_user_config_data(eth_config_t *config) { emac_config.phy_addr = config->phy_addr; emac_config.mac_mode = config->mac_mode; @@ -275,11 +293,12 @@ static void emac_set_user_config_data(eth_config_t *config ) emac_config.emac_flow_ctrl_enable = config->flow_ctrl_enable; #else if(config->flow_ctrl_enable == true) { - ESP_LOGE(TAG, "eth flow ctrl init err!!! Please run menuconfig and make sure DMA_RX_BUF_NUM > 9 ."); + ESP_LOGE(TAG, "Can only configure flow_ctrl_enable==true if DMA_RX_BUF_NUM in menuconfig is >9. Disabling flow control."); } emac_config.emac_flow_ctrl_enable = false; #endif - emac_config.emac_phy_get_partner_pause_enable = config->phy_get_partner_pause_enable; + emac_config.emac_phy_get_partner_pause_enable = + config->phy_get_partner_pause_enable; emac_config.emac_phy_power_enable = config->phy_power_enable; } @@ -347,12 +366,13 @@ static esp_err_t emac_verify_args(void) ret = ESP_FAIL; } - if (emac_config.emac_flow_ctrl_enable == true && emac_config.emac_phy_get_partner_pause_enable == NULL) { + if (emac_config.emac_flow_ctrl_enable == true && + emac_config.emac_phy_get_partner_pause_enable == NULL) { ESP_LOGE(TAG, "phy get partner pause enable func is null"); ret = ESP_FAIL; } - if(emac_config.emac_phy_power_enable == NULL) { + if (emac_config.emac_phy_power_enable == NULL) { ESP_LOGE(TAG, "phy power enable func is null"); ret = ESP_FAIL; } @@ -368,12 +388,12 @@ static void emac_process_tx(void) return; } - xSemaphoreTakeRecursive( emac_tx_xMutex, ( TickType_t ) portMAX_DELAY ); + xSemaphoreTakeRecursive(emac_tx_xMutex, portMAX_DELAY); - while (((uint32_t) & (emac_config.dma_etx[emac_config.dirty_tx].basic.desc0) != cur_tx_desc)) { - emac_clean_tx_desc(&(emac_config.dma_etx[emac_config.dirty_tx])); + while ((uint32_t)(emac_dma_tx_chain_buf[emac_config.dirty_tx]) != cur_tx_desc) { + emac_clean_tx_desc(emac_dma_tx_chain_buf[emac_config.dirty_tx]); emac_config.dirty_tx = (emac_config.dirty_tx + 1) % DMA_TX_BUF_NUM; - emac_config.cnt_tx --; + emac_config.cnt_tx--; if (emac_config.cnt_tx < 0) { ESP_LOGE(TAG, "emac tx chain err"); @@ -381,14 +401,14 @@ static void emac_process_tx(void) cur_tx_desc = emac_read_tx_cur_reg(); } - xSemaphoreGiveRecursive( emac_tx_xMutex ); + xSemaphoreGiveRecursive(emac_tx_xMutex); } void esp_eth_free_rx_buf(void *buf) { - xSemaphoreTakeRecursive( emac_rx_xMutex, ( TickType_t ) portMAX_DELAY ); + xSemaphoreTakeRecursive(emac_rx_xMutex, portMAX_DELAY); - emac_clean_rx_desc(&(emac_config.dma_erx[emac_config.cur_rx]), (uint32_t) buf); + emac_clean_rx_desc(emac_dma_rx_chain_buf[emac_config.cur_rx], (uint32_t)buf); emac_config.cur_rx = (emac_config.cur_rx + 1) % DMA_RX_BUF_NUM; emac_config.cnt_rx--; if (emac_config.cnt_rx < 0) { @@ -396,11 +416,12 @@ void esp_eth_free_rx_buf(void *buf) } emac_poll_rx_cmd(); - xSemaphoreGiveRecursive( emac_rx_xMutex ); + xSemaphoreGiveRecursive(emac_rx_xMutex); if (emac_config.emac_flow_ctrl_partner_support == true) { portENTER_CRITICAL(&g_emac_mux); - if (pause_send == true && emac_config.cnt_rx < FLOW_CONTROL_LOW_WATERMARK) { + if (pause_send == true && emac_config.cnt_rx < + FLOW_CONTROL_LOW_WATERMARK) { emac_send_pause_zero_frame_enable(); pause_send = false; } @@ -412,7 +433,7 @@ static uint32_t IRAM_ATTR emac_get_rxbuf_count_in_intr(void) { uint32_t cnt = 0; uint32_t cur_rx_desc = emac_read_rx_cur_reg(); - struct dma_extended_desc *cur_desc = (struct dma_extended_desc *)cur_rx_desc; + struct dma_extended_desc *cur_desc = (dma_extended_desc_t *)cur_rx_desc; while (cur_desc->basic.desc0 == EMAC_DESC_RX_OWN && cnt < DMA_RX_BUF_NUM) { cnt++; @@ -429,12 +450,16 @@ static void emac_process_rx(void) } uint32_t cur_rx_desc = emac_read_rx_cur_reg(); - while (((uint32_t) & (emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) != cur_rx_desc)) { + while (((uint32_t)(emac_dma_rx_chain_buf[emac_config.dirty_rx]) != cur_rx_desc)) { //copy data to lwip - emac_config.emac_tcpip_input((void *)(emac_config.dma_erx[emac_config.dirty_rx].basic.desc2), - (((emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL); + emac_config.emac_tcpip_input((emac_dma_rx_buf[emac_config.dirty_rx]), + (((emac_dma_rx_chain_buf[emac_config.dirty_rx]->basic.desc0) >> + EMAC_DESC_FRAME_LENGTH_S) & + EMAC_DESC_FRAME_LENGTH), + NULL); - emac_clean_rx_desc(&(emac_config.dma_erx[emac_config.dirty_rx]), (emac_config.dma_erx[emac_config.dirty_rx].basic.desc2)); + emac_clean_rx_desc(emac_dma_rx_chain_buf[emac_config.dirty_rx], + (uint32_t)(emac_dma_rx_buf[emac_config.dirty_rx])); emac_config.dirty_rx = (emac_config.dirty_rx + 1) % DMA_RX_BUF_NUM; //if open this ,one intr can do many intrs ? @@ -453,16 +478,20 @@ static void emac_process_rx_unavail(void) uint32_t dirty_cnt = 0; while (dirty_cnt < DMA_RX_BUF_NUM) { - if (emac_config.dma_erx[emac_config.dirty_rx].basic.desc0 == EMAC_DESC_RX_OWN) { + if (emac_dma_rx_chain_buf[emac_config.dirty_rx]->basic.desc0 == EMAC_DESC_RX_OWN) { break; } - dirty_cnt ++; + dirty_cnt++; //copy data to lwip - emac_config.emac_tcpip_input((void *)(emac_config.dma_erx[emac_config.dirty_rx].basic.desc2), - (((emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL); + emac_config.emac_tcpip_input((emac_dma_rx_buf[emac_config.dirty_rx]), + (((emac_dma_rx_chain_buf[emac_config.dirty_rx]->basic.desc0) >> + EMAC_DESC_FRAME_LENGTH_S) & + EMAC_DESC_FRAME_LENGTH), + NULL); - emac_clean_rx_desc(&(emac_config.dma_erx[emac_config.dirty_rx]), (emac_config.dma_erx[emac_config.dirty_rx].basic.desc2)); + emac_clean_rx_desc(emac_dma_rx_chain_buf[emac_config.dirty_rx], + (uint32_t)(emac_dma_rx_buf[emac_config.dirty_rx])); emac_config.dirty_rx = (emac_config.dirty_rx + 1) % DMA_RX_BUF_NUM; } emac_enable_rx_intr(); @@ -477,11 +506,11 @@ static void emac_process_rx_unavail(void) return; } - xSemaphoreTakeRecursive( emac_rx_xMutex, ( TickType_t ) portMAX_DELAY ); + xSemaphoreTakeRecursive(emac_rx_xMutex, portMAX_DELAY); while (emac_config.cnt_rx < DMA_RX_BUF_NUM) { - if (emac_config.dma_erx[emac_config.dirty_rx].basic.desc0 == EMAC_DESC_RX_OWN) { + if (emac_dma_rx_chain_buf[emac_config.dirty_rx]->basic.desc0 == EMAC_DESC_RX_OWN) { break; } @@ -493,13 +522,15 @@ static void emac_process_rx_unavail(void) emac_config.dirty_rx = (emac_config.dirty_rx + 1) % DMA_RX_BUF_NUM; //copy data to lwip - emac_config.emac_tcpip_input((void *)(emac_config.dma_erx[tmp_dirty].basic.desc2), - (((emac_config.dma_erx[tmp_dirty].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL); - - } + emac_config.emac_tcpip_input((emac_dma_rx_buf[tmp_dirty]), + (((emac_dma_rx_chain_buf[tmp_dirty]->basic.desc0) >> + EMAC_DESC_FRAME_LENGTH_S) & + EMAC_DESC_FRAME_LENGTH), + NULL); + } emac_enable_rx_intr(); emac_enable_rx_unavail_intr(); - xSemaphoreGiveRecursive( emac_rx_xMutex ); + xSemaphoreGiveRecursive(emac_rx_xMutex); } static void emac_process_rx(void) @@ -510,31 +541,36 @@ static void emac_process_rx(void) uint32_t cur_rx_desc = emac_read_rx_cur_reg(); - xSemaphoreTakeRecursive( emac_rx_xMutex, ( TickType_t ) portMAX_DELAY ); + xSemaphoreTakeRecursive(emac_rx_xMutex, portMAX_DELAY); - if (((uint32_t) & (emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) != cur_rx_desc)) { + if (((uint32_t)(emac_dma_rx_chain_buf[emac_config.dirty_rx])) != + cur_rx_desc) { - while (((uint32_t) & (emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) != cur_rx_desc) && emac_config.cnt_rx < DMA_RX_BUF_NUM ) { + while (((uint32_t)(emac_dma_rx_chain_buf[emac_config.dirty_rx]) != cur_rx_desc) && + emac_config.cnt_rx < DMA_RX_BUF_NUM) { emac_config.cnt_rx++; - if (emac_config.cnt_rx > DMA_RX_BUF_NUM ) { + if (emac_config.cnt_rx > DMA_RX_BUF_NUM) { ESP_LOGE(TAG, "emac rx buf err!!\n"); } uint32_t tmp_dirty = emac_config.dirty_rx; emac_config.dirty_rx = (emac_config.dirty_rx + 1) % DMA_RX_BUF_NUM; - //copy data to lwip - emac_config.emac_tcpip_input((void *)(emac_config.dma_erx[tmp_dirty].basic.desc2), - (((emac_config.dma_erx[tmp_dirty].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL); + emac_config.emac_tcpip_input((emac_dma_rx_buf[tmp_dirty]), + (((emac_dma_rx_chain_buf[tmp_dirty]->basic.desc0) >> + EMAC_DESC_FRAME_LENGTH_S) & + EMAC_DESC_FRAME_LENGTH), + NULL); cur_rx_desc = emac_read_rx_cur_reg(); } } else { if (emac_config.cnt_rx < DMA_RX_BUF_NUM) { - if ((emac_config.dma_erx[emac_config.dirty_rx].basic.desc0 & EMAC_DESC_RX_OWN) == 0) { + if ((emac_dma_rx_chain_buf[emac_config.dirty_rx]->basic.desc0 & + EMAC_DESC_RX_OWN) == 0) { while (emac_config.cnt_rx < DMA_RX_BUF_NUM) { - if (emac_config.dma_erx[emac_config.dirty_rx].basic.desc0 == EMAC_DESC_RX_OWN) { + if (emac_dma_rx_chain_buf[emac_config.dirty_rx]->basic.desc0 == EMAC_DESC_RX_OWN) { break; } @@ -543,18 +579,21 @@ static void emac_process_rx(void) ESP_LOGE(TAG, "emac rx buf err!!!\n"); } uint32_t tmp_dirty = emac_config.dirty_rx; - emac_config.dirty_rx = (emac_config.dirty_rx + 1) % DMA_RX_BUF_NUM; + emac_config.dirty_rx = (emac_config.dirty_rx + 1) % + DMA_RX_BUF_NUM; //copy data to lwip - emac_config.emac_tcpip_input((void *)(emac_config.dma_erx[tmp_dirty].basic.desc2), - (((emac_config.dma_erx[tmp_dirty].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL); - - } + emac_config.emac_tcpip_input((emac_dma_rx_buf[tmp_dirty]), + (((emac_dma_rx_chain_buf[tmp_dirty]->basic.desc0) >> + EMAC_DESC_FRAME_LENGTH_S) & + EMAC_DESC_FRAME_LENGTH), + NULL); + } } } } emac_enable_rx_intr(); - xSemaphoreGiveRecursive( emac_rx_xMutex ); + xSemaphoreGiveRecursive(emac_rx_xMutex); } #endif @@ -570,7 +609,8 @@ static void IRAM_ATTR emac_process_intr(void *arg) if (event & EMAC_RECV_INT) { emac_disable_rx_intr(); if (emac_config.emac_flow_ctrl_partner_support == true) { - if (emac_get_rxbuf_count_in_intr() < FLOW_CONTROL_HIGH_WATERMARK && pause_send == false ) { + if (emac_get_rxbuf_count_in_intr() < FLOW_CONTROL_HIGH_WATERMARK && + pause_send == false) { pause_send = true; emac_send_pause_frame_enable(); } @@ -591,7 +631,9 @@ static void IRAM_ATTR emac_process_intr(void *arg) static void emac_set_macaddr_reg(void) { REG_SET_FIELD(EMAC_ADDR0HIGH_REG, EMAC_ADDRESS0_HI, (emac_config.macaddr[0] << 8) | (emac_config.macaddr[1])); - REG_WRITE(EMAC_ADDR0LOW_REG, (emac_config.macaddr[2] << 24) | (emac_config.macaddr[3] << 16) | (emac_config.macaddr[4] << 8) | (emac_config.macaddr[5])); + REG_WRITE(EMAC_ADDR0LOW_REG, (emac_config.macaddr[2] << 24) | + (emac_config.macaddr[3] << 16) | (emac_config.macaddr[4] << 8) | + (emac_config.macaddr[5])); } static void emac_check_phy_init(void) @@ -612,7 +654,8 @@ static void emac_check_phy_init(void) emac_config.emac_flow_ctrl_partner_support = false; #else if (emac_config.emac_flow_ctrl_enable == true) { - if (emac_config.emac_phy_get_partner_pause_enable() == true && emac_config.emac_phy_get_duplex_mode() == ETH_MODE_FULLDUPLEX) { + if (emac_config.emac_phy_get_partner_pause_enable() == true && + emac_config.emac_phy_get_duplex_mode() == ETH_MODE_FULLDUPLEX) { emac_enable_flowctrl(); emac_config.emac_flow_ctrl_partner_support = true; } else { @@ -635,7 +678,7 @@ static void emac_process_link_updown(bool link_status) if (link_status == true) { emac_check_phy_init(); - ESP_LOGI(TAG, "eth link_up!!!"); + ESP_LOGD(TAG, "eth link_up"); emac_enable_dma_tx(); emac_enable_dma_rx(); for (i = 0; i < PHY_LINK_CHECK_NUM; i++) { @@ -644,7 +687,7 @@ static void emac_process_link_updown(bool link_status) evt.event_id = SYSTEM_EVENT_ETH_CONNECTED; } else { - ESP_LOGI(TAG, "eth link_down!!!"); + ESP_LOGD(TAG, "eth link_down"); emac_disable_dma_tx(); emac_disable_dma_rx(); evt.event_id = SYSTEM_EVENT_ETH_DISCONNECTED; @@ -667,31 +710,32 @@ esp_err_t esp_eth_tx(uint8_t *buf, uint16_t size) { esp_err_t ret = ESP_OK; - if (emac_config.emac_status != EMAC_RUNTIME_START || emac_config.emac_status == EMAC_RUNTIME_NOT_INIT) { - ESP_LOGI(TAG, "tx netif close"); + if (emac_config.emac_status != EMAC_RUNTIME_START) { + ESP_LOGE(TAG, "tx netif is not ready, emac_status=%d", + emac_config.emac_status); ret = ERR_IF; return ret; } - xSemaphoreTakeRecursive( emac_tx_xMutex, ( TickType_t ) portMAX_DELAY ); + xSemaphoreTakeRecursive(emac_tx_xMutex, portMAX_DELAY); if (emac_config.cnt_tx == DMA_TX_BUF_NUM - 1) { ESP_LOGD(TAG, "tx buf full"); ret = ERR_MEM; goto _exit; } - memcpy((uint8_t *)(emac_config.dma_etx[emac_config.cur_tx].basic.desc2), (uint8_t *)buf, size); + memcpy(emac_dma_tx_buf[emac_config.cur_tx], buf, size); - emac_setup_tx_desc(&(emac_config.dma_etx[emac_config.cur_tx]), size); + emac_setup_tx_desc(emac_dma_tx_chain_buf[emac_config.cur_tx], size); - emac_config.cnt_tx ++; - emac_config.cur_tx = (emac_config.cur_tx + 1) % DMA_TX_BUF_NUM ; + emac_config.cnt_tx++; + emac_config.cur_tx = (emac_config.cur_tx + 1) % DMA_TX_BUF_NUM; emac_poll_tx_cmd(); _exit: - xSemaphoreGiveRecursive( emac_tx_xMutex ); + xSemaphoreGiveRecursive(emac_tx_xMutex); return ret; } @@ -702,17 +746,16 @@ static void emac_init_default_data(void) void emac_process_link_check(void) { - if (emac_config.emac_status != EMAC_RUNTIME_START || - emac_config.emac_status == EMAC_RUNTIME_NOT_INIT) { + if (emac_config.emac_status != EMAC_RUNTIME_START) { return; } - if (emac_config.emac_phy_check_link() == true ) { - if (emac_config.phy_link_up == false) { + if (emac_config.emac_phy_check_link()) { + if (!emac_config.phy_link_up) { emac_process_link_updown(true); } } else { - if (emac_config.phy_link_up == true) { + if (emac_config.phy_link_up) { emac_process_link_updown(false); } } @@ -725,8 +768,12 @@ void emac_link_check_func(void *pv_parameters) static bool emac_link_check_timer_init(void) { - emac_timer = xTimerCreate("emac_timer", (2000 / portTICK_PERIOD_MS), - pdTRUE, (void *)rand(), emac_link_check_func); + emac_timer = xTimerCreate("emac_timer", + (CONFIG_EMAC_CHECK_LINK_PERIOD_MS / + portTICK_PERIOD_MS), + pdTRUE, + NULL, + emac_link_check_func); if (emac_timer == NULL) { return false; } else { @@ -761,10 +808,10 @@ static bool emac_link_check_timer_delete(void) static void emac_start(void *param) { - struct emac_post_cmd *post_cmd = (struct emac_post_cmd *)param; + struct emac_post_cmd *post_cmd = (struct emac_post_cmd *)param; struct emac_open_cmd *cmd = (struct emac_open_cmd *)(post_cmd->cmd); - ESP_LOGI(TAG , "emac start !!!\n"); + ESP_LOGD(TAG, "emac start"); cmd->err = EMAC_CMD_OK; emac_enable_clk(true); @@ -803,7 +850,7 @@ static void emac_start(void *param) xSemaphoreGive(emac_g_sem); } - ESP_LOGI(TAG, "emac start success !!!"); + ESP_LOGD(TAG, "emac start success"); } esp_err_t esp_eth_enable(void) @@ -849,8 +896,8 @@ cleanup: static void emac_stop(void *param) { - struct emac_post_cmd *post_cmd = (struct emac_post_cmd *)param; - ESP_LOGI(TAG, "emac stop"); + struct emac_post_cmd *post_cmd = (struct emac_post_cmd *)param; + ESP_LOGD(TAG, "emac stop"); emac_link_check_timer_stop(); emac_link_check_timer_delete(); @@ -871,7 +918,7 @@ static void emac_stop(void *param) xSemaphoreGive(emac_g_sem); } - ESP_LOGI(TAG, "emac stop success !!!"); + ESP_LOGD(TAG, "emac stop success"); } esp_err_t esp_eth_disable(void) @@ -906,7 +953,7 @@ esp_err_t esp_eth_disable(void) static esp_err_t emac_ioctl(emac_sig_t sig, emac_par_t par) { esp_err_t ret = ESP_OK; - struct emac_post_cmd *post_cmd = (struct emac_post_cmd *)par; + struct emac_post_cmd *post_cmd = (struct emac_post_cmd *)par; xTaskHandle task_hdl = xTaskGetCurrentTaskHandle(); if (emac_task_hdl != task_hdl) { @@ -948,7 +995,7 @@ void emac_task(void *pv) emac_event_t e; for (;;) { - if (xQueueReceive(emac_xqueue, &e, (portTickType)portMAX_DELAY) == pdTRUE) { + if (xQueueReceive(emac_xqueue, &e, portMAX_DELAY) == pdTRUE) { portENTER_CRITICAL(&g_emac_mux); emac_sig_cnt[e.sig]--; portEXIT_CRITICAL(&g_emac_mux); @@ -1020,8 +1067,18 @@ esp_err_t IRAM_ATTR emac_post(emac_sig_t sig, emac_par_t par) esp_err_t esp_eth_init(eth_config_t *config) { - esp_event_set_default_eth_handlers(); - return esp_eth_init_internal(config); + /* dynamically alloc memory for ethernet dma */ + for (int i = 0; i < DMA_RX_BUF_NUM; i++) { + emac_dma_rx_chain_buf[i] = (struct dma_extended_desc *)heap_caps_malloc(sizeof(struct dma_extended_desc), MALLOC_CAP_DMA); + emac_dma_rx_buf[i] = (uint8_t *)heap_caps_malloc(DMA_RX_BUF_SIZE, MALLOC_CAP_DMA); + } + for (int i = 0; i < DMA_TX_BUF_NUM; i++) { + emac_dma_tx_chain_buf[i] = (struct dma_extended_desc *)heap_caps_malloc(sizeof(struct dma_extended_desc), MALLOC_CAP_DMA); + emac_dma_tx_buf[i] = (uint8_t *)heap_caps_malloc(DMA_TX_BUF_SIZE, MALLOC_CAP_DMA); + } + + esp_event_set_default_eth_handlers(); + return esp_eth_init_internal(config); } esp_err_t esp_eth_init_internal(eth_config_t *config) @@ -1033,7 +1090,7 @@ esp_err_t esp_eth_init_internal(eth_config_t *config) emac_init_default_data(); - if (config != NULL ) { + if (config != NULL) { emac_set_user_config_data(config); } @@ -1051,31 +1108,28 @@ esp_err_t esp_eth_init_internal(eth_config_t *config) if (emac_config.clock_mode != ETH_CLOCK_GPIO0_IN) { // 50 MHz = 40MHz * (6 + 4) / (2 * (2 + 2) = 400MHz / 8 rtc_clk_apll_enable(1, 0, 0, 6, 2); - // the next to values have to be set AFTER "periph_module_enable" is called REG_SET_FIELD(EMAC_EX_CLKOUT_CONF_REG, EMAC_EX_CLK_OUT_H_DIV_NUM, 0); REG_SET_FIELD(EMAC_EX_CLKOUT_CONF_REG, EMAC_EX_CLK_OUT_DIV_NUM, 0); - if (emac_config.clock_mode == ETH_CLOCK_GPIO0_OUT) { - PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1); - REG_WRITE(PIN_CTRL, 6); - ESP_LOGD(TAG, "EMAC 50MHz clock output on GPIO0"); - } else if (emac_config.clock_mode == ETH_CLOCK_GPIO16_OUT) { + if (emac_config.clock_mode == ETH_CLOCK_GPIO16_OUT) { PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO16_U, FUNC_GPIO16_EMAC_CLK_OUT); ESP_LOGD(TAG, "EMAC 50MHz clock output on GPIO16"); } else if (emac_config.clock_mode == ETH_CLOCK_GPIO17_OUT) { - PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO17_U, FUNC_GPIO17_EMAC_CLK_OUT_180); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO17_U, + FUNC_GPIO17_EMAC_CLK_OUT_180); ESP_LOGD(TAG, "EMAC 50MHz inverted clock output on GPIO17"); } } emac_enable_clk(true); - REG_SET_FIELD(EMAC_EX_PHYINF_CONF_REG, EMAC_EX_PHY_INTF_SEL, EMAC_EX_PHY_INTF_RMII); + REG_SET_FIELD(EMAC_EX_PHYINF_CONF_REG, EMAC_EX_PHY_INTF_SEL, + EMAC_EX_PHY_INTF_RMII); emac_dma_init(); if (emac_config.clock_mode == ETH_CLOCK_GPIO0_IN) { // external clock on GPIO0 - REG_SET_BIT(EMAC_EX_CLK_CTRL_REG, EMAC_EX_EXT_OSC_EN); - REG_CLR_BIT(EMAC_EX_CLK_CTRL_REG, EMAC_EX_INT_OSC_EN); + REG_SET_BIT(EMAC_EX_CLK_CTRL_REG, EMAC_EX_EXT_OSC_EN); + REG_CLR_BIT(EMAC_EX_CLK_CTRL_REG, EMAC_EX_INT_OSC_EN); REG_SET_BIT(EMAC_EX_OSCCLK_CONF_REG, EMAC_EX_OSC_CLK_SEL); ESP_LOGD(TAG, "External clock input 50MHz on GPIO0"); if (emac_config.mac_mode == ETH_MODE_MII) { @@ -1084,8 +1138,8 @@ esp_err_t esp_eth_init_internal(eth_config_t *config) } } else { // internal clock by APLL - REG_CLR_BIT(EMAC_EX_CLK_CTRL_REG, EMAC_EX_EXT_OSC_EN); - REG_SET_BIT(EMAC_EX_CLK_CTRL_REG, EMAC_EX_INT_OSC_EN); + REG_CLR_BIT(EMAC_EX_CLK_CTRL_REG, EMAC_EX_EXT_OSC_EN); + REG_SET_BIT(EMAC_EX_CLK_CTRL_REG, EMAC_EX_INT_OSC_EN); REG_CLR_BIT(EMAC_EX_OSCCLK_CONF_REG, EMAC_EX_OSC_CLK_SEL); } @@ -1101,7 +1155,8 @@ esp_err_t esp_eth_init_internal(eth_config_t *config) emac_rx_xMutex = xSemaphoreCreateRecursiveMutex(); emac_tx_xMutex = xSemaphoreCreateRecursiveMutex(); emac_xqueue = xQueueCreate(EMAC_EVT_QNUM, sizeof(emac_event_t)); - xTaskCreate(emac_task, "emacT", 2048, NULL, EMAC_TASK_PRIORITY, &emac_task_hdl); + xTaskCreate(emac_task, "emacT", 2048, NULL, EMAC_TASK_PRIORITY, + &emac_task_hdl); emac_enable_clk(false); esp_intr_alloc(ETS_ETH_MAC_INTR_SOURCE, 0, emac_process_intr, NULL, NULL); @@ -1111,3 +1166,43 @@ esp_err_t esp_eth_init_internal(eth_config_t *config) _exit: return ret; } + +esp_err_t esp_eth_deinit(void) +{ + esp_err_t ret = ESP_FAIL; + + if (!emac_task_hdl) { + ret = ESP_ERR_INVALID_STATE; + goto _exit; + } + + vTaskDelete(emac_task_hdl); + emac_task_hdl = NULL; + + vQueueDelete(emac_xqueue); + vSemaphoreDelete(emac_tx_xMutex); + vSemaphoreDelete(emac_rx_xMutex); + vSemaphoreDelete(emac_g_sem); + emac_reset_dma_chain(); + emac_config.emac_phy_power_enable(false); + periph_module_disable(PERIPH_EMAC_MODULE); + emac_config.emac_status = EMAC_RUNTIME_NOT_INIT; + + /* free memory that dynamically allocted */ + for (int i = 0; i < DMA_RX_BUF_NUM; i++) { + free(emac_dma_rx_chain_buf[i]); + free(emac_dma_rx_buf[i]); + emac_dma_rx_chain_buf[i] = NULL; + emac_dma_rx_buf[i] = NULL; + } + for (int i = 0; i < DMA_TX_BUF_NUM; i++) { + free(emac_dma_tx_chain_buf[i]); + free(emac_dma_tx_buf[i]); + emac_dma_tx_chain_buf[i] = NULL; + emac_dma_tx_buf[i] = NULL; + } + ret = ESP_OK; + +_exit: + return ret; +} diff --git a/components/ethernet/include/esp_eth.h b/components/ethernet/include/esp_eth.h index f4622d0ee..3d16ffa5b 100644 --- a/components/ethernet/include/esp_eth.h +++ b/components/ethernet/include/esp_eth.h @@ -29,10 +29,9 @@ typedef enum { } eth_mode_t; typedef enum { - ETH_CLOCK_GPIO0_IN = 0, - ETH_CLOCK_GPIO0_OUT = 1, + ETH_CLOCK_GPIO0_IN = 0, ETH_CLOCK_GPIO16_OUT = 2, - ETH_CLOCK_GPIO17_OUT = 3 + ETH_CLOCK_GPIO17_OUT = 3, } eth_clock_mode_t; typedef enum { @@ -125,6 +124,16 @@ typedef struct { */ esp_err_t esp_eth_init(eth_config_t *config); +/** + * @brief Deinit ethernet mac + * + * @return + * - ESP_OK + * - ESP_FAIL + * - ESP_ERR_INVALID_STATE + */ +esp_err_t esp_eth_deinit(void); + /** * @brief Init Ethernet mac driver only * @@ -236,7 +245,8 @@ esp_err_t esp_eth_smi_wait_value(uint32_t reg_num, uint16_t value, uint16_t valu * * @return ESP_OK if desired value matches, ESP_ERR_TIMEOUT if timed out. */ -static inline esp_err_t esp_eth_smi_wait_set(uint32_t reg_num, uint16_t value_mask, int timeout_ms) { +static inline esp_err_t esp_eth_smi_wait_set(uint32_t reg_num, uint16_t value_mask, int timeout_ms) +{ return esp_eth_smi_wait_value(reg_num, value_mask, value_mask, timeout_ms); } @@ -265,7 +275,7 @@ void esp_eth_get_mac(uint8_t mac[6]); * * @param[in] mac: the Mac address. * - * @return + * @return * - ESP_OK: succeed * - ESP_ERR_INVALID_MAC: invalid mac address */ diff --git a/components/ethernet/test/component.mk b/components/ethernet/test/component.mk new file mode 100644 index 000000000..5dd172bdb --- /dev/null +++ b/components/ethernet/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/ethernet/test/test_emac_deinit.c b/components/ethernet/test/test_emac_deinit.c new file mode 100644 index 000000000..c9f7ee8cd --- /dev/null +++ b/components/ethernet/test/test_emac_deinit.c @@ -0,0 +1,87 @@ +/** + * @brief This test has just run in the ESP32_Ethernet_V3 board, which featured + * in PoE submodule and TLK110 PHY. The 50MHz clock used by MAC and PHY is + * supplied by external oscillator through GPIO0. + * + * @file test_emac_deinit.c + * @author morris + * @date 2018-08-24 + */ +#include +#include "unity.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include "esp_system.h" +#include "esp_err.h" +#include "esp_event_loop.h" +#include "esp_event.h" +#include "esp_log.h" +#include "esp_eth.h" + +#include "rom/gpio.h" + +#include "tcpip_adapter.h" +#include "driver/gpio.h" +#include "driver/periph_ctrl.h" +#include "esp_wifi.h" + +#include "eth_phy/phy_tlk110.h" + +#define DEFAULT_ETHERNET_PHY_CONFIG phy_tlk110_default_ethernet_config + +static const char *TAG = "eth_test_deinit"; + +#define PIN_PHY_POWER 17 +#define PIN_SMI_MDC 23 +#define PIN_SMI_MDIO 18 +#define CONFIG_PHY_ADDRESS 31 +#define CONFIG_PHY_CLOCK_MODE 0 + +static void phy_device_power_enable_via_gpio(bool enable) +{ + if (!enable) { + DEFAULT_ETHERNET_PHY_CONFIG.phy_power_enable(false); + } + + gpio_pad_select_gpio(PIN_PHY_POWER); + gpio_set_direction(PIN_PHY_POWER, GPIO_MODE_OUTPUT); + if (enable == true) { + gpio_set_level(PIN_PHY_POWER, 1); + ESP_LOGI(TAG, "power on ethernet phy"); + } else { + gpio_set_level(PIN_PHY_POWER, 0); + ESP_LOGI(TAG, "power off ethernet phy"); + } + + vTaskDelay(1); // Allow the power up/down to take effect, min 300us + + if (enable) { + /* operates the default phy-specific power on function */ + DEFAULT_ETHERNET_PHY_CONFIG.phy_power_enable(true); + } +} + +static void eth_gpio_config_rmii(void) +{ + phy_rmii_configure_data_interface_pins(); + phy_rmii_smi_configure_pins(PIN_SMI_MDC, PIN_SMI_MDIO); +} + +TEST_CASE("test emac deinit", "[ethernet][ignore]") +{ + eth_config_t config = DEFAULT_ETHERNET_PHY_CONFIG; + config.phy_addr = CONFIG_PHY_ADDRESS; + config.gpio_config = eth_gpio_config_rmii; + config.tcpip_input = tcpip_adapter_eth_input; + config.clock_mode = CONFIG_PHY_CLOCK_MODE; + config.phy_power_enable = phy_device_power_enable_via_gpio; + + ESP_ERROR_CHECK(esp_eth_init(&config)); + ESP_ERROR_CHECK(esp_eth_enable()); + + vTaskDelay(2000 / portTICK_RATE_MS); + + ESP_ERROR_CHECK(esp_eth_disable()); + ESP_ERROR_CHECK(esp_eth_deinit()); +} \ No newline at end of file diff --git a/components/freertos/include/freertos/portmacro.h b/components/freertos/include/freertos/portmacro.h index 6b98b8269..48b899395 100644 --- a/components/freertos/include/freertos/portmacro.h +++ b/components/freertos/include/freertos/portmacro.h @@ -258,8 +258,11 @@ static inline unsigned portENTER_CRITICAL_NESTED() { //Because the ROM routines don't necessarily handle a stack in external RAM correctly, we force //the stack memory to always be internal. -#define pvPortMallocTcbMem(size) heap_caps_malloc(size, MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT) -#define pvPortMallocStackMem(size) heap_caps_malloc(size, MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT) +#define portTcbMemoryCaps (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT) +#define portStackMemoryCaps (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT) + +#define pvPortMallocTcbMem(size) heap_caps_malloc(size, portTcbMemoryCaps) +#define pvPortMallocStackMem(size) heap_caps_malloc(size, portStackMemoryCaps) //xTaskCreateStatic uses these functions to check incoming memory. #define portVALID_TCB_MEM(ptr) (esp_ptr_internal(ptr) && esp_ptr_byte_accessible(ptr)) diff --git a/components/freertos/test/test_freertos_task_delete.c b/components/freertos/test/test_freertos_task_delete.c index 7c3394cbe..348e788ee 100644 --- a/components/freertos/test/test_freertos_task_delete.c +++ b/components/freertos/test/test_freertos_task_delete.c @@ -23,7 +23,8 @@ #define NO_OF_TSKS 3 #define DELAY_TICKS 2 -#define HEAP_CAPS (MALLOC_CAP_INTERNAL|MALLOC_CAP_DEFAULT) +/* Caps of all memory which is allocated from when a task is created */ +#define HEAP_CAPS (portTcbMemoryCaps | portStackMemoryCaps) #define DELAY_US_ITERATIONS 1000 diff --git a/components/heap/test/test_malloc.c b/components/heap/test/test_malloc.c index 1e13ce360..52dc5d171 100644 --- a/components/heap/test/test_malloc.c +++ b/components/heap/test/test_malloc.c @@ -88,23 +88,42 @@ TEST_CASE("Check if reserved DMA pool still can allocate even when malloc()'ed m #endif + +/* As you see, we are desperately trying to outsmart the compiler, so that it + * doesn't warn about oversized allocations in the next two unit tests. + * To be removed when we switch to GCC 8.2 and add + * -Wno-alloc-size-larger-than=PTRDIFF_MAX to CFLAGS for this file. + */ +void* (*g_test_malloc_ptr)(size_t) = &malloc; +void* (*g_test_calloc_ptr)(size_t, size_t) = &calloc; + +void* test_malloc_wrapper(size_t size) +{ + return (*g_test_malloc_ptr)(size); +} + +void* test_calloc_wrapper(size_t count, size_t size) +{ + return (*g_test_calloc_ptr)(count, size); +} + TEST_CASE("alloc overflows should all fail", "[heap]") { /* allocates 8 bytes */ - TEST_ASSERT_NULL(calloc(SIZE_MAX / 2 + 4, 2)); + TEST_ASSERT_NULL(test_calloc_wrapper(SIZE_MAX / 2 + 4, 2)); /* will overflow if any poisoning is enabled (should fail for sensible OOM reasons, otherwise) */ - TEST_ASSERT_NULL(malloc(SIZE_MAX - 1)); - TEST_ASSERT_NULL(calloc(SIZE_MAX - 1, 1)); + TEST_ASSERT_NULL(test_malloc_wrapper(SIZE_MAX - 1)); + TEST_ASSERT_NULL(test_calloc_wrapper(SIZE_MAX - 1, 1)); } TEST_CASE("unreasonable allocs should all fail", "[heap]") { - TEST_ASSERT_NULL(calloc(16, 1024*1024)); - TEST_ASSERT_NULL(malloc(16*1024*1024)); - TEST_ASSERT_NULL(malloc(SIZE_MAX / 2)); - TEST_ASSERT_NULL(malloc(SIZE_MAX - 256)); - TEST_ASSERT_NULL(malloc(xPortGetFreeHeapSize() - 1)); + TEST_ASSERT_NULL(test_calloc_wrapper(16, 1024*1024)); + TEST_ASSERT_NULL(test_malloc_wrapper(16*1024*1024)); + TEST_ASSERT_NULL(test_malloc_wrapper(SIZE_MAX / 2)); + TEST_ASSERT_NULL(test_malloc_wrapper(SIZE_MAX - 256)); + TEST_ASSERT_NULL(test_malloc_wrapper(xPortGetFreeHeapSize() - 1)); } diff --git a/components/idf_test/integration_test/TC_IT_BTSTK_GAP.yml b/components/idf_test/integration_test/TC_IT_BTSTK_GAP.yml index 95bde1cf2..ba1fa304d 100644 --- a/components/idf_test/integration_test/TC_IT_BTSTK_GAP.yml +++ b/components/idf_test/integration_test/TC_IT_BTSTK_GAP.yml @@ -2577,7 +2577,7 @@ test cases: - ID: BTSTK_GAP_14007 <<: *GAP_CASE test point 2: BLE set random address test - summary: BLE set random address as resolvable private address + summary: BLE set random address as resolvable private address and cannot be scan initial condition: BLE_INIT_SMP steps: | 1. SSC1 set adv params and config local privacy as true @@ -2604,7 +2604,7 @@ test cases: - ID: BTSTK_GAP_14008 <<: *GAP_CASE test point 2: BLE set random address test - summary: BLE set random address as resolvable private address + summary: disconnect after encryption and set random address as resolvable private address and reconnect steps: | 1. SSC2 set AuthReqMode and IOCAP,set RspKey as Enc and IRK 2. pairing @@ -2651,7 +2651,7 @@ test cases: - ID: BTSTK_GAP_14009 <<: *GAP_CASE test point 2: BLE set random address test - summary: BLE set random address as resolvable private address + summary: reboot after BLE DUT encryption and set random address as resolvable private address steps: | 1. SSC2 set AuthReqMode and IOCAP,set RspKey as Enc and IRK 2. pairing diff --git a/components/idf_test/integration_test/TC_IT_BTSTK_GATT.yml b/components/idf_test/integration_test/TC_IT_BTSTK_GATT.yml index f1a88881d..569e058e6 100644 --- a/components/idf_test/integration_test/TC_IT_BTSTK_GATT.yml +++ b/components/idf_test/integration_test/TC_IT_BTSTK_GATT.yml @@ -1547,7 +1547,7 @@ test cases: cmd set: - "" - *primary_service_discovery - - - "SSC SSC1 gattc -W -z char -s 0xA000 -c 0xC102 -p 0x10 -v -x01 -w 1" + - - "SSC SSC1 gattc -W -z char -s 0xA000 -c 0xC102 -p 0x10 -v 0x01 -w 1" - ["P SSC1 C +GATTC:WriteOnce"] - ID: BTSTK_GATT_25008 <<: *GATT_CASE @@ -1685,9 +1685,9 @@ test cases: cmd set: - "" - *primary_service_discovery - - - "SSC SSC1 gattc -W -z char -s 0xA002 -c 0xC302 -p 0x10 -v -x01 -w 1" + - - "SSC SSC1 gattc -W -z char -s 0xA002 -c 0xC302 -p 0x10 -v 0x01 -w 1" - ["R SSC1 C +GATTC:Write,OK"] - - - "SSC SSC1 gattc -W -z char -s 0xA002 -c 0xC302 -p 0x10 -v -x01 -w 1" + - - "SSC SSC1 gattc -W -z char -s 0xA002 -c 0xC302 -p 0x10 -v 0x01 -w 1" - ["R SSC1 C +GATTC:Write,OK"] - ID: BTSTK_GATT_25015 <<: *GATT_CASE diff --git a/components/idf_test/integration_test/TC_IT_MESH_EST.yml b/components/idf_test/integration_test/TC_IT_MESH_EST.yml index 9d5ac0b76..35243d59f 100644 --- a/components/idf_test/integration_test/TC_IT_MESH_EST.yml +++ b/components/idf_test/integration_test/TC_IT_MESH_EST.yml @@ -128,7 +128,7 @@ test cases: test point 1: basic function test point 2: mesh network establish version: v1 (2017-7-20) -- CI ready: 'Yes' +- CI ready: 'No' ID: MESH_EST_0404 SDK: ESP32_IDF Test App: SSC_MESH @@ -177,7 +177,7 @@ test cases: test point 1: basic function test point 2: mesh network re-establish version: v1 (2017-7-20) -- CI ready: 'Yes' +- CI ready: 'No' ID: MESH_EST_0405 SDK: ESP32_IDF Test App: SSC_MESH diff --git a/components/lwip/core/ipv4/dhcp.c b/components/lwip/core/ipv4/dhcp.c index 2c4a6b3d2..a31c69904 100644 --- a/components/lwip/core/ipv4/dhcp.c +++ b/components/lwip/core/ipv4/dhcp.c @@ -1712,7 +1712,7 @@ decode_next: offset_max -= q->len; if ((offset < offset_max) && offset_max) { q = q->next; - LWIP_ASSERT("next pbuf was null", q); + LWIP_ERROR("offset pointed to next pbuf which is null", q , return ERR_VAL;); options = (u8_t*)q->payload; } else { /* We've run out of bytes, probably no end marker. Don't proceed. */ diff --git a/components/lwip/test_afl_host/Makefile b/components/lwip/test_afl_host/Makefile index d20537977..8abccd91d 100644 --- a/components/lwip/test_afl_host/Makefile +++ b/components/lwip/test_afl_host/Makefile @@ -1,7 +1,7 @@ COMPONENTS_DIR=../.. CFLAGS=-std=gnu99 -Og -ggdb -ffunction-sections -fdata-sections -nostdlib -Wall -Werror=all -Wno-int-to-pointer-cast -Wno-error=unused-function -Wno-error=unused-variable -Wno-error=deprecated-declarations -Wextra \ -Wno-unused-parameter -Wno-sign-compare -Wno-address -Wno-unused-variable -DESP_PLATFORM -D IDF_VER=\"v3.1\" -MMD -MP -DWITH_POSIX -INC_DIRS=-I . -I $(COMPONENTS_DIR)/lwip/include/lwip -I $(COMPONENTS_DIR)/lwip/include/lwip/port -I $(COMPONENTS_DIR)/lwip/include/lwip/posix -I $(COMPONENTS_DIR)/lwip/apps/ping -I $(COMPONENTS_DIR)/app_trace/include -I $(COMPONENTS_DIR)/app_update/include -I $(COMPONENTS_DIR)/bootloader_support/include -I $(COMPONENTS_DIR)/bt/include -I $(COMPONENTS_DIR)/coap/port/include -I $(COMPONENTS_DIR)/coap/port/include/coap -I $(COMPONENTS_DIR)/coap/libcoap/include -I \ $(COMPONENTS_DIR)/coap/libcoap/include/coap -I $(COMPONENTS_DIR)/console -I $(COMPONENTS_DIR)/cxx/include -I $(COMPONENTS_DIR)/driver/include -I $(COMPONENTS_DIR)/esp-tls -I $(COMPONENTS_DIR)/esp32/include -I $(COMPONENTS_DIR)/esp_adc_cal/include -I $(COMPONENTS_DIR)/ethernet/include -I $(COMPONENTS_DIR)/expat/port/include -I $(COMPONENTS_DIR)/expat/include/expat -I $(COMPONENTS_DIR)/fatfs/src -I $(COMPONENTS_DIR)/freertos/include -I $(COMPONENTS_DIR)/heap/include -I \ $(COMPONENTS_DIR)/idf_test/include -I $(COMPONENTS_DIR)/jsmn/include -I $(COMPONENTS_DIR)/json/cJSON -I $(COMPONENTS_DIR)/libsodium/libsodium/src/libsodium/include -I $(COMPONENTS_DIR)/libsodium/port_include -I $(COMPONENTS_DIR)/log/include -I /home/david/esp/esp-idf/examples/wifi/simple_wifi/main/include -I $(COMPONENTS_DIR)/mbedtls/port/include -I $(COMPONENTS_DIR)/mbedtls/include -I $(COMPONENTS_DIR)/mdns/include -I $(COMPONENTS_DIR)/micro-ecc/micro-ecc -I \ $(COMPONENTS_DIR)/newlib/platform_include -I $(COMPONENTS_DIR)/newlib/include -I $(COMPONENTS_DIR)/nghttp/port/include -I $(COMPONENTS_DIR)/nghttp/nghttp2/lib/includes -I $(COMPONENTS_DIR)/nvs_flash/include -I $(COMPONENTS_DIR)/openssl/include -I $(COMPONENTS_DIR)/pthread/include -I $(COMPONENTS_DIR)/sdmmc/include -I $(COMPONENTS_DIR)/smartconfig/include -I $(COMPONENTS_DIR)/soc/esp32/include -I $(COMPONENTS_DIR)/soc/include -I $(COMPONENTS_DIR)/spi_flash/include -I \ $(COMPONENTS_DIR)/spiffs/include -I $(COMPONENTS_DIR)/tcpip_adapter/include -I $(COMPONENTS_DIR)/ulp/include -I $(COMPONENTS_DIR)/vfs/include -I $(COMPONENTS_DIR)/wear_levelling/include -I $(COMPONENTS_DIR)/wpa_supplicant/include -I $(COMPONENTS_DIR)/wpa_supplicant/port/include -I $(COMPONENTS_DIR)/esp32/include -I $(COMPONENTS_DIR)/xtensa-debug-module/include +INC_DIRS=-I . -I $(COMPONENTS_DIR)/newlib/platform_include -I $(COMPONENTS_DIR)/newlib/include -I $(COMPONENTS_DIR)/driver/include -I $(COMPONENTS_DIR)/esp32/include -I $(COMPONENTS_DIR)/ethernet/include -I $(COMPONENTS_DIR)/freertos/include -I $(COMPONENTS_DIR)/heap/include -I $(COMPONENTS_DIR)/lwip/include/lwip -I $(COMPONENTS_DIR)/lwip/include/lwip/port -I $(COMPONENTS_DIR)/lwip/include/lwip/posix -I $(COMPONENTS_DIR)/lwip/apps/ping -I $(COMPONENTS_DIR)/soc/esp32/include -I $(COMPONENTS_DIR)/soc/include -I $(COMPONENTS_DIR)/tcpip_adapter/include -I $(COMPONENTS_DIR)/xtensa-debug-module/include TEST_NAME=test FUZZ=afl-fuzz LD=$(CC) @@ -55,4 +55,4 @@ $(TEST_NAME): $(OBJECTS) @$(LD) $(OBJECTS) -o $@ $(LDLIBS) fuzz: $(TEST_NAME) - @$(FUZZ) -t 500 -i "$(SAMPLE_PACKETS)" -o "out" -- ./$(TEST_NAME) + @$(FUZZ) -t 5000+ -i "$(SAMPLE_PACKETS)" -o "out" -- ./$(TEST_NAME) diff --git a/components/lwip/test_afl_host/test_dhcp_server.c b/components/lwip/test_afl_host/test_dhcp_server.c index 4ed3792bf..e19cf28e7 100644 --- a/components/lwip/test_afl_host/test_dhcp_server.c +++ b/components/lwip/test_afl_host/test_dhcp_server.c @@ -9,6 +9,9 @@ const ip_addr_t ip_addr_any; ip4_addr_t server_ip; struct netif mynetif; +// dhcps callback +void dhcp_test_dhcps_cb (u8_t client_ip[4]) {} + // Dependency injected static function to pass the packet into parser void dhcp_test_handle_dhcp(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port); void dhcp_test_init_di(); @@ -26,6 +29,7 @@ int main(int argc, char** argv) dhcp_test_init_di(); IP4_ADDR(&server_ip, 192,168,4,1); + dhcps_set_new_lease_cb(dhcp_test_dhcps_cb); dhcps_start(&mynetif, server_ip); #ifdef INSTR_IS_OFF diff --git a/components/mbedtls/port/esp_bignum.c b/components/mbedtls/port/esp_bignum.c index 3bfcd6145..b8f2cf8bf 100644 --- a/components/mbedtls/port/esp_bignum.c +++ b/components/mbedtls/port/esp_bignum.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "mbedtls/bignum.h" #include "rom/bigint.h" #include "soc/hwcrypto_reg.h" @@ -41,6 +42,20 @@ #include "freertos/task.h" #include "freertos/semphr.h" +/* Some implementation notes: + * + * - Naming convention x_words, y_words, z_words for number of words (limbs) used in a particular + * bignum. This number may be less than the size of the bignum + * + * - Naming convention hw_words for the hardware length of the operation. This number is always + * rounded up to a 512 bit multiple, and may be larger than any of the numbers involved in the + * calculation. + * + * - Timing behaviour of these functions will depend on the length of the inputs. This is fundamentally + * the same constraint as the software mbedTLS implementations, and relies on the same + * countermeasures (exponent blinding, etc) which are used in mbedTLS. + */ + static const __attribute__((unused)) char *TAG = "bignum"; #define ciL (sizeof(mbedtls_mpi_uint)) /* chars in limb */ @@ -103,49 +118,49 @@ void esp_mpi_release_hardware( void ) _lock_release(&mpi_lock); } -/* Number of words used to hold 'mpi', rounded up to nearest - 16 words (512 bits) to match hardware support. +/* Convert bit count to word count + */ +static inline size_t bits_to_words(size_t bits) +{ + return (bits + 31) / 32; +} + +/* Round up number of words to nearest + 512 bit (16 word) block count. +*/ +static inline size_t hardware_words(size_t words) +{ + return (words + 0xF) & ~0xF; +} + +/* Number of words used to hold 'mpi'. + + Equivalent of bits_to_words(mbedtls_mpi_bitlen(mpi)), but uses less cycles if the + exact bit count is not needed. Note that mpi->n (size of memory buffer) may be higher than this number, if the high bits are mostly zeroes. - - This implementation may cause the caller to leak a small amount of - timing information when an operation is performed (length of a - given mpi value, rounded to nearest 512 bits), but not all mbedTLS - RSA operations succeed if we use mpi->N as-is (buffers are too long). */ -static inline size_t hardware_words_needed(const mbedtls_mpi *mpi) +static inline size_t word_length(const mbedtls_mpi *mpi) { - size_t res = 1; - for(size_t i = 0; i < mpi->n; i++) { - if( mpi->p[i] != 0 ) { - res = i + 1; + for(size_t i = mpi->n; i > 0; i--) { + if( mpi->p[i - 1] != 0 ) { + return i; } } - res = (res + 0xF) & ~0xF; - return res; -} - -/* Convert number of bits to number of words, rounded up to nearest - 512 bit (16 word) block count. -*/ -static inline size_t bits_to_hardware_words(size_t num_bits) -{ - return ((num_bits + 511) / 512) * 16; + return 0; } /* Copy mbedTLS MPI bignum 'mpi' to hardware memory block at 'mem_base'. - If num_words is higher than the number of words in the bignum then + If hw_words is higher than the number of words in the bignum then these additional words will be zeroed in the memory buffer. - As this function only writes to DPORT memory, no DPORT_STALL_OTHER_CPU_START() - is required. */ -static inline void mpi_to_mem_block(uint32_t mem_base, const mbedtls_mpi *mpi, size_t num_words) +static inline void mpi_to_mem_block(uint32_t mem_base, const mbedtls_mpi *mpi, size_t hw_words) { uint32_t *pbase = (uint32_t *)mem_base; - uint32_t copy_words = num_words < mpi->n ? num_words : mpi->n; + uint32_t copy_words = hw_words < mpi->n ? hw_words : mpi->n; /* Copy MPI data to memory block registers */ for (int i = 0; i < copy_words; i++) { @@ -153,7 +168,7 @@ static inline void mpi_to_mem_block(uint32_t mem_base, const mbedtls_mpi *mpi, s } /* Zero any remaining memory block data */ - for (int i = copy_words; i < num_words; i++) { + for (int i = copy_words; i < hw_words; i++) { pbase[i] = 0; } @@ -164,27 +179,21 @@ static inline void mpi_to_mem_block(uint32_t mem_base, const mbedtls_mpi *mpi, s Reads num_words words from block. - Can return a failure result if fails to grow the MPI result. - - Cannot be called inside DPORT_STALL_OTHER_CPU_START() (as may allocate memory). + Bignum 'x' should already be grown to at least num_words by caller (can be done while + calculation is in progress, to save some cycles) */ -static inline int mem_block_to_mpi(mbedtls_mpi *x, uint32_t mem_base, int num_words) +static inline void mem_block_to_mpi(mbedtls_mpi *x, uint32_t mem_base, int num_words) { - int ret = 0; - - MBEDTLS_MPI_CHK( mbedtls_mpi_grow(x, num_words) ); + assert(x->n >= num_words); /* Copy data from memory block registers */ esp_dport_access_read_buffer(x->p, mem_base, num_words); + /* Zero any remaining limbs in the bignum, if the buffer is bigger than num_words */ for(size_t i = num_words; i < x->n; i++) { x->p[i] = 0; } - - asm volatile ("memw"); - cleanup: - return ret; } @@ -245,9 +254,6 @@ static int calculate_rinv(mbedtls_mpi *Rinv, const mbedtls_mpi *M, int num_words /* Begin an RSA operation. op_reg specifies which 'START' register to write to. - - Because the only DPORT operations here are writes, - does not need protecting via DPORT_STALL_OTHER_CPU_START(); */ static inline void start_op(uint32_t op_reg) { @@ -261,9 +267,6 @@ static inline void start_op(uint32_t op_reg) } /* Wait for an RSA operation to complete. - - This should NOT be called inside a DPORT_STALL_OTHER_CPU_START(), as it will stall the other CPU for an unacceptably long - period (and - depending on config - may require interrupts enabled). */ static inline void wait_op_complete(uint32_t op_reg) { @@ -284,7 +287,7 @@ static inline void wait_op_complete(uint32_t op_reg) } /* Sub-stages of modulo multiplication/exponentiation operations */ -inline static int modular_multiply_finish(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t num_words); +inline static int modular_multiply_finish(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t hw_words, size_t z_words); /* Z = (X * Y) mod M @@ -293,27 +296,33 @@ inline static int modular_multiply_finish(mbedtls_mpi *Z, const mbedtls_mpi *X, int esp_mpi_mul_mpi_mod(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, const mbedtls_mpi *M) { int ret; - size_t num_words = hardware_words_needed(M); + size_t x_bits = mbedtls_mpi_bitlen(X); + size_t y_bits = mbedtls_mpi_bitlen(Y); + size_t m_bits = mbedtls_mpi_bitlen(M); + size_t z_bits = MIN(m_bits, x_bits + y_bits); + size_t x_words = bits_to_words(x_bits); + size_t y_words = bits_to_words(y_bits); + size_t m_words = bits_to_words(m_bits); + size_t z_words = bits_to_words(z_bits); + size_t hw_words = hardware_words(MAX(x_words, MAX(y_words, m_words))); /* longest operand */ mbedtls_mpi Rinv; mbedtls_mpi_uint Mprime; /* Calculate and load the first stage montgomery multiplication */ mbedtls_mpi_init(&Rinv); - MBEDTLS_MPI_CHK(calculate_rinv(&Rinv, M, num_words)); + MBEDTLS_MPI_CHK(calculate_rinv(&Rinv, M, hw_words)); Mprime = modular_inverse(M); esp_mpi_acquire_hardware(); - /* (As the following are all writes to DPORT memory, no DPORT_STALL_OTHER_CPU_START is required.) */ - /* Load M, X, Rinv, Mprime (Mprime is mod 2^32) */ - mpi_to_mem_block(RSA_MEM_M_BLOCK_BASE, M, num_words); - mpi_to_mem_block(RSA_MEM_X_BLOCK_BASE, X, num_words); - mpi_to_mem_block(RSA_MEM_RB_BLOCK_BASE, &Rinv, num_words); + mpi_to_mem_block(RSA_MEM_M_BLOCK_BASE, M, hw_words); + mpi_to_mem_block(RSA_MEM_X_BLOCK_BASE, X, hw_words); + mpi_to_mem_block(RSA_MEM_RB_BLOCK_BASE, &Rinv, hw_words); DPORT_REG_WRITE(RSA_M_DASH_REG, (uint32_t)Mprime); /* "mode" register loaded with number of 512-bit blocks, minus 1 */ - DPORT_REG_WRITE(RSA_MULT_MODE_REG, (num_words / 16) - 1); + DPORT_REG_WRITE(RSA_MULT_MODE_REG, (hw_words / 16) - 1); /* Execute first stage montgomery multiplication */ start_op(RSA_MULT_START_REG); @@ -321,7 +330,7 @@ int esp_mpi_mul_mpi_mod(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi wait_op_complete(RSA_MULT_START_REG); /* execute second stage */ - ret = modular_multiply_finish(Z, X, Y, num_words); + ret = modular_multiply_finish(Z, X, Y, hw_words, z_words); esp_mpi_release_hardware(); @@ -343,31 +352,20 @@ int esp_mpi_mul_mpi_mod(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi int mbedtls_mpi_exp_mod( mbedtls_mpi* Z, const mbedtls_mpi* X, const mbedtls_mpi* Y, const mbedtls_mpi* M, mbedtls_mpi* _Rinv ) { int ret = 0; - size_t z_words = hardware_words_needed(Z); - size_t x_words = hardware_words_needed(X); - size_t y_words = hardware_words_needed(Y); - size_t m_words = hardware_words_needed(M); - size_t num_words; + size_t x_words = word_length(X); + size_t y_words = word_length(Y); + size_t m_words = word_length(M); + + /* "all numbers must be the same length", so choose longest number + as cardinal length of operation... + */ + size_t hw_words = hardware_words(MAX(m_words, MAX(x_words, y_words))); mbedtls_mpi Rinv_new; /* used if _Rinv == NULL */ mbedtls_mpi *Rinv; /* points to _Rinv (if not NULL) othwerwise &RR_new */ mbedtls_mpi_uint Mprime; - /* "all numbers must be the same length", so choose longest number - as cardinal length of operation... - */ - num_words = z_words; - if (x_words > num_words) { - num_words = x_words; - } - if (y_words > num_words) { - num_words = y_words; - } - if (m_words > num_words) { - num_words = m_words; - } - - if (num_words * 32 > 4096) { + if (hw_words * 32 > 4096) { return MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; } @@ -380,30 +378,31 @@ int mbedtls_mpi_exp_mod( mbedtls_mpi* Z, const mbedtls_mpi* X, const mbedtls_mpi Rinv = _Rinv; } if (Rinv->p == NULL) { - MBEDTLS_MPI_CHK(calculate_rinv(Rinv, M, num_words)); + MBEDTLS_MPI_CHK(calculate_rinv(Rinv, M, hw_words)); } Mprime = modular_inverse(M); esp_mpi_acquire_hardware(); - /* (As the following are all writes to DPORT memory, no DPORT_STALL_OTHER_CPU_START is required.) */ - /* "mode" register loaded with number of 512-bit blocks, minus 1 */ - DPORT_REG_WRITE(RSA_MODEXP_MODE_REG, (num_words / 16) - 1); + DPORT_REG_WRITE(RSA_MODEXP_MODE_REG, (hw_words / 16) - 1); /* Load M, X, Rinv, M-prime (M-prime is mod 2^32) */ - mpi_to_mem_block(RSA_MEM_X_BLOCK_BASE, X, num_words); - mpi_to_mem_block(RSA_MEM_Y_BLOCK_BASE, Y, num_words); - mpi_to_mem_block(RSA_MEM_M_BLOCK_BASE, M, num_words); - mpi_to_mem_block(RSA_MEM_RB_BLOCK_BASE, Rinv, num_words); + mpi_to_mem_block(RSA_MEM_X_BLOCK_BASE, X, hw_words); + mpi_to_mem_block(RSA_MEM_Y_BLOCK_BASE, Y, hw_words); + mpi_to_mem_block(RSA_MEM_M_BLOCK_BASE, M, hw_words); + mpi_to_mem_block(RSA_MEM_RB_BLOCK_BASE, Rinv, hw_words); DPORT_REG_WRITE(RSA_M_DASH_REG, Mprime); start_op(RSA_START_MODEXP_REG); + /* X ^ Y may actually be shorter than M, but unlikely when used for crypto */ + MBEDTLS_MPI_CHK( mbedtls_mpi_grow(Z, m_words) ); + wait_op_complete(RSA_START_MODEXP_REG); - ret = mem_block_to_mpi(Z, RSA_MEM_Z_BLOCK_BASE, num_words); + mem_block_to_mpi(Z, RSA_MEM_Z_BLOCK_BASE, m_words); esp_mpi_release_hardware(); cleanup: @@ -417,55 +416,56 @@ int mbedtls_mpi_exp_mod( mbedtls_mpi* Z, const mbedtls_mpi* X, const mbedtls_mpi #endif /* MBEDTLS_MPI_EXP_MOD_ALT */ /* Second & final step of a modular multiply - load second multiplication - * factor Y, run the multiply, read back the result into Z. + * factor Y, run the operation (modular inverse), read back the result + * into Z. * * Called from both mbedtls_mpi_exp_mod and mbedtls_mpi_mod_mpi. * * @param Z result value * @param X first multiplication factor (used to set sign of result). * @param Y second multiplication factor. - * @param num_words size of modulo operation, in words (limbs). - * Should already be rounded up to a multiple of 16 words (512 bits) & range checked. + * @param hw_words Size of the hardware operation, in words + * @param z_words Size of the expected result, in words (may be less than hw_words). + * Z will be grown to at least this length. * * Caller must have already called esp_mpi_acquire_hardware(). */ -static int modular_multiply_finish(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t num_words) +static int modular_multiply_finish(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t hw_words, size_t z_words) { int ret = 0; /* Load Y to X input memory block, rerun */ - mpi_to_mem_block(RSA_MEM_X_BLOCK_BASE, Y, num_words); + mpi_to_mem_block(RSA_MEM_X_BLOCK_BASE, Y, hw_words); start_op(RSA_MULT_START_REG); + MBEDTLS_MPI_CHK( mbedtls_mpi_grow(Z, z_words) ); + wait_op_complete(RSA_MULT_START_REG); - /* Read result into Z */ - ret = mem_block_to_mpi(Z, RSA_MEM_Z_BLOCK_BASE, num_words); + mem_block_to_mpi(Z, RSA_MEM_Z_BLOCK_BASE, z_words); Z->s = X->s * Y->s; + cleanup: return ret; } #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); +static int mpi_mult_mpi_failover_mod_mult(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t z_words); +static int mpi_mult_mpi_overlong(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t Y_bits, size_t z_words); /* Z = X * Y */ int mbedtls_mpi_mul_mpi( mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y ) { int ret = 0; - size_t bits_x, bits_y, words_x, words_y, words_mult, words_z; - - /* Count words needed for X & Y in hardware */ - bits_x = mbedtls_mpi_bitlen(X); - bits_y = mbedtls_mpi_bitlen(Y); - /* Convert bit counts to words, rounded up to 512-bit - (16 word) blocks */ - words_x = bits_to_hardware_words(bits_x); - words_y = bits_to_hardware_words(bits_y); + size_t x_bits = mbedtls_mpi_bitlen(X); + size_t y_bits = mbedtls_mpi_bitlen(Y); + size_t x_words = bits_to_words(x_bits); + size_t y_words = bits_to_words(y_bits); + size_t z_words = bits_to_words(x_bits + y_bits); + size_t hw_words = hardware_words(MAX(x_words, y_words)); // length of one operand in hardware /* Short-circuit eval if either argument is 0 or 1. @@ -473,31 +473,22 @@ int mbedtls_mpi_mul_mpi( mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi argument will sometimes call in here when one argument is too large for the hardware unit, but the other argument is zero or one. - - This leaks some timing information, although overall there is a - lot less timing variation than a software MPI approach. */ - if (bits_x == 0 || bits_y == 0) { + if (x_bits == 0 || y_bits == 0) { mbedtls_mpi_lset(Z, 0); return 0; } - if (bits_x == 1) { + if (x_bits == 1) { ret = mbedtls_mpi_copy(Z, Y); Z->s *= X->s; return ret; } - if (bits_y == 1) { + if (y_bits == 1) { ret = mbedtls_mpi_copy(Z, X); Z->s *= Y->s; return ret; } - words_mult = (words_x > words_y ? words_x : words_y); - - /* Result Z has to have room for double the larger factor */ - words_z = words_mult * 2; - - /* If either factor is over 2048 bits, we can't use the standard hardware multiplier (it assumes result is double longest factor, and result is max 4096 bits.) @@ -505,21 +496,19 @@ int mbedtls_mpi_mul_mpi( mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi multiplication doesn't have the same restriction, so result is simply the number of bits in X plus number of bits in in Y.) */ - 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) { + if (hw_words * 32 > 2048) { + if (z_words * 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); + return mpi_mult_mpi_failover_mod_mult(Z, X, Y, z_words); } 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); + if(y_words > x_words) { + return mpi_mult_mpi_overlong(Z, X, Y, y_words, z_words); } else { - return mpi_mult_mpi_overlong(Z, Y, X, bits_x, words_z); + return mpi_mult_mpi_overlong(Z, Y, X, x_words, z_words); } } } @@ -529,8 +518,8 @@ int mbedtls_mpi_mul_mpi( mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi esp_mpi_acquire_hardware(); /* Copy X (right-extended) & Y (left-extended) to memory block */ - mpi_to_mem_block(RSA_MEM_X_BLOCK_BASE, X, words_mult); - mpi_to_mem_block(RSA_MEM_Z_BLOCK_BASE + words_mult * 4, Y, words_mult); + mpi_to_mem_block(RSA_MEM_X_BLOCK_BASE, X, hw_words); + mpi_to_mem_block(RSA_MEM_Z_BLOCK_BASE + hw_words * 4, Y, hw_words); /* NB: as Y is left-extended, we don't zero the bottom words_mult words of Y block. This is OK for now because zeroing is done by hardware when we do esp_mpi_acquire_hardware(). */ @@ -540,17 +529,20 @@ int mbedtls_mpi_mul_mpi( mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi /* "mode" register loaded with number of 512-bit blocks in result, plus 7 (for range 9-12). (this is ((N~ / 32) - 1) + 8)) */ - DPORT_REG_WRITE(RSA_MULT_MODE_REG, (words_z / 16) + 7); + DPORT_REG_WRITE(RSA_MULT_MODE_REG, ((hw_words * 2) / 16) + 7); start_op(RSA_MULT_START_REG); + MBEDTLS_MPI_CHK( mbedtls_mpi_grow(Z, z_words) ); + wait_op_complete(RSA_MULT_START_REG); /* Read back the result */ - ret = mem_block_to_mpi(Z, RSA_MEM_Z_BLOCK_BASE, words_z); + mem_block_to_mpi(Z, RSA_MEM_Z_BLOCK_BASE, z_words); Z->s = X->s * Y->s; + cleanup: esp_mpi_release_hardware(); return ret; @@ -560,7 +552,7 @@ int mbedtls_mpi_mul_mpi( mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi multiplication to calculate an mbedtls_mpi_mult_mpi result where either A or B are >2048 bits so can't use the standard multiplication method. - Result (A bits + B bits) must still be less than 4096 bits. + Result (z_words, based on A bits + B bits) must still be less than 4096 bits. This case is simpler than the general case modulo multiply of esp_mpi_mul_mpi_mod() because we can control the other arguments: @@ -573,29 +565,30 @@ int mbedtls_mpi_mul_mpi( mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi (See RSA Accelerator section in Technical Reference for more about Mprime, Rinv) */ -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_failover_mod_mult(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t z_words) { int ret = 0; + size_t hw_words = hardware_words(z_words); /* Load coefficients to hardware */ esp_mpi_acquire_hardware(); /* M = 2^num_words - 1, so block is entirely FF */ - for(int i = 0; i < num_words; i++) { + for(int i = 0; i < hw_words; i++) { DPORT_REG_WRITE(RSA_MEM_M_BLOCK_BASE + i * 4, UINT32_MAX); } /* Mprime = 1 */ DPORT_REG_WRITE(RSA_M_DASH_REG, 1); /* "mode" register loaded with number of 512-bit blocks, minus 1 */ - DPORT_REG_WRITE(RSA_MULT_MODE_REG, (num_words / 16) - 1); + DPORT_REG_WRITE(RSA_MULT_MODE_REG, (hw_words / 16) - 1); /* Load X */ - mpi_to_mem_block(RSA_MEM_X_BLOCK_BASE, X, num_words); + mpi_to_mem_block(RSA_MEM_X_BLOCK_BASE, X, hw_words); /* Rinv = 1 */ DPORT_REG_WRITE(RSA_MEM_RB_BLOCK_BASE, 1); - for(int i = 1; i < num_words; i++) { + for(int i = 1; i < hw_words; i++) { DPORT_REG_WRITE(RSA_MEM_RB_BLOCK_BASE + i * 4, 0); } @@ -604,7 +597,7 @@ static int mpi_mult_mpi_failover_mod_mult(mbedtls_mpi *Z, const mbedtls_mpi *X, wait_op_complete(RSA_MULT_START_REG); /* finish the modular multiplication */ - ret = modular_multiply_finish(Z, X, Y, num_words); + ret = modular_multiply_finish(Z, X, Y, hw_words, z_words); esp_mpi_release_hardware(); @@ -628,29 +621,28 @@ static int mpi_mult_mpi_failover_mod_mult(mbedtls_mpi *Z, const mbedtls_mpi *X, Note that this function may recurse multiple times, if both X & Y are too long for the hardware multiplication unit. */ -static int mpi_mult_mpi_overlong(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t bits_y, size_t words_result) +static int mpi_mult_mpi_overlong(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t y_words, size_t z_words) { int ret = 0; mbedtls_mpi Ztemp; - const size_t limbs_y = (bits_y + biL - 1) / biL; /* Rather than slicing in two on bits we slice on limbs (32 bit words) */ - const size_t limbs_slice = limbs_y / 2; + const size_t words_slice = y_words / 2; /* Yp holds lower bits of Y (declared to reuse Y's array contents to save on copying) */ const mbedtls_mpi Yp = { .p = Y->p, - .n = limbs_slice, + .n = words_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, + .p = Y->p + words_slice, + .n = y_words - words_slice, .s = Y->s }; mbedtls_mpi_init(&Ztemp); /* Grow Z to result size early, avoid interim allocations */ - mbedtls_mpi_grow(Z, words_result); + mbedtls_mpi_grow(Z, z_words); /* Get result Ztemp = Yp * X (need temporary variable Ztemp) */ MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi(&Ztemp, X, &Yp) ); @@ -659,7 +651,7 @@ static int mpi_mult_mpi_overlong(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbe MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi(Z, X, &Ypp) ); /* Z = Z << b */ - MBEDTLS_MPI_CHK( mbedtls_mpi_shift_l(Z, limbs_slice * biL) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_shift_l(Z, words_slice * 32) ); /* Z += Ztemp */ MBEDTLS_MPI_CHK( mbedtls_mpi_add_mpi(Z, Z, &Ztemp) ); diff --git a/components/mbedtls/test/test_ecp.c b/components/mbedtls/test/test_ecp.c new file mode 100644 index 000000000..0c8f571db --- /dev/null +++ b/components/mbedtls/test/test_ecp.c @@ -0,0 +1,77 @@ +/* mbedTLS Elliptic Curve functionality tests + + Focus on testing functionality where we use ESP32 hardware + accelerated crypto features. + +*/ +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "unity.h" + +/* Note: negative value here so that assert message prints a grep-able + error hex value (mbedTLS uses -N for error codes) */ +#define TEST_ASSERT_MBEDTLS_OK(X) TEST_ASSERT_EQUAL_HEX32(0, -(X)) + +TEST_CASE("mbedtls ECDH Generate Key", "[mbedtls]") +{ + mbedtls_ecdh_context ctx; + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + + mbedtls_ecdh_init(&ctx); + mbedtls_ctr_drbg_init(&ctr_drbg); + + mbedtls_entropy_init(&entropy); + TEST_ASSERT_MBEDTLS_OK( mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0) ); + + TEST_ASSERT_MBEDTLS_OK( mbedtls_ecp_group_load(&ctx.grp, MBEDTLS_ECP_DP_CURVE25519) ); + + TEST_ASSERT_MBEDTLS_OK( mbedtls_ecdh_gen_public(&ctx.grp, &ctx.d, &ctx.Q, + mbedtls_ctr_drbg_random, &ctr_drbg ) ); + + mbedtls_ecdh_free(&ctx); + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_entropy_free(&entropy); +} + +TEST_CASE("mbedtls ECP self-tests", "[mbedtls]") +{ + TEST_ASSERT_EQUAL(0, mbedtls_ecp_self_test(1)); +} + +TEST_CASE("mbedtls ECP mul w/ koblitz", "[mbedtls]") +{ + /* Test case code via https://github.com/espressif/esp-idf/issues/1556 */ + mbedtls_entropy_context ctxEntropy; + mbedtls_ctr_drbg_context ctxRandom; + mbedtls_ecdsa_context ctxECDSA; + const char* pers = "myecdsa"; + + mbedtls_entropy_init(&ctxEntropy); + mbedtls_ctr_drbg_init(&ctxRandom); + TEST_ASSERT_MBEDTLS_OK( mbedtls_ctr_drbg_seed(&ctxRandom, mbedtls_entropy_func, &ctxEntropy, + (const unsigned char*) pers, strlen(pers)) ); + + mbedtls_ecdsa_init(&ctxECDSA); + + TEST_ASSERT_MBEDTLS_OK( mbedtls_ecdsa_genkey(&ctxECDSA, MBEDTLS_ECP_DP_SECP256K1, + mbedtls_ctr_drbg_random, &ctxRandom) ); + + + TEST_ASSERT_MBEDTLS_OK(mbedtls_ecp_mul(&ctxECDSA.grp, &ctxECDSA.Q, &ctxECDSA.d, &ctxECDSA.grp.G, + mbedtls_ctr_drbg_random, &ctxRandom) ); + + mbedtls_ecdsa_free(&ctxECDSA); + mbedtls_ctr_drbg_free(&ctxRandom); + mbedtls_entropy_free(&ctxEntropy); +} + diff --git a/components/newlib/syscall_table.c b/components/newlib/syscall_table.c index 624ffcffa..2b3e4ed15 100644 --- a/components/newlib/syscall_table.c +++ b/components/newlib/syscall_table.c @@ -41,6 +41,10 @@ extern int _scanf_float(struct _reent *rptr, FILE *fp, va_list *ap); +static void raise_r_stub(struct _reent *rptr) +{ + _raise_r(rptr, 0); +} static struct syscall_stub_table s_stub_table = { .__getreent = &__getreent, @@ -53,7 +57,7 @@ static struct syscall_stub_table s_stub_table = { ._rename_r = &esp_vfs_rename, ._times_r = &_times_r, ._gettimeofday_r = &_gettimeofday_r, - ._raise_r = (void (*)(struct _reent *r)) &_raise_r, + ._raise_r = &raise_r_stub, ._unlink_r = &esp_vfs_unlink, ._link_r = &esp_vfs_link, ._stat_r = &esp_vfs_stat, diff --git a/components/nvs_flash/nvs_partition_generator/README.rst b/components/nvs_flash/nvs_partition_generator/README.rst index f324f016f..2dacb79a4 100644 --- a/components/nvs_flash/nvs_partition_generator/README.rst +++ b/components/nvs_flash/nvs_partition_generator/README.rst @@ -5,7 +5,7 @@ Introduction ------------ :component_file:`nvs_flash/nvs_partition_generator/nvs_partition_gen.py` utility is designed to help create a binary file, compatible with NVS architecture defined in :doc:`Non-Volatile Storage `, based on user provided key-value pairs in a CSV file. -Utility is ideally suited for generating a binary blob, containing data specific to ODM/OEM, which can be flashed externally at the time of device manufacturing. This helps manufacturers set unique value for various parameters for each device, e.g. serial number, while using same application firmaware for all devices. +Utility is ideally suited for generating a binary blob, containing data specific to ODM/OEM, which can be flashed externally at the time of device manufacturing. This helps manufacturers set unique value for various parameters for each device, e.g. serial number, while using same application firmware for all devices. CSV file format --------------- @@ -49,9 +49,19 @@ When a new namespace entry is encountered in the CSV file, each follow-up entrie Running the utility ------------------- -A sample CSV file provided with the utility. You can run the utility using below command:: +You can run the utility using below command:: + + python nvs_partition_gen.py [-h] input output size + + +Positional arguments: + +| Arguments | Description +| --- | --- +| input | Path to CSV file to parse. Will use stdin if omitted (a sample.csv is provided) +| output | Path to output converted binary file. Will use stdout if omitted +| size | Size of NVS Partition in KB. E.g. 12KB - python nvs_partition_generator.py sample.csv sample.bin Caveats ------- diff --git a/components/nvs_flash/nvs_partition_generator/nvs_partition_gen.py b/components/nvs_flash/nvs_partition_generator/nvs_partition_gen.py index 6a51e54d9..389e8d944 100755 --- a/components/nvs_flash/nvs_partition_generator/nvs_partition_gen.py +++ b/components/nvs_flash/nvs_partition_generator/nvs_partition_gen.py @@ -58,12 +58,13 @@ class Page(object): ACTIVE = 0xFFFFFFFE FULL = 0xFFFFFFFC - def __init__(self, page_num): + def __init__(self, page_num, is_rsrv_page=False): self.entry_num = 0 self.bitmap_array = array.array('B') self.page_buf = bytearray(b'\xff')*Page.PAGE_PARAMS["max_size"] - self.bitmap_array = self.create_bitmap_array() - self.set_header(page_num) + if not is_rsrv_page: + self.bitmap_array = self.create_bitmap_array() + self.set_header(page_num) def set_header(self, page_num): global page_header @@ -127,8 +128,8 @@ class Page(object): chunk_size = 0 # Get the size available in current page - if self.entry_num < (Page.PAGE_PARAMS["max_entries"] - 1): - tailroom = (Page.PAGE_PARAMS["max_entries"] - self.entry_num - 1) * Page.SINGLE_ENTRY_SIZE + tailroom = (Page.PAGE_PARAMS["max_entries"] - self.entry_num - 1) * Page.SINGLE_ENTRY_SIZE + assert tailroom >=0, "Page overflow!!" # Split the binary data into two and store a chunk of available size onto curr page if tailroom < remaining_size: @@ -322,7 +323,8 @@ class Page(object): NVS class encapsulates all NVS specific operations to create a binary with given key-value pairs. Binary can later be flashed onto device via a flashing utility. """ class NVS(object): - def __init__(self, fout): + def __init__(self, fout, input_size): + self.size = input_size self.namespace_idx = 0 self.page_num = -1 self.pages = [] @@ -334,12 +336,27 @@ class NVS(object): def __exit__(self, exc_type, exc_value, traceback): if exc_type == None and exc_value == None: + # Create pages for remaining available size + while True: + try: + new_page = self.create_new_page() + except InsufficientSizeError: + self.size = None + # Creating the last reserved page + self.create_new_page(True) + break + result = self.get_binary_data() self.fout.write(result) - def create_new_page(self): + def create_new_page(self, is_rsrv_page=False): + # Update available size as each page is created + if self.size == 0: + raise InsufficientSizeError("Size parameter is is less than the size of data in csv.Please increase size.") + if not is_rsrv_page: + self.size = self.size - Page.PAGE_PARAMS["max_size"] self.page_num += 1 - new_page = Page(self.page_num) + new_page = Page(self.page_num, is_rsrv_page) self.pages.append(new_page) self.cur_page = new_page return new_page @@ -418,13 +435,21 @@ class InputError(RuntimeError): def __init__(self, e): super(InputError, self).__init__(e) -def nvs_open(result_obj): +class InsufficientSizeError(RuntimeError): + """ + Represents error when NVS Partition size given is insufficient + to accomodate the data in the given csv file + """ + def __init__(self, e): + super(InsufficientSizeError, self).__init__(e) + +def nvs_open(result_obj, input_size): """ Wrapper to create and NVS class object. This object can later be used to set key-value pairs :param result_obj: File/Stream object to dump resultant binary. If data is to be dumped into memory, one way is to use BytesIO object :return: NVS class instance """ - return NVS(result_obj) + return NVS(result_obj, input_size) def write_entry(nvs_instance, key, datatype, encoding, value): """ Wrapper to set key-value pair in NVS format @@ -457,16 +482,34 @@ def nvs_close(nvs_instance): """ nvs_instance.__exit__(None, None, None) -def nvs_part_gen(input_filename=None, output_filename=None): +def nvs_part_gen(input_filename=None, output_filename=None, input_size=None): + """ Wrapper to generate nvs partition binary + + :param input_filename: Name of input file containing data + :param output_filename: Name of output file to store generated binary + :param input_size: Size of partition + :return: None + """ + if input_size % 4096 !=0: + sys.exit("Size parameter should be a multiple of 4KB.") + + # Update size as a page needs to be reserved of size 4KB + input_size = input_size - Page.PAGE_PARAMS["max_size"] + + if input_size == 0: + sys.exit("Size parameter is insufficient.") + input_file = open(input_filename, 'rb') output_file = open(output_filename, 'wb') - with nvs_open(output_file) as nvs_obj: + with nvs_open(output_file, input_size) as nvs_obj: + # Update size as one page is created + #nvs_obj.size = input_size - Page.PAGE_PARAMS["max_size"] reader = csv.DictReader(input_file, delimiter=',') for row in reader: try: write_entry(nvs_obj, row["key"], row["type"], row["encoding"], row["value"]) - except InputError as e: + except (InputError, InsufficientSizeError) as e: print(e) input_file.close() output_file.close() @@ -487,10 +530,17 @@ def main(): help='Path to output converted binary file. Will use stdout if omitted', default=sys.stdout) + parser.add_argument( + "size", + help='Size of NVS Partition in KB. Eg. 12KB') + args = parser.parse_args() input_filename = args.input output_filename = args.output - nvs_part_gen(input_filename, output_filename) + + # Set size + input_size = int(args.size.split('KB')[0]) * 1024 + nvs_part_gen(input_filename, output_filename, input_size) diff --git a/components/nvs_flash/src/nvs_storage.cpp b/components/nvs_flash/src/nvs_storage.cpp index 461309771..3ad61667c 100644 --- a/components/nvs_flash/src/nvs_storage.cpp +++ b/components/nvs_flash/src/nvs_storage.cpp @@ -180,6 +180,9 @@ esp_err_t Storage::writeMultiPageBlob(uint8_t nsIndex, const char* key, const vo err = mPageManager.requestNewPage(); if (err != ESP_OK) { return err; + } else if(getCurrentPage().getVarDataTailroom() == tailroom) { + /* We got the same page or we are not improving.*/ + return ESP_ERR_NVS_NOT_ENOUGH_SPACE; } else { continue; } @@ -257,7 +260,6 @@ esp_err_t Storage::writeItem(uint8_t nsIndex, ItemType datatype, const char* key err = findItem(nsIndex, datatype, key, findPage, item); } - if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) { return err; } @@ -280,6 +282,7 @@ esp_err_t Storage::writeItem(uint8_t nsIndex, ItemType datatype, const char* key } /* Write the blob with new version*/ err = writeMultiPageBlob(nsIndex, key, data, dataSize, nextStart); + if (err == ESP_ERR_NVS_PAGE_FULL) { return ESP_ERR_NVS_NOT_ENOUGH_SPACE; } diff --git a/components/nvs_flash/test_nvs_host/test_nvs.cpp b/components/nvs_flash/test_nvs_host/test_nvs.cpp index 411cb3ef8..6c59f8fa0 100644 --- a/components/nvs_flash/test_nvs_host/test_nvs.cpp +++ b/components/nvs_flash/test_nvs_host/test_nvs.cpp @@ -1710,6 +1710,27 @@ TEST_CASE("Check that orphaned blobs are erased during init", "[nvs]") TEST_ESP_OK(storage.writeItem(1, ItemType::BLOB, "key3", blob, sizeof(blob))); } +TEST_CASE("nvs code handles errors properly when partition is near to full", "[nvs]") +{ + const size_t blob_size = Page::CHUNK_MAX_SIZE * 0.3 ; + uint8_t blob[blob_size] = {0x11}; + SpiFlashEmulator emu(5); + Storage storage; + char nvs_key[16] = ""; + + TEST_ESP_OK(storage.init(0, 5)); + + /* Four pages should fit roughly 12 blobs*/ + for(uint8_t count = 1; count <= 12; count++) { + sprintf(nvs_key, "key:%u", count); + TEST_ESP_OK(storage.writeItem(1, ItemType::BLOB, nvs_key, blob, sizeof(blob))); + } + + for(uint8_t count = 13; count <= 20; count++) { + sprintf(nvs_key, "key:%u", count); + TEST_ESP_ERR(storage.writeItem(1, ItemType::BLOB, nvs_key, blob, sizeof(blob)), ESP_ERR_NVS_NOT_ENOUGH_SPACE); + } +} TEST_CASE("Check for nvs version incompatibility", "[nvs]") { @@ -1977,7 +1998,8 @@ TEST_CASE("check partition generation utility", "[nvs_part_gen]") exit(execlp("python", "python", "../nvs_partition_generator/nvs_partition_gen.py", "../nvs_partition_generator/sample.csv", - "../nvs_partition_generator/partition.bin", NULL)); + "../nvs_partition_generator/partition.bin", + "12KB",NULL)); } else { CHECK(childpid > 0); int status; diff --git a/components/partition_table/Makefile.projbuild b/components/partition_table/Makefile.projbuild index b53419620..5d04636b1 100644 --- a/components/partition_table/Makefile.projbuild +++ b/components/partition_table/Makefile.projbuild @@ -56,7 +56,7 @@ else PARTITION_TABLE_BIN_UNSIGNED := $(PARTITION_TABLE_BIN) endif -$(PARTITION_TABLE_BIN_UNSIGNED): $(PARTITION_TABLE_CSV_PATH) $(SDKCONFIG_MAKEFILE) +$(PARTITION_TABLE_BIN_UNSIGNED): $(PARTITION_TABLE_CSV_PATH) $(SDKCONFIG_MAKEFILE) | check_python_dependencies @echo "Building partitions from $(PARTITION_TABLE_CSV_PATH)..." $(GEN_ESP32PART) $< $@ @@ -76,7 +76,7 @@ export OTA_DATA_SIZE PARTITION_TABLE_FLASH_CMD = $(ESPTOOLPY_SERIAL) write_flash $(PARTITION_TABLE_OFFSET) $(PARTITION_TABLE_BIN) ESPTOOL_ALL_FLASH_ARGS += $(PARTITION_TABLE_OFFSET) $(PARTITION_TABLE_BIN) -partition_table: $(PARTITION_TABLE_BIN) partition_table_get_info +partition_table: $(PARTITION_TABLE_BIN) partition_table_get_info check_python_dependencies @echo "Partition table binary generated. Contents:" @echo $(SEPARATOR) $(GEN_ESP32PART) $< @@ -84,7 +84,7 @@ partition_table: $(PARTITION_TABLE_BIN) partition_table_get_info @echo "Partition flashing command:" @echo "$(PARTITION_TABLE_FLASH_CMD)" -partition_table-flash: $(PARTITION_TABLE_BIN) +partition_table-flash: $(PARTITION_TABLE_BIN) check_python_dependencies @echo "Flashing partition table..." $(PARTITION_TABLE_FLASH_CMD) diff --git a/components/soc/component.mk b/components/soc/component.mk index 1c0f3d421..6dfb407f1 100644 --- a/components/soc/component.mk +++ b/components/soc/component.mk @@ -4,3 +4,5 @@ SOC_NAME := esp32 COMPONENT_SRCDIRS := $(SOC_NAME) src/ COMPONENT_ADD_INCLUDEDIRS := $(SOC_NAME)/include include + +-include $(COMPONENT_PATH)/$(SOC_NAME)/component.mk diff --git a/components/soc/esp32/component.mk b/components/soc/esp32/component.mk new file mode 100644 index 000000000..83a2ef722 --- /dev/null +++ b/components/soc/esp32/component.mk @@ -0,0 +1 @@ +esp32/rtc_clk.o: CFLAGS += -fno-jump-tables -fno-tree-switch-conversion diff --git a/components/soc/esp32/include/soc/rtc.h b/components/soc/esp32/include/soc/rtc.h index 1ece26c97..a528bdd15 100644 --- a/components/soc/esp32/include/soc/rtc.h +++ b/components/soc/esp32/include/soc/rtc.h @@ -75,6 +75,26 @@ typedef enum { RTC_CPU_FREQ_2M = 4, //!< 2 MHz } rtc_cpu_freq_t; +/** + * @brief CPU clock source + */ +typedef enum { + RTC_CPU_FREQ_SRC_XTAL, //!< XTAL + RTC_CPU_FREQ_SRC_PLL, //!< PLL (480M or 320M) + RTC_CPU_FREQ_SRC_8M, //!< Internal 8M RTC oscillator + RTC_CPU_FREQ_SRC_APLL //!< APLL +} rtc_cpu_freq_src_t; + +/** + * @brief CPU clock configuration structure + */ +typedef struct { + rtc_cpu_freq_src_t source; //!< The clock from which CPU clock is derived + uint32_t source_freq_mhz; //!< Source clock frequency + uint32_t div; //!< Divider, freq_mhz = source_freq_mhz / div + uint32_t freq_mhz; //!< CPU clock frequency +} rtc_cpu_freq_config_t; + /** * @brief RTC SLOW_CLK frequency values */ @@ -108,13 +128,13 @@ typedef enum { * Initialization parameters for rtc_clk_init */ typedef struct { - rtc_xtal_freq_t xtal_freq : 8; //!< Main XTAL frequency - rtc_cpu_freq_t cpu_freq : 3; //!< CPU frequency to set - rtc_fast_freq_t fast_freq : 1; //!< RTC_FAST_CLK frequency to set - rtc_slow_freq_t slow_freq : 2; //!< RTC_SLOW_CLK frequency to set - uint32_t clk_8m_div : 3; //!< RTC 8M clock divider (division is by clk_8m_div+1, i.e. 0 means 8MHz frequency) - uint32_t slow_clk_dcap : 8; //!< RTC 150k clock adjustment parameter (higher value leads to lower frequency) - uint32_t clk_8m_dfreq : 8; //!< RTC 8m clock adjustment parameter (higher value leads to higher frequency) + rtc_xtal_freq_t xtal_freq : 8; //!< Main XTAL frequency + rtc_cpu_freq_t cpu_freq_mhz : 10; //!< CPU frequency to set, in MHz + rtc_fast_freq_t fast_freq : 1; //!< RTC_FAST_CLK frequency to set + rtc_slow_freq_t slow_freq : 2; //!< RTC_SLOW_CLK frequency to set + uint32_t clk_8m_div : 3; //!< RTC 8M clock divider (division is by clk_8m_div+1, i.e. 0 means 8MHz frequency) + uint32_t slow_clk_dcap : 8; //!< RTC 150k clock adjustment parameter (higher value leads to lower frequency) + uint32_t clk_8m_dfreq : 8; //!< RTC 8m clock adjustment parameter (higher value leads to higher frequency) } rtc_clk_config_t; /** @@ -122,7 +142,7 @@ typedef struct { */ #define RTC_CLK_CONFIG_DEFAULT() { \ .xtal_freq = RTC_XTAL_FREQ_AUTO, \ - .cpu_freq = RTC_CPU_FREQ_80M, \ + .cpu_freq_mhz = 80, \ .fast_freq = RTC_FAST_FREQ_8M, \ .slow_freq = RTC_SLOW_FREQ_RTC, \ .clk_8m_div = 0, \ @@ -173,6 +193,11 @@ void rtc_clk_xtal_freq_update(rtc_xtal_freq_t xtal_freq); */ void rtc_clk_32k_enable(bool en); +/** + * @brief Configure 32 kHz XTAL oscillator to accept external clock signal + */ +void rtc_clk_32k_enable_external(); + /** * @brief Get the state of 32k XTAL oscillator * @return true if 32k XTAL oscillator has been enabled @@ -281,6 +306,9 @@ rtc_fast_freq_t rtc_clk_fast_freq_get(); /** * @brief Switch CPU frequency * + * @note This function is deprecated and will be removed. + * See rtc_clk_cpu_freq_config_set instead. + * * If a PLL-derived frequency is requested (80, 160, 240 MHz), this function * will enable the PLL. Otherwise, PLL will be disabled. * Note: this function is not optimized for switching speed. It may take several @@ -288,11 +316,14 @@ rtc_fast_freq_t rtc_clk_fast_freq_get(); * * @param cpu_freq new CPU frequency */ -void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq); +void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq) __attribute__((deprecated)); /** * @brief Switch CPU frequency * + * @note This function is deprecated and will be removed. + * See rtc_clk_cpu_freq_set_config_fast instead. + * * This is a faster version of rtc_clk_cpu_freq_set, which can handle some of * the frequency switch paths (XTAL -> PLL, PLL -> XTAL). * When switching from PLL to XTAL, PLL is not disabled (unlike rtc_clk_cpu_freq_set). @@ -307,11 +338,14 @@ void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq); * * @param cpu_freq new CPU frequency */ -void rtc_clk_cpu_freq_set_fast(rtc_cpu_freq_t cpu_freq); +void rtc_clk_cpu_freq_set_fast(rtc_cpu_freq_t cpu_freq) __attribute__((deprecated)); /** * @brief Get the currently selected CPU frequency * + * @note This function is deprecated and will be removed. + * See rtc_clk_cpu_freq_get_config instead. + * * Although CPU can be clocked by APLL and RTC 8M sources, such support is not * exposed through this library. As such, this function will not return * meaningful values when these clock sources are configured (e.g. using direct @@ -320,22 +354,97 @@ void rtc_clk_cpu_freq_set_fast(rtc_cpu_freq_t cpu_freq); * * @return CPU frequency (one of rtc_cpu_freq_t values) */ -rtc_cpu_freq_t rtc_clk_cpu_freq_get(); +rtc_cpu_freq_t rtc_clk_cpu_freq_get() __attribute__((deprecated)); /** * @brief Get corresponding frequency value for rtc_cpu_freq_t enum value + * + * @note This function is deprecated and will be removed. + * See rtc_clk_cpu_freq_get/set_config instead. + * * @param cpu_freq CPU frequency, on of rtc_cpu_freq_t values * @return CPU frequency, in HZ */ -uint32_t rtc_clk_cpu_freq_value(rtc_cpu_freq_t cpu_freq); +uint32_t rtc_clk_cpu_freq_value(rtc_cpu_freq_t cpu_freq) __attribute__((deprecated)); /** * @brief Get rtc_cpu_freq_t enum value for given CPU frequency + * + * @note This function is deprecated and will be removed. + * See rtc_clk_cpu_freq_mhz_to_config instead. + * * @param cpu_freq_mhz CPU frequency, one of 80, 160, 240, 2, and XTAL frequency * @param[out] out_val output, rtc_cpu_freq_t value corresponding to the frequency * @return true if the given frequency value matches one of enum values */ - bool rtc_clk_cpu_freq_from_mhz(int cpu_freq_mhz, rtc_cpu_freq_t* out_val); + bool rtc_clk_cpu_freq_from_mhz(int cpu_freq_mhz, rtc_cpu_freq_t* out_val) __attribute__((deprecated)); + +/** + * @brief Get CPU frequency config corresponding to a rtc_cpu_freq_t value + * @param cpu_freq CPU frequency enumeration value + * @param[out] out_config Output, CPU frequency configuration structure + */ + void rtc_clk_cpu_freq_to_config(rtc_cpu_freq_t cpu_freq, rtc_cpu_freq_config_t* out_config); + + /** + * @brief Get CPU frequency config for a given frequency + * @param freq_mhz Frequency in MHz + * @param[out] out_config Output, CPU frequency configuration structure + * @return true if frequency can be obtained, false otherwise + */ + bool rtc_clk_cpu_freq_mhz_to_config(uint32_t freq_mhz, rtc_cpu_freq_config_t* out_config); + + /** + * @brief Switch CPU frequency + * + * This function sets CPU frequency according to the given configuration + * structure. It enables PLLs, if necessary. + * + * @note This function in not intended to be called by applications in FreeRTOS + * environment. This is because it does not adjust various timers based on the + * new CPU frequency. + * + * @param config CPU frequency configuration structure + */ + void rtc_clk_cpu_freq_set_config(const rtc_cpu_freq_config_t* config); + + /** + * @brief Switch CPU frequency (optimized for speed) + * + * This function is a faster equivalent of rtc_clk_cpu_freq_set_config. + * It works faster because it does not disable PLLs when switching from PLL to + * XTAL and does not enabled them when switching back. If PLL is not already + * enabled when this function is called to switch from XTAL to PLL frequency, + * or the PLL which is enabled is the wrong one, this function will fall back + * to calling rtc_clk_cpu_freq_set_config. + * + * Unlike rtc_clk_cpu_freq_set_config, this function relies on static data, + * so it is less safe to use it e.g. from a panic handler (when memory might + * be corrupted). + * + * @note This function in not intended to be called by applications in FreeRTOS + * environment. This is because it does not adjust various timers based on the + * new CPU frequency. + * + * @param config CPU frequency configuration structure + */ + void rtc_clk_cpu_freq_set_config_fast(const rtc_cpu_freq_config_t* config); + + /** + * @brief Get the currently used CPU frequency configuration + * @param[out] out_config Output, CPU frequency configuration structure + */ + void rtc_clk_cpu_freq_get_config(rtc_cpu_freq_config_t* out_config); + + /** + * @brief Switch CPU clock source to XTAL + * + * Short form for filling in rtc_cpu_freq_config_t structure and calling + * rtc_clk_cpu_freq_set_config when a switch to XTAL is needed. + * Assumes that XTAL frequency has been determined — don't call in startup code. + */ + void rtc_clk_cpu_freq_set_xtal(); + /** * @brief Store new APB frequency value into RTC_APB_FREQ_REG diff --git a/components/soc/esp32/rtc_clk.c b/components/soc/esp32/rtc_clk.c index d919bb822..30f20e0eb 100644 --- a/components/soc/esp32/rtc_clk.c +++ b/components/soc/esp32/rtc_clk.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "rom/ets_sys.h" #include "rom/rtc.h" #include "rom/uart.h" @@ -31,9 +32,7 @@ #include "soc_log.h" #include "sdkconfig.h" #include "xtensa/core-macros.h" - - -#define MHZ (1000000) +#include "rtc_clk_common.h" /* Frequency of the 8M oscillator is 8.5MHz +/- 5%, at the default DCAP setting */ #define RTC_FAST_CLK_FREQ_8M 8500000 @@ -41,12 +40,6 @@ #define RTC_SLOW_CLK_FREQ_8MD256 (RTC_FAST_CLK_FREQ_8M / 256) #define RTC_SLOW_CLK_FREQ_32K 32768 -static const char* TAG = "rtc_clk"; - -/* Various constants related to the analog internals of the chip. - * Defined here because they don't have any use outside of this file. - */ - #define BBPLL_ENDIV5_VAL_320M 0x43 #define BBPLL_BBADC_DSMP_VAL_320M 0x84 #define BBPLL_ENDIV5_VAL_480M 0xc3 @@ -69,6 +62,10 @@ static const char* TAG = "rtc_clk"; #define XTAL_32K_BOOTSTRAP_DBIAS_VAL 0 #define XTAL_32K_BOOTSTRAP_TIME_US 7 +#define XTAL_32K_EXT_DAC_VAL 2 +#define XTAL_32K_EXT_DRES_VAL 3 +#define XTAL_32K_EXT_DBIAS_VAL 1 + /* Delays for various clock sources to be enabled/switched. * All values are in microseconds. * TODO: some of these are excessive, and should be reduced. @@ -80,11 +77,6 @@ static const char* TAG = "rtc_clk"; #define DELAY_SLOW_CLK_SWITCH 300 #define DELAY_8M_ENABLE 50 -/* Number of 8M/256 clock cycles to use for XTAL frequency estimation. - * 10 cycles will take approximately 300 microseconds. - */ -#define XTAL_FREQ_EST_CYCLES 10 - /* Core voltage needs to be increased in two cases: * 1. running at 240 MHz * 2. running with 80MHz Flash frequency @@ -98,19 +90,19 @@ static const char* TAG = "rtc_clk"; #define DIG_DBIAS_XTAL RTC_CNTL_DBIAS_1V10 #define DIG_DBIAS_2M RTC_CNTL_DBIAS_1V00 -/* PLL currently enabled, if any */ -typedef enum { - RTC_PLL_NONE, - RTC_PLL_320M, - RTC_PLL_480M -} rtc_pll_t; -static rtc_pll_t s_cur_pll = RTC_PLL_NONE; +#define RTC_PLL_FREQ_320M 320 +#define RTC_PLL_FREQ_480M 480 -/* Current CPU frequency; saved in a variable for faster freq. switching */ -static rtc_cpu_freq_t s_cur_freq = RTC_CPU_FREQ_XTAL; +static void rtc_clk_cpu_freq_to_8m(); +static void rtc_clk_bbpll_disable(); +static void rtc_clk_bbpll_enable(); +static void rtc_clk_cpu_freq_to_pll_mhz(int cpu_freq_mhz); +static bool rtc_clk_cpu_freq_from_mhz_internal(int mhz, rtc_cpu_freq_t* out_val); +// Current PLL frequency, in MHZ (320 or 480). Zero if PLL is not enabled. +static int s_cur_pll_freq; -static void rtc_clk_32k_enable_internal(int dac, int dres, int dbias) +static void rtc_clk_32k_enable_common(int dac, int dres, int dbias) { SET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32N_MUX_SEL | RTC_IO_X32P_MUX_SEL); CLEAR_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, @@ -125,12 +117,17 @@ static void rtc_clk_32k_enable_internal(int dac, int dres, int dbias) void rtc_clk_32k_enable(bool enable) { if (enable) { - rtc_clk_32k_enable_internal(XTAL_32K_DAC_VAL, XTAL_32K_DRES_VAL, XTAL_32K_DBIAS_VAL); + rtc_clk_32k_enable_common(XTAL_32K_DAC_VAL, XTAL_32K_DRES_VAL, XTAL_32K_DBIAS_VAL); } else { CLEAR_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_XPD_XTAL_32K); } } +void rtc_clk_32k_enable_external() +{ + rtc_clk_32k_enable_common(XTAL_32K_EXT_DAC_VAL, XTAL_32K_EXT_DRES_VAL, XTAL_32K_EXT_DBIAS_VAL); +} + /* Helping external 32kHz crystal to start up. * External crystal connected to outputs GPIO32 GPIO33. * Forms N pulses with a frequency of about 32KHz on the outputs of the crystal. @@ -162,7 +159,7 @@ void rtc_clk_32k_bootstrap(uint32_t cycle) SET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32P_RUE | RTC_IO_X32N_RDE); ets_delay_us(XTAL_32K_BOOTSTRAP_TIME_US); - rtc_clk_32k_enable_internal(XTAL_32K_BOOTSTRAP_DAC_VAL, + rtc_clk_32k_enable_common(XTAL_32K_BOOTSTRAP_DAC_VAL, XTAL_32K_BOOTSTRAP_DRES_VAL, XTAL_32K_BOOTSTRAP_DBIAS_VAL); } @@ -275,7 +272,7 @@ rtc_fast_freq_t rtc_clk_fast_freq_get() return REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_FAST_CLK_RTC_SEL); } -void rtc_clk_bbpll_set(rtc_xtal_freq_t xtal_freq, rtc_cpu_freq_t cpu_freq) +void rtc_clk_bbpll_configure(rtc_xtal_freq_t xtal_freq, int pll_freq) { uint8_t div_ref; uint8_t div7_0; @@ -284,7 +281,7 @@ void rtc_clk_bbpll_set(rtc_xtal_freq_t xtal_freq, rtc_cpu_freq_t cpu_freq) uint8_t dcur; uint8_t bw; - if (cpu_freq != RTC_CPU_FREQ_240M) { + if (pll_freq == RTC_PLL_FREQ_320M) { /* Raise the voltage, if needed */ REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_80M_160M); /* Configure 320M PLL */ @@ -376,96 +373,47 @@ void rtc_clk_bbpll_set(rtc_xtal_freq_t xtal_freq, rtc_cpu_freq_t cpu_freq) uint32_t delay_pll_en = (rtc_clk_slow_freq_get() == RTC_SLOW_FREQ_RTC) ? DELAY_PLL_ENABLE_WITH_150K : DELAY_PLL_ENABLE_WITH_32K; ets_delay_us(delay_pll_en); + s_cur_pll_freq = pll_freq; } /** * Switch to XTAL frequency. Does not disable the PLL. */ -static void rtc_clk_cpu_freq_to_xtal() +void rtc_clk_cpu_freq_to_xtal(int freq, int div) { - rtc_xtal_freq_t xtal_freq = rtc_clk_xtal_freq_get(); - ets_update_cpu_frequency(xtal_freq); - REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_XTAL); - REG_SET_FIELD(APB_CTRL_SYSCLK_CONF_REG, APB_CTRL_PRE_DIV_CNT, 0); - REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_XTL); - DPORT_REG_WRITE(DPORT_CPU_PER_CONF_REG, 0); // clear DPORT_CPUPERIOD_SEL - - rtc_clk_apb_freq_update(xtal_freq * MHZ); - s_cur_freq = RTC_CPU_FREQ_XTAL; -} - -/** - * Switch to one of PLL-based frequencies. Current frequency can be XTAL or PLL. - * PLL must already be enabled. - * If switching between frequencies derived from different PLLs (320M and 480M), - * fall back to rtc_clk_cpu_freq_set. - * @param cpu_freq new CPU frequency - */ -static void rtc_clk_cpu_freq_to_pll(rtc_cpu_freq_t cpu_freq) -{ - int freq = 0; - if (s_cur_pll == RTC_PLL_NONE || - (cpu_freq == RTC_CPU_FREQ_240M && s_cur_pll == RTC_PLL_320M) || - (cpu_freq != RTC_CPU_FREQ_240M && s_cur_pll == RTC_PLL_480M)) { - /* need to switch PLLs, fall back to full implementation */ - rtc_clk_cpu_freq_set(cpu_freq); - return; - } - - if (cpu_freq == RTC_CPU_FREQ_80M) { - REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_80M_160M); - DPORT_REG_WRITE(DPORT_CPU_PER_CONF_REG, 0); - freq = 80; - } else if (cpu_freq == RTC_CPU_FREQ_160M) { - REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_80M_160M); - DPORT_REG_WRITE(DPORT_CPU_PER_CONF_REG, 1); - freq = 160; - } else if (cpu_freq == RTC_CPU_FREQ_240M) { - REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_240M); - DPORT_REG_WRITE(DPORT_CPU_PER_CONF_REG, 2); - freq = 240; - } - REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_PLL); - rtc_clk_apb_freq_update(80 * MHZ); ets_update_cpu_frequency(freq); - s_cur_freq = cpu_freq; -} - -void rtc_clk_cpu_freq_set_fast(rtc_cpu_freq_t cpu_freq) -{ - if (cpu_freq == s_cur_freq) { - return; - } else if (cpu_freq == RTC_CPU_FREQ_2M || s_cur_freq == RTC_CPU_FREQ_2M) { - /* fall back to full implementation if switch to/from 2M is needed */ - rtc_clk_cpu_freq_set(cpu_freq); - } else if (cpu_freq == RTC_CPU_FREQ_XTAL) { - rtc_clk_cpu_freq_to_xtal(); - } else if (cpu_freq > RTC_CPU_FREQ_XTAL) { - rtc_clk_cpu_freq_to_pll(cpu_freq); - rtc_clk_wait_for_slow_cycle(); + /* set divider from XTAL to APB clock */ + REG_SET_FIELD(APB_CTRL_SYSCLK_CONF_REG, APB_CTRL_PRE_DIV_CNT, div - 1); + /* adjust ref_tick */ + REG_WRITE(APB_CTRL_XTAL_TICK_CONF_REG, freq * MHZ / REF_CLK_FREQ - 1); + /* switch clock source */ + REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_XTL); + DPORT_REG_WRITE(DPORT_CPU_PER_CONF_REG, 0); /* clear DPORT_CPUPERIOD_SEL */ + rtc_clk_apb_freq_update(freq * MHZ); + /* lower the voltage */ + if (freq <= 2) { + REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_2M); + } else { + REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_XTAL); } } -void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq) +static void rtc_clk_cpu_freq_to_8m() { - rtc_xtal_freq_t xtal_freq = rtc_clk_xtal_freq_get(); - /* Switch CPU to XTAL frequency first */ + ets_update_cpu_frequency(8); REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_XTAL); - REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_XTL); REG_SET_FIELD(APB_CTRL_SYSCLK_CONF_REG, APB_CTRL_PRE_DIV_CNT, 0); - ets_update_cpu_frequency(xtal_freq); + REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_8M); + DPORT_REG_WRITE(DPORT_CPU_PER_CONF_REG, 0); // clear DPORT_CPUPERIOD_SEL + rtc_clk_apb_freq_update(RTC_FAST_CLK_FREQ_8M); +} - /* Frequency switch is synchronized to SLOW_CLK cycle. Wait until the switch - * is complete before disabling the PLL. - */ - rtc_clk_wait_for_slow_cycle(); - - DPORT_REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 0); +static void rtc_clk_bbpll_disable() +{ SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BB_I2C_FORCE_PD | RTC_CNTL_BBPLL_FORCE_PD | RTC_CNTL_BBPLL_I2C_FORCE_PD); - s_cur_pll = RTC_PLL_NONE; - rtc_clk_apb_freq_update(xtal_freq * MHZ); + s_cur_pll_freq = 0; /* is APLL under force power down? */ uint32_t apll_fpd = REG_GET_FIELD(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_PLLA_FORCE_PD); @@ -473,76 +421,73 @@ void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq) /* then also power down the internal I2C bus */ SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BIAS_I2C_FORCE_PD); } - /* now switch to the desired frequency */ - if (cpu_freq == RTC_CPU_FREQ_XTAL) { - /* already at XTAL, nothing to do */ - } else if (cpu_freq == RTC_CPU_FREQ_2M) { - /* set up divider to produce 2MHz from XTAL */ - REG_SET_FIELD(APB_CTRL_SYSCLK_CONF_REG, APB_CTRL_PRE_DIV_CNT, (xtal_freq / 2) - 1); - ets_update_cpu_frequency(2); - rtc_clk_apb_freq_update(2 * MHZ); - /* lower the voltage */ - REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_2M); +} + +static void rtc_clk_bbpll_enable() +{ + CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, + RTC_CNTL_BIAS_I2C_FORCE_PD | RTC_CNTL_BB_I2C_FORCE_PD | + RTC_CNTL_BBPLL_FORCE_PD | RTC_CNTL_BBPLL_I2C_FORCE_PD); +} + +/** + * Switch to one of PLL-based frequencies. Current frequency can be XTAL or PLL. + * PLL must already be enabled. + * @param cpu_freq new CPU frequency + */ +static void rtc_clk_cpu_freq_to_pll_mhz(int cpu_freq_mhz) +{ + int dbias = DIG_DBIAS_80M_160M; + int per_conf = 0; + if (cpu_freq_mhz == 80) { + /* nothing to do */ + } else if (cpu_freq_mhz == 160) { + per_conf = 1; + } else if (cpu_freq_mhz == 240) { + dbias = DIG_DBIAS_240M; + per_conf = 2; } else { - /* use PLL as clock source */ - CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, - RTC_CNTL_BIAS_I2C_FORCE_PD | RTC_CNTL_BB_I2C_FORCE_PD | - RTC_CNTL_BBPLL_FORCE_PD | RTC_CNTL_BBPLL_I2C_FORCE_PD); - rtc_clk_bbpll_set(xtal_freq, cpu_freq); - if (cpu_freq == RTC_CPU_FREQ_80M) { - DPORT_REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 0); - ets_update_cpu_frequency(80); - s_cur_pll = RTC_PLL_320M; - } else if (cpu_freq == RTC_CPU_FREQ_160M) { - DPORT_REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 1); - ets_update_cpu_frequency(160); - s_cur_pll = RTC_PLL_320M; - } else if (cpu_freq == RTC_CPU_FREQ_240M) { - DPORT_REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 2); - ets_update_cpu_frequency(240); - s_cur_pll = RTC_PLL_480M; - } - REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_PLL); - rtc_clk_wait_for_slow_cycle(); - rtc_clk_apb_freq_update(80 * MHZ); + assert(false && "invalid frequency"); } - s_cur_freq = cpu_freq; + DPORT_REG_WRITE(DPORT_CPU_PER_CONF_REG, per_conf); + REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, dbias); + REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_PLL); + rtc_clk_apb_freq_update(80 * MHZ); + ets_update_cpu_frequency(cpu_freq_mhz); + rtc_clk_wait_for_slow_cycle(); +} + + +void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq) +{ + rtc_cpu_freq_config_t config; + rtc_clk_cpu_freq_to_config(cpu_freq, &config); + rtc_clk_cpu_freq_set_config(&config); +} + +void rtc_clk_cpu_freq_set_fast(rtc_cpu_freq_t cpu_freq) +{ + rtc_cpu_freq_config_t config; + rtc_clk_cpu_freq_to_config(cpu_freq, &config); + rtc_clk_cpu_freq_set_config_fast(&config); +} + +void rtc_clk_cpu_freq_set_xtal() +{ + int freq_mhz = (int) rtc_clk_xtal_freq_get(); + + rtc_clk_cpu_freq_to_xtal(freq_mhz, 1); + rtc_clk_wait_for_slow_cycle(); + rtc_clk_bbpll_disable(); } rtc_cpu_freq_t rtc_clk_cpu_freq_get() { - uint32_t soc_clk_sel = REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL); - switch (soc_clk_sel) { - case RTC_CNTL_SOC_CLK_SEL_XTL: { - uint32_t pre_div = REG_GET_FIELD(APB_CTRL_SYSCLK_CONF_REG, APB_CTRL_PRE_DIV_CNT); - if (pre_div == 0) { - return RTC_CPU_FREQ_XTAL; - } else if (pre_div == rtc_clk_xtal_freq_get() / 2 - 1) { - return RTC_CPU_FREQ_2M; - } else { - assert(false && "unsupported frequency"); - } - break; - } - case RTC_CNTL_SOC_CLK_SEL_PLL: { - uint32_t cpuperiod_sel = DPORT_REG_GET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL); - if (cpuperiod_sel == 0) { - return RTC_CPU_FREQ_80M; - } else if (cpuperiod_sel == 1) { - return RTC_CPU_FREQ_160M; - } else if (cpuperiod_sel == 2) { - return RTC_CPU_FREQ_240M; - } else { - assert(false && "unsupported frequency"); - } - break; - } - case RTC_CNTL_SOC_CLK_SEL_APLL: - case RTC_CNTL_SOC_CLK_SEL_8M: - default: - assert(false && "unsupported frequency"); - } - return RTC_CNTL_SOC_CLK_SEL_XTL; + rtc_cpu_freq_config_t config; + rtc_clk_cpu_freq_get_config(&config); + rtc_cpu_freq_t freq; + rtc_clk_cpu_freq_from_mhz_internal(config.freq_mhz, &freq); + return freq; } uint32_t rtc_clk_cpu_freq_value(rtc_cpu_freq_t cpu_freq) @@ -564,7 +509,7 @@ uint32_t rtc_clk_cpu_freq_value(rtc_cpu_freq_t cpu_freq) } } -bool rtc_clk_cpu_freq_from_mhz(int mhz, rtc_cpu_freq_t* out_val) +static bool rtc_clk_cpu_freq_from_mhz_internal(int mhz, rtc_cpu_freq_t* out_val) { if (mhz == 240) { *out_val = RTC_CPU_FREQ_240M; @@ -582,22 +527,197 @@ bool rtc_clk_cpu_freq_from_mhz(int mhz, rtc_cpu_freq_t* out_val) return true; } -/* Values of RTC_XTAL_FREQ_REG and RTC_APB_FREQ_REG are stored as two copies in - * lower and upper 16-bit halves. These are the routines to work with such a - * representation. - */ -static bool clk_val_is_valid(uint32_t val) { - return (val & 0xffff) == ((val >> 16) & 0xffff) && - val != 0 && - val != UINT32_MAX; +bool rtc_clk_cpu_freq_from_mhz(int mhz, rtc_cpu_freq_t* out_val) +{ + return rtc_clk_cpu_freq_from_mhz_internal(mhz, out_val); } -static uint32_t reg_val_to_clk_val(uint32_t val) { - return val & UINT16_MAX; +void rtc_clk_cpu_freq_to_config(rtc_cpu_freq_t cpu_freq, rtc_cpu_freq_config_t* out_config) +{ + uint32_t source_freq_mhz; + rtc_cpu_freq_src_t source; + uint32_t freq_mhz; + uint32_t divider; + + switch (cpu_freq) { + case RTC_CPU_FREQ_XTAL: + case RTC_CPU_FREQ_2M: + source_freq_mhz = rtc_clk_xtal_freq_get(); + source = RTC_CPU_FREQ_SRC_XTAL; + if (cpu_freq == RTC_CPU_FREQ_2M) { + freq_mhz = 2; + divider = out_config->source_freq_mhz / 2; + } else { + freq_mhz = source_freq_mhz; + divider = 1; + } + break; + case RTC_CPU_FREQ_80M: + source = RTC_CPU_FREQ_SRC_PLL; + source_freq_mhz = RTC_PLL_FREQ_320M; + divider = 4; + freq_mhz = 80; + break; + case RTC_CPU_FREQ_160M: + source = RTC_CPU_FREQ_SRC_PLL; + source_freq_mhz = RTC_PLL_FREQ_320M; + divider = 2; + freq_mhz = 160; + break; + case RTC_CPU_FREQ_240M: + source = RTC_CPU_FREQ_SRC_PLL; + source_freq_mhz = RTC_PLL_FREQ_480M; + divider = 2; + freq_mhz = 240; + break; + default: + assert(false && "invalid rtc_cpu_freq_t value"); + abort(); + } + + *out_config = (rtc_cpu_freq_config_t) { + .source = source, + .source_freq_mhz = source_freq_mhz, + .div = divider, + .freq_mhz = freq_mhz + }; } -static uint32_t clk_val_to_reg_val(uint32_t val) { - return (val & UINT16_MAX) | ((val & UINT16_MAX) << 16); +bool rtc_clk_cpu_freq_mhz_to_config(uint32_t freq_mhz, rtc_cpu_freq_config_t* out_config) +{ + uint32_t source_freq_mhz; + rtc_cpu_freq_src_t source; + uint32_t divider; + uint32_t real_freq_mhz; + + uint32_t xtal_freq = (uint32_t) rtc_clk_xtal_freq_get(); + if (freq_mhz <= xtal_freq) { + divider = xtal_freq / freq_mhz; + real_freq_mhz = (xtal_freq + divider / 2) / divider; /* round */ + if (real_freq_mhz != freq_mhz) { + // no suitable divider + return false; + } + + source_freq_mhz = xtal_freq; + source = RTC_CPU_FREQ_SRC_XTAL; + } else if (freq_mhz == 80) { + real_freq_mhz = freq_mhz; + source = RTC_CPU_FREQ_SRC_PLL; + source_freq_mhz = RTC_PLL_FREQ_320M; + divider = 4; + } else if (freq_mhz == 160) { + real_freq_mhz = freq_mhz; + source = RTC_CPU_FREQ_SRC_PLL; + source_freq_mhz = RTC_PLL_FREQ_320M; + divider = 2; + } else if (freq_mhz == 240) { + real_freq_mhz = freq_mhz; + source = RTC_CPU_FREQ_SRC_PLL; + source_freq_mhz = RTC_PLL_FREQ_480M; + divider = 2; + } else { + // unsupported frequency + return false; + } + *out_config = (rtc_cpu_freq_config_t) { + .source = source, + .div = divider, + .source_freq_mhz = source_freq_mhz, + .freq_mhz = real_freq_mhz + }; + return true; +} + +void rtc_clk_cpu_freq_set_config(const rtc_cpu_freq_config_t* config) +{ + rtc_xtal_freq_t xtal_freq = rtc_clk_xtal_freq_get(); + uint32_t soc_clk_sel = REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL); + if (soc_clk_sel != RTC_CNTL_SOC_CLK_SEL_XTL) { + rtc_clk_cpu_freq_to_xtal(xtal_freq, 1); + rtc_clk_wait_for_slow_cycle(); + } + if (soc_clk_sel == RTC_CNTL_SOC_CLK_SEL_PLL) { + rtc_clk_bbpll_disable(); + } + if (config->source == RTC_CPU_FREQ_SRC_XTAL) { + if (config->div > 1) { + rtc_clk_cpu_freq_to_xtal(config->freq_mhz, config->div); + } + } else if (config->source == RTC_CPU_FREQ_SRC_PLL) { + rtc_clk_bbpll_enable(); + rtc_clk_wait_for_slow_cycle(); + rtc_clk_bbpll_configure(rtc_clk_xtal_freq_get(), config->source_freq_mhz); + rtc_clk_cpu_freq_to_pll_mhz(config->freq_mhz); + } else if (config->source == RTC_CPU_FREQ_SRC_8M) { + rtc_clk_cpu_freq_to_8m(); + } +} + +void rtc_clk_cpu_freq_get_config(rtc_cpu_freq_config_t* out_config) +{ + rtc_cpu_freq_src_t source; + uint32_t source_freq_mhz; + uint32_t div; + uint32_t freq_mhz; + uint32_t soc_clk_sel = REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL); + switch (soc_clk_sel) { + case RTC_CNTL_SOC_CLK_SEL_XTL: { + source = RTC_CPU_FREQ_SRC_XTAL; + div = REG_GET_FIELD(APB_CTRL_SYSCLK_CONF_REG, APB_CTRL_PRE_DIV_CNT) + 1; + source_freq_mhz = (uint32_t) rtc_clk_xtal_freq_get(); + freq_mhz = source_freq_mhz / div; + } + break; + case RTC_CNTL_SOC_CLK_SEL_PLL: { + source = RTC_CPU_FREQ_SRC_PLL; + uint32_t cpuperiod_sel = DPORT_REG_GET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL); + if (cpuperiod_sel == 0) { + source_freq_mhz = RTC_PLL_FREQ_320M; + div = 4; + freq_mhz = 80; + } else if (cpuperiod_sel == 1) { + source_freq_mhz = RTC_PLL_FREQ_320M; + div = 2; + freq_mhz = 160; + } else if (cpuperiod_sel == 2) { + source_freq_mhz = RTC_PLL_FREQ_480M; + div = 2; + freq_mhz = 240; + } else { + assert(false && "unsupported frequency configuration"); + } + break; + } + case RTC_CNTL_SOC_CLK_SEL_8M: + source = RTC_CPU_FREQ_SRC_8M; + source_freq_mhz = 8; + div = 1; + freq_mhz = source_freq_mhz; + break; + case RTC_CNTL_SOC_CLK_SEL_APLL: + default: + assert(false && "unsupported frequency configuration"); + } + *out_config = (rtc_cpu_freq_config_t) { + .source = source, + .source_freq_mhz = source_freq_mhz, + .div = div, + .freq_mhz = freq_mhz + }; +} + +void rtc_clk_cpu_freq_set_config_fast(const rtc_cpu_freq_config_t* config) +{ + if (config->source == RTC_CPU_FREQ_SRC_XTAL) { + rtc_clk_cpu_freq_to_xtal(config->freq_mhz, config->div); + } else if (config->source == RTC_CPU_FREQ_SRC_PLL && + s_cur_pll_freq == config->source_freq_mhz) { + rtc_clk_cpu_freq_to_pll_mhz(config->freq_mhz); + } else { + /* fallback */ + rtc_clk_cpu_freq_set_config(config); + } } rtc_xtal_freq_t rtc_clk_xtal_freq_get() @@ -605,7 +725,6 @@ rtc_xtal_freq_t rtc_clk_xtal_freq_get() /* We may have already written XTAL value into RTC_XTAL_FREQ_REG */ uint32_t xtal_freq_reg = READ_PERI_REG(RTC_XTAL_FREQ_REG); if (!clk_val_is_valid(xtal_freq_reg)) { - SOC_LOGW(TAG, "invalid RTC_XTAL_FREQ_REG value: 0x%08x", xtal_freq_reg); return RTC_XTAL_FREQ_AUTO; } return reg_val_to_clk_val(xtal_freq_reg); @@ -616,42 +735,6 @@ void rtc_clk_xtal_freq_update(rtc_xtal_freq_t xtal_freq) WRITE_PERI_REG(RTC_XTAL_FREQ_REG, clk_val_to_reg_val(xtal_freq)); } -static rtc_xtal_freq_t rtc_clk_xtal_freq_estimate() -{ - /* Enable 8M/256 clock if needed */ - const bool clk_8m_enabled = rtc_clk_8m_enabled(); - const bool clk_8md256_enabled = rtc_clk_8md256_enabled(); - if (!clk_8md256_enabled) { - rtc_clk_8m_enable(true, true); - } - - uint64_t cal_val = rtc_clk_cal_ratio(RTC_CAL_8MD256, XTAL_FREQ_EST_CYCLES); - /* cal_val contains period of 8M/256 clock in XTAL clock cycles - * (shifted by RTC_CLK_CAL_FRACT bits). - * Xtal frequency will be (cal_val * 8M / 256) / 2^19 - */ - uint32_t freq_mhz = (cal_val * RTC_FAST_CLK_FREQ_APPROX / MHZ / 256 ) >> RTC_CLK_CAL_FRACT; - /* Guess the XTAL type. For now, only 40 and 26MHz are supported. - */ - switch (freq_mhz) { - case 21 ... 31: - return RTC_XTAL_FREQ_26M; - case 32 ... 33: - SOC_LOGW(TAG, "Potentially bogus XTAL frequency: %d MHz, guessing 26 MHz", freq_mhz); - return RTC_XTAL_FREQ_26M; - case 34 ... 35: - SOC_LOGW(TAG, "Potentially bogus XTAL frequency: %d MHz, guessing 40 MHz", freq_mhz); - return RTC_XTAL_FREQ_40M; - case 36 ... 45: - return RTC_XTAL_FREQ_40M; - default: - SOC_LOGW(TAG, "Bogus XTAL frequency: %d MHz", freq_mhz); - return RTC_XTAL_FREQ_AUTO; - } - /* Restore 8M and 8md256 clocks to original state */ - rtc_clk_8m_enable(clk_8m_enabled, clk_8md256_enabled); -} - void rtc_clk_apb_freq_update(uint32_t apb_freq) { WRITE_PERI_REG(RTC_APB_FREQ_REG, clk_val_to_reg_val(apb_freq >> 12)); @@ -666,90 +749,6 @@ uint32_t rtc_clk_apb_freq_get() return freq_hz - remainder; } - -void rtc_clk_init(rtc_clk_config_t cfg) -{ - rtc_cpu_freq_t cpu_source_before = rtc_clk_cpu_freq_get(); - - /* If we get a TG WDT system reset while running at 240MHz, - * DPORT_CPUPERIOD_SEL register will be reset to 0 resulting in 120MHz - * APB and CPU frequencies after reset. This will cause issues with XTAL - * frequency estimation, so we switch to XTAL frequency first. - * - * Ideally we would only do this if RTC_CNTL_SOC_CLK_SEL == PLL and - * PLL is configured for 480M, but it takes less time to switch to 40M and - * run the following code than querying the PLL does. - */ - if (REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL) == RTC_CNTL_SOC_CLK_SEL_PLL) { - rtc_clk_cpu_freq_set(RTC_CPU_FREQ_XTAL); - } - - /* Set tuning parameters for 8M and 150k clocks. - * Note: this doesn't attempt to set the clocks to precise frequencies. - * Instead, we calibrate these clocks against XTAL frequency later, when necessary. - * - SCK_DCAP value controls tuning of 150k clock. - * The higher the value of DCAP is, the lower is the frequency. - * - CK8M_DFREQ value controls tuning of 8M clock. - * CLK_8M_DFREQ constant gives the best temperature characteristics. - */ - REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_SCK_DCAP, cfg.slow_clk_dcap); - REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_DFREQ, cfg.clk_8m_dfreq); - - /* Configure 8M clock division */ - REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_DIV_SEL, cfg.clk_8m_div); - - /* Enable the internal bus used to configure PLLs */ - SET_PERI_REG_BITS(ANA_CONFIG_REG, ANA_CONFIG_M, ANA_CONFIG_M, ANA_CONFIG_S); - CLEAR_PERI_REG_MASK(ANA_CONFIG_REG, I2C_APLL_M | I2C_BBPLL_M); - - /* Estimate XTAL frequency */ - rtc_xtal_freq_t xtal_freq = cfg.xtal_freq; - if (xtal_freq == RTC_XTAL_FREQ_AUTO) { - if (clk_val_is_valid(READ_PERI_REG(RTC_XTAL_FREQ_REG))) { - /* XTAL frequency has already been set, use existing value */ - xtal_freq = rtc_clk_xtal_freq_get(); - } else { - /* Not set yet, estimate XTAL frequency based on RTC_FAST_CLK */ - xtal_freq = rtc_clk_xtal_freq_estimate(); - if (xtal_freq == RTC_XTAL_FREQ_AUTO) { - SOC_LOGW(TAG, "Can't estimate XTAL frequency, assuming 26MHz"); - xtal_freq = RTC_XTAL_FREQ_26M; - } - } - } else if (!clk_val_is_valid(READ_PERI_REG(RTC_XTAL_FREQ_REG))) { - /* Exact frequency was set in sdkconfig, but still warn if autodetected - * frequency is different. If autodetection failed, worst case we get a - * bit of garbage output. - */ - rtc_xtal_freq_t est_xtal_freq = rtc_clk_xtal_freq_estimate(); - if (est_xtal_freq != xtal_freq) { - SOC_LOGW(TAG, "Possibly invalid CONFIG_ESP32_XTAL_FREQ setting (%dMHz). Detected %d MHz.", - xtal_freq, est_xtal_freq); - } - } - uart_tx_wait_idle(0); - rtc_clk_xtal_freq_update(xtal_freq); - rtc_clk_apb_freq_update(xtal_freq * MHZ); - /* Set CPU frequency */ - rtc_clk_cpu_freq_set(cfg.cpu_freq); - - /* Re-calculate the ccount to make time calculation correct. */ - uint32_t freq_before = rtc_clk_cpu_freq_value(cpu_source_before) / MHZ; - uint32_t freq_after = rtc_clk_cpu_freq_value(cfg.cpu_freq) / MHZ; - XTHAL_SET_CCOUNT( XTHAL_GET_CCOUNT() * freq_after / freq_before ); - - /* Slow & fast clocks setup */ - if (cfg.slow_freq == RTC_SLOW_FREQ_32K_XTAL) { - rtc_clk_32k_enable(true); - } - if (cfg.fast_freq == RTC_FAST_FREQ_8M) { - bool need_8md256 = cfg.slow_freq == RTC_SLOW_FREQ_8MD256; - rtc_clk_8m_enable(true, need_8md256); - } - rtc_clk_fast_freq_set(cfg.fast_freq); - rtc_clk_slow_freq_set(cfg.slow_freq); -} - /* Name used in libphy.a:phy_chip_v7.o * TODO: update the library to use rtc_clk_xtal_freq_get */ diff --git a/components/soc/esp32/rtc_clk_common.h b/components/soc/esp32/rtc_clk_common.h new file mode 100644 index 000000000..b1efa175d --- /dev/null +++ b/components/soc/esp32/rtc_clk_common.h @@ -0,0 +1,38 @@ +// Copyright 2015-2018 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 + +#define MHZ (1000000) + +void rtc_clk_cpu_freq_to_xtal(int freq, int div); + +/* Values of RTC_XTAL_FREQ_REG and RTC_APB_FREQ_REG are stored as two copies in + * lower and upper 16-bit halves. These are the routines to work with such a + * representation. + */ +static inline bool clk_val_is_valid(uint32_t val) { + return (val & 0xffff) == ((val >> 16) & 0xffff) && + val != 0 && + val != UINT32_MAX; +} + +static inline uint32_t reg_val_to_clk_val(uint32_t val) { + return val & UINT16_MAX; +} + +static inline uint32_t clk_val_to_reg_val(uint32_t val) { + return (val & UINT16_MAX) | ((val & UINT16_MAX) << 16); +} + diff --git a/components/soc/esp32/rtc_clk_init.c b/components/soc/esp32/rtc_clk_init.c new file mode 100644 index 000000000..6672f460b --- /dev/null +++ b/components/soc/esp32/rtc_clk_init.c @@ -0,0 +1,176 @@ +// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include "rom/ets_sys.h" +#include "rom/rtc.h" +#include "rom/uart.h" +#include "rom/gpio.h" +#include "soc/rtc.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/rtc_io_reg.h" +#include "soc/sens_reg.h" +#include "soc/dport_reg.h" +#include "soc/efuse_reg.h" +#include "soc/apb_ctrl_reg.h" +#include "i2c_rtc_clk.h" +#include "soc_log.h" +#include "sdkconfig.h" +#include "xtensa/core-macros.h" +#include "rtc_clk_common.h" + +/* Number of 8M/256 clock cycles to use for XTAL frequency estimation. + * 10 cycles will take approximately 300 microseconds. + */ +#define XTAL_FREQ_EST_CYCLES 10 + +static rtc_xtal_freq_t rtc_clk_xtal_freq_estimate(); + +static const char* TAG = "rtc_clk_init"; + +void rtc_clk_init(rtc_clk_config_t cfg) +{ + rtc_cpu_freq_config_t old_config, new_config; + + /* If we get a TG WDT system reset while running at 240MHz, + * DPORT_CPUPERIOD_SEL register will be reset to 0 resulting in 120MHz + * APB and CPU frequencies after reset. This will cause issues with XTAL + * frequency estimation, so we switch to XTAL frequency first. + * + * Ideally we would only do this if RTC_CNTL_SOC_CLK_SEL == PLL and + * PLL is configured for 480M, but it takes less time to switch to 40M and + * run the following code than querying the PLL does. + */ + if (REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL) == RTC_CNTL_SOC_CLK_SEL_PLL) { + /* We don't know actual XTAL frequency yet, assume 40MHz. + * REF_TICK divider will be corrected below, once XTAL frequency is + * determined. + */ + rtc_clk_cpu_freq_to_xtal(40, 1); + } + + /* Set tuning parameters for 8M and 150k clocks. + * Note: this doesn't attempt to set the clocks to precise frequencies. + * Instead, we calibrate these clocks against XTAL frequency later, when necessary. + * - SCK_DCAP value controls tuning of 150k clock. + * The higher the value of DCAP is, the lower is the frequency. + * - CK8M_DFREQ value controls tuning of 8M clock. + * CLK_8M_DFREQ constant gives the best temperature characteristics. + */ + REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_SCK_DCAP, cfg.slow_clk_dcap); + REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_DFREQ, cfg.clk_8m_dfreq); + + /* Configure 8M clock division */ + REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_DIV_SEL, cfg.clk_8m_div); + + /* Enable the internal bus used to configure PLLs */ + SET_PERI_REG_BITS(ANA_CONFIG_REG, ANA_CONFIG_M, ANA_CONFIG_M, ANA_CONFIG_S); + CLEAR_PERI_REG_MASK(ANA_CONFIG_REG, I2C_APLL_M | I2C_BBPLL_M); + + /* Estimate XTAL frequency */ + rtc_xtal_freq_t xtal_freq = cfg.xtal_freq; + if (xtal_freq == RTC_XTAL_FREQ_AUTO) { + if (clk_val_is_valid(READ_PERI_REG(RTC_XTAL_FREQ_REG))) { + /* XTAL frequency has already been set, use existing value */ + xtal_freq = rtc_clk_xtal_freq_get(); + } else { + /* Not set yet, estimate XTAL frequency based on RTC_FAST_CLK */ + xtal_freq = rtc_clk_xtal_freq_estimate(); + if (xtal_freq == RTC_XTAL_FREQ_AUTO) { + SOC_LOGW(TAG, "Can't estimate XTAL frequency, assuming 26MHz"); + xtal_freq = RTC_XTAL_FREQ_26M; + } + } + } else if (!clk_val_is_valid(READ_PERI_REG(RTC_XTAL_FREQ_REG))) { + /* Exact frequency was set in sdkconfig, but still warn if autodetected + * frequency is different. If autodetection failed, worst case we get a + * bit of garbage output. + */ + + rtc_xtal_freq_t est_xtal_freq = rtc_clk_xtal_freq_estimate(); + if (est_xtal_freq != xtal_freq) { + SOC_LOGW(TAG, "Possibly invalid CONFIG_ESP32_XTAL_FREQ setting (%dMHz). Detected %d MHz.", + xtal_freq, est_xtal_freq); + } + } + uart_tx_wait_idle(0); + rtc_clk_xtal_freq_update(xtal_freq); + rtc_clk_apb_freq_update(xtal_freq * MHZ); + + /* Set CPU frequency */ + + rtc_clk_cpu_freq_get_config(&old_config); + uint32_t freq_before = old_config.freq_mhz; + + bool res = rtc_clk_cpu_freq_mhz_to_config(cfg.cpu_freq_mhz, &new_config); + assert(res && "invalid CPU frequency value"); + + /* Configure REF_TICK */ + REG_WRITE(APB_CTRL_XTAL_TICK_CONF_REG, xtal_freq - 1); + REG_WRITE(APB_CTRL_PLL_TICK_CONF_REG, APB_CLK_FREQ / MHZ - 1); /* Under PLL, APB frequency is always 80MHz */ + + /* Re-calculate the ccount to make time calculation correct. */ + XTHAL_SET_CCOUNT( XTHAL_GET_CCOUNT() * cfg.cpu_freq_mhz / freq_before ); + + /* Slow & fast clocks setup */ + if (cfg.slow_freq == RTC_SLOW_FREQ_32K_XTAL) { + rtc_clk_32k_enable(true); + } + if (cfg.fast_freq == RTC_FAST_FREQ_8M) { + bool need_8md256 = cfg.slow_freq == RTC_SLOW_FREQ_8MD256; + rtc_clk_8m_enable(true, need_8md256); + } + rtc_clk_fast_freq_set(cfg.fast_freq); + rtc_clk_slow_freq_set(cfg.slow_freq); +} + +static rtc_xtal_freq_t rtc_clk_xtal_freq_estimate() +{ + /* Enable 8M/256 clock if needed */ + const bool clk_8m_enabled = rtc_clk_8m_enabled(); + const bool clk_8md256_enabled = rtc_clk_8md256_enabled(); + if (!clk_8md256_enabled) { + rtc_clk_8m_enable(true, true); + } + + uint64_t cal_val = rtc_clk_cal_ratio(RTC_CAL_8MD256, XTAL_FREQ_EST_CYCLES); + /* cal_val contains period of 8M/256 clock in XTAL clock cycles + * (shifted by RTC_CLK_CAL_FRACT bits). + * Xtal frequency will be (cal_val * 8M / 256) / 2^19 + */ + uint32_t freq_mhz = (cal_val * RTC_FAST_CLK_FREQ_APPROX / MHZ / 256 ) >> RTC_CLK_CAL_FRACT; + /* Guess the XTAL type. For now, only 40 and 26MHz are supported. + */ + switch (freq_mhz) { + case 21 ... 31: + return RTC_XTAL_FREQ_26M; + case 32 ... 33: + SOC_LOGW(TAG, "Potentially bogus XTAL frequency: %d MHz, guessing 26 MHz", freq_mhz); + return RTC_XTAL_FREQ_26M; + case 34 ... 35: + SOC_LOGW(TAG, "Potentially bogus XTAL frequency: %d MHz, guessing 40 MHz", freq_mhz); + return RTC_XTAL_FREQ_40M; + case 36 ... 45: + return RTC_XTAL_FREQ_40M; + default: + SOC_LOGW(TAG, "Bogus XTAL frequency: %d MHz", freq_mhz); + return RTC_XTAL_FREQ_AUTO; + } + /* Restore 8M and 8md256 clocks to original state */ + rtc_clk_8m_enable(clk_8m_enabled, clk_8md256_enabled); +} diff --git a/components/soc/esp32/rtc_sleep.c b/components/soc/esp32/rtc_sleep.c index 041c2d1b6..6b16aa288 100644 --- a/components/soc/esp32/rtc_sleep.c +++ b/components/soc/esp32/rtc_sleep.c @@ -197,6 +197,12 @@ void rtc_sleep_init(rtc_sleep_config_t cfg) REG_SET_FIELD(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_XTL_FORCE_PU, cfg.xtal_fpu); + if (REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ANA_CLK_RTC_SEL) == RTC_SLOW_FREQ_8MD256) { + REG_SET_BIT(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_FORCE_PU); + } else { + REG_CLR_BIT(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_FORCE_PU); + } + /* enable VDDSDIO control by state machine */ REG_CLR_BIT(RTC_CNTL_SDIO_CONF_REG, RTC_CNTL_SDIO_FORCE); REG_SET_FIELD(RTC_CNTL_SDIO_CONF_REG, RTC_CNTL_SDIO_PD_EN, cfg.vddsdio_pd_en); diff --git a/components/soc/esp32/rtc_wdt.c b/components/soc/esp32/rtc_wdt.c index 8daa352f3..5fea2fd55 100644 --- a/components/soc/esp32/rtc_wdt.c +++ b/components/soc/esp32/rtc_wdt.c @@ -73,7 +73,7 @@ esp_err_t rtc_wdt_set_time(rtc_wdt_stage_t stage, unsigned int timeout_ms) if (stage > 3) { return ESP_ERR_INVALID_ARG; } - uint32_t timeout = rtc_clk_slow_freq_get_hz() * timeout_ms / 1000; + uint32_t timeout = (uint32_t) ((uint64_t) rtc_clk_slow_freq_get_hz() * timeout_ms / 1000); if (stage == RTC_WDT_STAGE0) { WRITE_PERI_REG(RTC_CNTL_WDTCONFIG1_REG, timeout); } else if (stage == RTC_WDT_STAGE1) { diff --git a/components/soc/esp32/soc_memory_layout.c b/components/soc/esp32/soc_memory_layout.c index e60fe8330..c6fa4635d 100644 --- a/components/soc/esp32/soc_memory_layout.c +++ b/components/soc/esp32/soc_memory_layout.c @@ -156,9 +156,9 @@ SOC_RESERVE_MEMORY_REGION(0x3ffae000, 0x3ffae6e0, rom_data); #if CONFIG_MEMMAP_TRACEMEM #if CONFIG_MEMMAP_TRACEMEM_TWOBANKS -SOC_RESERVE_MEMORY_REGION(0x3fff8000, 0x40000000, trace_mem); //Reserve trace mem region +SOC_RESERVE_MEMORY_REGION(0x3fff8000, 0x40000000, trace_mem); //Reserve trace mem region, 32K for both cpu #else -SOC_RESERVE_MEMORY_REGION(0x3fff8000, 0x3fffc000, trace_mem); //Reserve trace mem region +SOC_RESERVE_MEMORY_REGION(0x3fffc000, 0x40000000, trace_mem); //Reserve trace mem region, 16K (upper-half) for pro cpu #endif #endif diff --git a/components/soc/esp32/test/test_rtc_clk.c b/components/soc/esp32/test/test_rtc_clk.c index 39c3ac7fd..ee05a302b 100644 --- a/components/soc/esp32/test/test_rtc_clk.c +++ b/components/soc/esp32/test/test_rtc_clk.c @@ -95,7 +95,7 @@ TEST_CASE("Output 8M XTAL clock to GPIO25", "[rtc_clk][ignore]") pull_out_clk(RTC_IO_DEBUG_SEL0_8M); } -static void test_clock_switching(void (*switch_func)(rtc_cpu_freq_t)) +static void test_clock_switching(void (*switch_func)(const rtc_cpu_freq_config_t* config)) { uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM); @@ -103,11 +103,16 @@ static void test_clock_switching(void (*switch_func)(rtc_cpu_freq_t)) ref_clock_init(); uint64_t t_start = ref_clock_get(); - rtc_cpu_freq_t cur_freq = rtc_clk_cpu_freq_get(); + rtc_cpu_freq_config_t cur_config; + rtc_clk_cpu_freq_get_config(&cur_config); + + rtc_cpu_freq_config_t xtal_config; + rtc_clk_cpu_freq_mhz_to_config((uint32_t) rtc_clk_xtal_freq_get(), &xtal_config); + int count = 0; while (ref_clock_get() - t_start < test_duration_sec * 1000000) { - switch_func(RTC_CPU_FREQ_XTAL); - switch_func(cur_freq); + switch_func(&xtal_config); + switch_func(&cur_config); ++count; } uint64_t t_end = ref_clock_get(); @@ -126,12 +131,12 @@ TEST_CASE("Calculate 8M clock frequency", "[rtc_clk]") TEST_CASE("Test switching between PLL and XTAL", "[rtc_clk]") { - test_clock_switching(rtc_clk_cpu_freq_set); + test_clock_switching(rtc_clk_cpu_freq_set_config); } TEST_CASE("Test fast switching between PLL and XTAL", "[rtc_clk]") { - test_clock_switching(rtc_clk_cpu_freq_set_fast); + test_clock_switching(rtc_clk_cpu_freq_set_config_fast); } #define COUNT_TEST 3 diff --git a/components/soc/include/soc/soc_memory_layout.h b/components/soc/include/soc/soc_memory_layout.h index 65382ba97..b1e8d6eb9 100644 --- a/components/soc/include/soc/soc_memory_layout.h +++ b/components/soc/include/soc/soc_memory_layout.h @@ -20,6 +20,38 @@ #include "sdkconfig.h" #include "esp_attr.h" +#ifdef CONFIG_BT_ENABLED + +#define SOC_MEM_BT_DATA_START 0x3ffae6e0 +#define SOC_MEM_BT_DATA_END 0x3ffaff10 +#define SOC_MEM_BT_EM_START 0x3ffb0000 +#define SOC_MEM_BT_EM_END 0x3ffb7cd8 +#define SOC_MEM_BT_EM_BTDM0_START 0x3ffb0000 +#define SOC_MEM_BT_EM_BTDM0_END 0x3ffb09a8 +#define SOC_MEM_BT_EM_BLE_START 0x3ffb09a8 +#define SOC_MEM_BT_EM_BLE_END 0x3ffb1ddc +#define SOC_MEM_BT_EM_BTDM1_START 0x3ffb1ddc +#define SOC_MEM_BT_EM_BTDM1_END 0x3ffb2730 +#define SOC_MEM_BT_EM_BREDR_START 0x3ffb2730 +#define SOC_MEM_BT_EM_BREDR_NO_SYNC_END 0x3ffb6388 //Not calculate with synchronize connection support +#define SOC_MEM_BT_EM_BREDR_END 0x3ffb7cd8 //Calculate with synchronize connection support +#define SOC_MEM_BT_EM_SYNC0_START 0x3ffb6388 +#define SOC_MEM_BT_EM_SYNC0_END 0x3ffb6bf8 +#define SOC_MEM_BT_EM_SYNC1_START 0x3ffb6bf8 +#define SOC_MEM_BT_EM_SYNC1_END 0x3ffb7468 +#define SOC_MEM_BT_EM_SYNC2_START 0x3ffb7468 +#define SOC_MEM_BT_EM_SYNC2_END 0x3ffb7cd8 +#define SOC_MEM_BT_BSS_START 0x3ffb8000 +#define SOC_MEM_BT_BSS_END 0x3ffb9a20 +#define SOC_MEM_BT_MISC_START 0x3ffbdb28 +#define SOC_MEM_BT_MISC_END 0x3ffbdb5c + +#define SOC_MEM_BT_EM_PER_SYNC_SIZE 0x870 + +#define SOC_MEM_BT_EM_BREDR_REAL_END (SOC_MEM_BT_EM_BREDR_NO_SYNC_END + CONFIG_BTDM_CONTROLLER_BR_EDR_MAX_SYNC_CONN_EFF * SOC_MEM_BT_EM_PER_SYNC_SIZE) + +#endif //CONFIG_BT_ENABLED + #define SOC_MEMORY_TYPE_NO_PRIOS 3 /* Type descriptor holds a description for a particular type of memory on a particular SoC. diff --git a/components/spiffs/test/test_spiffs.c b/components/spiffs/test/test_spiffs.c index 60bca410b..cb2164ac1 100644 --- a/components/spiffs/test/test_spiffs.c +++ b/components/spiffs/test/test_spiffs.c @@ -291,7 +291,8 @@ void test_spiffs_readdir_many_files(const char* dir_prefix) if (!de) { break; } - snprintf(file_name, sizeof(file_name), "%s/%s", dir_prefix, de->d_name); + int len = snprintf(file_name, sizeof(file_name), "%s/%s", dir_prefix, de->d_name); + assert(len < sizeof(file_name)); unlink(file_name); } } diff --git a/components/tcpip_adapter/Kconfig b/components/tcpip_adapter/Kconfig index eecf12054..5c76b66d1 100644 --- a/components/tcpip_adapter/Kconfig +++ b/components/tcpip_adapter/Kconfig @@ -1,4 +1,4 @@ -menu "tcpip adapter" +menu "TCP/IP Adapter" config IP_LOST_TIMER_INTERVAL int "IP Address lost timer interval (seconds)" @@ -13,4 +13,15 @@ config IP_LOST_TIMER_INTERVAL the timer expires. The IP lost timer is stopped if the station get the IP again before the timer expires. +choice USE_TCPIP_STACK_LIB + prompt "TCP/IP Stack Library" + default TCPIP_LWIP + help + Choose the TCP/IP Stack to work, for example, LwIP, uIP, etc. +config TCPIP_LWIP + bool "LwIP" + help + lwIP is a small independent implementation of the TCP/IP protocol suite. +endchoice + endmenu diff --git a/components/tcpip_adapter/include/tcpip_adapter.h b/components/tcpip_adapter/include/tcpip_adapter.h index 7ab31d1c1..4644bd81e 100644 --- a/components/tcpip_adapter/include/tcpip_adapter.h +++ b/components/tcpip_adapter/include/tcpip_adapter.h @@ -33,15 +33,13 @@ * get free station list APIs in application side. Other APIs are used in esp-idf internal, * otherwise the state maybe wrong. * - * TODO: ipv6 support will be added, use menuconfig to disable CONFIG_TCPIP_LWIP + * TODO: ipv6 support will be added */ #include #include "rom/queue.h" #include "esp_wifi_types.h" - -#define CONFIG_TCPIP_LWIP 1 -#define CONFIG_DHCP_STA_LIST 1 +#include "sdkconfig.h" #if CONFIG_TCPIP_LWIP #include "lwip/ip_addr.h" @@ -81,7 +79,6 @@ typedef struct { typedef dhcps_lease_t tcpip_adapter_dhcps_lease_t; -#if CONFIG_DHCP_STA_LIST typedef struct { uint8_t mac[6]; ip4_addr_t ip; @@ -91,12 +88,10 @@ typedef struct { tcpip_adapter_sta_info_t sta[ESP_WIFI_MAX_CONN_NUM]; int num; } tcpip_adapter_sta_list_t; -#endif #endif -#define ESP_ERR_TCPIP_ADAPTER_BASE 0x5000 // TODO: move base address to esp_err.h - +#define ESP_ERR_TCPIP_ADAPTER_BASE 0x5000 #define ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS ESP_ERR_TCPIP_ADAPTER_BASE + 0x01 #define ESP_ERR_TCPIP_ADAPTER_IF_NOT_READY ESP_ERR_TCPIP_ADAPTER_BASE + 0x02 #define ESP_ERR_TCPIP_ADAPTER_DHCPC_START_FAILED ESP_ERR_TCPIP_ADAPTER_BASE + 0x03 @@ -105,7 +100,6 @@ typedef struct { #define ESP_ERR_TCPIP_ADAPTER_NO_MEM ESP_ERR_TCPIP_ADAPTER_BASE + 0x06 #define ESP_ERR_TCPIP_ADAPTER_DHCP_NOT_STOPPED ESP_ERR_TCPIP_ADAPTER_BASE + 0x07 -/* TODO: add Ethernet interface */ typedef enum { TCPIP_ADAPTER_IF_STA = 0, /**< ESP32 station interface */ TCPIP_ADAPTER_IF_AP, /**< ESP32 soft-AP interface */ @@ -522,8 +516,17 @@ esp_err_t tcpip_adapter_dhcpc_start(tcpip_adapter_if_t tcpip_if); */ esp_err_t tcpip_adapter_dhcpc_stop(tcpip_adapter_if_t tcpip_if); - - +/** + * @brief Get data from ethernet interface + * + * This function should be installed by esp_eth_init, so Ethernet packets will be forward to TCPIP stack. + * + * @param[in] void *buffer: the received data point + * @param[in] uint16_t len: the received data length + * @param[in] void *eb: parameter + * + * @return ESP_OK + */ esp_err_t tcpip_adapter_eth_input(void *buffer, uint16_t len, void *eb); /** @@ -561,7 +564,7 @@ esp_err_t tcpip_adapter_ap_input(void *buffer, uint16_t len, void *eb); * * @return ESP_IF_WIFI_STA * ESP_IF_WIFI_AP - ESP_IF_ETH + * ESP_IF_ETH * ESP_IF_MAX */ esp_interface_t tcpip_adapter_get_esp_if(void *dev); diff --git a/components/tcpip_adapter/tcpip_adapter_lwip.c b/components/tcpip_adapter/tcpip_adapter_lwip.c index 8198cb344..31b9db24e 100644 --- a/components/tcpip_adapter/tcpip_adapter_lwip.c +++ b/components/tcpip_adapter/tcpip_adapter_lwip.c @@ -161,7 +161,7 @@ static esp_err_t tcpip_adapter_update_default_netif(void) return ESP_OK; } -esp_err_t tcpip_adapter_start(tcpip_adapter_if_t tcpip_if, uint8_t *mac, tcpip_adapter_ip_info_t *ip_info) +static 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; diff --git a/docs/Doxyfile b/docs/Doxyfile index d91b7cf23..2f50b6520 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -180,7 +180,9 @@ INPUT = \ ../../components/freertos/include/freertos/event_groups.h \ ../../components/freertos/include/freertos/ringbuf.h \ ### Helper functions for error codes - ../../components/esp32/include/esp_err.h + ../../components/esp32/include/esp_err.h \ + ### System APIs + ../../components/esp32/include/esp_system.h diff --git a/docs/_static/espressif-logo.svg b/docs/_static/espressif-logo.svg index 210ae205e..5fccc316b 100644 --- a/docs/_static/espressif-logo.svg +++ b/docs/_static/espressif-logo.svg @@ -1,113 +1,77 @@ - - - - - - - - - - - - - - - ESPRESSIF LOGOPantone No. 3556 CCMYK 0/83/96/0RGB 255/48/52# FF3034 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/docs_common.mk b/docs/docs_common.mk index 85056dc39..c79cb0d4b 100644 --- a/docs/docs_common.mk +++ b/docs/docs_common.mk @@ -15,15 +15,12 @@ # You can set these variables from the command line. SPHINXOPTS = -SPHINXBUILD = sphinx-build +# note: this is changed from sphinx-build so it depends on default python interpreter, not on /bin/sphinx-build +# (which will be the most recently installed version of sphinx and may not match) +SPHINXBUILD = python2 -m sphinx PAPER = BUILDDIR = _build -# User-friendly check for sphinx-build -ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) -$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don\'t have Sphinx installed, grab it from http://sphinx-doc.org/) -endif - # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter diff --git a/docs/en/api-guides/jtag-debugging/index.rst b/docs/en/api-guides/jtag-debugging/index.rst index 16ab37d78..c74609fdf 100644 --- a/docs/en/api-guides/jtag-debugging/index.rst +++ b/docs/en/api-guides/jtag-debugging/index.rst @@ -281,6 +281,7 @@ This section provides collection of links to all tips and quirks referred to fro * :ref:`jtag-debugging-tip-breakpoints` * :ref:`jtag-debugging-tip-where-breakpoints` +* :ref:`jtag-debugging-tip-flash-mappings` * :ref:`jtag-debugging-tip-why-next-works-as-step` * :ref:`jtag-debugging-tip-code-options` * :ref:`jtag-debugging-tip-freertos-support` diff --git a/docs/en/api-guides/jtag-debugging/tips-and-quirks.rst b/docs/en/api-guides/jtag-debugging/tips-and-quirks.rst index e104ff0ff..e473873f3 100644 --- a/docs/en/api-guides/jtag-debugging/tips-and-quirks.rst +++ b/docs/en/api-guides/jtag-debugging/tips-and-quirks.rst @@ -20,6 +20,25 @@ What else should I know about breakpoints? Emulating part of hardware breakpoints using software flash ones means that the GDB command ``hb myFunction`` which is invoked for function in flash will use pure hardware breakpoint if it is avalable otherwise one of the 32 software flash breakpoints is used. The same rule applies to ``b myFunction``-like commands. In this case GDB will decide what type of breakpoint to set itself. If ``myFunction`` is resided in writable region (IRAM) software IRAM breakpoint will be used otherwise hardware or software flash breakpoint is used as it is done for ``hb`` command. +.. _jtag-debugging-tip-flash-mappings: + +Flash Mappings vs SW Flash Breakpoints +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In order to set/clear software breakpoints in flash, OpenOCD needs to know their flash addresses. To accomplish conversion from the ESP32 address space to the flash one, OpenOCD uses mappings of program's code regions resided in flash. Those mappings are kept in the image header which is prepended to program binary data (code and data segments) and is specific to every application image written to the flash. So to support software flash breakpoints OpenOCD should know where application image under debugging is resided in the flash. By default OpenOCD reads partition table at 0x8000 and uses mappings from the first found application image, but there can be the cases when it will not work, e.g. partition table is not at standard flash location or even there can be multiple images: one factory and two OTA and you may want to debbug any of them. To cover all possible debugging scenarios OpenOCD supports special command which can be used to set arbitrary location of application image to debug. The command has the following format: + +``esp32 appimage_offset `` + +Offset should be in hex format. To reset to the default behaviour you can specify ``-1`` as offset. + +.. note:: + + Since GDB requests memory map from OpenOCD only once when connecting to it, this command should be specified in one of the TCL configuration files, or passed to OpenOCD via its command line. In the latter case command line should look like below: + + ``bin/openocd -s share/openocd/scripts -f interface/ftdi/esp32_devkitj_v1.cfg -f board/esp-wroom-32.cfg -c "init; halt; esp32 appimage_offset 0x210000"`` + + Another option is to execute that command via OpenOCD telnet session and then connect GDB, but it seems to be less handy. + .. _jtag-debugging-tip-why-next-works-as-step: Why stepping with "next" does not bypass subroutine calls? diff --git a/docs/en/api-reference/peripherals/spi_master.rst b/docs/en/api-reference/peripherals/spi_master.rst index 26c3933cb..e836c1be4 100644 --- a/docs/en/api-reference/peripherals/spi_master.rst +++ b/docs/en/api-reference/peripherals/spi_master.rst @@ -217,7 +217,7 @@ speed a lot if small transactions are used. 2. When the DMA is enabled, it needs about 2us per transaction to setup the linked list. When the master is transferring, it automatically read data from the linked list. If the DMA is not enabled, CPU has to write/read each byte to/from the FIFO by itself. Usually this is faster than 2us, but the - transaction length is limited to 32 bytes for both write and read. + transaction length is limited to 64 bytes for both write and read. Typical transaction interval with one byte data is as below: @@ -401,7 +401,7 @@ Known Issues 2. disable the DMA by setting the last parameter to 0 in bus initialization function just as below: ``ret=spi_bus_initialize(VSPI_HOST, &buscfg, 0);`` - this may prohibit you from transmitting and receiving data longer than 32 bytes. + this may prohibit you from transmitting and receiving data longer than 64 bytes. 3. try to use command and address field to replace the write phase. 2. Full duplex mode is not compatible with the *dummy bit workaround*, hence the frequency is limited. See :ref:`dummy diff --git a/docs/en/api-reference/system/base_mac_address.rst b/docs/en/api-reference/system/base_mac_address.rst deleted file mode 100644 index abb4781d8..000000000 --- a/docs/en/api-reference/system/base_mac_address.rst +++ /dev/null @@ -1,82 +0,0 @@ -Base MAC address -================ - -Overview --------- - -Serveral MAC addresses (universally administered by IEEE) are uniquely assigned to the networking interfaces (WiFi/BT/Ethernet). -The final octet of each universally administered MAC address increases by one. Only the first one which is called base MAC address -of them is stored in EFUSE or external storage, the others are generated from it. Here, 'generate' means adding 0, 1, 2 and 3 -(respectively) to the final octet of the base MAC address. - -If the universally administered MAC addresses are not enough for all of the networking interfaces. Local administered MAC addresses -which are derived from universally administered MAC addresses are assigned to the reset of networking interfaces. - -A `definition of local vs universal MAC address can be found on Wikipedia `_. - -The number of universally administered MAC address can be configured using ``make menuconfig``. - -Base MAC address -^^^^^^^^^^^^^^^^ - -If using the default base MAC address factory programmed by Espressif in BLK0 of EFUSE, nothing needs to be done. - -If using a custom base MAC address stored in BLK3 of EFUSE, call API ``esp_efuse_mac_get_custom()`` to get the base MAC address -which is stored in BLK3 of EFUSE. If correct MAC address is returned, then call ``esp_base_mac_addr_set()`` to set the base MAC -address for system to generate the MAC addresses used by the networking interfaces(WiFi/BT/Ethernet). -There are 192 bits storage spaces for custom to store base MAC address in BLK3 of EFUSE. They are EFUSE_BLK3_RDATA0, -EFUSE_BLK3_RDATA1, EFUSE_BLK3_RDATA2, EFUSE_BLK3_RDATA3, EFUSE_BLK3_RDATA4 and EFUSE_BLK3_RDATA5, each of them is 32 bits -register. The format of the 192 bits storage spaces is: - -.. highlight:: none - -:: - - ------------------------------------------------------ - Field |Bits |Range |Description - ------------------------------------------------------ - version |8 |[191:184] |1: useful. 0: useless - ------------------------------------------------------ - reserve |112 |[183:72] |reserved - ------------------------------------------------------ - mac address |64 |[71:8] |base MAC address - ------------------------------------------------------ - mac crc |8 |[7:0] |crc of base MAC address - ------------------------------------------------------ - -If using base MAC address stored in external storage, firstly get the base MAC address stored in external storage, then call -API ``esp_base_mac_addr_set()`` to set the base MAC address for system to generate the MAC addresses used by the networking -interfaces(WiFi/BT/Ethernet). - -All of the steps must be done before initializing the networking interfaces(WiFi/BT/Ethernet). It is recommended to do it in -``app_main()`` which can be referenced in :example:`system/base_mac_address`. - -Number of universally administered MAC address -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If the number of universal MAC addresses is two, only two interfaces (WiFi station and Bluetooth) receive a universally -administered MAC address. These are generated sequentially by adding 0 and 1 (respectively) to the base MAC address. -The remaining two interfaces (WiFi softap and Ethernet) receive local MAC addresses. These are derived from the universal -WiFi station and Bluetooth MAC addresses, respectively. - -If the number of universal MAC addresses is four, all four interfaces (WiFi station, WiFi softap, Bluetooth and Ethernet) -receive a universally administered MAC address. These are generated sequentially by adding 0, 1, 2 and 3 (respectively) -to the final octet of the base MAC address. - -When using the default (Espressif-assigned) base MAC address, either setting can be used. When using a custom universal MAC -address range, the correct setting will depend on the allocation of MAC addresses in this range (either 2 or 4 per device.) - -API Reference -------------- - -Header Files -^^^^^^^^^^^^ - - * :component_file:`esp32/include/esp_system.h` - - -Functions ---------- - -.. doxygenfunction:: esp_base_mac_addr_set -.. doxygenfunction:: esp_efuse_mac_get_custom diff --git a/docs/en/api-reference/system/index.rst b/docs/en/api-reference/system/index.rst index 2b94e6d55..76b7c8d62 100644 --- a/docs/en/api-reference/system/index.rst +++ b/docs/en/api-reference/system/index.rst @@ -16,11 +16,11 @@ System API Application Level Tracing Power Management Sleep Modes - Base MAC address Over The Air Updates (OTA) ESP HTTPS OTA ESP pthread Error Codes and Helper Functions + Miscellaneous System APIs Example code for this API section is provided in :example:`system` directory of ESP-IDF examples. diff --git a/docs/en/api-reference/system/power_management.rst b/docs/en/api-reference/system/power_management.rst index 80fcda64b..618482441 100644 --- a/docs/en/api-reference/system/power_management.rst +++ b/docs/en/api-reference/system/power_management.rst @@ -20,16 +20,25 @@ Power management can be enabled at compile time, using :envvar:`CONFIG_PM_ENABLE Enabling power management features comes at the cost of increased interrupt latency. Extra latency depends on a number of factors, among which are CPU frequency, single/dual core mode, whether frequency switch needs to be performed or not. Minimal extra latency is 0.2us (when CPU frequency is 240MHz, and frequency scaling is not enabled), maximum extra latency is 40us (when frequency scaling is enabled, and a switch from 40MHz to 80MHz is performed on interrupt entry). -Dynamic frequency scaling (DFS) and automatic light sleep can be enabled in the application by calling :cpp:func:`esp_pm_configure` function. Its argument is a structure defining frequency scaling settings (for ESP32, minimum and maximum CPU frequencies). Alternatively, :envvar:`CONFIG_PM_DFS_INIT_AUTO` option can be enabled in menuconfig. If enabled, maximal CPU frequency is determined by :envvar:`CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ` setting, and minimal CPU frequency is set to the XTAL frequency. +Dynamic frequency scaling (DFS) and automatic light sleep can be enabled in the application by calling :cpp:func:`esp_pm_configure` function. Its argument is a structure defining frequency scaling settings, cpp:class:`esp_pm_config_esp32_t`. In this structure, 3 fields need to be initialized: + +* ``max_freq_mhz`` - Maximal CPU frequency, in MHZ (i.e. frequency used when ``ESP_PM_CPU_FREQ_MAX`` lock is taken). This will usually be set to :envvar:`CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ`. + +* ``min_freq_mhz`` — Minimal CPU frequency, in MHz (i.e. frequency used when only ``ESP_PM_APB_FREQ_MAX`` locks are taken). This can be set to XTAL frequency, or XTAL frequency divided by integer. Note that 10MHz is the lowest frequency at which the default REF_TICK clock of 1MHz can be generated. + +* ``light_sleep_enable`` — Whether system should automatically enter light sleep when no locks are taken (``true``/``false``). .. note:: - Automatic light sleep is based on FreeRTOS Tickless Idle functionality. :cpp:func:`esp_pm_configure` will return an `ESP_ERR_NOT_SUPPORTED` error if :envvar:`CONFIG_FREERTOS_USE_TICKLESS_IDLE` option is not enabled in menuconfig, but automatic light sleep is requested. + Automatic light sleep is based on FreeRTOS Tickless Idle functionality. :cpp:func:`esp_pm_configure` will return an `ESP_ERR_NOT_SUPPORTED` error if :envvar:`CONFIG_FREERTOS_USE_TICKLESS_IDLE` option is not enabled in menuconfig, but automatic light sleep is requested. .. note:: In light sleep, peripherals are clock gated, and interrupts (from GPIOs and internal peripherals) will not be generated. Wakeup source described in :doc:`Sleep Modes ` documentation can be used to wake from light sleep state. For example, EXT0 and EXT1 wakeup source can be used to wake up from a GPIO. +Alternatively, :envvar:`CONFIG_PM_DFS_INIT_AUTO` option can be enabled in menuconfig. If enabled, maximal CPU frequency is determined by :envvar:`CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ` setting, and minimal CPU frequency is set to the XTAL frequency. + + Power Management Locks ---------------------- @@ -58,7 +67,7 @@ When dynamic frequency scaling is enabled, CPU frequency will be switched as fol 1. When ``ESP_PM_CPU_FREQ_MAX`` or ``ESP_PM_APB_FREQ_MAX`` locks are acquired, CPU frequency will be 240 MHz, and APB frequency will be 80 MHz. - 2. Otherwise, frequency will be switched to the minimal value set using :cpp:func:`esp_pm_configure` (usually, XTAL). + 2. Otherwise, frequency will be switched to the minimal value set using :cpp:func:`esp_pm_configure`. - If maximal CPU frequency is 160 MHz: @@ -66,13 +75,13 @@ When dynamic frequency scaling is enabled, CPU frequency will be switched as fol 2. When ``ESP_PM_CPU_FREQ_MAX`` is not acquired, but ``ESP_PM_APB_FREQ_MAX`` is, CPU and APB frequencies are set to 80 MHz. - 3. Otherwise, frequency will be switched to the minimal value set using :cpp:func:`esp_pm_configure` (usually, XTAL). + 3. Otherwise, frequency will be switched to the minimal value set using :cpp:func:`esp_pm_configure`. - If maximal CPU frequency is 80 MHz: 1. When ``ESP_PM_CPU_FREQ_MAX`` or ``ESP_PM_APB_FREQ_MAX`` locks are acquired, CPU and APB frequencies will be 80 MHz. - 2. Otherwise, frequency will be switched to the minimal value set using :cpp:func:`esp_pm_configure` (usually, XTAL). + 2. Otherwise, frequency will be switched to the minimal value set using :cpp:func:`esp_pm_configure`. - When none of the locks are aquired, and light sleep is enabled in a call to :cpp:func:`esp_pm_configure`, the system will go into light sleep mode. The duration of light sleep will be determined by: diff --git a/docs/en/api-reference/system/system.rst b/docs/en/api-reference/system/system.rst new file mode 100644 index 000000000..2f0834a56 --- /dev/null +++ b/docs/en/api-reference/system/system.rst @@ -0,0 +1,118 @@ +Miscellaneous System APIs +========================= + +Software reset +-------------- + +To perform software reset of the chip, :cpp:func:`esp_restart` function is provided. When the function is called, execution of the program will stop, both CPUs will be reset, application will be loaded by the bootloader and started again. + +Additionally, :cpp:func:`esp_register_shutdown_handler` function is provided to register a routine which needs to be called prior to restart (when done by :cpp:func:`esp_restart`). This is similar to the functionality of ``atexit`` POSIX function. + +Reset reason +------------ + +ESP-IDF application can be started or restarted due to a variety of reasons. To get the last reset reason, call :cpp:func:`esp_reset_reason` function. See description of :cpp:type:`esp_reset_reason_t` for the list of possible reset reasons. + +Heap memory +----------- + +Two heap memory related functions are provided: + +* :cpp:func:`esp_get_free_heap_size` returns the current size of free heap memory +* :cpp:func:`esp_get_minimum_free_heap_size` returns the minimum size of free heap memory that was available during program execution. + +Note that ESP-IDF supports multiple heaps with different capabilities. Functions mentioned in this section return the size of heap memory which can be allocated using ``malloc`` family of functions. For further information about heap memory see :doc:`Heap Memory Allocation `. + +Random number generation +------------------------ + +ESP32 contains a hardware random number generator, values from it can be obtained using :cpp:func:`esp_random`. + +When Wi-Fi or Bluetooth are enabled, numbers returned by hardware random number generator (RNG) can be considered true random numbers. Without Wi-Fi or Bluetooth enabled, hardware RNG is a pseudo-random number generator. At startup, ESP-IDF bootloader seeds the hardware RNG with entropy, but care must be taken when reading random values between the start of ``app_main`` and initialization of Wi-Fi or Bluetooth drivers. + + +MAC Address +----------- + +These APIs allow querying and customizing MAC addresses used by Wi-Fi, Bluetooth, and Ethernet drivers. + +ESP32 has up to 4 network interfaces: Wi-Fi station, Wi-Fi AP, Ethernet, and Bluetooth. Each of these interfaces needs to have a MAC address assigned to it. In ESP-IDF these addresses are calculated from *Base MAC address*. Base MAC address can be initialized with factory-programmed value from EFUSE, or with a user-defined value. In addition to setting the base MAC address, applications can specify the way in which MAC addresses are allocated to devices. See `Number of universally administered MAC address`_ section for more details. + ++---------------+--------------------------------+----------------------------------+ +| Interface | MAC address | MAC address | +| | (4 universally administered) | (2 universally administered) | ++===============+================================+==================================+ +| Wi-Fi Station | base_mac | base_mac | ++---------------+--------------------------------+----------------------------------+ +| Wi-Fi SoftAP | base_mac, +1 to the last octet | base_mac, first octet randomized | ++---------------+--------------------------------+----------------------------------+ +| Bluetooth | base_mac, +2 to the last octet | base_mac, +1 to the last octet | ++---------------+--------------------------------+----------------------------------+ +| Ethernet | base_mac, +3 to the last octet | base_mac, +1 to the last octet, | +| | | first octet randomized | ++---------------+--------------------------------+----------------------------------+ + + +Base MAC address +^^^^^^^^^^^^^^^^ + +Wi-Fi, Bluetooth, and Ethernet drivers use :cpp:func:`esp_read_mac` function to get MAC address for a specific interface. + +By default, this function will use MAC address factory programmed in BLK0 of EFUSE as the base MAC address. MAC addresses of each interface will be calculated according to the table above. + +Applications which don't use MAC address factory programmed into BLK0 of EFUSE can modify base MAC address used by :cpp:func:`esp_read_mac` using a call to :cpp:func:`esp_base_mac_addr_set`. Custom value of MAC address can come from application defined storage, such as Flash, NVS, etc. Note that the call to :cpp:func:`esp_base_mac_addr_set` needs to happen before network protocol stacks are initialized, for example, early in ``app_main``. + +Custom MAC address in BLK3 of EFUSE +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +To facilitate usage of custom MAC addresses, ESP-IDF provides :cpp:func:`esp_efuse_mac_get_custom` function, which loads MAC address from BLK3 of EFUSE. This function assumes that custom MAC address is stored in BLK3 of EFUSE (EFUSE_BLK3_RDATA0, EFUSE_BLK3_RDATA1, EFUSE_BLK3_RDATA2, EFUSE_BLK3_RDATA3, EFUSE_BLK3_RDATA4, EFUSE_BLK3_RDATA5 registers) in the following format: + ++-----------------+-----------+---------------+------------------------------+ +| Field | # of bits | Range of bits | Notes | ++=================+===========+===============+==============================+ +| Version | 8 | 191:184 | 0: invalid, others — valid | ++-----------------+-----------+---------------+------------------------------+ +| Reserved | 128 | 183:56 | | ++-----------------+-----------+---------------+------------------------------+ +| MAC address | 48 | 55:8 | | ++-----------------+-----------+---------------+------------------------------+ +| MAC address CRC | 8 | 7:0 | CRC-8-CCITT, polynomial 0x07 | ++-----------------+-----------+---------------+------------------------------+ + +Once MAC address has been obtained using :cpp:func:`esp_efuse_mac_get_custom`, call :cpp:func:`esp_base_mac_addr_set` to set this MAC address as base MAC address. + + +Number of universally administered MAC address +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Serveral MAC addresses (universally administered by IEEE) are uniquely assigned to the networking interfaces (Wi-Fi/BT/Ethernet). The final octet of each universally administered MAC address increases by one. Only the first one of them (which is called base MAC address) is stored in EFUSE or external storage, the others are generated from it. Here, 'generate' means adding 0, 1, 2 and 3 (respectively) to the final octet of the base MAC address. + +If the universally administered MAC addresses are not enough for all of the networking interfaces, locally administered MAC addresses which are derived from universally administered MAC addresses are assigned to the rest of networking interfaces. + +See `this article `_ for the definition of local and universally administered MAC addresses. + +The number of universally administered MAC address can be configured using :envvar:`CONFIG_NUMBER_OF_UNIVERSAL_MAC_ADDRESS`. + +If the number of universal MAC addresses is two, only two interfaces (Wi-Fi Station and Bluetooth) receive a universally administered MAC address. These are generated sequentially by adding 0 and 1 (respectively) to the base MAC address. The remaining two interfaces (Wi-Fi SoftAP and Ethernet) receive local MAC addresses. These are derived from the universal Wi-Fi station and Bluetooth MAC addresses, respectively. + +If the number of universal MAC addresses is four, all four interfaces (Wi-Fi Station, Wi-Fi SoftAP, Bluetooth and Ethernet) receive a universally administered MAC address. These are generated sequentially by adding 0, 1, 2 and 3 (respectively) to the final octet of the base MAC address. + +When using the default (Espressif-assigned) base MAC address, either setting can be used. When using a custom universal MAC address range, the correct setting will depend on the allocation of MAC addresses in this range (either 2 or 4 per device.) + +Chip version +------------ + +:cpp:func:`esp_chip_info` function fills :cpp:class:`esp_chip_info_t` structure with information about the chip. This includes the chip revision, number of CPU cores, and a bit mask of features enabled in the chip. + +SDK version +----------- + +:cpp:func:`esp_get_idf_version` returns a string describing the IDF version which was used to compile the application. This is the same value as the one available through ``IDF_VER`` variable of the build system. The version string generally has the format of ``git describe`` output. + + +API Reference +------------- + +.. include:: /_build/inc/esp_system.inc + + diff --git a/docs/en/get-started-cmake/get-started-devkitc.rst b/docs/en/get-started-cmake/get-started-devkitc.rst index 20b2d86f6..483a0ed62 100644 --- a/docs/en/get-started-cmake/get-started-devkitc.rst +++ b/docs/en/get-started-cmake/get-started-devkitc.rst @@ -17,7 +17,7 @@ Overview ESP32-DevKitC V4 is a small-sized ESP32-based development board produced by `Espressif `_. Most of the I/O pins are broken out to the pin headers on both sides for easy interfacing. Developers can connect these pins to peripherals as needed. Standard headers also make development easy and convenient when using a breadboard. -The board comes in two versions, either with :ref:`esp-modules-and-boards-esp-wroom-32` or :ref:`esp-modules-and-boards-esp32-wrover` module soldered. +The board supports various ESP32 modules, including :ref:`esp-modules-and-boards-esp32-wroom-32`, :ref:`ESP32-WROOM-32U `, :ref:`ESP32-WROOM-32D ` and :ref:`esp-modules-and-boards-esp32-solo-1`. Functional Description @@ -26,9 +26,7 @@ Functional Description The following list and figure below describe key components, interfaces and controls of ESP32-DevKitC V4 board. ESP-WROOM-32 - :ref:`esp-modules-and-boards-esp-wroom-32` module soldered to the ESP32-DevKitC V4 board. -ESP32-WROVER - Optionally :ref:`esp-modules-and-boards-esp32-wrover` module may be soldered instead of the ESP-WROOM-32. + :ref:`esp-modules-and-boards-esp32-wroom-32` module soldered to the ESP32-DevKitC V4 board. Optionally ESP32-WROOM-32D, ESP32-WROOM-32U or ESP32-SOLO-1 module may be soldered instead of the ESP32-WROOM-32. USB-UART Bridge A single chip USB-UART bridge provides up to 3 Mbps transfers rates. Boot diff --git a/docs/en/get-started/get-started-wrover-kit.rst b/docs/en/get-started/get-started-wrover-kit.rst index f39f6c502..89e0b7028 100644 --- a/docs/en/get-started/get-started-wrover-kit.rst +++ b/docs/en/get-started/get-started-wrover-kit.rst @@ -169,7 +169,7 @@ The JP1 connector is shown in two columns in the middle under "I/O" headers. The +----------------------+------+------+----------------------+ | PSRAM | IO16 | IO4 | LED, Camera, MicroSD | +----------------------+------+------+----------------------+ -| LED, Boot | IO0 | IO2 | LED, Camera, MicroSD | +| Camera, LED, Boot | IO0 | IO2 | LED, MicroSD | +----------------------+------+------+----------------------+ | JTAG, MicroSD | IO15 | 5V | | +----------------------+------+------+----------------------+ @@ -255,39 +255,45 @@ JTAG / JP8 Camera / JP4 """""""""""" -+----+--------------+----------------------+ -| | ESP32 Pin | Camera Signal | -+====+==============+======================+ -| 1 | GPIO27 | SCCB Clock | -+----+--------------+----------------------+ -| 2 | GPIO26 | SCCB Data | -+----+--------------+----------------------+ -| 3 | GPIO21 | System Clock | -+----+--------------+----------------------+ -| 4 | GPIO25 | Vertical Sync | -+----+--------------+----------------------+ -| 5 | GPIO23 | Horizontal Reference | -+----+--------------+----------------------+ -| 6 | GPIO22 | Pixel Clock | -+----+--------------+----------------------+ -| 7 | GPIO4 | Pixel Data Bit 0 | -+----+--------------+----------------------+ -| 8 | GPIO5 | Pixel Data Bit 1 | -+----+--------------+----------------------+ -| 9 | GPIO18 | Pixel Data Bit 2 | -+----+--------------+----------------------+ -| 10 | GPIO19 | Pixel Data Bit 3 | -+----+--------------+----------------------+ -| 11 | GPIO36 | Pixel Data Bit 4 | -+----+--------------+----------------------+ -| 11 | GPIO39 | Pixel Data Bit 5 | -+----+--------------+----------------------+ -| 11 | GPIO34 | Pixel Data Bit 6 | -+----+--------------+----------------------+ -| 11 | GPIO35 | Pixel Data Bit 7 | -+----+--------------+----------------------+ -| 11 | GPIO2 | Camera Reset | -+----+--------------+----------------------+ ++----+--------------+------------------------------+ +| | ESP32 Pin | Camera Signal | ++====+==============+==============================+ +| 1 | n/a | 3.3V | ++----+--------------+------------------------------+ +| 2 | n/a | Ground | ++----+--------------+------------------------------+ +| 3 | GPIO27 | SIO_C / SCCB Clock | ++----+--------------+------------------------------+ +| 4 | GPIO26 | SIO_D / SCCB Data | ++----+--------------+------------------------------+ +| 5 | GPIO25 | VSYNC / Vertical Sync | ++----+--------------+------------------------------+ +| 6 | GPIO23 | HREF / Horizontal Reference | ++----+--------------+------------------------------+ +| 7 | GPIO22 | PCLK / Pixel Clock | ++----+--------------+------------------------------+ +| 8 | GPIO21 | XCLK / System Clock | ++----+--------------+------------------------------+ +| 9 | GPIO35 | D7 / Pixel Data Bit 7 | ++----+--------------+------------------------------+ +| 10 | GPIO34 | D6 / Pixel Data Bit 6 | ++----+--------------+------------------------------+ +| 11 | GPIO39 | D5 / Pixel Data Bit 5 | ++----+--------------+------------------------------+ +| 12 | GPIO36 | D4 / Pixel Data Bit 4 | ++----+--------------+------------------------------+ +| 13 | GPIO19 | D3 / Pixel Data Bit 3 | ++----+--------------+------------------------------+ +| 14 | GPIO18 | D2 / Pixel Data Bit 2 | ++----+--------------+------------------------------+ +| 15 | GPIO5 | D1 / Pixel Data Bit 1 | ++----+--------------+------------------------------+ +| 16 | GPIO4 | D0 / Pixel Data Bit 0 | ++----+--------------+------------------------------+ +| 17 | GPIO0 | RESET / Camera Reset | ++----+--------------+------------------------------+ +| 18 | n/a | PWDN / Camera Power Down | ++----+--------------+------------------------------+ .. _get-started-esp-wrover-rgb-led-connections: diff --git a/docs/en/get-started/index.rst b/docs/en/get-started/index.rst index 188b6938d..02a302dd0 100644 --- a/docs/en/get-started/index.rst +++ b/docs/en/get-started/index.rst @@ -136,6 +136,26 @@ Setup Path to ESP-IDF The toolchain programs access ESP-IDF using ``IDF_PATH`` environment variable. This variable should be set up on your PC, otherwise projects will not build. Setting may be done manually, each time PC is restarted. Another option is to set up it permanently by defining ``IDF_PATH`` in user profile. To do so, follow instructions specific to :ref:`Windows ` , :ref:`Linux and MacOS ` in section :doc:`add-idf_path-to-profile`. +.. _get-started-get-packages: + +Install the Required Python Packages +==================================== + +Python packages required by ESP-IDF are located in the ``$IDF_PATH/requirements.txt`` file. You can install them by running:: + + sudo python -m pip install -r $IDF_PATH/requirements.txt + +or you can use the following command for installing them into the user install directory on systems where you don't have administrator rights:: + + python -m pip install --user -r $IDF_PATH/requirements.txt + +.. note:: + + Please invoke that version of the Python interpreter which you will be using with ESP-IDF. The version of the + interpreter can be checked by running command ``python --version`` and depending on the result, you might want to + use ``python2``, ``python2.7`` or similar instead of ``python``, e.g.:: + + sudo python2.7 -m pip install -r $IDF_PATH/requirements.txt .. _get-started-start-project: diff --git a/docs/en/get-started/linux-setup-scratch.rst b/docs/en/get-started/linux-setup-scratch.rst index b2cb96ec3..44644e1d5 100644 --- a/docs/en/get-started/linux-setup-scratch.rst +++ b/docs/en/get-started/linux-setup-scratch.rst @@ -18,6 +18,11 @@ To compile with ESP-IDF you need to get the following packages: sudo pacman -S --needed gcc git make ncurses flex bison gperf python2-pyserial +.. note:: + + Some older (pre-2014) Linux distributions may use ``pyserial`` version 2.x which is not supported by ESP-IDF. + In this case please install a supported version via ``pip`` as it is described in section + :ref:`get-started-get-packages`. Compile the Toolchain from Source ================================= diff --git a/docs/en/get-started/linux-setup.rst b/docs/en/get-started/linux-setup.rst index b352a310b..123d3f29f 100644 --- a/docs/en/get-started/linux-setup.rst +++ b/docs/en/get-started/linux-setup.rst @@ -20,6 +20,11 @@ To compile with ESP-IDF you need to get the following packages: sudo pacman -S --needed gcc git make ncurses flex bison gperf python2-pyserial +.. note:: + + Some older (pre-2014) Linux distributions may use ``pyserial`` version 2.x which is not supported by ESP-IDF. + In this case please install a supported version via ``pip`` as it is described in section + :ref:`get-started-get-packages`. Toolchain Setup =============== diff --git a/docs/en/get-started/macos-setup-scratch.rst b/docs/en/get-started/macos-setup-scratch.rst index 7516158a1..731122723 100644 --- a/docs/en/get-started/macos-setup-scratch.rst +++ b/docs/en/get-started/macos-setup-scratch.rst @@ -9,10 +9,9 @@ Install Prerequisites sudo easy_install pip -- install pyserial:: - - sudo pip install pyserial +.. note:: + ``pip`` will be used later for installing :ref:`the required Python packages `. Compile the Toolchain from Source ================================= diff --git a/docs/en/get-started/macos-setup.rst b/docs/en/get-started/macos-setup.rst index 16123cb03..00e5be431 100644 --- a/docs/en/get-started/macos-setup.rst +++ b/docs/en/get-started/macos-setup.rst @@ -10,10 +10,9 @@ Install Prerequisites sudo easy_install pip -- install pyserial:: - - sudo pip install pyserial +.. note:: + ``pip`` will be used later for installing :ref:`the required Python packages `. Toolchain Setup =============== diff --git a/docs/en/security/flash-encryption.rst b/docs/en/security/flash-encryption.rst index acc4b6b16..051fd469f 100644 --- a/docs/en/security/flash-encryption.rst +++ b/docs/en/security/flash-encryption.rst @@ -3,7 +3,7 @@ Flash Encryption Flash Encryption is a feature for encrypting the contents of the ESP32's attached SPI flash. When flash encryption is enabled, physical readout of the SPI flash is not sufficient to recover most flash contents. -Flash Encryption is separate from the :doc:`Secure Boot ` feature, and you can use flash encryption without enabling secure boot. However we recommend using both features together for a secure environment. +Flash Encryption is separate from the :doc:`Secure Boot ` feature, and you can use flash encryption without enabling secure boot. However we recommend using both features together for a secure environment. In absence of secure boot, additional configuration needs to be performed to ensure effectiveness of flash encryption. See :ref:`flash-encryption-without-secure-boot` for more details. .. important:: Enabling flash encryption limits your options for further updates of your ESP32. Make sure to read this document (including :ref:`flash-encryption-limitations`) and understand the implications of enabling flash encryption. @@ -288,6 +288,17 @@ It is recommended to use flash encryption and secure boot together. However, if - :ref:`Plaintext serial flash updates ` are only possible if the :envvar:`Reflashable ` Secure Boot mode is selected and a Secure Boot key was pre-generated and burned to the ESP32 (refer to :ref:`Secure Boot ` docs.). In this configuration, ``make bootloader`` will produce a pre-digested bootloader and secure boot digest file for flashing at offset 0x0. When following the plaintext serial reflashing steps it is necessary to re-flash this file before flashing other plaintext data. - :ref:`pregenerated-flash-encryption-key` is still possible, provided the bootloader is not reflashed. Reflashing the bootloader requires the same :envvar:`Reflashable ` option to be enabled in the Secure Boot config. +.. _flash-encryption-without-secure-boot: + +Using Flash Encryption without Secure Boot +------------------------------------------ + +If flash encryption is used without secure boot, it is possible to load unauthorised code using serial re-flashing. See :ref:`updating-encrypted-flash-serial` for details. This unauthorised code can then read all encrypted partitions (in decrypted form) making flash-encryption ineffective. This can be avoided by write-protecting :ref:`FLASH_CRYPT_CNT` and thereby disallowing serial re-flashing. :ref:`FLASH_CRYPT_CNT` can be write-protected using command:: + + espefuse.py --port PORT write_protect_efuse FLASH_CRYPT_CNT + +Alternatively, the app can call :func:`esp_flash_write_protect_crypt_cnt` during its startup process. + .. _flash-encryption-advanced-features: Flash Encryption Advanced Features diff --git a/docs/en/security/secure-boot.rst b/docs/en/security/secure-boot.rst index dbf0e5267..d03a614d5 100644 --- a/docs/en/security/secure-boot.rst +++ b/docs/en/security/secure-boot.rst @@ -3,7 +3,7 @@ Secure Boot Secure Boot is a feature for ensuring only your code can run on the chip. Data loaded from flash is verified on each reset. -Secure Boot is separate from the :doc:`Flash Encryption ` feature, and you can use secure boot without encrypting the flash contents. However we recommend using both features together for a secure environment. +Secure Boot is separate from the :doc:`Flash Encryption ` feature, and you can use secure boot without encrypting the flash contents. However we recommend using both features together for a secure environment. See :ref:`secure-boot-and-flash-encr` for more details. .. important:: @@ -253,3 +253,10 @@ Keyfile is the 32 byte raw secure boot key for the device. To flash this digest esptool.py write_flash 0x0 bootloader-digest.bin + +.. _secure-boot-and-flash-encr: + +Secure Boot & Flash Encryption +------------------------------ + +If secure boot is used without :doc:`Flash Encryption `, it is possible to launch "time-of-check to time-of-use" attack, where flash contents are swapped after the image is verified and running. Therefore, it is recommended to use both the features together. diff --git a/docs/zh_CN/api-guides/build-system.rst b/docs/zh_CN/api-guides/build-system.rst index 12ac41ed8..a829bd470 100644 --- a/docs/zh_CN/api-guides/build-system.rst +++ b/docs/zh_CN/api-guides/build-system.rst @@ -1 +1,530 @@ -.. include:: ../../en/api-guides/build-system.rst \ No newline at end of file +编译系统 +======== + +本文将介绍乐鑫物联网开发框架中的``编译系统``和``组件``的相关概念。 + +如果您想了解如何构建一个新的 ESP-IDF 项目,请阅读本文档。 + +我们建议您使用 `ESP-IDF 模板工程 `_ 来开始您的新项目。 + +使用编译系统 +------------ + +ESP-IDF 的 :idf_file:`README.md` 文件对如何使用编译系统来构建项目作了简要的说明。 + +概述 +---- + +一个 ESP-IDF 项目可以看作是许多不同组件的集合,例如对于一个展示当前湿度的网站服务器来说,它可能会包含如下一些组件: + +- ESP32 基础库(libc,rom bindings 等) +- WiFi 驱动库 +- TCP/IP 协议栈 +- FreeRTOS 操作系统 +- 网站服务器 +- 湿度传感器的驱动 +- 将上述组件组织在一起的主代码 + +ESP-IDF 可以显式地指定和配置每个组件。在构建项目的时候,编译系统会查找 ESP-IDF 目录、项目目录和用户自定义目录(可选)中所有的组件,然后使用基于文本的菜单系统让用户配置 ESP-IDF 项目中需要的每个组件。在配置结束后,编译系统开始编译整个项目。 + +概念 +~~~~ + +- ``项目`` 特指一个目录,其中包含了构建可执行文件的所有源文件和配置,还有其他的支持型输出文件,比如分区表、数据/文件系统分区和引导程序。 + +- ``项目配置`` 保存在项目根目录下名为 sdkconfig 的文件中,它可以通过 ``make menuconfig`` 进行修改,且一个项目只能包含一个项目配置。 + +- ``应用程序`` 是由 ESP-IDF 构建得到的可执行文件。一个项目通常会构建两个应用程序:项目应用程序(主可执行文件,即用户自定义的固件)和引导程序(启动并初始化项目应用程序的引导程序)。 + +- ``组件`` 是模块化的、独立的代码,它们被编译成静态库(.a 文件)后再链接成应用程序,有些组件是 ESP-IDF 官方提供的,有些则可能来自其它项目。 + +以下内容并不是项目的组成部分: + +- ``ESP-IDF`` 并不是项目的一部分,相反,它是独立的,并通过 IDF_PATH 环境变量链接到项目中,这样做就可以使 IDF 框架与您的项目分离,其中 IDF_PATH 变量保存了 ESP-IDF 目录的路径。 + +- 交叉编译工具链并不是项目的组成部分,它应该被安装在系统 PATH 环境变量中,或者可以在项目配置中显式指定工具链的前缀为本地的安装路径。 + +示例项目 +~~~~~~~~ + +示例项目的目录树结构可能如下所示: + +.. code:: + + - myProject/ + - Makefile + - sdkconfig + - components/ - component1/ - component.mk + - Kconfig + - src1.c + - component2/ - component.mk + - Kconfig + - src1.c + - include/ - component2.h + - main/ - src1.c + - src2.c + - component.mk + + - build/ + +该示例项目 ``myProject`` 包含以下组成部分: + +- 项目顶层 Makefile,该 Makefile 设置了 ``PROJECT_NAME`` 变量,还可以定义作用于整个项目的其它 make 变量(可选)。顶层 Makefile 会导入核心 Makefile 文件 ``$(IDF_PATH)/make/project.mk`` ,由它负责实现 ESP-IDF 编译系统的剩余部分。 + +- 项目配置文件 sdkconfig,执行 ``make menuconfig`` 后会创建或更新此文件,该文件中保存了项目中所有组件的配置信息(包括 ESP-IDF 本身)。``sdkconfig`` 文件可能会也可能不会被添加到项目的源代码管理系统中。 + +- 可选的组件目录中包含了属于项目一部分的自定义组件,不是每一个项目都需要它,但它有助于构建可重用代码或者导入第三方组件。 + +- ``main`` 目录是一个特殊的 ``伪组件``,它包含项目本身的源代码。``main`` 是默认名称,Makefile 变量 ``COMPONENT_DIRS`` 默认会导入此组件,但您也可以修改此变量(或者设置 ``EXTRA_COMPONENT_DIRS`` )以查找其他位置的组件。 + +- ``build`` 目录在项目编译的时候创建或者更新,里面包含有编译生成的临时目标文件和库以及最终输出的二进制文件。此目录通常不会被添加到项目的源代码管理系统中,也不会随着项目源代码被发布。 + +组件目录中会包含组件自己的 Makefile 文件 ``component.mk`` ,里面会定义一些变量来控制该组件的编译过程,以及它与整个项目的集成。更多详细信息请参考 `组件 Makefiles <#component-makefiles>`_。 + +每个组件还可以包含一个 ``Kconfig`` 文件,它用于定义 ``menuconfig`` 时展示的组件配置信息的选项规则。某些组件还可能还会包含 ``Kconfig.projbuild`` 和 ``Makefile.projbuild`` 特殊文件,他们可以用来覆盖项目的部分配置。 + +项目 Makefiles +~~~~~~~~~~~~~~ + +每个项目都有一个 Makefile ,它包含整个项目的构建设置。默认情况下,项目 Makefile 可以非常小。 + +最小 Makefile 示例 +^^^^^^^^^^^^^^^^^^ + +.. code:: + + PROJECT_NAME := myProject + + include $(IDF_PATH)/make/project.mk + +必须设置的项目变量 +^^^^^^^^^^^^^^^^^^ + +- ``PROJECT_NAME``: 项目名称,最终输出的二进制文件也使用该名称,即 myProject.bin,myProject.elf 。 + +可选的项目变量 +^^^^^^^^^^^^^^ + +以下这些变量都有默认值,用户可以重写这些变量以自定义编译行为。查看 ``make/project.mk`` 文件可以获得所有的实现细节。 + +- ``PROJECT_PATH``: 顶层项目目录,默认是包含 Makefile 文件的目录,许多其他的项目变量都基于此变量。注意,项目路径中不能包含有空格。 +- ``BUILD_DIR_BASE``: 所有对象、库、二进制文件的输出目录,默认为 ``$(PROJECT_PATH)/build``。 +- ``COMPONENT_DIRS``: 搜索组件的目录,默认为 ``$(IDF_PATH)/components``,``$(PROJECT_PATH)/components``,``$(PROJECT_PATH)/main`` 和 ``EXTRA_COMPONENT_DIRS`` 。如果您不希望从这些目录中搜索组件,请重写此变量。 +- ``EXTRA_COMPONENT_DIRS``: 组件额外的搜索路径,可选。 +- ``COMPONENTS``: 要编译进项目中的组件列表,默认为 ``COMPONENT_DIRS`` 指定目录中所有的组件。 +- ``EXCLUDE_COMPONENTS``: 在编译的过程中需要剔除的组件列表,可选。请注意这只会减少编译的时间,并不会减少最终二进制文件的大小。 +- ``TEST_EXCLUDE_COMPONENTS``: 在单元测试的编译过程中需要剔除的组件列表,可选。 + +以上这些 Makefile 变量中的任何路径都要使用绝对路径,您可以使用 ``$(PROJECT_PATH)/xxx``,``$(IDF_PATH)/xxx``,或者使用 Make 内置函数 ``$(abspath xxx)`` 将相对路径转换为绝对路径。 + +以上这些变量要在 Makefile 中 ``include $(IDF_PATH)/make/project.mk`` 的前面进行设置。 + +.. _component-makefiles: + +组件 Makefiles +~~~~~~~~~~~~~~ + +每个项目都包含一个或者多个组件,这些组件可以是 ESP-IDF 的一部分,也可以从其他组件目录添加。 + +组件是包含 ``component.mk`` 文件的任何目录。 + +搜索组件 +~~~~~~~~ + +搜索 ``COMPONENT_DIRS`` 中指定的目录以查找项目会使用的组件,目录可以是组件本身(即他们包含 ``component.mk`` 文件),也可以是包含组件的上层目录。 + +运行 ``make list-components`` 命令可以查询这些变量的值,这有助于调试组件的搜索路径是否正确。 + +具有相同名字的多个组件 +^^^^^^^^^^^^^^^^^^^^^^ + +ESP-IDF 搜索组件时,会按照 ``COMPONENT_DIRS`` 指定的顺序依次进行,这意味着在默认情况下,首先是 ESP-IDF 组件,然后是项目组件,最后是 ``EXTRA_COMPONENT_DIRS`` 中的组件。如果这些目录中的两个或者多个包含具有相同名字的组件,则使用搜索到的最后一个位置的组件。这就允许将组件复制到项目目录中再修改来覆盖 ESP-IDF 组件,如果使用这种方式,ESP-IDF 目录本身可以保持不变。 + +.. _minimal-component-makefile: + +最小组件 Makefile +^^^^^^^^^^^^^^^^^ + +最简单的 ``component.mk`` 文件可以是一个空文件,如果文件为空,则组件的默认编译行为会被设置为: + +- makefile 所在目录中的所有源文件(``*.c``,``*.cpp``,``*.cc``,``*.S``)将会被编译进组件库中。 +- 子目录 ``include`` 将被添加到其他组件的全局头文件搜索路径中。 +- 组件库将会被链接到项目的应用程序中。 + +更完整的组件 makefile 可以查看 `组件 Makefile 示例 <#example-component-makefile>`_。 + +请注意,空的 ``component.mk`` 文件同没有 ``component.mk`` 文件之间存在本质差异,前者会调用默认的组件编译行为,后者不会发生默认的组件编译行为。一个组件中如果只包含影响项目配置或编译过程的文件,那么它可以没有 ``component.mk`` 文件。 + +.. _preset-component-variables: + +预设的组件变量 +^^^^^^^^^^^^^^ + +以下特定于组件的变量可以在 ``component.mk`` 中使用,但不应该被修改。 + +- ``COMPONENT_PATH``: 组件的目录,即包含 ``component.mk`` 文件的绝对路径,路径中不能包含空格。 +- ``COMPONENT_NAME``: 组件的名字,默认为组件目录的名称。 +- ``COMPONENT_BUILD_DIR``: 组件的编译目录,即存放组件编译输出的绝对路径,它是 `$(BUILD_DIR_BASE)` 的子目录。该变量也是编译组件时的当前工作目录,所以 make 中的相对路径都以此目录为基础。 +- ``COMPONENT_LIBRARY``: 组件编译后的静态库文件(相对于组件的编译目录)的名字,默认为 ``$(COMPONENT_NAME).a``。 + +以下变量在项目顶层中设置,并被导出到组件中编译时使用: + +- ``PROJECT_NAME``: 项目名称,在项目的 Makefile 中设置。 +- ``PROJECT_PATH``: 包含项目 Makefile 的目录的绝对路径。 +- ``COMPONENTS``: 此次编译中包含的所有组件的名字。 +- ``CONFIG_*``: 项目配置中的每个值在 make 中都对应一个以 ``CONFIG_`` 开头的变量。 +- ``CC``,``LD``,``AR``,``OBJCOPY``: gcc xtensa 交叉编译工具链中每个工具的完整路径。 +- ``HOSTCC``,``HOSTLD``,``HOSTAR``: 主机本地工具链中每个工具的全名。 +- ``IDF_VER``: ESP-IDF 的版本号,可以通过检索 ``$(IDF_PATH)/version.txt`` 文件(假如存在的话)或者使用 git 命令 ``git describe`` 来获取。这里推荐的格式是在一行中指定主 IDF 的发布版本号,例如标记为 ``v2.0`` 的发布版本或者是标记任意一次提交记录的 ``v2.0-275-g0efaa4f``。应用程序可以通过调用 :cpp:func:`esp_get_idf_version` 函数来使用该变量。 + +如果您在 ``component.mk`` 文件中修改这些变量,这并不会影响其它组件的编译,但可能会使您的组件变得难以编译或调试。 + +.. _optional-project-wide-component-variables: + +可选的项目通用组件变量 +^^^^^^^^^^^^^^^^^^^^^^ + +可以在 ``component.mk`` 中设置以下变量来控制整个项目的编译行为: + +- ``COMPONENT_ADD_INCLUDEDIRS``: 相对于组件目录的路径,将被添加到项目中所有组件的头文件搜索路径中。如果该变量未被覆盖,则默认为 ``include`` 目录。如果一个头文件路径仅仅为当前组件所用,那么应该将该路径添加到 ``COMPONENT_PRIV_INCLUDEDIRS`` 中。 +- ``COMPONENT_ADD_LDFLAGS``: 添加链接参数到全局 ``LDFLAGS`` 中用以指导链接最终的可执行文件,默认为 ``-l$(COMPONENT_NAME)``。如果将预编译好的库添加到此目录,请使用它们为绝对路径,即 ``$(COMPONENT_PATH)/libwhatever.a``。 +- ``COMPONENT_DEPENDS``: 需要在当前组件之前编译的组件列表,这对于处理链接时的依赖不是必需的,因为所有组件的头文件目录始终可用。如果一个组件会生成一个头文件,然后另外一个组件需要使用它,此时该变量就有必要进行设置。大多数的组件不需要设置此变量。 +- ``COMPONENT_ADD_LINKER_DEPS``: 保存一些文件的路径,当这些文件发生改变时,会触发 ELF 文件重新链接。该变量通常用于链接脚本文件和二进制文件,大多数的组件不需要设置此变量。 + +以下变量仅适用于属于 ESP-IDF 的组件: + +- ``COMPONENT_SUBMODULES``: 组件使用的 git 子模块的路径列表(相对于 ``COMPONENT_PATH``)。这些路径会在编译的过程中被检查(并在必要的时候初始化)。如果组件位于 ``IDF_PATH`` 目录之外,则忽略此变量。 + + +可选的组件特定变量 +^^^^^^^^^^^^^^^^^^ + +以下变量可以在 ``component.mk`` 中进行设置,用以控制该组件的编译行为: + +- ``COMPONENT_PRIV_INCLUDEDIRS``: 相对于组件目录的目录路径,该目录仅会被添加到该组件源文件的头文件搜索路径中。 +- ``COMPONENT_EXTRA_INCLUDES``: 编译组件的源文件时需要指定的额外的头文件搜索路径,这些路径将以 ``-l`` 为前缀传递给编译器。这和 ``COMPONENT_PRIV_INCLUDEDIRS`` 变量的功能有些类似,但是这些路径不会相对于组件目录进行扩展。 +- ``COMPONENT_SRCDIRS``: 相对于组件目录的目录路径,这些路径用于搜索源文件(``*.cpp``,``*.c``,``*.S``),默认为 ``.``,即组件目录本身。重写该变量可以指定包含源文件的不同目录列表。 +- ``COMPONENT_OBJS``: 要编译生成的目标文件,默认是 ``COMPONENT_SRCDIRS`` 中每个源文件的 .o 文件。重写该变量将允许您剔除 ``COMPONENT_SRCDIRS`` 中的某些源文件,否则他们将会被编译。相关示例请参阅 `指定需要编译的组件源文件 <#specify-source-files>`_。 +- ``COMPONENT_EXTRA_CLEAN``: 相对于组件编译目录的路径,指向 ``component.mk`` 文件中自定义 make 规则生成的任何文件,它们也是 ``make clean`` 命令需要删除的文件。相关示例请参阅 `源代码生成 <#source-code-generation>`_。 +- ``COMPONENT_OWNBUILDTARGET`` & ``COMPONENT_OWNCLEANTARGET``: 这些目标允许您完全覆盖组件的默认编译行为。有关详细信息,请参阅 `完全覆盖组件的 Makefile <#fully-overriding-component-makefile>`_。 +- ``COMPONENT_CONFIG_ONLY``: 如果设置了此标志,则表示组件根本不会产生编译输出(即不会编译得到 ``COMPONENT_LIBRARY``),并且会忽略大多数其它组件变量。此标志用于 IDF 内部组件,其中仅包含 ``KConfig.projbuild`` 和/或 ``Makefile.projbuild`` 文件来配置项目,但是没有源文件。 +- ``CFLAGS``: 传递给 C 编译器的标志。根据项目设置已经定义一组默认的 ``CFLAGS``,可以通过 ``CFLAGS +=`` 来为组件添加特定的标志,也可以完全重写该变量(尽管不推荐这么做)。 +- ``CPPFLAGS``: 传递给 C 预处理器的标志(用于 ``.c``,``.cpp`` 和 ``.S`` 文件)。根据项目设置已经定义一组默认的 ``CPPFLAGS`` ,可以通过 ``CPPFLAGS +=`` 来为组件添加特定的标志,也可以完全重写该变量(尽管不推荐这么做)。 +- ``CXXFLAGS``: 传递给 C++ 编译器的标志。根据项目设置已经定义一组默认的 ``CXXFLAGS`` ,可以通过 ``CXXFLAGS +=`` 来为组件添加特定的标志,也可以完全重写该变量(尽管不推荐这么做)。 + +如果要将编译标志应用于单个源文件,您可以将该源文件的目标规则覆盖,例如: + +.. code:: makefile + + apps/dhcpserver.o: CFLAGS += -Wno-unused-variable + +如果上游代码在编译的时候发出了警告,那这么做可能会很有效。 + +配置组件 +~~~~~~~~ + +每个组件都可以包含一个 Kconfig 文件,和 ``component.mk`` 放在同一个目录下。Kconfig 中包含此组件在 ``make menuconfig`` 时要展示的配置规则的设置。 + +运行 menuconfig 时,可以在 ``Component Settings`` 菜单栏下找到这些设置。 + +创建一个组件的 Kconfig 文件,最简单的方法就是使用 ESP-IDF 中现有的 Kconfig 文件作为模板,在这基础上进行修改。 + +有关示例请参阅 `添加条件配置 <#add-conditional-configuration>`_。 + +预处理器定义 +~~~~~~~~~~~~ + +ESP-IDF 编译系统会在命令行中添加以下 C 预处理定义: + +- ``ESP_PLATFORM`` — 可以用来检测在 ESP-IDF 内发生的编译行为。 +- ``IDF_VER`` — ESP-IDF 的版本,请参阅 `预设的组件变量 <#preset-component-variables>`_。 + +编译的内部过程 +~~~~~~~~~~~~~~ + +顶层:项目 Makefile +^^^^^^^^^^^^^^^^^^^ + +- ``make`` 始终从项目目录处运行,并且项目的 makefile 名字通常为 Makefile 。 +- 项目的 makefile 文件会设置 ``PROJECT_NAME`` ,并且可以自定义其他可选的项目变量。 +- 项目 makefile 文件会导入 ``$(IDF_PATH)/make/project.mk`` ,该文件中会导入项目级的 Make 逻辑。 +- ``project.mk`` 填写默认的项目级 make 变量,并导入项目配置中的 make 变量。如果生成的包含项目配置的 makefile 文件已经过期,那么它将会被重新生成(通过 ``project_config.mk`` 中的目标规则),然后 make 进程从顶层重新开始。 +- ``project.mk`` 根据默认组件目录或者可选项目变量中设置的自定义组件列表来编译组件。 +- 每个组件都可以设置一些 `可选的项目通用组件变量 <#optional-project-wide-component-variables>`_ ,他们会通过 ``component_project_vars.mk`` 被导入 ``project.mk`` 文件中。如果这些文件有缺失或者过期,他们会被重新生成(通过对组件 makefile 的递归调用),然后 make 进程从顶层重新开始。 +- 组件中的 Makefile.projbuild 文件被包含在了 make 的进程中,以添加额外的目标或者配置。 +- 默认情况下,项目 makefile 还为每个组件生成顶层的编译和清理目标,并设置 app 和 clean 目标来调用所有这些子目标。 +- 为了编译每个组件,对组件 makefile 执行递归构建。 + +为了更好地理解项目的构建过程,请通读 ``project.mk`` 文件。 + +第二层:组件 Makefile 文件 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- 每次调用组件 makefile 文件都是通过 ``$(IDF_PATH)/make/component_wrapper.mk`` 这个包装器进行的。 +- 此组件包装器包含了所有组件的 ``Makefile.componentbuild`` 文件,使这些文件中的任何配置,变量都可用于每个组件。 +- 调用 ``component_wrapper.mk`` 时将当前目录设置为组件构建目录,并将 ``COMPONENT_MAKEFILE`` 变量设置为 ``component.mk`` 的绝对路径。 +- ``component_wrapper.mk`` 为所有组件变量设置默认值,然后导入 ``component.mk`` 文件来覆盖或修改这些变量。 +- 如果未定义 ``COMPONENT_OWNBUILDTARGET`` 和 ``COMPONENT_OWNCLEANTARGET`` 文件,则会为组件的源文件和必备组件 ``COMPONENT_LIBRARY`` 静态库文件创建默认构建和清理目标。 +- ``component_project_vars.mk`` 文件在 ``component_wrapper.mk`` 中有自己的目标,如果由于组件的 makefile 或者项目配置的更改而需要重建此文件,则从 ``project.mk`` 文件中进行评估。 + +为了更好地理解组件制作过程,请阅读 ``component_wrapper.mk`` 文件和 ESP-IDF 中的 ``component.mk`` 文件。 + +以非交互的方式运行 Make +~~~~~~~~~~~~~~~~~~~~~~~ + +如果在运行 ``make`` 的时候不希望出现交互式提示(例如:在IDE或自动构建系统中),可以将 ``BATCH_BUILD=1`` 添加到make的参数中(或者将其设置为环境变量)。 + +设置 ``BATCH_BUILD`` 意味着: + +- 详细输出(与 ``V=1`` 相同,见下文),如果不需要详细输出,就设置 ``V=0`` 。 +- 如果项目配置缺少新配置项(来自新组件或者 ESP-IDF 更新),则项目使用默认值,而不是提示用户输入每个项目。 +- 如果构建系统需要调用 ``menuconfig`` ,则会打印错误并且构建失败。 + +调试 Make 的过程 +~~~~~~~~~~~~~~~~ + +调试 ESP-IDF 编译系统的一些技巧: + +- 将 ``V=1`` 添加到 make 的参数中(或将其设置为环境变量)将使 make 回显所有已经执行的命令,以及为子 make 输入的每个目录。 +- 运行 ``make -w`` 将导致 make 在为子 make 输入时回显每个目录——与 ``V=1`` 相同但不回显所有命令。 +- 运行 ``make --trace`` (可能除了上述参数之一)将打印出构建时的每个目标,以及导致它构建的依赖项)。 +- 运行 ``make -p`` 会打印每个 makefile 中每个生成的目标的(非常详细的)摘要。 + +更多调试技巧和通用的构建信息,请参阅 `GNU 构建手册 `_。 + +.. _warn-undefined-variables: + +警告未定义的变量 +^^^^^^^^^^^^^^^^ + +默认情况下,如果引用了未定义的变量(如 ``$(DOES_NOT_EXIST)`` ,构建过程将会打印警告,这对于查找变量名称中的错误非常有用。 + +如果不想要此行为,可以在 menuconfig 顶层菜单下的 `SDK tool configuration` 中禁用它。 + +请注意,如果在 Makefile 中使用 ``ifdef`` 或者 ``ifndef`` ,则此选项不会出发警告。 + +覆盖项目的部分内容 +~~~~~~~~~~~~~~~~~~ + +Makefile.projbuild +^^^^^^^^^^^^^^^^^^ + +如果一个组件含有必须要在项目构建过程的顶层进行计算的变量,则可以在组件目录下创建名为 ``Makefile.projbuild`` 的文件,项目在执行 ``project.mk`` 的时候会导入此 makefile 。 + +例如,如果您的组件需要为整个项目添加 CFLAGS(不仅仅是为自身的源文件),那么可以在 ``Makefile.projbuild`` 中设置 ``CFLAGS +=`` 。 + +``Makefile.projbuild`` 文件在 ESP-IDF 中大量使用,用于定义项目范围的构建功能,例如 ``esptool.py`` 命令行参数和 ``bootloader`` 这个特殊的程序。 + +请注意, ``Makefile.projbuild`` 对于最常见的组件不是必需的 - 例如向项目中添加 include 目录,或者将 LDFLAGS 添加到最终链接步骤,同样可以通过 ``component.mk`` 文件来自定义这些值。有关详细信息,请参阅 `可选的项目通用组件变量 <#optional-project-wide-component-variables>`_ 。 + +.. warning:: + + 在此文件中设置变量或者目标时要小心,由于这些值包含在项目的顶层 makefile 中,因此他们可以影响或者破坏所有组件的功能! + +KConfig.projbuild +^^^^^^^^^^^^^^^^^ + +这相当于 ``Makefile.projbuild`` 的组件配置 KConfig 文件,如果要在 menuconfig 的顶层添加配置选项,而不是在 ``组件配置`` 子菜单中,则可以在 ``component.mk`` 文件所在目录中的 KConfig.projbuild 文件中定义这些选项。 + +在此文件中添加配置时要小心,因为他们将包含在整个项目配置中,在可能的情况下,通常最好为组件创建和配置 KConfig 文件。 + +Makefile.componentbuild +^^^^^^^^^^^^^^^^^^^^^^^ + +对于一些特殊的组件,比如它们会使用工具从其余文件中生成源文件,这时就有必要将配置、宏或者变量的定义添加到每个组件的构建过程中。这是通过在组件目录中包含 ``Makefile.componentbuild`` 文件来实现的。此文件在 ``component.mk`` 文件之前被导入 ``component_wrapper.mk`` 中。同 ``Makefile.projbuild`` 文件一样,请留意这些文件,因为他们包含在每个组件的构建中,所有只有在编译完全不同的组件时才会出现 ``Makefile.componentbuild`` 错误。 + +仅配置的组件 +^^^^^^^^^^^^ + +仅配置的组件是一类不包含源文件的特殊组件,只有 ``Kconfig.projbuild`` 和 ``Makefile.projbuild`` 文件,可以在 ``conponent.mk`` 文件中设置标志 ``COMPONENT_CONFIG_ONLY``。如果设置了此标志,则忽略大多数其他组件变量,并且不会为组件执行构建操作。 + +.. _example-component-makefile: + +组件 Makefile 示例 +~~~~~~~~~~~~~~~~~~ + +因为构建环境试图设置大多数情况都能工作的合理默认值,所以 ``component.mk`` 可能非常小,甚至是空的,请参考 `最小组件 Makefile <#minimal-component-makefile>`_。但是某些功能通常需要覆盖组件的变量。 + +以下是 ``component.mk`` 的一些更高级的示例: + +增加源文件目录 +^^^^^^^^^^^^^^ + +默认情况下,将忽略子目录。如果您的项目在子目录中而不是在组件的根目录中有源文件,那么您可以通过设置 ``COMPONENT_SRCDIRS`` 将其告知编译系统: + +.. code:: + + COMPONENT_SRCDIRS := src1 src2 + +编译系统将会编译 src1/ 和 src2/ 子目录中的所有源文件。 + +.. _specify-source-files: + +指定源文件 +^^^^^^^^^^ + +标准 ``component.mk`` 逻辑将源目录中的所有 .S 和 .c 文件添加为无条件编译的源。通过将 ``COMPONENT_OBJS`` 变量手动设置为需要生成的对象的名称,可以绕过该逻辑并对要编译的对象进行硬编码。 + +.. code:: + + COMPONENT_OBJS := file1.o file2.o thing/filea.o thing/fileb.o anotherthing/main.o + COMPONENT_SRCDIRS := . thing anotherthing + +请注意,还需要另外设置 ``COMPONENT_SRCDIRS`` 。 + +.. _add-conditional-configuration: + +添加条件配置 +^^^^^^^^^^^^ + +配置系统可用于有条件地编译某些文件,具体取决于 ``make menuconfig`` 中选择的选项。为此,ESP-IDF 具有 ``compile_only_if`` 和 ``compile_only_if_not`` 的宏: + +``Kconfig``: + +.. code:: + + config FOO_ENABLE_BAR + bool "Enable the BAR feature." + help + This enables the BAR feature of the FOO component. + +``component.mk``: + +.. code:: + + $(call compile_only_if,$(CONFIG_FOO_ENABLE_BAR),bar.o) + +从示例中可以看出, ``compile_only_if`` 宏将条件和目标文件列表作为参数。如果条件为真(在这种情况下:如果在 menuconfig 中启用了 BAR 功能),将始终编译目标文件(在本例中为 bar.o)。相反的情况也是如此,如果条件不成立,bar.o 将永远不会被编译。 ``compile_only_if_not`` 执行相反的操作,如果条件为 false 则编译,如果条件为 true 则不编译。 + +这也可以用于选择或者删除实现,如下所示: + +``Kconfig``: + +.. code:: + + config ENABLE_LCD_OUTPUT + bool "Enable LCD output." + help + Select this if your board has a LCD. + + config ENABLE_LCD_CONSOLE + bool "Output console text to LCD" + depends on ENABLE_LCD_OUTPUT + help + Select this to output debugging output to the lcd + + config ENABLE_LCD_PLOT + bool "Output temperature plots to LCD" + depends on ENABLE_LCD_OUTPUT + help + Select this to output temperature plots + +``component.mk``: + +.. code:: + + # If LCD is enabled, compile interface to it, otherwise compile dummy interface + $(call compile_only_if,$(CONFIG_ENABLE_LCD_OUTPUT),lcd-real.o lcd-spi.o) + $(call compile_only_if_not,$(CONFIG_ENABLE_LCD_OUTPUT),lcd-dummy.o) + + #We need font if either console or plot is enabled + $(call compile_only_if,$(or $(CONFIG_ENABLE_LCD_CONSOLE),$(CONFIG_ENABLE_LCD_PLOT)), font.o) + +请注意使用 Make 或者函数来包含字体文件。其他的替换函数,比如 ``and`` 和 ``if`` 也适用于此处。也可以使用不在 menuconfig 中定义的变量,ESP-IDF 使用默认的 Make 策略,将一个空的或者只包含空格的变量视为 false ,而其中任何非空格的比那辆都为 true 。 + +(注意:本文档的历史版本建议将目标文件添加到 ``COMPONENT_OBJS`` 中,虽然这仍然可行,但是只有当组件中的所有目标文件都明确命名时才会起作用,并且在 ``make clean`` 后并不会清除 make 中取消选择的目标文件)。 + +.. _source-code-generation: + +源代码生成 +^^^^^^^^^^ + +某些组件会出现源文件未随组件本身提供,而必须从另外一个文件生成的情况。假设我们的组件有一个头文件,该文件由 BMP 文件转换后的二进制数据组成,假设使用 bmp2h 的工具进行转换,然后将头文件包含在名为 graphics_lib.c 的文件中: + +.. code:: + + COMPONENT_EXTRA_CLEAN := logo.h + + graphics_lib.o: logo.h + + logo.h: $(COMPONENT_PATH)/logo.bmp + bmp2h -i $^ -o $@ + +这个示例会在当前目录(构建目录)中生成 graphics_lib.o 和 logo.h 文件,而 logo.bmp 随组件一起提供并位于组件路径下。因为 logo.h 是一个生成的文件,所以当调用 ``make clean`` 时需要清理它,这就是为什么要将它添加到 ``COMPONENT_EXTRA_CLEAN`` 变量中。 + +润色与改进 +^^^^^^^^^^ + +将 logo.h 添加作为 ``graphics_lib.o`` 的依赖项会导致在编译 ``graphics_lib.c`` 之前先生成它。 + +如果另一个组件中的源文件需要使用 logo.h,则必须将此组件的名称添加到另一个组件的 ``COMPONENT_DEPENDS`` 列表中,以确保组件按顺序编译。 + +嵌入二进制数据 +^^^^^^^^^^^^^^ + +有时您的组件希望使用一个二进制文件或者文本文件,但是您又不希望将它重新格式化为 C 源文件。 + +这时,您可以在 ``component.mk`` 文件中设置变量 ``COMPONENT_EMBED_FILES``,以这种方式指定要嵌入的文件的名称: + +.. code:: + + COMPONENT_EMBED_FILES := server_root_cert.der + +或者,如果文件是字符串,则可以使用变量 ``COMPONENT_EMBED_TXTFILES``,这将把文本文件的内容当成以 null 结尾的字符串嵌入: + +.. code:: + + COMPONENT_EMBED_TXTFILES := server_root_cert.pem + +文件的内容会被编译进 flash 中的 .rodata 段,并通过符号名称来访问,如下所示: + +.. code:: c + + extern const uint8_t server_root_cert_pem_start[] asm("_binary_server_root_cert_pem_start"); + extern const uint8_t server_root_cert_pem_end[] asm("_binary_server_root_cert_pem_end"); + +符号名称是根据文件的全名生成的,如 ``COMPONENT_EMBED_FILES`` 中的所示,字符 / , . , 等都将会被下划线替代。符号名称中的 ``_binary`` 前缀由 ``objcopy`` 添加,对于文本和二进制文件都是相同的。 + +有关使用此技术的示例,请参考 :example:`protocols/https_request` - 证书文件的内容会在编译时从 .pem 文件中加载。 + +.. _fully-overriding-component-makefile: + +完全覆盖组件的 Makefile +~~~~~~~~~~~~~~~~~~~~~~~ + +显然,在某些情况下,所有这些配置都不足以满足某个组件,例如,当组件基本上是另一个第三方组件的包装器时,该第三方组件最初不打算在 ESP-IDF 编译系统下工作,在这种情况下,可以通过设置 ``COMPONENT_OWNBUILDTARGET`` 和可能的 ``COMPONENT_OWNCLEANTARGET``,并在 ``component.mk`` 中定义名为 ``build`` 和 ``clean`` 的目标。构建目标可以执行任何操作,只要它为项目生成了 ``$(COMPONENT_LIBRARY)`` ,并最终被链接到应用程序二进制文件中即可。 + +(实际上,这并不是必须的 - 如果 ``COMPONENT_ADD_LDFLAGS`` 变量被覆盖,那么组件可以指示链接器链接其他二进制文件。) + +.. _custom-sdkconfig-defaults: + +自定义 sdkconfig 的默认值 +~~~~~~~~~~~~~~~~~~~~~~~~~ + +对于示例工程或者其他您不想指定完整 sdkconfig 配置的项目,但是您确实希望覆盖 ESP-IDF 默认值中的某些键值,则可以在项目中创建文件 ``sdkconfig.defaults``,运行 ``make defconfig`` 或从头创建新配置时将会使用此文件。 + +要想覆盖此文件的名称,请设置 ``SDKCONFIG_DEFAULTS`` 环境变量。 + +保存 flash 参数 +~~~~~~~~~~~~~~~ + +在某些情况下,我们希望在没有 IDF 的情况下烧写目标板卡,对于这种情况,我们希望保存构建的二进制文件、esptool.py 和 esptool write_flash 命令的参数。可以简单编写一段脚本来保存二进制文件和 esptool.py,并且使用命令 ``make print_flash_cmd`` 来查看烧写 flash 时的参数。 + +.. code:: bash + + --flash_mode dio --flash_freq 40m --flash_size detect 0x1000 bootloader/bootloader.bin 0x10000 example_app.bin 0x8000 partition_table_unit_test_app.bin + +然后使用这段 flash 参数作为 esptool write_flash 命令的参数: + +.. code:: bash + + python esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size detect 0x1000 bootloader/bootloader.bin 0x10000 example_app.bin 0x8000 partition_table_unit_test_app.bin + +编译 Bootloader +--------------- + +引导程序默认作为 ``make all`` 的一部分被编译,或者也可以通过 ``make bootloader-clean`` 来独立构建,此外还可以通过 ``make bootloader-list-components`` 来查看构建引导程序时包含的组件。 + +引导程序是一个特殊的组件,因为主项目中的二级引导程序拥有单独的 .EFL 和 .BIN 文件。但是它与主项目共享配置和构建目录。 + +这是通过在 components/bootloader/subproject 下添加子项目来完成的。这个子项目有自己的 Makefile,但它希望通过 components/bootloader/Makefile.projectbuild 文件中的一些配置使自己从主项目的 Makefile 被调用。有关详细信息,请参阅这些文件。 diff --git a/docs/zh_CN/api-guides/index.rst b/docs/zh_CN/api-guides/index.rst index acfcca62e..d993b44d2 100644 --- a/docs/zh_CN/api-guides/index.rst +++ b/docs/zh_CN/api-guides/index.rst @@ -5,7 +5,8 @@ API Guides :maxdepth: 1 General Notes - Build System + 编译系统 + 编译系统 (CMake) Error Handling Fatal Errors Deep Sleep Wake Stubs @@ -19,11 +20,11 @@ API Guides Partition Tables Secure Boot <../security/secure-boot> ULP Coprocessor - Unit Testing + 单元测试 Application Level Tracing Console Component ROM debug console WiFi Driver Mesh Stack BluFi - External SPI-connected RAM \ No newline at end of file + External SPI-connected RAM diff --git a/docs/zh_CN/api-reference/system/base_mac_address.rst b/docs/zh_CN/api-reference/system/base_mac_address.rst deleted file mode 100644 index 05cfa83ba..000000000 --- a/docs/zh_CN/api-reference/system/base_mac_address.rst +++ /dev/null @@ -1 +0,0 @@ -.. include:: ../../../en/api-reference/system/base_mac_address.rst \ No newline at end of file diff --git a/docs/zh_CN/api-reference/system/system.rst b/docs/zh_CN/api-reference/system/system.rst new file mode 100644 index 000000000..fc07ecfe3 --- /dev/null +++ b/docs/zh_CN/api-reference/system/system.rst @@ -0,0 +1 @@ +.. include:: ../../../en/api-reference/system/system.rst diff --git a/docs/zh_CN/get-started/index.rst b/docs/zh_CN/get-started/index.rst index 043569da5..11ff33a8f 100644 --- a/docs/zh_CN/get-started/index.rst +++ b/docs/zh_CN/get-started/index.rst @@ -135,6 +135,28 @@ ESP32 是一套 Wi-Fi (2.4 GHz) 和蓝牙 (4.2) 双模解决方案,集成了 工具链程序使用环境变量 ``IDF_PATH`` 来访问 ESP-IDF。这个变量应该设置在你的 PC 中,否则工程将不能编译。你可以在每次 PC 重启时手工设置,也可以通过在用户配置文件中定义 ``IDF_PATH`` 变量来永久性设置。要永久性设置,请参考 :doc:`add-idf_path-to-profile` 文档中 :ref:`Windows ` 或 :ref:`Linux and MacOS ` 相关的指导进行操作。 +.. _get-started-get-packages: + +安装依赖的 Python 软件包 +==================================== + +ESP-IDF 所依赖的 Python 软件包位于 ``$IDF_PATH/requirements.txt`` 文件中,您可以通过运行以下命令来安装它们: + +.. code:: bash + + sudo python -m pip install -r $IDF_PATH/requirements.txt + +如果您没有系统的管理员权限,那么可以使用如下命令来将软件包安装到用户目录中: + +.. code:: bash + + python -m pip install --user -r $IDF_PATH/requirements.txt + +.. note:: + + 请调用 ESP-IDF 使用的相同版本的 Python 解释器,解释器的版本号可以通过运行命令 ``python --version`` 来获得,根据结果,您可能要使用 ``python2``, ``python2.7`` 或者类似的名字而不是 ``python``,例如:: + + sudo python2.7 -m pip install -r $IDF_PATH/requirements.txt .. _get-started-start-project: diff --git a/docs/zh_CN/get-started/linux-setup-scratch.rst b/docs/zh_CN/get-started/linux-setup-scratch.rst index be9ac0158..230858752 100644 --- a/docs/zh_CN/get-started/linux-setup-scratch.rst +++ b/docs/zh_CN/get-started/linux-setup-scratch.rst @@ -1 +1,70 @@ -.. include:: ../../en/get-started/linux-setup-scratch.rst \ No newline at end of file +********************************** +从零开始设置 Linux 环境下的工具链 +********************************** + +除了直接从 Espressif 官网下载二进制格式的工具链,下面将再介绍一种可替代的办法。如果想要快速设置二进制工具链而不是手动从源码编译,请做好备份,并前往 :doc:`Linux 环境下的设置 ` 章节。 + + +安装必要的工具 +============== + +要想使用 ESP-IDF 进行编译,您需要获取以下软件包: + +- Ubuntu 和 Debian:: + + sudo apt-get install git wget make libncurses-dev flex bison gperf python python-serial + +- Arch:: + + sudo pacman -S --needed gcc git make ncurses flex bison gperf python2-pyserial + +.. note:: + + 一些旧的(2014年之前)Linux 发行版中使用的 ``pyserial`` 版本可能是 2.x , ESP-IDF并不支持。 + 在这种情况下,请参考 :ref:`安装依赖的 Python 软件包 ` 章节,通过 ``pip`` 工具来安装支持的版本。 + +从源代码编译工具链 +================== + +- 安装依赖: + + - CentOS 7:: + + sudo yum install gawk gperf grep gettext ncurses-devel python python-devel automake bison flex texinfo help2man libtool + + - 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 9:: + + sudo apt-get install gawk gperf grep gettext libncurses-dev python python-dev automake bison flex texinfo help2man libtool libtool-bin + + - Arch:: + + TODO + +下载 ``crosstool-NG`` 然后编译:: + + cd ~/esp + git clone -b xtensa-1.22.x https://github.com/espressif/crosstool-NG.git + cd crosstool-NG + ./bootstrap && ./configure --enable-local && make install + +编译工具链:: + + ./ct-ng xtensa-esp32-elf + ./ct-ng build + chmod -R u+w builds/xtensa-esp32-elf + +编译得到的工具链会被保存到 ``~/esp/crosstool-NG/builds/xtensa-esp32-elf``。根据 :ref:`Linux 下设置环境变量的标准方法 ` 中的介绍,将工具链添加到 ``PATH`` 中。 + + +下一步 +====== + +继续设置开发环境,请前往 :ref:`获取 ESP-IDF ` 章节。 diff --git a/docs/zh_CN/get-started/linux-setup.rst b/docs/zh_CN/get-started/linux-setup.rst index 5a9521351..4a922ccac 100644 --- a/docs/zh_CN/get-started/linux-setup.rst +++ b/docs/zh_CN/get-started/linux-setup.rst @@ -22,6 +22,11 @@ Linux 平台工具链的标准设置 sudo pacman -S --needed gcc git make ncurses flex bison gperf python2-pyserial +.. note:: + + 一些旧的(2014年之前)Linux 发行版中使用的 ``pyserial`` 版本可能是 2.x , ESP-IDF并不支持。 + 在这种情况下,请参考 :ref:`安装依赖的 Python 软件包 ` 章节,通过 ``pip`` 工具来安装支持的版本。 + 工具链的设置 =============== diff --git a/docs/zh_CN/get-started/macos-setup-scratch.rst b/docs/zh_CN/get-started/macos-setup-scratch.rst index f80efb806..5b0ab044c 100644 --- a/docs/zh_CN/get-started/macos-setup-scratch.rst +++ b/docs/zh_CN/get-started/macos-setup-scratch.rst @@ -1 +1,66 @@ -.. include:: ../../en/get-started/macos-setup-scratch.rst \ No newline at end of file +********************************** +从零开始设置 Mac OS 环境下的工具链 +********************************** + +安装必要的工具 +===================== + +- 安装 pip:: + + sudo easy_install pip + +.. note:: + + ``pip`` 稍后将用于安装 :ref:`必要的 Python 软件包 `。 + +从源代码编译工具链 +================== + +- 安装依赖: + + - 安装 MacPorts_ 或者 homebrew_ 包管理器。MacPorts 需要安装完整的 XCode 软件,但是 homebrew 只需要安装 XCode 命令行工具即可。 + + .. _homebrew: https://brew.sh/ + .. _MacPorts: https://www.macports.org/install.php + + - 对于 MacPorts:: + + sudo port install gsed gawk binutils gperf grep gettext wget libtool autoconf automake + + - 对于 homebrew:: + + brew install gnu-sed gawk binutils gperftools gettext wget help2man libtool autoconf automake + +创建大小写敏感的文件系统镜像:: + + hdiutil create ~/esp/crosstool.dmg -volname "ctng" -size 10g -fs "Case-sensitive HFS+" + +挂载:: + + hdiutil mount ~/esp/crosstool.dmg + +创建指向你工作目录的符号链接:: + + cd ~/esp + ln -s /Volumes/ctng crosstool-NG + +下载 ``crosstool-NG`` 然后编译:: + + cd ~/esp + git clone -b xtensa-1.22.x https://github.com/espressif/crosstool-NG.git + cd crosstool-NG + ./bootstrap && ./configure --enable-local && make install + +编译工具链:: + + ./ct-ng xtensa-esp32-elf + ./ct-ng build + chmod -R u+w builds/xtensa-esp32-elf + +编译得到的工具链会被保存到 ``~/esp/crosstool-NG/builds/xtensa-esp32-elf``。根据 :ref:`Mac OS 下设置环境变量的标准方法 ` 中的介绍,将工具链添加到 ``PATH`` 中。 + + +下一步 +====== + +继续设置开发环境,请前往 :ref:`获取 ESP-IDF ` 章节。 diff --git a/docs/zh_CN/get-started/macos-setup.rst b/docs/zh_CN/get-started/macos-setup.rst index 5b6951e87..255a792a7 100644 --- a/docs/zh_CN/get-started/macos-setup.rst +++ b/docs/zh_CN/get-started/macos-setup.rst @@ -12,10 +12,9 @@ sudo easy_install pip -- 安装 pyserial:: - - sudo pip install pyserial +.. note:: + ``pip`` 稍后将用于安装 :ref:`必要的 Python 软件包 `。 安装工具链 =============== diff --git a/examples/bluetooth/a2dp_gatts_coex/main/bt_app_av.c b/examples/bluetooth/a2dp_gatts_coex/main/bt_app_av.c index c015bfa62..2bf069da2 100644 --- a/examples/bluetooth/a2dp_gatts_coex/main/bt_app_av.c +++ b/examples/bluetooth/a2dp_gatts_coex/main/bt_app_av.c @@ -79,6 +79,7 @@ void bt_app_rc_ct_cb(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param switch (event) { case ESP_AVRC_CT_METADATA_RSP_EVT: bt_app_alloc_meta_buffer(param); + /* fall through */ case ESP_AVRC_CT_CONNECTION_STATE_EVT: case ESP_AVRC_CT_PASSTHROUGH_RSP_EVT: case ESP_AVRC_CT_CHANGE_NOTIFY_EVT: diff --git a/examples/bluetooth/a2dp_gatts_coex/sdkconfig.defaults b/examples/bluetooth/a2dp_gatts_coex/sdkconfig.defaults index 8c4616123..1a4822bb3 100644 --- a/examples/bluetooth/a2dp_gatts_coex/sdkconfig.defaults +++ b/examples/bluetooth/a2dp_gatts_coex/sdkconfig.defaults @@ -1,6 +1,9 @@ # Override some defaults so BT stack is enabled and # Classic BT is enabled and BT_DRAM_RELEASE is disabled CONFIG_BT_ENABLED=y +CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY= +CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY= +CONFIG_BTDM_CONTROLLER_MODE_BTDM=y CONFIG_BTDM_CONTROLLER_PINNED_TO_CORE_0=y CONFIG_BTDM_CONTROLLER_PINNED_TO_CORE_1=n CONFIG_BTDM_CONTROLLER_PINNED_TO_CORE=0 @@ -27,4 +30,3 @@ CONFIG_BT_ACL_CONNECTIONS=4 CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST=n CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY=n CONFIG_SMP_ENABLE=y -CONFIG_BT_RESERVE_DRAM=0x10000 diff --git a/examples/bluetooth/a2dp_sink/main/bt_app_av.c b/examples/bluetooth/a2dp_sink/main/bt_app_av.c index 64d554fd9..af67c5aa9 100644 --- a/examples/bluetooth/a2dp_sink/main/bt_app_av.c +++ b/examples/bluetooth/a2dp_sink/main/bt_app_av.c @@ -75,6 +75,7 @@ void bt_app_rc_ct_cb(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param switch (event) { case ESP_AVRC_CT_METADATA_RSP_EVT: bt_app_alloc_meta_buffer(param); + /* fall through */ case ESP_AVRC_CT_CONNECTION_STATE_EVT: case ESP_AVRC_CT_PASSTHROUGH_RSP_EVT: case ESP_AVRC_CT_CHANGE_NOTIFY_EVT: diff --git a/examples/bluetooth/a2dp_sink/main/main.c b/examples/bluetooth/a2dp_sink/main/main.c index 045672125..834c79048 100644 --- a/examples/bluetooth/a2dp_sink/main/main.c +++ b/examples/bluetooth/a2dp_sink/main/main.c @@ -45,12 +45,12 @@ static void bt_av_hdl_stack_evt(uint16_t event, void *p_param); void app_main() { /* Initialize NVS — it is used to store PHY calibration data */ - esp_err_t ret = nvs_flash_init(); - if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + esp_err_t err = nvs_flash_init(); + if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_ERROR_CHECK(nvs_flash_erase()); - ret = nvs_flash_init(); + err = nvs_flash_init(); } - ESP_ERROR_CHECK( ret ); + ESP_ERROR_CHECK(err); i2s_config_t i2s_config = { #ifdef CONFIG_A2DP_SINK_OUTPUT_INTERNAL_DAC @@ -86,25 +86,24 @@ void app_main() ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_BLE)); - esp_err_t err; esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); if ((err = esp_bt_controller_init(&bt_cfg)) != ESP_OK) { - ESP_LOGE(BT_AV_TAG, "%s initialize controller failed: %s\n", __func__, esp_err_to_name(ret)); + ESP_LOGE(BT_AV_TAG, "%s initialize controller failed: %s\n", __func__, esp_err_to_name(err)); return; } if ((err = esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT)) != ESP_OK) { - ESP_LOGE(BT_AV_TAG, "%s enable controller failed: %s\n", __func__, esp_err_to_name(ret)); + ESP_LOGE(BT_AV_TAG, "%s enable controller failed: %s\n", __func__, esp_err_to_name(err)); return; } if ((err = esp_bluedroid_init()) != ESP_OK) { - ESP_LOGE(BT_AV_TAG, "%s initialize bluedroid failed: %s\n", __func__, esp_err_to_name(ret)); + ESP_LOGE(BT_AV_TAG, "%s initialize bluedroid failed: %s\n", __func__, esp_err_to_name(err)); return; } if ((err = esp_bluedroid_enable()) != ESP_OK) { - ESP_LOGE(BT_AV_TAG, "%s enable bluedroid failed: %s\n", __func__, esp_err_to_name(ret)); + ESP_LOGE(BT_AV_TAG, "%s enable bluedroid failed: %s\n", __func__, esp_err_to_name(err)); return; } diff --git a/examples/bluetooth/a2dp_sink/sdkconfig.defaults b/examples/bluetooth/a2dp_sink/sdkconfig.defaults index 9769836c2..193dc1c8d 100644 --- a/examples/bluetooth/a2dp_sink/sdkconfig.defaults +++ b/examples/bluetooth/a2dp_sink/sdkconfig.defaults @@ -1,6 +1,9 @@ # Override some defaults so BT stack is enabled and # Classic BT is enabled and BT_DRAM_RELEASE is disabled CONFIG_BT_ENABLED=y +CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY= +CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY=y +CONFIG_BTDM_CONTROLLER_MODE_BTDM= CONFIG_BLUEDROID_ENABLED=y CONFIG_CLASSIC_BT_ENABLED=y CONFIG_A2DP_ENABLE=y diff --git a/examples/bluetooth/a2dp_source/sdkconfig.defaults b/examples/bluetooth/a2dp_source/sdkconfig.defaults index 38b09494b..7741a3428 100644 --- a/examples/bluetooth/a2dp_source/sdkconfig.defaults +++ b/examples/bluetooth/a2dp_source/sdkconfig.defaults @@ -1,6 +1,9 @@ # Override some defaults so BT stack is enabled and # Classic BT is enabled CONFIG_BT_ENABLED=y +CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY= +CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY=y +CONFIG_BTDM_CONTROLLER_MODE_BTDM= CONFIG_BLUEDROID_ENABLED=y CONFIG_CLASSIC_BT_ENABLED=y CONFIG_A2DP_ENABLE=y diff --git a/examples/bluetooth/ble_adv/sdkconfig.defaults b/examples/bluetooth/ble_adv/sdkconfig.defaults index dcf4ad2c2..c617298b5 100644 --- a/examples/bluetooth/ble_adv/sdkconfig.defaults +++ b/examples/bluetooth/ble_adv/sdkconfig.defaults @@ -5,3 +5,6 @@ # BT config # CONFIG_BT_ENABLED=y +CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY=y +CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY= +CONFIG_BTDM_CONTROLLER_MODE_BTDM= diff --git a/examples/bluetooth/ble_eddystone/sdkconfig.defaults b/examples/bluetooth/ble_eddystone/sdkconfig.defaults index 9d51df5ee..9ea420884 100644 --- a/examples/bluetooth/ble_eddystone/sdkconfig.defaults +++ b/examples/bluetooth/ble_eddystone/sdkconfig.defaults @@ -1,4 +1,6 @@ # Override some defaults so BT stack is enabled # and WiFi disabled by default in this example CONFIG_BT_ENABLED=y -CONFIG_WIFI_ENABLED=n +CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY=y +CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY= +CONFIG_BTDM_CONTROLLER_MODE_BTDM= diff --git a/examples/bluetooth/ble_hid_device_demo/sdkconfig.defaults b/examples/bluetooth/ble_hid_device_demo/sdkconfig.defaults index 14f5546ff..50fc4ba9d 100644 --- a/examples/bluetooth/ble_hid_device_demo/sdkconfig.defaults +++ b/examples/bluetooth/ble_hid_device_demo/sdkconfig.defaults @@ -1,3 +1,6 @@ # Override some defaults so BT stack is enabled # by default in this example CONFIG_BT_ENABLED=y +CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY=y +CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY= +CONFIG_BTDM_CONTROLLER_MODE_BTDM= diff --git a/examples/bluetooth/ble_ibeacon/sdkconfig.defaults b/examples/bluetooth/ble_ibeacon/sdkconfig.defaults index 9d51df5ee..9ea420884 100644 --- a/examples/bluetooth/ble_ibeacon/sdkconfig.defaults +++ b/examples/bluetooth/ble_ibeacon/sdkconfig.defaults @@ -1,4 +1,6 @@ # Override some defaults so BT stack is enabled # and WiFi disabled by default in this example CONFIG_BT_ENABLED=y -CONFIG_WIFI_ENABLED=n +CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY=y +CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY= +CONFIG_BTDM_CONTROLLER_MODE_BTDM= diff --git a/examples/bluetooth/ble_spp_client/sdkconfig.defaults b/examples/bluetooth/ble_spp_client/sdkconfig.defaults index 9d51df5ee..9ea420884 100644 --- a/examples/bluetooth/ble_spp_client/sdkconfig.defaults +++ b/examples/bluetooth/ble_spp_client/sdkconfig.defaults @@ -1,4 +1,6 @@ # Override some defaults so BT stack is enabled # and WiFi disabled by default in this example CONFIG_BT_ENABLED=y -CONFIG_WIFI_ENABLED=n +CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY=y +CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY= +CONFIG_BTDM_CONTROLLER_MODE_BTDM= diff --git a/examples/bluetooth/ble_spp_server/sdkconfig.defaults b/examples/bluetooth/ble_spp_server/sdkconfig.defaults index e435f383c..696a01a19 100644 --- a/examples/bluetooth/ble_spp_server/sdkconfig.defaults +++ b/examples/bluetooth/ble_spp_server/sdkconfig.defaults @@ -5,7 +5,10 @@ # BT config # CONFIG_BT_ENABLED=y - +CONFIG_BT_ENABLED=y +CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY=y +CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY= +CONFIG_BTDM_CONTROLLER_MODE_BTDM= # # ESP32-specific config # diff --git a/examples/bluetooth/ble_throughput/throughput_client/sdkconfig.defaults b/examples/bluetooth/ble_throughput/throughput_client/sdkconfig.defaults index fff804e3c..b174cdca8 100644 --- a/examples/bluetooth/ble_throughput/throughput_client/sdkconfig.defaults +++ b/examples/bluetooth/ble_throughput/throughput_client/sdkconfig.defaults @@ -1,6 +1,10 @@ # Override some defaults so BT stack is enabled # by default in this example CONFIG_BT_ENABLED=y +CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY=y +CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY= +CONFIG_BTDM_CONTROLLER_MODE_BTDM= +CONFIG_BTDM_CONTROLLER_BLE_MAX_CONN=9 CONFIG_GATTS_NOTIFY_THROUGHPUT=y CONFIG_BTDM_CONTROLLER_MODEM_SLEEP=n CONFIG_BTDM_CONTROLLER_PINNED_TO_CORE_1=y diff --git a/examples/bluetooth/ble_throughput/throughput_server/sdkconfig.defaults b/examples/bluetooth/ble_throughput/throughput_server/sdkconfig.defaults index fff804e3c..b174cdca8 100644 --- a/examples/bluetooth/ble_throughput/throughput_server/sdkconfig.defaults +++ b/examples/bluetooth/ble_throughput/throughput_server/sdkconfig.defaults @@ -1,6 +1,10 @@ # Override some defaults so BT stack is enabled # by default in this example CONFIG_BT_ENABLED=y +CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY=y +CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY= +CONFIG_BTDM_CONTROLLER_MODE_BTDM= +CONFIG_BTDM_CONTROLLER_BLE_MAX_CONN=9 CONFIG_GATTS_NOTIFY_THROUGHPUT=y CONFIG_BTDM_CONTROLLER_MODEM_SLEEP=n CONFIG_BTDM_CONTROLLER_PINNED_TO_CORE_1=y diff --git a/examples/bluetooth/blufi/sdkconfig.defaults b/examples/bluetooth/blufi/sdkconfig.defaults index 2d73b0662..f0f3f8c1a 100644 --- a/examples/bluetooth/blufi/sdkconfig.defaults +++ b/examples/bluetooth/blufi/sdkconfig.defaults @@ -5,6 +5,9 @@ # BT config # CONFIG_BT_ENABLED=y +CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY=y +CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY= +CONFIG_BTDM_CONTROLLER_MODE_BTDM= CONFIG_BTDM_CONTROLLER_PINNED_TO_CORE_0=y CONFIG_BTDM_CONTROLLER_PINNED_TO_CORE_1=n CONFIG_BTDM_CONTROLLER_PINNED_TO_CORE=0 @@ -26,4 +29,3 @@ CONFIG_BT_ACL_CONNECTIONS=4 CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST=n CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY=n CONFIG_SMP_ENABLE=n -CONFIG_BT_RESERVE_DRAM=0x10000 diff --git a/examples/bluetooth/bt_discovery/sdkconfig.defaults b/examples/bluetooth/bt_discovery/sdkconfig.defaults index 793fe05c2..2c792c747 100644 --- a/examples/bluetooth/bt_discovery/sdkconfig.defaults +++ b/examples/bluetooth/bt_discovery/sdkconfig.defaults @@ -1,6 +1,9 @@ # Override some defaults so BT stack is enabled and # Classic BT is enabled and BT_DRAM_RELEASE is disabled CONFIG_BT_ENABLED=y +CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY= +CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY=y +CONFIG_BTDM_CONTROLLER_MODE_BTDM= CONFIG_CLASSIC_BT_ENABLED=y CONFIG_A2DP_ENABLE=n CONFIG_GATTS_ENABLE=n diff --git a/examples/bluetooth/bt_spp_acceptor/sdkconfig.defaults b/examples/bluetooth/bt_spp_acceptor/sdkconfig.defaults index a8ace2198..dbd886f79 100644 --- a/examples/bluetooth/bt_spp_acceptor/sdkconfig.defaults +++ b/examples/bluetooth/bt_spp_acceptor/sdkconfig.defaults @@ -1,6 +1,9 @@ # Override some defaults so BT stack is enabled # and WiFi disabled by default in this example CONFIG_BT_ENABLED=y +CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY= +CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY=y +CONFIG_BTDM_CONTROLLER_MODE_BTDM= CONFIG_CLASSIC_BT_ENABLED=y CONFIG_WIFI_ENABLED=n -CONFIG_BT_SPP_ENABLED=y \ No newline at end of file +CONFIG_BT_SPP_ENABLED=y diff --git a/examples/bluetooth/bt_spp_initiator/sdkconfig.defaults b/examples/bluetooth/bt_spp_initiator/sdkconfig.defaults index a8ace2198..dbd886f79 100644 --- a/examples/bluetooth/bt_spp_initiator/sdkconfig.defaults +++ b/examples/bluetooth/bt_spp_initiator/sdkconfig.defaults @@ -1,6 +1,9 @@ # Override some defaults so BT stack is enabled # and WiFi disabled by default in this example CONFIG_BT_ENABLED=y +CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY= +CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY=y +CONFIG_BTDM_CONTROLLER_MODE_BTDM= CONFIG_CLASSIC_BT_ENABLED=y CONFIG_WIFI_ENABLED=n -CONFIG_BT_SPP_ENABLED=y \ No newline at end of file +CONFIG_BT_SPP_ENABLED=y diff --git a/examples/bluetooth/bt_spp_vfs_acceptor/main/example_spp_vfs_acceptor_demo.c b/examples/bluetooth/bt_spp_vfs_acceptor/main/example_spp_vfs_acceptor_demo.c index 51c23c094..f14d39448 100644 --- a/examples/bluetooth/bt_spp_vfs_acceptor/main/example_spp_vfs_acceptor_demo.c +++ b/examples/bluetooth/bt_spp_vfs_acceptor/main/example_spp_vfs_acceptor_demo.c @@ -67,8 +67,11 @@ static void spp_read_handle(void * param) spp_wr_task_shut_down(); } -static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) +static void esp_spp_cb(uint16_t e, void *p) { + esp_spp_cb_event_t event = e; + esp_spp_cb_param_t *param = p; + switch (event) { case ESP_SPP_INIT_EVT: ESP_LOGI(SPP_TAG, "ESP_SPP_INIT_EVT"); @@ -102,7 +105,7 @@ static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) static void esp_spp_stack_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) { - spp_task_work_dispatch((spp_task_cb_t)esp_spp_cb, event, param, sizeof(esp_spp_cb_param_t), NULL); + spp_task_work_dispatch(esp_spp_cb, event, param, sizeof(esp_spp_cb_param_t), NULL); } void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) diff --git a/examples/bluetooth/bt_spp_vfs_acceptor/sdkconfig.defaults b/examples/bluetooth/bt_spp_vfs_acceptor/sdkconfig.defaults index a8ace2198..dbd886f79 100644 --- a/examples/bluetooth/bt_spp_vfs_acceptor/sdkconfig.defaults +++ b/examples/bluetooth/bt_spp_vfs_acceptor/sdkconfig.defaults @@ -1,6 +1,9 @@ # Override some defaults so BT stack is enabled # and WiFi disabled by default in this example CONFIG_BT_ENABLED=y +CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY= +CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY=y +CONFIG_BTDM_CONTROLLER_MODE_BTDM= CONFIG_CLASSIC_BT_ENABLED=y CONFIG_WIFI_ENABLED=n -CONFIG_BT_SPP_ENABLED=y \ No newline at end of file +CONFIG_BT_SPP_ENABLED=y diff --git a/examples/bluetooth/bt_spp_vfs_initiator/main/example_spp_vfs_initiator_demo.c b/examples/bluetooth/bt_spp_vfs_initiator/main/example_spp_vfs_initiator_demo.c index f5ef880a7..fa144215e 100644 --- a/examples/bluetooth/bt_spp_vfs_initiator/main/example_spp_vfs_initiator_demo.c +++ b/examples/bluetooth/bt_spp_vfs_initiator/main/example_spp_vfs_initiator_demo.c @@ -105,8 +105,11 @@ static bool get_name_from_eir(uint8_t *eir, char *bdname, uint8_t *bdname_len) return false; } -static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) +static void esp_spp_cb(uint16_t e, void *p) { + esp_spp_cb_event_t event = e; + esp_spp_cb_param_t *param = p; + switch (event) { case ESP_SPP_INIT_EVT: ESP_LOGI(SPP_TAG, "ESP_SPP_INIT_EVT"); @@ -198,7 +201,7 @@ static void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *pa static void esp_spp_stack_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) { - spp_task_work_dispatch((spp_task_cb_t)esp_spp_cb, event, param, sizeof(esp_spp_cb_param_t), NULL); + spp_task_work_dispatch(esp_spp_cb, event, param, sizeof(esp_spp_cb_param_t), NULL); } void app_main() diff --git a/examples/bluetooth/bt_spp_vfs_initiator/sdkconfig.defaults b/examples/bluetooth/bt_spp_vfs_initiator/sdkconfig.defaults index a8ace2198..dbd886f79 100644 --- a/examples/bluetooth/bt_spp_vfs_initiator/sdkconfig.defaults +++ b/examples/bluetooth/bt_spp_vfs_initiator/sdkconfig.defaults @@ -1,6 +1,9 @@ # Override some defaults so BT stack is enabled # and WiFi disabled by default in this example CONFIG_BT_ENABLED=y +CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY= +CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY=y +CONFIG_BTDM_CONTROLLER_MODE_BTDM= CONFIG_CLASSIC_BT_ENABLED=y CONFIG_WIFI_ENABLED=n -CONFIG_BT_SPP_ENABLED=y \ No newline at end of file +CONFIG_BT_SPP_ENABLED=y diff --git a/examples/bluetooth/controller_hci_uart/sdkconfig.defaults b/examples/bluetooth/controller_hci_uart/sdkconfig.defaults index a20ece1da..51fe6ac90 100644 --- a/examples/bluetooth/controller_hci_uart/sdkconfig.defaults +++ b/examples/bluetooth/controller_hci_uart/sdkconfig.defaults @@ -5,6 +5,12 @@ # BT config # CONFIG_BT_ENABLED=y +CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY= +CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY= +CONFIG_BTDM_CONTROLLER_MODE_BTDM=y +CONFIG_BTDM_CONTROLLER_BLE_MAX_CONN=9 +CONFIG_BTDM_CONTROLLER_BR_EDR_MAX_ACL_CONN=7 +CONFIG_BTDM_CONTROLLER_BR_EDR_MAX_SYNC_CONN=3 CONFIG_BLUEDROID_ENABLED=n CONFIG_BT_HCI_UART=y CONFIG_BT_HCI_UART_NO_DEFAULT=1 diff --git a/examples/bluetooth/gatt_client/sdkconfig.defaults b/examples/bluetooth/gatt_client/sdkconfig.defaults index 14f5546ff..50fc4ba9d 100644 --- a/examples/bluetooth/gatt_client/sdkconfig.defaults +++ b/examples/bluetooth/gatt_client/sdkconfig.defaults @@ -1,3 +1,6 @@ # Override some defaults so BT stack is enabled # by default in this example CONFIG_BT_ENABLED=y +CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY=y +CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY= +CONFIG_BTDM_CONTROLLER_MODE_BTDM= diff --git a/examples/bluetooth/gatt_security_client/sdkconfig.defaults b/examples/bluetooth/gatt_security_client/sdkconfig.defaults index 9d51df5ee..9ea420884 100644 --- a/examples/bluetooth/gatt_security_client/sdkconfig.defaults +++ b/examples/bluetooth/gatt_security_client/sdkconfig.defaults @@ -1,4 +1,6 @@ # Override some defaults so BT stack is enabled # and WiFi disabled by default in this example CONFIG_BT_ENABLED=y -CONFIG_WIFI_ENABLED=n +CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY=y +CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY= +CONFIG_BTDM_CONTROLLER_MODE_BTDM= diff --git a/examples/bluetooth/gatt_security_server/sdkconfig.defaults b/examples/bluetooth/gatt_security_server/sdkconfig.defaults index 14f5546ff..50fc4ba9d 100644 --- a/examples/bluetooth/gatt_security_server/sdkconfig.defaults +++ b/examples/bluetooth/gatt_security_server/sdkconfig.defaults @@ -1,3 +1,6 @@ # Override some defaults so BT stack is enabled # by default in this example CONFIG_BT_ENABLED=y +CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY=y +CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY= +CONFIG_BTDM_CONTROLLER_MODE_BTDM= diff --git a/examples/bluetooth/gatt_server/sdkconfig.defaults b/examples/bluetooth/gatt_server/sdkconfig.defaults index 14f5546ff..50fc4ba9d 100644 --- a/examples/bluetooth/gatt_server/sdkconfig.defaults +++ b/examples/bluetooth/gatt_server/sdkconfig.defaults @@ -1,3 +1,6 @@ # Override some defaults so BT stack is enabled # by default in this example CONFIG_BT_ENABLED=y +CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY=y +CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY= +CONFIG_BTDM_CONTROLLER_MODE_BTDM= diff --git a/examples/bluetooth/gatt_server_service_table/sdkconfig.defaults b/examples/bluetooth/gatt_server_service_table/sdkconfig.defaults index e435f383c..a24c32100 100644 --- a/examples/bluetooth/gatt_server_service_table/sdkconfig.defaults +++ b/examples/bluetooth/gatt_server_service_table/sdkconfig.defaults @@ -5,6 +5,9 @@ # BT config # CONFIG_BT_ENABLED=y +CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY=y +CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY= +CONFIG_BTDM_CONTROLLER_MODE_BTDM= # # ESP32-specific config diff --git a/examples/bluetooth/gattc_multi_connect/sdkconfig.defaults b/examples/bluetooth/gattc_multi_connect/sdkconfig.defaults index 9d51df5ee..711312520 100644 --- a/examples/bluetooth/gattc_multi_connect/sdkconfig.defaults +++ b/examples/bluetooth/gattc_multi_connect/sdkconfig.defaults @@ -1,4 +1,7 @@ # Override some defaults so BT stack is enabled # and WiFi disabled by default in this example CONFIG_BT_ENABLED=y -CONFIG_WIFI_ENABLED=n +CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY=y +CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY= +CONFIG_BTDM_CONTROLLER_MODE_BTDM= +CONFIG_BTDM_CONTROLLER_BLE_MAX_CONN=9 diff --git a/examples/ethernet/ethernet/README.md b/examples/ethernet/ethernet/README.md index cedff71d0..c79a1d0a1 100644 --- a/examples/ethernet/ethernet/README.md +++ b/examples/ethernet/ethernet/README.md @@ -25,7 +25,6 @@ Possible configurations of the 50MHz clock signal: | Mode | GPIO Pin | Signal name | Notes | | -------- | -------- | -------------- | -------------------------------------------------------------------------------------------------- | | external | `GPIO0` | `EMAC_TX_CLK` | Input of 50MHz PHY clock | -| internal | `GPIO0` | `CLK_OUT1` | Output of 50MHz APLL clock. Signal quality might be an issue. | | internal | `GPIO16` | `EMAC_CLK_OUT` | Output of 50MHz APLL clock. | | internal | `GPIO17` | `EMAC_CLK_180` | Inverted output of 50MHz APLL clock. Found to be best suitable for LAN8720 with long signal lines. | @@ -36,8 +35,6 @@ The external reference clock of 50MHz must be supplied on `GPIO0`. See note abou #### Internal PHY Clock The ESP32 can generate a 50MHz clock using its APLL. When the APLL is already used as clock source for other purposes (most likely I²S) external PHY has to be used. -On different test setups clock output on `GPIO0` was found unstable because in most designs the signal path is not ideal for this high frequency (the PCB trace has several devices added to it and therefore the capacitive load is relatively high) - The inverted clock signal `EMAC_CLK_180` was found working best with a LAN8720 PHY. ## RMII PHY Wiring diff --git a/examples/ethernet/ethernet/main/Kconfig.projbuild b/examples/ethernet/ethernet/main/Kconfig.projbuild index b762dc54a..7ef25aeae 100644 --- a/examples/ethernet/ethernet/main/Kconfig.projbuild +++ b/examples/ethernet/ethernet/main/Kconfig.projbuild @@ -41,11 +41,6 @@ config PHY_CLOCK_GPIO0_IN help Input of 50MHz refclock on GPIO0 -config PHY_CLOCK_GPIO0_OUT - bool "GPIO0 output" - help - Output the internal 50MHz APLL clock on GPIO0 - config PHY_CLOCK_GPIO16_OUT bool "GPIO16 output" help @@ -61,7 +56,6 @@ endchoice config PHY_CLOCK_MODE int default 0 if PHY_CLOCK_GPIO0_IN - default 1 if PHY_CLOCK_GPIO0_OUT default 2 if PHY_CLOCK_GPIO16_OUT default 3 if PHY_CLOCK_GPIO17_OUT diff --git a/examples/ethernet/ethernet/main/ethernet_example_main.c b/examples/ethernet/ethernet/main/ethernet_example_main.c index 832be191e..d4e961042 100644 --- a/examples/ethernet/ethernet/main/ethernet_example_main.c +++ b/examples/ethernet/ethernet/main/ethernet_example_main.c @@ -16,27 +16,15 @@ #include "esp_err.h" #include "esp_event_loop.h" #include "esp_event.h" -#include "esp_attr.h" #include "esp_log.h" #include "esp_eth.h" -#include "rom/ets_sys.h" #include "rom/gpio.h" -#include "soc/dport_reg.h" -#include "soc/io_mux_reg.h" -#include "soc/rtc_cntl_reg.h" -#include "soc/gpio_reg.h" -#include "soc/gpio_sig_map.h" - #include "tcpip_adapter.h" -#include "nvs_flash.h" #include "driver/gpio.h" - -#include "soc/emac_ex_reg.h" #include "driver/periph_ctrl.h" - #ifdef CONFIG_PHY_LAN8720 #include "eth_phy/phy_lan8720.h" #define DEFAULT_ETHERNET_PHY_CONFIG phy_lan8720_default_ethernet_config @@ -49,103 +37,121 @@ static const char *TAG = "eth_example"; #define PIN_PHY_POWER CONFIG_PHY_POWER_PIN -#define PIN_SMI_MDC CONFIG_PHY_SMI_MDC_PIN -#define PIN_SMI_MDIO CONFIG_PHY_SMI_MDIO_PIN +#define PIN_SMI_MDC CONFIG_PHY_SMI_MDC_PIN +#define PIN_SMI_MDIO CONFIG_PHY_SMI_MDIO_PIN #ifdef CONFIG_PHY_USE_POWER_PIN -/* This replaces the default PHY power on/off function with one that - also uses a GPIO for power on/off. - - If this GPIO is not connected on your device (and PHY is always powered), you can use the default PHY-specific power - on/off function rather than overriding with this one. -*/ +/** + * @brief re-define power enable func for phy + * + * @param enable true to enable, false to disable + * + * @note This function replaces the default PHY power on/off function. + * If this GPIO is not connected on your device (and PHY is always powered), + * you can use the default PHY-specific power on/off function. + */ static void phy_device_power_enable_via_gpio(bool enable) { assert(DEFAULT_ETHERNET_PHY_CONFIG.phy_power_enable); if (!enable) { - /* Do the PHY-specific power_enable(false) function before powering down */ DEFAULT_ETHERNET_PHY_CONFIG.phy_power_enable(false); } gpio_pad_select_gpio(PIN_PHY_POWER); - gpio_set_direction(PIN_PHY_POWER,GPIO_MODE_OUTPUT); - if(enable == true) { + gpio_set_direction(PIN_PHY_POWER, GPIO_MODE_OUTPUT); + if (enable == true) { gpio_set_level(PIN_PHY_POWER, 1); - ESP_LOGD(TAG, "phy_device_power_enable(TRUE)"); + ESP_LOGI(TAG, "Power On Ethernet PHY"); } else { gpio_set_level(PIN_PHY_POWER, 0); - ESP_LOGD(TAG, "power_enable(FALSE)"); + ESP_LOGI(TAG, "Power Off Ethernet PHY"); } - // Allow the power up/down to take effect, min 300us - vTaskDelay(1); + vTaskDelay(1); // Allow the power up/down to take effect, min 300us if (enable) { - /* Run the PHY-specific power on operations now the PHY has power */ + /* call the default PHY-specific power on function */ DEFAULT_ETHERNET_PHY_CONFIG.phy_power_enable(true); } } #endif +/** + * @brief gpio specific init + * + * @note RMII data pins are fixed in esp32: + * TXD0 <=> GPIO19 + * TXD1 <=> GPIO22 + * TX_EN <=> GPIO21 + * RXD0 <=> GPIO25 + * RXD1 <=> GPIO26 + * CLK <=> GPIO0 + * + */ static void eth_gpio_config_rmii(void) { - // RMII data pins are fixed: - // TXD0 = GPIO19 - // TXD1 = GPIO22 - // TX_EN = GPIO21 - // RXD0 = GPIO25 - // RXD1 = GPIO26 - // CLK == GPIO0 phy_rmii_configure_data_interface_pins(); - // MDC is GPIO 23, MDIO is GPIO 18 phy_rmii_smi_configure_pins(PIN_SMI_MDC, PIN_SMI_MDIO); } -void eth_task(void *pvParameter) +/** + * @brief event handler for ethernet + * + * @param ctx + * @param event + * @return esp_err_t + */ +static esp_err_t eth_event_handler(void *ctx, system_event_t *event) { tcpip_adapter_ip_info_t ip; - memset(&ip, 0, sizeof(tcpip_adapter_ip_info_t)); - vTaskDelay(2000 / portTICK_PERIOD_MS); - while (1) { - - vTaskDelay(2000 / portTICK_PERIOD_MS); - - if (tcpip_adapter_get_ip_info(ESP_IF_ETH, &ip) == 0) { - ESP_LOGI(TAG, "~~~~~~~~~~~"); - ESP_LOGI(TAG, "ETHIP:"IPSTR, IP2STR(&ip.ip)); - ESP_LOGI(TAG, "ETHPMASK:"IPSTR, IP2STR(&ip.netmask)); - ESP_LOGI(TAG, "ETHPGW:"IPSTR, IP2STR(&ip.gw)); - ESP_LOGI(TAG, "~~~~~~~~~~~"); - } + switch (event->event_id) { + case SYSTEM_EVENT_ETH_CONNECTED: + ESP_LOGI(TAG, "Ethernet Link Up"); + break; + case SYSTEM_EVENT_ETH_DISCONNECTED: + ESP_LOGI(TAG, "Ethernet Link Down"); + break; + case SYSTEM_EVENT_ETH_START: + ESP_LOGI(TAG, "Ethernet Started"); + break; + case SYSTEM_EVENT_ETH_GOT_IP: + memset(&ip, 0, sizeof(tcpip_adapter_ip_info_t)); + ESP_ERROR_CHECK(tcpip_adapter_get_ip_info(ESP_IF_ETH, &ip)); + ESP_LOGI(TAG, "Ethernet Got IP Addr"); + ESP_LOGI(TAG, "~~~~~~~~~~~"); + ESP_LOGI(TAG, "ETHIP:" IPSTR, IP2STR(&ip.ip)); + ESP_LOGI(TAG, "ETHMASK:" IPSTR, IP2STR(&ip.netmask)); + ESP_LOGI(TAG, "ETHGW:" IPSTR, IP2STR(&ip.gw)); + ESP_LOGI(TAG, "~~~~~~~~~~~"); + break; + case SYSTEM_EVENT_ETH_STOP: + ESP_LOGI(TAG, "Ethernet Stopped"); + break; + default: + break; } + return ESP_OK; } void app_main() { - esp_err_t ret = ESP_OK; tcpip_adapter_init(); - esp_event_loop_init(NULL, NULL); + + ESP_ERROR_CHECK(esp_event_loop_init(eth_event_handler, NULL)); eth_config_t config = DEFAULT_ETHERNET_PHY_CONFIG; - /* Set the PHY address in the example configuration */ config.phy_addr = CONFIG_PHY_ADDRESS; config.gpio_config = eth_gpio_config_rmii; config.tcpip_input = tcpip_adapter_eth_input; config.clock_mode = CONFIG_PHY_CLOCK_MODE; - #ifdef CONFIG_PHY_USE_POWER_PIN - /* Replace the default 'power enable' function with an example-specific - one that toggles a power GPIO. */ + /* Replace the default 'power enable' function with an example-specific one + that toggles a power GPIO. */ config.phy_power_enable = phy_device_power_enable_via_gpio; #endif - ret = esp_eth_init(&config); - - if(ret == ESP_OK) { - esp_eth_enable(); - xTaskCreate(eth_task, "eth_task", 2048, NULL, (tskIDLE_PRIORITY + 2), NULL); - } - + ESP_ERROR_CHECK(esp_eth_init(&config)); + ESP_ERROR_CHECK(esp_eth_enable()) ; } diff --git a/examples/mesh/internal_communication/main/Kconfig.projbuild b/examples/mesh/internal_communication/main/Kconfig.projbuild index a3bad4bfd..d8de07e5b 100644 --- a/examples/mesh/internal_communication/main/Kconfig.projbuild +++ b/examples/mesh/internal_communication/main/Kconfig.projbuild @@ -21,7 +21,7 @@ config MESH_ROUTER_PASSWD choice bool "Mesh AP Authentication Mode" - default MAP_AUTH_MODE_OPEN + default WIFI_AUTH_WPA2_PSK help Authentication mode. @@ -70,11 +70,5 @@ config MESH_ROUTE_TABLE_SIZE default 50 help The number of devices over the network(max: 300). - -config MESH_PARENT_SSID - string "Parent SSID" - default "PARENT_SSID" - help - Parent SSID. endmenu diff --git a/examples/mesh/internal_communication/main/mesh_main.c b/examples/mesh/internal_communication/main/mesh_main.c index 4e6b10901..fae5fa279 100644 --- a/examples/mesh/internal_communication/main/mesh_main.c +++ b/examples/mesh/internal_communication/main/mesh_main.c @@ -217,8 +217,8 @@ void mesh_event_handler(mesh_event_t event) event.info.routing_table.rt_size_change, event.info.routing_table.rt_size_new); break; - case MESH_EVENT_NO_PARNET_FOUND: - ESP_LOGI(MESH_TAG, "scan times:%d", + case MESH_EVENT_NO_PARENT_FOUND: + ESP_LOGI(MESH_TAG, "scan times:%d", event.info.no_parent.scan_times); /* TODO handler for the failure */ break; @@ -350,7 +350,6 @@ void app_main(void) /* mesh initialization */ ESP_ERROR_CHECK(esp_mesh_init()); ESP_ERROR_CHECK(esp_mesh_set_max_layer(CONFIG_MESH_MAX_LAYER)); - ESP_ERROR_CHECK(esp_mesh_set_ap_authmode(CONFIG_MESH_AP_AUTHMODE)); ESP_ERROR_CHECK(esp_mesh_set_vote_percentage(1)); ESP_ERROR_CHECK(esp_mesh_set_ap_assoc_expire(10)); #ifdef MESH_FIX_ROOT @@ -368,6 +367,7 @@ void app_main(void) memcpy((uint8_t *) &cfg.router.password, CONFIG_MESH_ROUTER_PASSWD, strlen(CONFIG_MESH_ROUTER_PASSWD)); /* mesh softAP */ + ESP_ERROR_CHECK(esp_mesh_set_ap_authmode(CONFIG_MESH_AP_AUTHMODE)); cfg.mesh_ap.max_connection = CONFIG_MESH_AP_CONNECTIONS; memcpy((uint8_t *) &cfg.mesh_ap.password, CONFIG_MESH_AP_PASSWD, strlen(CONFIG_MESH_AP_PASSWD)); diff --git a/examples/mesh/manual_networking/main/Kconfig.projbuild b/examples/mesh/manual_networking/main/Kconfig.projbuild index a3bad4bfd..32dd47197 100644 --- a/examples/mesh/manual_networking/main/Kconfig.projbuild +++ b/examples/mesh/manual_networking/main/Kconfig.projbuild @@ -21,7 +21,7 @@ config MESH_ROUTER_PASSWD choice bool "Mesh AP Authentication Mode" - default MAP_AUTH_MODE_OPEN + default WIFI_AUTH_WPA2_PSK help Authentication mode. diff --git a/examples/mesh/manual_networking/main/mesh_main.c b/examples/mesh/manual_networking/main/mesh_main.c index 2703dd5b6..79b547a18 100644 --- a/examples/mesh/manual_networking/main/mesh_main.c +++ b/examples/mesh/manual_networking/main/mesh_main.c @@ -173,8 +173,8 @@ void mesh_event_handler(mesh_event_t event) event.info.routing_table.rt_size_change, event.info.routing_table.rt_size_new); break; - case MESH_EVENT_NO_PARNET_FOUND: - ESP_LOGI(MESH_TAG, "scan times:%d", + case MESH_EVENT_NO_PARENT_FOUND: + ESP_LOGI(MESH_TAG, "scan times:%d", event.info.no_parent.scan_times); /* TODO handler for the failure */ break; diff --git a/examples/peripherals/spi_slave/sender/main/app_main.c b/examples/peripherals/spi_slave/sender/main/app_main.c index b1e37dd49..b033fcf2a 100644 --- a/examples/peripherals/spi_slave/sender/main/app_main.c +++ b/examples/peripherals/spi_slave/sender/main/app_main.c @@ -118,8 +118,8 @@ void app_main() }; int n=0; - char sendbuf[128]=""; - char recvbuf[128]=""; + char sendbuf[128] = {0}; + char recvbuf[128] = {0}; spi_transaction_t t; memset(&t, 0, sizeof(t)); @@ -143,12 +143,16 @@ void app_main() xSemaphoreGive(rdySem); while(1) { - snprintf(sendbuf, 128, "Sender, transmission no. %04i. Last time, I received: \"%s\"", n, recvbuf); - t.length=128*8; //128 bytes + int res = snprintf(sendbuf, sizeof(sendbuf), + "Sender, transmission no. %04i. Last time, I received: \"%s\"", n, recvbuf); + if (res >= sizeof(sendbuf)) { + printf("Data truncated\n"); + } + t.length=sizeof(sendbuf)*8; t.tx_buffer=sendbuf; t.rx_buffer=recvbuf; //Wait for slave to be ready for next byte before sending - xSemaphoreTake(rdySem, 100);//portMAX_DELAY); //Wait until slave is ready + xSemaphoreTake(rdySem, portMAX_DELAY); //Wait until slave is ready ret=spi_device_transmit(handle, &t); printf("Received: %s\n", recvbuf); n++; diff --git a/examples/wifi/power_save/main/Kconfig.projbuild b/examples/wifi/power_save/main/Kconfig.projbuild index 974474d33..ea2b0aa26 100644 --- a/examples/wifi/power_save/main/Kconfig.projbuild +++ b/examples/wifi/power_save/main/Kconfig.projbuild @@ -1,43 +1,96 @@ menu "Example Configuration" config WIFI_SSID - string "WiFi SSID" - default "myssid" - help - SSID (network name) for the example to connect to. + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. config WIFI_PASSWORD - string "WiFi Password" - default "mypassword" - help - WiFi password (WPA or WPA2) for the example to use. - + string "WiFi Password" + default "mypassword" + help + WiFi password (WPA or WPA2) for the example to use. + config WIFI_LISTEN_INTERVAL - int "WiFi listen interval" - default 3 - help - Interval for station to listen to beacon from AP. The unit of listen interval is one beacon interval. - For example, if beacon interval is 100 ms and listen interval is 3, the interval for station to listen - to beacon is 300 ms. + int "WiFi listen interval" + default 3 + help + Interval for station to listen to beacon from AP. The unit of listen interval is one beacon interval. + For example, if beacon interval is 100 ms and listen interval is 3, the interval for station to listen + to beacon is 300 ms. choice POWER_SAVE_MODE - prompt "power save mode" - default POWER_SAVE_MIN_MODEM - help - Power save mode for the esp32 to use. Modem sleep mode includes minimum and maximum power save modes. - In minimum power save mode, station wakes up every DTIM to receive beacon. Broadcast data will not be - lost because it is transmitted after DTIM. However, it can not save much more power if DTIM is short - for DTIM is determined by AP. - In maximum power save mode, station wakes up every listen interval to receive beacon. Broadcast data - may be lost because station may be in sleep state at DTIM time. If listen interval is longer, more power - is saved but broadcast data is more easy to lose. + prompt "power save mode" + default POWER_SAVE_MIN_MODEM + help + Power save mode for the esp32 to use. Modem sleep mode includes minimum and maximum power save modes. + In minimum power save mode, station wakes up every DTIM to receive beacon. Broadcast data will not be + lost because it is transmitted after DTIM. However, it can not save much more power if DTIM is short + for DTIM is determined by AP. + In maximum power save mode, station wakes up every listen interval to receive beacon. Broadcast data + may be lost because station may be in sleep state at DTIM time. If listen interval is longer, more power + is saved but broadcast data is more easy to lose. config POWER_SAVE_NONE - bool "none" + bool "none" config POWER_SAVE_MIN_MODEM - bool "minimum modem" + bool "minimum modem" config POWER_SAVE_MAX_MODEM - bool "maximum modem" + bool "maximum modem" endchoice +choice EXAMPLE_MAX_CPU_FREQ + prompt "Maximum CPU frequency" + default EXAMPLE_MAX_CPU_FREQ_80 + help + Maximum CPU frequency to use for dynamic frequency scaling. + +config EXAMPLE_MAX_CPU_FREQ_80 + bool "80 MHz" +config EXAMPLE_MAX_CPU_FREQ_160 + bool "160 MHz" +config EXAMPLE_MAX_CPU_FREQ_240 + bool "240 MHz" +endchoice + +config EXAMPLE_MAX_CPU_FREQ_MHZ + int + default 80 if EXAMPLE_MAX_CPU_FREQ_80 + default 160 if EXAMPLE_MAX_CPU_FREQ_160 + default 240 if EXAMPLE_MAX_CPU_FREQ_240 + + +choice EXAMPLE_MIN_CPU_FREQ + prompt "Minimum CPU frequency" + default EXAMPLE_MIN_CPU_FREQ_10M + help + Minimum CPU frequency to use for dynamic frequency scaling. + Should be set to XTAL frequency or XTAL frequency divided by integer. + +config EXAMPLE_MIN_CPU_FREQ_40M + bool "40 MHz (use with 40MHz XTAL)" + depends on ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO +config EXAMPLE_MIN_CPU_FREQ_20M + bool "20 MHz (use with 40MHz XTAL)" + depends on ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO +config EXAMPLE_MIN_CPU_FREQ_10M + bool "10 MHz (use with 40MHz XTAL)" + depends on ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO +config EXAMPLE_MIN_CPU_FREQ_26M + bool "26 MHz (use with 26MHz XTAL)" + depends on ESP32_XTAL_FREQ_26 || ESP32_XTAL_FREQ_AUTO +config EXAMPLE_MIN_CPU_FREQ_13M + bool "13 MHz (use with 26MHz XTAL)" + depends on ESP32_XTAL_FREQ_26 || ESP32_XTAL_FREQ_AUTO +endchoice + +config EXAMPLE_MIN_CPU_FREQ_MHZ + int + default 40 if EXAMPLE_MIN_CPU_FREQ_40M + default 20 if EXAMPLE_MIN_CPU_FREQ_20M + default 10 if EXAMPLE_MIN_CPU_FREQ_10M + default 26 if EXAMPLE_MIN_CPU_FREQ_26M + default 13 if EXAMPLE_MIN_CPU_FREQ_13M + endmenu diff --git a/examples/wifi/power_save/main/power_save.c b/examples/wifi/power_save/main/power_save.c index d2e60fdfd..c9576a9aa 100644 --- a/examples/wifi/power_save/main/power_save.c +++ b/examples/wifi/power_save/main/power_save.c @@ -96,13 +96,12 @@ void app_main() ESP_ERROR_CHECK( ret ); #if CONFIG_PM_ENABLE - // Configure dynamic frequency scaling: maximum frequency is set in sdkconfig, - // minimum frequency is XTAL. - rtc_cpu_freq_t max_freq; - rtc_clk_cpu_freq_from_mhz(CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ, &max_freq); + // Configure dynamic frequency scaling: + // maximum and minimum frequencies are set in sdkconfig, + // automatic light sleep is enabled if tickless idle support is enabled. esp_pm_config_esp32_t pm_config = { - .max_cpu_freq = max_freq, - .min_cpu_freq = RTC_CPU_FREQ_XTAL, + .max_freq_mhz = CONFIG_EXAMPLE_MAX_CPU_FREQ_MHZ, + .min_freq_mhz = CONFIG_EXAMPLE_MIN_CPU_FREQ_MHZ, #if CONFIG_FREERTOS_USE_TICKLESS_IDLE .light_sleep_enable = true #endif diff --git a/make/project.mk b/make/project.mk index a5fea2f52..f3ceeba1a 100644 --- a/make/project.mk +++ b/make/project.mk @@ -13,7 +13,7 @@ .PHONY: build-components menuconfig defconfig all build clean all_binaries check-submodules size size-components size-files size-symbols list-components MAKECMDGOALS ?= all -all: all_binaries +all: all_binaries check_python_dependencies # see below for recipe of 'all' target # # # other components will add dependencies to 'all_binaries'. The @@ -43,12 +43,13 @@ help: @echo "make app-flash - Flash just the app" @echo "make app-clean - Clean just the app" @echo "make print_flash_cmd - Print the arguments for esptool when flash" + @echo "make check_python_dependencies - Check that the required python packages are installed" @echo "" @echo "See also 'make bootloader', 'make bootloader-flash', 'make bootloader-clean', " @echo "'make partition_table', etc, etc." # Non-interactive targets. Mostly, those for which you do not need to build a binary -NON_INTERACTIVE_TARGET += defconfig clean% %clean help list-components print_flash_cmd +NON_INTERACTIVE_TARGET += defconfig clean% %clean help list-components print_flash_cmd check_python_dependencies # dependency checks ifndef MAKE_RESTARTS @@ -421,6 +422,14 @@ else @echo $(ESPTOOLPY_WRITE_FLASH) $(APP_OFFSET) $(APP_BIN) endif +.PHONY: check_python_dependencies + +# Notify users when some of the required python packages are not installed +check_python_dependencies: +ifndef IS_BOOTLOADER_BUILD + $(PYTHON) $(IDF_PATH)/tools/check_python_dependencies.py +endif + all_binaries: $(APP_BIN) $(BUILD_DIR_BASE): @@ -476,16 +485,16 @@ app-clean: $(addprefix component-,$(addsuffix -clean,$(notdir $(COMPONENT_PATHS) $(summary) RM $(APP_ELF) rm -f $(APP_ELF) $(APP_BIN) $(APP_MAP) -size: $(APP_ELF) +size: check_python_dependencies $(APP_ELF) $(PYTHON) $(IDF_PATH)/tools/idf_size.py $(APP_MAP) -size-files: $(APP_ELF) +size-files: check_python_dependencies $(APP_ELF) $(PYTHON) $(IDF_PATH)/tools/idf_size.py --files $(APP_MAP) -size-components: $(APP_ELF) +size-components: check_python_dependencies $(APP_ELF) $(PYTHON) $(IDF_PATH)/tools/idf_size.py --archives $(APP_MAP) -size-symbols: $(APP_ELF) +size-symbols: check_python_dependencies $(APP_ELF) ifndef COMPONENT $(error "ERROR: Please enter the component to look symbols for, e.g. COMPONENT=heap") else diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 000000000..88cfc676a --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +# This is a list of python packages needed for ESP-IDF. This file is used with pip. +# Please see the Get Started section of the ESP-IDF Programming Guide for further information. +# +pyserial>=3.0 +future>=0.16.0 diff --git a/tools/check_python_dependencies.py b/tools/check_python_dependencies.py new file mode 100644 index 000000000..9c5f8e855 --- /dev/null +++ b/tools/check_python_dependencies.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +# +# Copyright 2018 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. + +import os +import sys +try: + import pkg_resources +except: + print('pkg_resources cannot be imported probably because the pip package is not installed and/or using a ' + 'legacy Python interpreter. Please refer to the Get Started section of the ESP-IDF Programming Guide for ' + 'setting up the required packages.') + sys.exit(1) + +req_file = '{}/requirements.txt'.format(os.getenv("IDF_PATH")) + +if __name__ == "__main__": + not_satisfied = [] + with open(req_file) as f: + for line in f: + line = line.strip() + try: + pkg_resources.require(line) + except: + not_satisfied.append(line) + + if len(not_satisfied) > 0: + print('The following Python requirements are not satisfied:') + for requirement in not_satisfied: + print(requirement) + print('Please run "{} -m pip install -r {}" for resolving the issue.'.format(sys.executable, req_file)) + sys.exit(1) + + print('Python requirements are satisfied.') diff --git a/tools/ci/build_examples.sh b/tools/ci/build_examples.sh index f36ff60df..a36ab8f80 100755 --- a/tools/ci/build_examples.sh +++ b/tools/ci/build_examples.sh @@ -158,7 +158,7 @@ echo -e "\nFound issues:" # "error.o" or "-Werror" in compiler's command line # "reassigning to symbol" or "changes choice state" in sdkconfig sort -u "${LOG_SUSPECTED}" | \ -grep -v "library/error.o\|\ -Werror\|reassigning to symbol\|changes choice state" \ +grep -v "library/error\.o\|\ -Werror\|error\.d\|reassigning to symbol\|changes choice state" \ && RESULT=$RESULT_ISSUES \ || echo -e "\tNone" diff --git a/tools/ci/executable-list.txt b/tools/ci/executable-list.txt index 71e70c20b..a6c44996c 100644 --- a/tools/ci/executable-list.txt +++ b/tools/ci/executable-list.txt @@ -7,6 +7,8 @@ components/idf_test/unit_test/TestCaseScript/IDFUnitTest/__init__.py components/nvs_flash/nvs_partition_generator/nvs_partition_gen.py components/partition_table/gen_esp32part.py components/partition_table/parttool.py +components/app_update/gen_empty_partition.py +components/app_update/dump_otadata.py components/partition_table/test_gen_esp32part_host/gen_esp32part_tests.py components/ulp/esp32ulp_mapgen.py docs/check_doc_warnings.sh diff --git a/tools/gen_esp_err_to_name.py b/tools/gen_esp_err_to_name.py index 932dfd8bc..b37054152 100755 --- a/tools/gen_esp_err_to_name.py +++ b/tools/gen_esp_err_to_name.py @@ -14,13 +14,19 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import print_function +from __future__ import unicode_literals +from builtins import str +from builtins import range +from builtins import object +from io import open import os import argparse import re import fnmatch -import string import collections import textwrap +import functools # list files here which should not be parsed ignore_files = [ 'components/mdns/test_afl_fuzz_host/esp32_compat.h' ] @@ -35,7 +41,7 @@ err_dict = collections.defaultdict(list) #identified errors are stored here; map rev_err_dict = dict() #map of error string to error code unproc_list = list() #errors with unknown codes which depend on other errors -class ErrItem: +class ErrItem(object): """ Contains information about the error: - name - error string @@ -99,7 +105,7 @@ def process(line, idf_path): words = re.split(r' +', line, 2) # words[1] is the error name # words[2] is the rest of the line (value, base + value, comment) - if len(words) < 2: + if len(words) < 3: raise InputError(idf_path, "Error at line %s" % line) line = "" @@ -109,8 +115,8 @@ def process(line, idf_path): # identify possible comment m = re.search(r'/\*!<(.+?(?=\*/))', todo_str) if m: - comment = string.strip(m.group(1)) - todo_str = string.strip(todo_str[:m.start()]) # keep just the part before the comment + comment = m.group(1).strip() + todo_str = todo_str[:m.start()].strip() # keep just the part before the comment # identify possible parentheses () m = re.search(r'\((.+)\)', todo_str) @@ -181,14 +187,14 @@ def path_to_include(path): are inside the "include" directory. Other special cases need to be handled here when the compiler gives an unknown header file error message. """ - spl_path = string.split(path, os.sep) + spl_path = path.split(os.sep) try: i = spl_path.index('include') except ValueError: # no include in the path -> use just the filename return os.path.basename(path) else: - return str(os.sep).join(spl_path[i+1:]) # subdirectories and filename in "include" + return os.sep.join(spl_path[i+1:]) # subdirectories and filename in "include" def print_warning(error_list, error_code): """ @@ -200,7 +206,7 @@ def print_warning(error_list, error_code): def max_string_width(): max = 0 - for k in err_dict.keys(): + for k in err_dict: for e in err_dict[k]: x = len(e.name) if x > max: @@ -214,7 +220,7 @@ def generate_c_output(fin, fout): """ # make includes unique by using a set includes = set() - for k in err_dict.keys(): + for k in err_dict: for e in err_dict[k]: includes.add(path_to_include(e.file)) @@ -226,7 +232,7 @@ def generate_c_output(fin, fout): include_list.sort() max_width = max_string_width() + 17 + 1 # length of " ERR_TBL_IT()," with spaces is 17 - max_decdig = max(len(str(k)) for k in err_dict.keys()) + max_decdig = max(len(str(k)) for k in err_dict) for line in fin: if re.match(r'@COMMENT@', line): @@ -239,7 +245,7 @@ def generate_c_output(fin, fout): last_file = "" for k in sorted(err_dict.keys()): if len(err_dict[k]) > 1: - err_dict[k].sort() + err_dict[k].sort(key=functools.cmp_to_key(ErrItem.__cmp__)) print_warning(err_dict[k], k) for e in err_dict[k]: if e.file != last_file: @@ -297,22 +303,25 @@ def main(): path_in_idf = os.path.relpath(full_path, idf_path) if path_in_idf in ignore_files or path_in_idf.startswith(ignore_dirs): continue - with open(full_path, "r+") as f: - for line in f: - # match also ESP_OK and ESP_FAIL because some of ESP_ERRs are referencing them - if re.match(r"\s*#define\s+(ESP_ERR_|ESP_OK|ESP_FAIL)", line): - try: - process(str.strip(line), path_in_idf) - except InputError as e: - print (e) + with open(full_path, encoding='utf-8') as f: + try: + for line in f: + # match also ESP_OK and ESP_FAIL because some of ESP_ERRs are referencing them + if re.match(r"\s*#define\s+(ESP_ERR_|ESP_OK|ESP_FAIL)", line): + try: + process(line.strip(), path_in_idf) + except InputError as e: + print (e) + except UnicodeDecodeError: + raise ValueError("The encoding of {} is not Unicode.".format(path_in_idf)) process_remaining_errors() if args.rst_output is not None: - with open(args.rst_output, 'w') as fout: + with open(args.rst_output, 'w', encoding='utf-8') as fout: generate_rst_output(fout) else: - with open(args.c_input, 'r') as fin, open(args.c_output, 'w') as fout: + with open(args.c_input, 'r', encoding='utf-8') as fin, open(args.c_output, 'w', encoding='utf-8') as fout: generate_c_output(fin, fout) if __name__ == "__main__": diff --git a/tools/idf_monitor.py b/tools/idf_monitor.py index 91ec1a9be..79aef4092 100755 --- a/tools/idf_monitor.py +++ b/tools/idf_monitor.py @@ -28,6 +28,12 @@ # Originally released under BSD-3-Clause license. # from __future__ import print_function, division +from __future__ import unicode_literals +from future import standard_library +standard_library.install_aliases() +from builtins import chr +from builtins import object +from builtins import bytes import subprocess import argparse import codecs @@ -224,7 +230,7 @@ class SerialReader(StoppableThread): except: pass -class LineMatcher: +class LineMatcher(object): """ Assembles a dictionary of filtering rules based on the --print_filter argument of idf_monitor. Then later it is used to match lines and @@ -296,7 +302,7 @@ class Monitor(object): self.event_queue = queue.Queue() self.console = miniterm.Console() if os.name == 'nt': - sys.stderr = ANSIColorConverter(sys.stderr) + sys.stderr = ANSIColorConverter(sys.stderr, decode_output=True) self.console.output = ANSIColorConverter(self.console.output) self.console.byte_output = ANSIColorConverter(self.console.byte_output) @@ -304,8 +310,8 @@ class Monitor(object): # Use Console.getkey implementation from 3.3.0 (to be in sync with the ConsoleReader._cancel patch above) def getkey_patched(self): c = self.enc_stdin.read(1) - if c == unichr(0x7f): - c = unichr(8) # map the BS key (which yields DEL) to backspace + if c == chr(0x7f): + c = chr(8) # map the BS key (which yields DEL) to backspace return c self.console.getkey = types.MethodType(getkey_patched, self.console) @@ -324,9 +330,9 @@ class Monitor(object): self.exit_key = CTRL_RBRACKET self.translate_eol = { - "CRLF": lambda c: c.replace(b"\n", b"\r\n"), - "CR": lambda c: c.replace(b"\n", b"\r"), - "LF": lambda c: c.replace(b"\r", b"\n"), + "CRLF": lambda c: c.replace("\n", "\r\n"), + "CR": lambda c: c.replace("\n", "\r"), + "LF": lambda c: c.replace("\r", "\n"), }[eol] # internal state @@ -408,9 +414,9 @@ class Monitor(object): self._last_line_part = sp.pop() for line in sp: if line != b"": - if self._serial_check_exit and line == self.exit_key: + if self._serial_check_exit and line == self.exit_key.encode('latin-1'): raise SerialStopException() - if self._output_enabled and (self._force_line_print or self._line_matcher.match(line)): + if self._output_enabled and (self._force_line_print or self._line_matcher.match(line.decode(errors="ignore"))): self.console.write_bytes(line + b'\n') self.handle_possible_pc_address_in_line(line) self.check_gdbstub_trigger(line) @@ -420,7 +426,7 @@ class Monitor(object): # of the line. But after some time when we didn't received it we need # to make a decision. if self._last_line_part != b"": - if self._force_line_print or (finalize_line and self._line_matcher.match(self._last_line_part)): + if self._force_line_print or (finalize_line and self._line_matcher.match(self._last_line_part.decode(errors="ignore"))): self._force_line_print = True; if self._output_enabled: self.console.write_bytes(self._last_line_part) @@ -442,7 +448,7 @@ class Monitor(object): def handle_possible_pc_address_in_line(self, line): line = self._pc_address_buffer + line self._pc_address_buffer = b"" - for m in re.finditer(MATCH_PCADDR, line): + for m in re.finditer(MATCH_PCADDR, line.decode(errors="ignore")): self.lookup_pc_address(m.group()) def handle_menu_key(self, c): @@ -553,8 +559,8 @@ class Monitor(object): ["%saddr2line" % self.toolchain_prefix, "-pfiaC", "-e", self.elf_file, pc_addr], cwd=".") - if not "?? ??:0" in translation: - yellow_print(translation) + if not b"?? ??:0" in translation: + yellow_print(translation.decode()) def check_gdbstub_trigger(self, line): line = self._gdb_buffer + line @@ -562,7 +568,7 @@ class Monitor(object): m = re.search(b"\\$(T..)#(..)", line) # look for a gdb "reason" for a break if m is not None: try: - chsum = sum(ord(p) for p in m.group(1)) & 0xFF + chsum = sum(ord(bytes([p])) for p in m.group(1)) & 0xFF calc_chsum = int(m.group(2), 16) except ValueError: return # payload wasn't valid hex digits @@ -714,14 +720,18 @@ if os.name == 'nt': least-bad working solution, as winpty doesn't support any "passthrough" mode for raw output. """ - def __init__(self, output): + def __init__(self, output=None, decode_output=False): self.output = output + self.decode_output = decode_output self.handle = GetStdHandle(STD_ERROR_HANDLE if self.output == sys.stderr else STD_OUTPUT_HANDLE) self.matched = b'' def _output_write(self, data): try: - self.output.write(data) + if self.decode_output: + self.output.write(data.decode()) + else: + self.output.write(data) except IOError: # Windows 10 bug since the Fall Creators Update, sometimes writing to console randomly throws # an exception (however, the character is still written to the screen) @@ -729,13 +739,18 @@ if os.name == 'nt': pass def write(self, data): + if type(data) is not bytes: + data = data.encode('latin-1') for b in data: + b = bytes([b]) l = len(self.matched) - if b == '\033': # ESC + if b == b'\033': # ESC self.matched = b - elif (l == 1 and b == '[') or (1 < l < 7): + elif (l == 1 and b == b'[') or (1 < l < 7): self.matched += b - if self.matched == ANSI_NORMAL: # reset console + if self.matched == ANSI_NORMAL.encode('latin-1'): # reset console + # Flush is required only with Python3 - switching color before it is printed would mess up the console + self.flush() SetConsoleTextAttribute(self.handle, FOREGROUND_GREY) self.matched = b'' elif len(self.matched) == 7: # could be an ANSI sequence @@ -744,6 +759,8 @@ if os.name == 'nt': color = ANSI_TO_WINDOWS_COLOR[int(m.group(2))] if m.group(1) == b'1': color |= FOREGROUND_INTENSITY + # Flush is required only with Python3 - switching color before it is printed would mess up the console + self.flush() SetConsoleTextAttribute(self.handle, color) else: self._output_write(self.matched) # not an ANSI color code, display verbatim @@ -755,6 +772,5 @@ if os.name == 'nt': def flush(self): self.output.flush() - if __name__ == "__main__": main() diff --git a/tools/test_idf_monitor/run_test_idf_monitor.py b/tools/test_idf_monitor/run_test_idf_monitor.py index 2b3f41480..da59bea86 100755 --- a/tools/test_idf_monitor/run_test_idf_monitor.py +++ b/tools/test_idf_monitor/run_test_idf_monitor.py @@ -14,6 +14,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import print_function +from __future__ import unicode_literals +from builtins import object +from io import open import os import signal import time @@ -34,11 +38,11 @@ test_list = ( in_dir = 'tests/' # tests are in this directory out_dir = 'outputs/' # test results are written to this directory (kept only for debugging purposes) socat_in = './socatfile'# temporary socat file (deleted after run) -monitor_error_output = out_dir + 'monitor_error_output' +err_out = out_dir + 'monitor_error_output' elf_file = './dummy.elf' # ELF file used for starting the monitor idf_monitor = '{}/tools/idf_monitor.py'.format(os.getenv("IDF_PATH")) -class SocatRunner: +class SocatRunner(object): """ Runs socat in the background for creating a socket. """ @@ -54,7 +58,7 @@ class SocatRunner: '-U', # unidirectional pipe from file to port 'TCP4-LISTEN:2399,reuseaddr,fork', 'exec:"tail -c 1GB -F ' + socat_in + '"'] - print ' '.join(socat_cmd) + print(' '.join(socat_cmd)) self._socat_process = subprocess.Popen(socat_cmd, preexec_fn=os.setsid) # See __exit__ for os.setsid return self @@ -82,38 +86,38 @@ def main(): # another reason while the temporary socat_in file is used instead of directly reading the test files). time.sleep(1) for t in test_list: - print 'Running test on {} with filter "{}" and expecting {}'.format(t[0], t[1], t[2]) - with open(in_dir + t[0], "r") as input_file, open(socat_in, "w") as socat_file: - print 'cat {} > {}'.format(input_file.name, socat_file.name) - for line in input_file: - socat_file.write(line) + print('Running test on {} with filter "{}" and expecting {}'.format(t[0], t[1], t[2])) + with open(in_dir + t[0], "r", encoding='utf-8') as i_f, open(socat_in, "w", encoding='utf-8') as s_f: + print('cat {} > {}'.format(i_f.name, s_f.name)) + for line in i_f: + s_f.write(line) idf_exit_sequence = b'\x1d\n' - print 'echo "" >> {}'.format(socat_file.name) - socat_file.write(idf_exit_sequence) + print('echo "" >> {}'.format(s_f.name)) + s_f.write(idf_exit_sequence.decode()) monitor_cmd = [idf_monitor, '--port', 'socket://localhost:2399', '--print_filter', t[1], elf_file] - with open(out_dir + t[2], "w") as mon_out_f, open(monitor_error_output, "w") as mon_err_f: + with open(out_dir + t[2], "w", encoding='utf-8') as o_f, open(err_out, "w", encoding='utf-8') as e_f: try: (master_fd, slave_fd) = pty.openpty() - print ' '.join(monitor_cmd), - print ' > {} 2> {} < {}'.format(mon_out_f.name, mon_err_f.name, os.ttyname(slave_fd)) - proc = subprocess.Popen(monitor_cmd, stdin=slave_fd, stdout=mon_out_f, stderr=mon_err_f, + print(' '.join(monitor_cmd), end=' ') + print(' > {} 2> {} < {}'.format(o_f.name, e_f.name, os.ttyname(slave_fd))) + proc = subprocess.Popen(monitor_cmd, stdin=slave_fd, stdout=o_f, stderr=e_f, close_fds=True) proc.wait() finally: os.close(slave_fd) os.close(master_fd) diff_cmd = ['diff', in_dir + t[2], out_dir + t[2]] - print ' '.join(diff_cmd) + print(' '.join(diff_cmd)) subprocess.check_call(diff_cmd) - print 'Test has passed' + print('Test has passed') finally: cleanup() end = time.time() - print 'Execution took {:.2f} seconds'.format(end - start) + print('Execution took {:.2f} seconds'.format(end - start)) if __name__ == "__main__": main() diff --git a/tools/tiny-test-fw/DUT.py b/tools/tiny-test-fw/DUT.py index 091a96154..97487525e 100644 --- a/tools/tiny-test-fw/DUT.py +++ b/tools/tiny-test-fw/DUT.py @@ -576,7 +576,10 @@ class BaseDUT(object): data = self.data_cache.get_data(time_remaining) if match_succeed: - # do callback and flush matched data cache + # sort matched items according to order of appearance in the input data, + # so that the callbacks are invoked in correct order + matched_expect_items = sorted(matched_expect_items, key=lambda it: it["index"]) + # invoke callbacks and flush matched data cache slice_index = -1 for expect_item in matched_expect_items: # trigger callback diff --git a/tools/tiny-test-fw/IDF/IDFApp.py b/tools/tiny-test-fw/IDF/IDFApp.py index 4bf667f64..b173aaf80 100644 --- a/tools/tiny-test-fw/IDF/IDFApp.py +++ b/tools/tiny-test-fw/IDF/IDFApp.py @@ -32,7 +32,12 @@ class IDFApp(App.BaseApp): self.idf_path = self.get_sdk_path() self.binary_path = self.get_binary_path(app_path) assert os.path.exists(self.binary_path) - assert self.IDF_DOWNLOAD_CONFIG_FILE in os.listdir(self.binary_path) + try: + assert self.IDF_DOWNLOAD_CONFIG_FILE in os.listdir(self.binary_path) + except AssertionError as e: + e.args += ("{} doesn't exist. Try to run 'make print_flash_cmd | tail -n 1 > {}/{}' for resolving the issue" + "".format(self.IDF_DOWNLOAD_CONFIG_FILE, self.binary_path, self.IDF_DOWNLOAD_CONFIG_FILE),) + raise self.esptool, self.partition_tool = self.get_tools() @classmethod diff --git a/tools/unit-test-app/configs/app_update b/tools/unit-test-app/configs/app_update new file mode 100644 index 000000000..4069be24d --- /dev/null +++ b/tools/unit-test-app/configs/app_update @@ -0,0 +1,13 @@ +TEST_COMPONENTS=app_update +TEST_EXCLUDE_COMPONENTS=libsodium bt +CONFIG_UNITY_FREERTOS_STACK_SIZE=12288 +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partition_table_unit_test_two_ota.csv" +CONFIG_PARTITION_TABLE_FILENAME="partition_table_unit_test_two_ota.csv" +CONFIG_PARTITION_TABLE_OFFSET=0x18000 +CONFIG_BOOTLOADER_FACTORY_RESET=y +CONFIG_BOOTLOADER_APP_TEST=y +CONFIG_BOOTLOADER_HOLD_TIME_GPIO=2 +CONFIG_BOOTLOADER_OTA_DATA_ERASE=y +CONFIG_BOOTLOADER_NUM_PIN_FACTORY_RESET=4 +CONFIG_BOOTLOADER_NUM_PIN_APP_TEST=32 \ No newline at end of file diff --git a/tools/unit-test-app/configs/bt b/tools/unit-test-app/configs/bt index cc83ba16d..9bb486471 100644 --- a/tools/unit-test-app/configs/bt +++ b/tools/unit-test-app/configs/bt @@ -1,3 +1,4 @@ TEST_COMPONENTS=bt +TEST_EXCLUDE_COMPONENTS=app_update CONFIG_BT_ENABLED=y -CONFIG_UNITY_FREERTOS_STACK_SIZE=12288 +CONFIG_UNITY_FREERTOS_STACK_SIZE=12288 \ No newline at end of file diff --git a/tools/unit-test-app/configs/default b/tools/unit-test-app/configs/default index b83aec589..f7f508a43 100644 --- a/tools/unit-test-app/configs/default +++ b/tools/unit-test-app/configs/default @@ -1 +1 @@ -EXCLUDE_COMPONENTS=libsodium bt +TEST_EXCLUDE_COMPONENTS=libsodium bt app_update \ No newline at end of file diff --git a/tools/unit-test-app/configs/libsodium b/tools/unit-test-app/configs/libsodium index 7828480c9..af090a53c 100644 --- a/tools/unit-test-app/configs/libsodium +++ b/tools/unit-test-app/configs/libsodium @@ -1,3 +1,3 @@ TEST_COMPONENTS=libsodium -EXCLUDE_COMPONENTS=bt -CONFIG_UNITY_FREERTOS_STACK_SIZE=12288 +TEST_EXCLUDE_COMPONENTS=bt app_update +CONFIG_UNITY_FREERTOS_STACK_SIZE=12288 \ No newline at end of file diff --git a/tools/unit-test-app/configs/psram b/tools/unit-test-app/configs/psram index dc74b5a2d..4575118a4 100644 --- a/tools/unit-test-app/configs/psram +++ b/tools/unit-test-app/configs/psram @@ -1,2 +1,2 @@ -EXCLUDE_COMPONENTS=libsodium bt -CONFIG_SPIRAM_SUPPORT=y +TEST_EXCLUDE_COMPONENTS=libsodium bt app_update +CONFIG_SPIRAM_SUPPORT=y \ No newline at end of file diff --git a/tools/unit-test-app/configs/release b/tools/unit-test-app/configs/release index 370039d16..86fbc9a49 100644 --- a/tools/unit-test-app/configs/release +++ b/tools/unit-test-app/configs/release @@ -1,3 +1,3 @@ -EXCLUDE_COMPONENTS=bt +TEST_EXCLUDE_COMPONENTS=bt app_update CONFIG_OPTIMIZATION_LEVEL_RELEASE=y -CONFIG_OPTIMIZATION_ASSERTIONS_SILENT=y +CONFIG_OPTIMIZATION_ASSERTIONS_SILENT=y \ No newline at end of file diff --git a/tools/unit-test-app/configs/single_core b/tools/unit-test-app/configs/single_core index 29d0350f5..21d11cbad 100644 --- a/tools/unit-test-app/configs/single_core +++ b/tools/unit-test-app/configs/single_core @@ -1,3 +1,3 @@ -EXCLUDE_COMPONENTS=libsodium bt +TEST_EXCLUDE_COMPONENTS=libsodium bt app_update CONFIG_MEMMAP_SMP=n -CONFIG_FREERTOS_UNICORE=y +CONFIG_FREERTOS_UNICORE=y \ No newline at end of file diff --git a/tools/unit-test-app/partition_table_unit_test_two_ota.csv b/tools/unit-test-app/partition_table_unit_test_two_ota.csv new file mode 100644 index 000000000..7c698c17b --- /dev/null +++ b/tools/unit-test-app/partition_table_unit_test_two_ota.csv @@ -0,0 +1,11 @@ +# Special partition table for unit test app_update +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, , 0x4000 +otadata, data, ota, , 0x2000 +phy_init, data, phy, , 0x1000 +factory, 0, 0, , 0xB0000 +ota_0, 0, ota_0, , 0xB0000 +ota_1, 0, ota_1, , 0xB0000 +test, 0, test, , 0xB0000 +# flash_test partition used for SPI flash tests, WL FAT tests, and SPIFFS tests +flash_test, data, fat, , 528K \ No newline at end of file diff --git a/tools/unit-test-app/sdkconfig.defaults b/tools/unit-test-app/sdkconfig.defaults index db4404ed4..badf01bb3 100644 --- a/tools/unit-test-app/sdkconfig.defaults +++ b/tools/unit-test-app/sdkconfig.defaults @@ -4,8 +4,8 @@ CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y CONFIG_ESPTOOLPY_FLASHSIZE_DETECT=n 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="partition_table_unit_test_app.csv" +CONFIG_PARTITION_TABLE_OFFSET=0x8000 CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y CONFIG_ESP32_XTAL_FREQ_AUTO=y CONFIG_FREERTOS_HZ=1000 diff --git a/tools/unit-test-app/unit_test.py b/tools/unit-test-app/unit_test.py index c1e6f96d1..7d142ba28 100755 --- a/tools/unit-test-app/unit_test.py +++ b/tools/unit-test-app/unit_test.py @@ -43,7 +43,7 @@ from IDF.IDFApp import UT UT_APP_BOOT_UP_DONE = "Press ENTER to see the list of tests." RESET_PATTERN = re.compile(r"(ets [\w]{3}\s+[\d]{1,2} [\d]{4} [\d]{2}:[\d]{2}:[\d]{2}[^()]*\([\w].*?\))") EXCEPTION_PATTERN = re.compile(r"(Guru Meditation Error: Core\s+\d panic'ed \([\w].*?\))") -ABORT_PATTERN = re.compile(r"(abort\(\) was called at PC 0x[a-eA-E\d]{8} on core \d)") +ABORT_PATTERN = re.compile(r"(abort\(\) was called at PC 0x[a-fA-F\d]{8} on core \d)") FINISH_PATTERN = re.compile(r"1 Tests (\d) Failures (\d) Ignored") END_LIST_STR = r'\r?\nEnter test for running' TEST_PATTERN = re.compile(r'\((\d+)\)\s+"([^"]+)" ([^\r]+)\r?\n(' + END_LIST_STR + r')?') @@ -254,6 +254,7 @@ def run_unit_test_cases(env, extra_data): raise AssertionError("Unit Test Failed") + class Handler(threading.Thread): WAIT_SIGNAL_PATTERN = re.compile(r'Waiting for signal: \[(.+)\]!') @@ -271,6 +272,7 @@ class Handler(threading.Thread): self.result = False self.fail_name = None self.timeout = timeout + self.force_stop = threading.Event() # it show the running status threading.Thread.__init__(self, name="{} Handler".format(dut)) def run(self): @@ -289,7 +291,7 @@ class Handler(threading.Thread): def device_wait_action(data): start_time = time.time() expected_signal = data[0] - while not THREAD_TERMINATE_FLAG: + while 1: if time.time() > start_time + self.timeout: Utility.console_log("Timeout in device for function: %s"%self.child_case_name, color="orange") break @@ -321,9 +323,7 @@ class Handler(threading.Thread): self.dut.expect("Running " + self.parent_case_name + "...") except ExpectTimeout: Utility.console_log("No case detected!", color="orange") - THREAD_TERMINATE_FLAG = True - - while not self.finish and not THREAD_TERMINATE_FLAG: + while not self.finish and not self.force_stop.isSet(): try: self.dut.expect_any((re.compile('\(' + str(self.child_case_index) + '\)\s"(\w+)"'), get_child_case_name), (self.WAIT_SIGNAL_PATTERN, device_wait_action), # wait signal pattern @@ -335,6 +335,9 @@ class Handler(threading.Thread): one_device_case_finish(False) break + def stop(self): + self.force_stop.set() + def get_case_info(one_case): parent_case = one_case["name"] @@ -360,9 +363,6 @@ def case_run(duts, ut_config, env, one_case, failed_cases, app_bin): result = True parent_case, case_num = get_case_info(one_case) - global THREAD_TERMINATE_FLAG - THREAD_TERMINATE_FLAG = False - for i in range(case_num): dut = get_dut(duts, env, "dut%d" % i, ut_config, app_bin) threads.append(Handler(dut, send_signal_list, lock, @@ -374,7 +374,7 @@ def case_run(duts, ut_config, env, one_case, failed_cases, app_bin): thread.join() result = result and thread.result if not thread.result: - THREAD_TERMINATE_FLAG = True + [thd.stop() for thd in threads] if result: Utility.console_log("Success: " + one_case["name"], color="green") diff --git a/tools/windows/windows_install_prerequisites.sh b/tools/windows/windows_install_prerequisites.sh index 0960f4a17..a08137197 100644 --- a/tools/windows/windows_install_prerequisites.sh +++ b/tools/windows/windows_install_prerequisites.sh @@ -41,8 +41,6 @@ rm -f /mingw32/bin/envsubst.exe python -m pip install --upgrade pip -pip install pyserial - # Automatically download precompiled toolchain, unpack at /opt/xtensa-esp32-elf/ TOOLCHAIN_ZIP=xtensa-esp32-elf-win32-1.22.0-80-g6c4433a-5.2.0.zip echo "Downloading precompiled toolchain ${TOOLCHAIN_ZIP}..."