diff --git a/components/app_update/include/esp_ota_ops.h b/components/app_update/include/esp_ota_ops.h index 1a92da1f1e46..8ae4cdd3150f 100644 --- a/components/app_update/include/esp_ota_ops.h +++ b/components/app_update/include/esp_ota_ops.h @@ -113,10 +113,6 @@ esp_err_t esp_ota_begin(const esp_partition_t* partition, size_t image_size, esp * Unlike esp_ota_begin(), this function does not erase the partition which receives the OTA update, but rather expects that part of the image * has already been written correctly, and it resumes writing from the given offset. * - * @note When flash encryption is enabled, data writes must be 16-byte aligned. - * Any leftover (non-aligned) data is temporarily cached and may be lost after reboot. - * Therefore, during resumption, ensure that image offset is always 16-byte aligned. - * * @param partition Pointer to info for the partition which is receiving the OTA update. Required. * @param erase_size Specifies how much flash memory to erase before resuming OTA, depending on whether a sequential write or a bulk erase is being used. * @param image_offset Offset from where to resume the OTA process. Should be set to the number of bytes already written. diff --git a/components/bootloader_support/src/esp_image_format.c b/components/bootloader_support/src/esp_image_format.c index 90aa13b491db..27d02f9736d3 100644 --- a/components/bootloader_support/src/esp_image_format.c +++ b/components/bootloader_support/src/esp_image_format.c @@ -24,6 +24,7 @@ #include "hal/cache_ll.h" #include "spi_flash_mmap.h" #include "hal/efuse_hal.h" +#include "sdkconfig.h" #define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) @@ -141,6 +142,22 @@ static bool is_bootloader(uint32_t offset) ); } +#if BOOTLOADER_BUILD && (SECURE_BOOT_CHECK_SIGNATURE == 1) +#if CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP +static bool skip_verify(esp_image_load_mode_t mode, bool verify_sha) +{ + // Multi level check to ensure that its a legit exit from deep sleep case + return (esp_rom_get_reset_reason(0) == RESET_REASON_CORE_DEEP_SLEEP && + mode == ESP_IMAGE_LOAD_NO_VALIDATE && + !verify_sha) ? true : false; +} +#else + +#define skip_verify(mode, verify_sha) (false) + +#endif +#endif // BOOTLOADER_BUILD && (SECURE_BOOT_CHECK_SIGNATURE == 1) + static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_t *part, esp_image_metadata_t *data) { #ifdef BOOTLOADER_BUILD @@ -236,9 +253,9 @@ static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_ "only verify signature in bootloader" into the macro so it's tested multiple times. */ #if CONFIG_SECURE_BOOT_V2_ENABLED - ESP_FAULT_ASSERT(!esp_secure_boot_enabled() || memcmp(image_digest, verified_digest, ESP_SECURE_BOOT_DIGEST_LEN) == 0); + ESP_FAULT_ASSERT(!esp_secure_boot_enabled() || skip_verify(mode, verify_sha) || memcmp(image_digest, verified_digest, ESP_SECURE_BOOT_DIGEST_LEN) == 0); #else // Secure Boot V1 on ESP32, only verify signatures for apps not bootloaders - ESP_FAULT_ASSERT(is_bootloader(data->start_addr) || memcmp(image_digest, verified_digest, HASH_LEN) == 0); + ESP_FAULT_ASSERT(is_bootloader(data->start_addr) || skip_verify(mode, verify_sha) || memcmp(image_digest, verified_digest, HASH_LEN) == 0); #endif #endif // SECURE_BOOT_CHECK_SIGNATURE diff --git a/components/bt/common/ble_log/Kconfig.in b/components/bt/common/ble_log/Kconfig.in index 2bb05ede5af5..edf459311de9 100644 --- a/components/bt/common/ble_log/Kconfig.in +++ b/components/bt/common/ble_log/Kconfig.in @@ -91,6 +91,12 @@ if BLE_LOG_ENABLED checksum of frame head and payload all together by default, or only calculate the checksum of frame head to minimize performance decrease + config BLE_LOG_XOR_CHECKSUM_ENABLED + bool "Enable XOR checksum for BLE Log payload integrity check" + default y + help + XOR checksum is introduced for integrity check performance optimization. + config BLE_LOG_ENH_STAT_ENABLED bool "Enable enhanced statistics for BLE Log" default n diff --git a/components/bt/common/ble_log/src/ble_log_lbm.c b/components/bt/common/ble_log/src/ble_log_lbm.c index 5078f2bad7dc..0cc996c7213f 100644 --- a/components/bt/common/ble_log/src/ble_log_lbm.c +++ b/components/bt/common/ble_log/src/ble_log_lbm.c @@ -198,6 +198,7 @@ bool ble_log_lbm_init(void) /* Initialize lock types for spin pool */ for (int i = 0; i < BLE_LOG_LBM_SPIN_MAX; i++) { lbm_ctx->spin_pool[i].lock_type = BLE_LOG_LBM_LOCK_SPIN; + portMUX_INITIALIZE(&(lbm_ctx->spin_pool[i].spin_lock)); } #if CONFIG_BLE_LOG_LL_ENABLED @@ -421,6 +422,7 @@ void ble_log_flush(void) BLE_LOG_REF_COUNT_RELEASE(&lbm_ref_count); } +BLE_LOG_IRAM_ATTR bool ble_log_write_hex(ble_log_src_t src_code, const uint8_t *addr, size_t len) { BLE_LOG_REF_COUNT_ACQUIRE(&lbm_ref_count); diff --git a/components/bt/common/ble_log/src/ble_log_rt.c b/components/bt/common/ble_log/src/ble_log_rt.c index 382438c0cd89..7268797e3611 100644 --- a/components/bt/common/ble_log/src/ble_log_rt.c +++ b/components/bt/common/ble_log/src/ble_log_rt.c @@ -60,7 +60,9 @@ BLE_LOG_IRAM_ATTR BLE_LOG_STATIC void ble_log_rt_task(void *pvParameters) if (rt_ts_enabled) { ble_log_ts_info_t *ts_info = NULL; ble_log_ts_info_update(&ts_info); - ble_log_write_hex(BLE_LOG_SRC_INTERNAL, (const uint8_t *)ts_info, sizeof(ble_log_ts_info_t)); + if (ts_info) { + ble_log_write_hex(BLE_LOG_SRC_INTERNAL, (const uint8_t *)ts_info, sizeof(ble_log_ts_info_t)); + } } #endif /* CONFIG_BLE_LOG_TS_ENABLED */ @@ -139,6 +141,7 @@ bool ble_log_sync_enable(bool enable) return false; } rt_ts_enabled = enable; + ble_log_ts_reset(enable); return true; } #endif /* CONFIG_BLE_LOG_TS_ENABLED */ diff --git a/components/bt/common/ble_log/src/ble_log_ts.c b/components/bt/common/ble_log/src/ble_log_ts.c index 38ad21c21089..e01611da10d9 100644 --- a/components/bt/common/ble_log/src/ble_log_ts.c +++ b/components/bt/common/ble_log/src/ble_log_ts.c @@ -63,6 +63,10 @@ void ble_log_ts_deinit(void) void ble_log_ts_info_update(ble_log_ts_info_t **info) { + if (!ts_inited) { + return; + } + BLE_LOG_ENTER_CRITICAL(); ts_info->io_level = !ts_info->io_level; gpio_set_level(CONFIG_BLE_LOG_SYNC_IO_NUM, ts_info->io_level); @@ -73,3 +77,16 @@ void ble_log_ts_info_update(ble_log_ts_info_t **info) *info = ts_info; } + +void ble_log_ts_reset(bool status) +{ + if (!ts_inited) { + return; + } + + if (!status && !ts_info->io_level) { + gpio_set_level(CONFIG_BLE_LOG_SYNC_IO_NUM, 1); + } + ts_info->io_level = 0; + gpio_set_level(CONFIG_BLE_LOG_SYNC_IO_NUM, 0); +} diff --git a/components/bt/common/ble_log/src/ble_log_util.c b/components/bt/common/ble_log/src/ble_log_util.c index 7657ad92ddcc..c084d6d249ca 100644 --- a/components/bt/common/ble_log/src/ble_log_util.c +++ b/components/bt/common/ble_log/src/ble_log_util.c @@ -17,7 +17,60 @@ portMUX_TYPE ble_log_spin_lock = portMUX_INITIALIZER_UNLOCKED; #endif /* !UNIT_TEST */ /* INTERNAL INTERFACE */ -BLE_LOG_IRAM_ATTR uint32_t ble_log_fast_checksum(const uint8_t *data, size_t len) +#if CONFIG_BLE_LOG_XOR_CHECKSUM_ENABLED +#include "esp_compiler.h" + +static inline uint32_t ror32(uint32_t word, uint32_t shift) +{ + if (unlikely(shift == 0)) { + return word; + } + return (word >> shift) | (word << ((32 - shift) & 0x1F)); +} + +__attribute__((optimize("-O3"))) +BLE_LOG_IRAM_ATTR +uint32_t ble_log_fast_checksum(const uint8_t *data, size_t len) +{ + /* Validate input length */ + if (unlikely(len == 0)) { + return 0; + } + + /* Step 1: Force 32-bit align read and calculate offset */ + const uint32_t start_offset_shift = ((uintptr_t)data & 0x3) << 3; + const uint32_t *p_aligned = (const uint32_t *)((uintptr_t)data & ~0x3); + const uint32_t *p_end_aligned = (const uint32_t *)((uintptr_t)(data + len) & ~0x3); + + /* Step 2: Handle first word with mask (Little endian) */ + uint32_t checksum = (*p_aligned) & (0xFFFFFFFFU << start_offset_shift); + + /* Step 3: Check if first word is last word */ + const uint32_t end_offset_shift = ((uintptr_t)(data + len) & 0x3) << 3; + const uint32_t end_offset_shift_mask = ~(0xFFFFFFFFU << end_offset_shift); + if (unlikely(p_aligned == p_end_aligned)) { + if (end_offset_shift) { + checksum &= end_offset_shift_mask; + } + } else { + /* Step 4: Handle word in the middle */ + p_aligned++; + while (p_aligned < p_end_aligned) { + checksum ^= *p_aligned++; + } + + /* Step 5: Handle last word with mask (Little endian) */ + if (end_offset_shift) { + checksum ^= (*p_aligned) & end_offset_shift_mask; + } + } + + /* Step 6: Rotate the final result */ + return ror32(checksum, start_offset_shift); +} +#else /* !CONFIG_BLE_LOG_XOR_CHECKSUM_ENABLED */ +BLE_LOG_IRAM_ATTR +uint32_t ble_log_fast_checksum(const uint8_t *data, size_t len) { uint32_t sum = 0; size_t i = 0; @@ -46,3 +99,4 @@ BLE_LOG_IRAM_ATTR uint32_t ble_log_fast_checksum(const uint8_t *data, size_t len return sum; } +#endif /* CONFIG_BLE_LOG_XOR_CHECKSUM_ENABLED */ diff --git a/components/bt/common/ble_log/src/internal_include/ble_log_ts.h b/components/bt/common/ble_log/src/internal_include/ble_log_ts.h index 11e74e1508e5..6a09cd70f1b2 100644 --- a/components/bt/common/ble_log/src/internal_include/ble_log_ts.h +++ b/components/bt/common/ble_log/src/internal_include/ble_log_ts.h @@ -29,10 +29,10 @@ extern uint32_t r_ble_lll_timer_current_tick_get(void); #elif defined(CONFIG_IDF_TARGET_ESP32C2) extern uint32_t r_os_cputime_get32(void); #define BLE_LOG_GET_LC_TS r_os_cputime_get32() -/* Legacy BLE Controller (Wait for support) */ -// #elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3) -// extern uint32_t lld_read_clock_us(void); -// #define BLE_LOG_GET_LC_TS lld_read_clock_us() +/* Legacy BLE Controller */ +#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3) +extern uint32_t lld_read_clock_us(void); +#define BLE_LOG_GET_LC_TS lld_read_clock_us() #else /* Other targets */ #define BLE_LOG_GET_LC_TS 0 #endif /* BLE targets */ @@ -53,5 +53,6 @@ typedef struct { bool ble_log_ts_init(void); void ble_log_ts_deinit(void); void ble_log_ts_info_update(ble_log_ts_info_t **ts_info); +void ble_log_ts_reset(bool status); #endif /* __BLE_LOG_TS_H__ */ diff --git a/components/bt/common/ble_log/src/internal_include/ble_log_util.h b/components/bt/common/ble_log/src/internal_include/ble_log_util.h index d0e7c460f0c7..352ae80cbd51 100644 --- a/components/bt/common/ble_log/src/internal_include/ble_log_util.h +++ b/components/bt/common/ble_log/src/internal_include/ble_log_util.h @@ -130,7 +130,7 @@ bool ble_log_cas_acquire(volatile bool *cas_lock); void ble_log_cas_release(volatile bool *cas_lock); #endif /* UNIT_TEST */ -#define BLE_LOG_VERSION (2) +#define BLE_LOG_VERSION (3) /* TYPEDEF */ typedef enum { diff --git a/components/bt/controller/esp32c5/Kconfig.in b/components/bt/controller/esp32c5/Kconfig.in index c85bd425c8d8..cfcef62e17e7 100644 --- a/components/bt/controller/esp32c5/Kconfig.in +++ b/components/bt/controller/esp32c5/Kconfig.in @@ -191,14 +191,14 @@ config BT_LE_MAX_PERIODIC_ADVERTISER_LIST config BT_LE_POWER_CONTROL_ENABLED bool "Enable controller support for BLE Power Control" - depends on BT_LE_50_FEATURE_SUPPORT && !BT_NIMBLE_ENABLED && IDF_TARGET_ESP32C6 + depends on BT_LE_50_FEATURE_SUPPORT && !BT_NIMBLE_ENABLED default n help Set this option to enable the Power Control feature on controller config BT_LE_PERIODIC_ADV_WITH_RESPONSE_ENABLED bool "Enable BLE periodic advertising with response" - depends on BT_LE_50_FEATURE_SUPPORT + depends on BT_LE_50_FEATURE_SUPPORT && SOC_BLE_PERIODIC_ADV_WITH_RESPONSE default n help This enables BLE periodic advertising with response feature @@ -943,3 +943,11 @@ menu "Scheduling Priority Level Config" default 2 if BT_LE_SYNC_SCHED_PRIO_HIGH_LEVEL default 1 endmenu + +config BT_LE_CTRL_SLV_FAST_RX_CONN_DATA_EN + bool "Enable Peripheral fast PDU reception during latency" + default n + help + When this option is enabled, the Controller continues receiving PDUs + In the next connection event instead of entering latency + After a data packet is received. diff --git a/components/bt/controller/esp32c5/bt.c b/components/bt/controller/esp32c5/bt.c index d543b571e0e2..7453a6917fd7 100644 --- a/components/bt/controller/esp32c5/bt.c +++ b/components/bt/controller/esp32c5/bt.c @@ -155,7 +155,7 @@ extern void esp_unregister_npl_funcs (void); extern void npl_freertos_mempool_deinit(void); extern uint32_t r_os_cputime_get32(void); extern uint32_t r_os_cputime_ticks_to_usecs(uint32_t ticks); -extern void r_ble_lll_rfmgmt_set_sleep_cb(void *s_cb, void *w_cb, void *s_arg, +extern void r_ble_lll_sleep_set_sleep_cb(void *s_cb, void *w_cb, void *s_arg, void *w_arg, uint32_t us_to_enabled); extern void r_ble_rtc_wake_up_state_clr(void); extern int os_msys_init(void); @@ -788,10 +788,10 @@ esp_err_t controller_sleep_init(void) #ifdef CONFIG_BT_LE_SLEEP_ENABLE ESP_LOGW(NIMBLE_PORT_LOG_TAG, "BLE modem sleep is enabled"); #if CONFIG_FREERTOS_USE_TICKLESS_IDLE - r_ble_lll_rfmgmt_set_sleep_cb(controller_sleep_cb, controller_wakeup_cb, 0, 0, + r_ble_lll_sleep_set_sleep_cb(controller_sleep_cb, controller_wakeup_cb, 0, 0, BLE_RTC_DELAY_US_LIGHT_SLEEP); #else - r_ble_lll_rfmgmt_set_sleep_cb(controller_sleep_cb, controller_wakeup_cb, 0, 0, + r_ble_lll_sleep_set_sleep_cb(controller_sleep_cb, controller_wakeup_cb, 0, 0, BLE_RTC_DELAY_US_MODEM_SLEEP); #endif /* FREERTOS_USE_TICKLESS_IDLE */ #endif // CONFIG_BT_LE_SLEEP_ENABLE diff --git a/components/bt/controller/esp32c5/esp_bt_cfg.h b/components/bt/controller/esp32c5/esp_bt_cfg.h index e79eb742e2dd..4dbe2f2fe89b 100644 --- a/components/bt/controller/esp32c5/esp_bt_cfg.h +++ b/components/bt/controller/esp32c5/esp_bt_cfg.h @@ -209,6 +209,12 @@ extern "C" { #define DEFAULT_BT_LE_CTRL_FAST_CONN_DATA_TX_EN (0) #endif +#if defined(CONFIG_BT_LE_CTRL_SLV_FAST_RX_CONN_DATA_EN) +#define DEFAULT_BT_LE_CTRL_SLV_FAST_RX_CONN_DATA_EN (CONFIG_BT_LE_CTRL_SLV_FAST_RX_CONN_DATA_EN) +#else +#define DEFAULT_BT_LE_CTRL_SLV_FAST_RX_CONN_DATA_EN (0) +#endif + #ifdef CONFIG_BT_LE_HCI_INTERFACE_USE_UART #define HCI_UART_EN CONFIG_BT_LE_HCI_INTERFACE_USE_UART #else diff --git a/components/bt/controller/esp32c6/Kconfig.in b/components/bt/controller/esp32c6/Kconfig.in index 93ba37845426..bffab12e6690 100644 --- a/components/bt/controller/esp32c6/Kconfig.in +++ b/components/bt/controller/esp32c6/Kconfig.in @@ -222,7 +222,7 @@ config BT_LE_MAX_PERIODIC_ADVERTISER_LIST config BT_LE_POWER_CONTROL_ENABLED bool "Enable controller support for BLE Power Control" - depends on BT_LE_50_FEATURE_SUPPORT && !BT_NIMBLE_ENABLED && IDF_TARGET_ESP32C6 + depends on BT_LE_50_FEATURE_SUPPORT && !BT_NIMBLE_ENABLED default n help Set this option to enable the Power Control feature on controller @@ -239,7 +239,7 @@ config BT_LE_CTE_FEATURE_ENABLED config BT_LE_PERIODIC_ADV_WITH_RESPONSE_ENABLED bool "Enable BLE periodic advertising with response" - depends on BT_LE_50_FEATURE_SUPPORT + depends on BT_LE_50_FEATURE_SUPPORT && SOC_BLE_PERIODIC_ADV_WITH_RESPONSE default n help This enables BLE periodic advertising with response feature @@ -977,3 +977,11 @@ menu "Scheduling Priority Level Config" default 2 if BT_LE_SYNC_SCHED_PRIO_HIGH_LEVEL default 1 endmenu + +config BT_LE_CTRL_SLV_FAST_RX_CONN_DATA_EN + bool "Enable Peripheral fast PDU reception during latency" + default n + help + When this option is enabled, the Controller continues receiving PDUs + In the next connection event instead of entering latency + After a data packet is received. diff --git a/components/bt/controller/esp32c6/bt.c b/components/bt/controller/esp32c6/bt.c index 0b7825de344a..a442f8d85c83 100644 --- a/components/bt/controller/esp32c6/bt.c +++ b/components/bt/controller/esp32c6/bt.c @@ -167,7 +167,7 @@ extern void esp_unregister_npl_funcs (void); extern void npl_freertos_mempool_deinit(void); extern uint32_t r_os_cputime_get32(void); extern uint32_t r_os_cputime_ticks_to_usecs(uint32_t ticks); -extern void r_ble_lll_rfmgmt_set_sleep_cb(void *s_cb, void *w_cb, void *s_arg, +extern void r_ble_lll_sleep_set_sleep_cb(void *s_cb, void *w_cb, void *s_arg, void *w_arg, uint32_t us_to_enabled); extern void r_ble_rtc_wake_up_state_clr(void); extern int os_msys_init(void); @@ -848,10 +848,10 @@ esp_err_t controller_sleep_init(void) #ifdef CONFIG_BT_LE_SLEEP_ENABLE ESP_LOGW(NIMBLE_PORT_LOG_TAG, "BLE modem sleep is enabled"); #if CONFIG_FREERTOS_USE_TICKLESS_IDLE - r_ble_lll_rfmgmt_set_sleep_cb(controller_sleep_cb, controller_wakeup_cb, 0, 0, + r_ble_lll_sleep_set_sleep_cb(controller_sleep_cb, controller_wakeup_cb, 0, 0, BLE_RTC_DELAY_US_LIGHT_SLEEP); #else - r_ble_lll_rfmgmt_set_sleep_cb(controller_sleep_cb, controller_wakeup_cb, 0, 0, + r_ble_lll_sleep_set_sleep_cb(controller_sleep_cb, controller_wakeup_cb, 0, 0, BLE_RTC_DELAY_US_MODEM_SLEEP); #endif /* FREERTOS_USE_TICKLESS_IDLE */ #endif // CONFIG_BT_LE_SLEEP_ENABLE diff --git a/components/bt/controller/esp32c6/esp_bt_cfg.h b/components/bt/controller/esp32c6/esp_bt_cfg.h index 2586976c90d5..71f7f9d19404 100644 --- a/components/bt/controller/esp32c6/esp_bt_cfg.h +++ b/components/bt/controller/esp32c6/esp_bt_cfg.h @@ -212,6 +212,12 @@ extern "C" { #define DEFAULT_BT_LE_CTRL_FAST_CONN_DATA_TX_EN (0) #endif +#if defined(CONFIG_BT_LE_CTRL_SLV_FAST_RX_CONN_DATA_EN) +#define DEFAULT_BT_LE_CTRL_SLV_FAST_RX_CONN_DATA_EN (CONFIG_BT_LE_CTRL_SLV_FAST_RX_CONN_DATA_EN) +#else +#define DEFAULT_BT_LE_CTRL_SLV_FAST_RX_CONN_DATA_EN (0) +#endif + #ifdef CONFIG_BT_LE_HCI_INTERFACE_USE_UART #define HCI_UART_EN CONFIG_BT_LE_HCI_INTERFACE_USE_UART #else diff --git a/components/bt/controller/esp32h2/Kconfig.in b/components/bt/controller/esp32h2/Kconfig.in index 17a1d9abf2ce..c52de7cf2be1 100644 --- a/components/bt/controller/esp32h2/Kconfig.in +++ b/components/bt/controller/esp32h2/Kconfig.in @@ -242,7 +242,7 @@ config BT_LE_CTE_FEATURE_ENABLED config BT_LE_PERIODIC_ADV_WITH_RESPONSE_ENABLED bool "Enable BLE periodic advertising with response" - depends on BT_LE_50_FEATURE_SUPPORT + depends on BT_LE_50_FEATURE_SUPPORT && SOC_BLE_PERIODIC_ADV_WITH_RESPONSE default n help This enables BLE periodic advertising with response feature @@ -981,3 +981,11 @@ menu "Scheduling Priority Level Config" default 2 if BT_LE_SYNC_SCHED_PRIO_HIGH_LEVEL default 1 endmenu + +config BT_LE_CTRL_SLV_FAST_RX_CONN_DATA_EN + bool "Enable Peripheral fast PDU reception during latency" + default n + help + When this option is enabled, the Controller continues receiving PDUs + In the next connection event instead of entering latency + After a data packet is received. diff --git a/components/bt/controller/esp32h2/bt.c b/components/bt/controller/esp32h2/bt.c index 4daa9d3fbeba..6ea4185a2712 100644 --- a/components/bt/controller/esp32h2/bt.c +++ b/components/bt/controller/esp32h2/bt.c @@ -159,7 +159,7 @@ extern void esp_unregister_npl_funcs (void); extern void npl_freertos_mempool_deinit(void); extern uint32_t r_os_cputime_get32(void); extern uint32_t r_os_cputime_ticks_to_usecs(uint32_t ticks); -extern void r_ble_lll_rfmgmt_set_sleep_cb(void *s_cb, void *w_cb, void *s_arg, +extern void r_ble_lll_sleep_set_sleep_cb(void *s_cb, void *w_cb, void *s_arg, void *w_arg, uint32_t us_to_enabled); extern void r_ble_rtc_wake_up_state_clr(void); extern int os_msys_init(void); @@ -816,10 +816,10 @@ esp_err_t controller_sleep_init(void) #ifdef CONFIG_BT_LE_SLEEP_ENABLE ESP_LOGW(NIMBLE_PORT_LOG_TAG, "BLE modem sleep is enabled"); #if CONFIG_FREERTOS_USE_TICKLESS_IDLE - r_ble_lll_rfmgmt_set_sleep_cb(controller_sleep_cb, controller_wakeup_cb, 0, 0, + r_ble_lll_sleep_set_sleep_cb(controller_sleep_cb, controller_wakeup_cb, 0, 0, BLE_RTC_DELAY_US_LIGHT_SLEEP); #else - r_ble_lll_rfmgmt_set_sleep_cb(controller_sleep_cb, controller_wakeup_cb, 0, 0, + r_ble_lll_sleep_set_sleep_cb(controller_sleep_cb, controller_wakeup_cb, 0, 0, BLE_RTC_DELAY_US_MODEM_SLEEP); #endif /* FREERTOS_USE_TICKLESS_IDLE */ #endif // CONFIG_BT_LE_SLEEP_ENABLE diff --git a/components/bt/controller/esp32h2/esp_bt_cfg.h b/components/bt/controller/esp32h2/esp_bt_cfg.h index e79eb742e2dd..4dbe2f2fe89b 100644 --- a/components/bt/controller/esp32h2/esp_bt_cfg.h +++ b/components/bt/controller/esp32h2/esp_bt_cfg.h @@ -209,6 +209,12 @@ extern "C" { #define DEFAULT_BT_LE_CTRL_FAST_CONN_DATA_TX_EN (0) #endif +#if defined(CONFIG_BT_LE_CTRL_SLV_FAST_RX_CONN_DATA_EN) +#define DEFAULT_BT_LE_CTRL_SLV_FAST_RX_CONN_DATA_EN (CONFIG_BT_LE_CTRL_SLV_FAST_RX_CONN_DATA_EN) +#else +#define DEFAULT_BT_LE_CTRL_SLV_FAST_RX_CONN_DATA_EN (0) +#endif + #ifdef CONFIG_BT_LE_HCI_INTERFACE_USE_UART #define HCI_UART_EN CONFIG_BT_LE_HCI_INTERFACE_USE_UART #else diff --git a/components/bt/controller/lib_esp32c2/esp32c2-bt-lib b/components/bt/controller/lib_esp32c2/esp32c2-bt-lib index c82c623de457..7c104a8d09e3 160000 --- a/components/bt/controller/lib_esp32c2/esp32c2-bt-lib +++ b/components/bt/controller/lib_esp32c2/esp32c2-bt-lib @@ -1 +1 @@ -Subproject commit c82c623de457e1b06cf0dad5c963d023dbb6fe76 +Subproject commit 7c104a8d09e35f3244636fcf01004d5c767f12c2 diff --git a/components/bt/controller/lib_esp32c5/esp32c5-bt-lib b/components/bt/controller/lib_esp32c5/esp32c5-bt-lib index e97692ec3090..a5feba34e144 160000 --- a/components/bt/controller/lib_esp32c5/esp32c5-bt-lib +++ b/components/bt/controller/lib_esp32c5/esp32c5-bt-lib @@ -1 +1 @@ -Subproject commit e97692ec3090b03cba9e49a75d248e2dbbfda25c +Subproject commit a5feba34e144c82374fb9818108d6efc5216dc1f diff --git a/components/bt/controller/lib_esp32c6/esp32c6-bt-lib b/components/bt/controller/lib_esp32c6/esp32c6-bt-lib index d2dffe055672..cf7c287226c2 160000 --- a/components/bt/controller/lib_esp32c6/esp32c6-bt-lib +++ b/components/bt/controller/lib_esp32c6/esp32c6-bt-lib @@ -1 +1 @@ -Subproject commit d2dffe055672f10ff5fbeb359c533d9561c33ea4 +Subproject commit cf7c287226c2c8575dc1cb9896d54a14a18d1dd4 diff --git a/components/bt/controller/lib_esp32h2/esp32h2-bt-lib b/components/bt/controller/lib_esp32h2/esp32h2-bt-lib index efca94610a5e..a568fa5a0354 160000 --- a/components/bt/controller/lib_esp32h2/esp32h2-bt-lib +++ b/components/bt/controller/lib_esp32h2/esp32h2-bt-lib @@ -1 +1 @@ -Subproject commit efca94610a5e62b8d18abf8a5f6a07b6b23b72ca +Subproject commit a568fa5a03545dd133a301ccdbae99afa3692923 diff --git a/components/bt/host/bluedroid/Kconfig.in b/components/bt/host/bluedroid/Kconfig.in index 1d4b24b195b4..41807eedebf2 100644 --- a/components/bt/host/bluedroid/Kconfig.in +++ b/components/bt/host/bluedroid/Kconfig.in @@ -114,7 +114,9 @@ menu "AVRCP Features" default y select BT_GOEPC_ENABLED help - This enable Cover Art feature of AVRCP CT role + This enable Cover Art feature for AVRCP CT role, Cover Art requires AVRCP + version 1.6 or higher. Therefore, if the Cover Art feature is enabled, + AVRCP version will set to 1.6, otherwise, it will remain at 1.5 endmenu diff --git a/components/bt/host/bluedroid/api/esp_a2dp_api.c b/components/bt/host/bluedroid/api/esp_a2dp_api.c index 254c43d5a380..e6761835d8bc 100644 --- a/components/bt/host/bluedroid/api/esp_a2dp_api.c +++ b/components/bt/host/bluedroid/api/esp_a2dp_api.c @@ -356,7 +356,7 @@ esp_err_t esp_a2d_source_register_stream_endpoint(uint8_t seid, const esp_a2d_mc return ESP_ERR_INVALID_STATE; } - if (g_a2dp_on_deinit || g_a2dp_sink_ongoing_deinit) { + if (g_a2dp_on_deinit || g_a2dp_source_ongoing_deinit) { return ESP_ERR_INVALID_STATE; } diff --git a/components/bt/host/bluedroid/api/esp_spp_api.c b/components/bt/host/bluedroid/api/esp_spp_api.c index 5c01aeb2e511..18ef661c165c 100644 --- a/components/bt/host/bluedroid/api/esp_spp_api.c +++ b/components/bt/host/bluedroid/api/esp_spp_api.c @@ -145,22 +145,34 @@ esp_err_t esp_spp_disconnect(uint32_t handle) esp_err_t esp_spp_start_srv(esp_spp_sec_t sec_mask, esp_spp_role_t role, uint8_t local_scn, const char *name) +{ + esp_spp_start_srv_cfg_t cfg = {0}; + + cfg.local_scn = local_scn; + cfg.sec_mask = sec_mask; + cfg.role = role; + cfg.create_spp_record = true; + cfg.name = name; + return esp_spp_start_srv_with_cfg(&cfg); +} + +esp_err_t esp_spp_start_srv_with_cfg(const esp_spp_start_srv_cfg_t *cfg) { btc_msg_t msg; btc_spp_args_t arg; ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); - if (name == NULL || strlen(name) > ESP_SPP_SERVER_NAME_MAX) { + if (cfg == NULL || cfg->name == NULL || strlen(cfg->name) > ESP_SPP_SERVER_NAME_MAX) { LOG_ERROR("Invalid server name!\n"); return ESP_ERR_INVALID_ARG; } - if (sec_mask != ESP_SPP_SEC_NONE && - sec_mask != ESP_SPP_SEC_AUTHENTICATE && - sec_mask != (ESP_SPP_SEC_AUTHENTICATE | ESP_SPP_SEC_ENCRYPT) && - sec_mask != ESP_SPP_SEC_IN_16_DIGITS && - sec_mask != (ESP_SPP_SEC_IN_16_DIGITS | ESP_SPP_SEC_AUTHENTICATE) && - sec_mask != (ESP_SPP_SEC_IN_16_DIGITS | ESP_SPP_SEC_AUTHENTICATE | ESP_SPP_SEC_ENCRYPT)) { + if (cfg->sec_mask != ESP_SPP_SEC_NONE && + cfg->sec_mask != ESP_SPP_SEC_AUTHENTICATE && + cfg->sec_mask != (ESP_SPP_SEC_AUTHENTICATE | ESP_SPP_SEC_ENCRYPT) && + cfg->sec_mask != ESP_SPP_SEC_IN_16_DIGITS && + cfg->sec_mask != (ESP_SPP_SEC_IN_16_DIGITS | ESP_SPP_SEC_AUTHENTICATE) && + cfg->sec_mask != (ESP_SPP_SEC_IN_16_DIGITS | ESP_SPP_SEC_AUTHENTICATE | ESP_SPP_SEC_ENCRYPT)) { LOG_WARN("Suggest to use ESP_SPP_SEC_NONE, ESP_SPP_SEC_AUTHENTICATE," "(ESP_SPP_SEC_AUTHENTICATE | ESP_SPP_SEC_ENCRYPT)," "ESP_SPP_SEC_IN_16_DIGITS, (ESP_SPP_SEC_IN_16_DIGITS | ESP_SPP_SEC_AUTHENTICATE), or" @@ -171,11 +183,12 @@ esp_err_t esp_spp_start_srv(esp_spp_sec_t sec_mask, msg.pid = BTC_PID_SPP; msg.act = BTC_SPP_ACT_START_SRV; - arg.start_srv.sec_mask = sec_mask; - arg.start_srv.role = role; - arg.start_srv.local_scn = local_scn; + arg.start_srv.sec_mask = cfg->sec_mask; + arg.start_srv.role = cfg->role; + arg.start_srv.local_scn = cfg->local_scn; + arg.start_srv.create_spp_record = cfg->create_spp_record; arg.start_srv.max_session = ESP_SPP_MAX_SESSION; - strcpy(arg.start_srv.name, name); + strcpy(arg.start_srv.name, cfg->name); return (btc_transfer_context(&msg, &arg, sizeof(btc_spp_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } diff --git a/components/bt/host/bluedroid/api/include/api/esp_spp_api.h b/components/bt/host/bluedroid/api/include/api/esp_spp_api.h index 40788667c027..e6cdf2fc6928 100644 --- a/components/bt/host/bluedroid/api/include/api/esp_spp_api.h +++ b/components/bt/host/bluedroid/api/include/api/esp_spp_api.h @@ -74,7 +74,7 @@ typedef enum { } esp_spp_mode_t; /** - * @brief SPP configuration parameters + * @brief SPP initialization configuration parameters. */ typedef struct { esp_spp_mode_t mode; /*!< Choose the mode of SPP, ESP_SPP_MODE_CB or ESP_SPP_MODE_VFS. */ @@ -82,6 +82,17 @@ typedef struct { uint16_t tx_buffer_size; /*!< Tx buffer size for a new SPP channel. A smaller setting can save memory, but may incur a decrease in throughput. Only for ESP_SPP_MODE_VFS mode. */ } esp_spp_cfg_t; +/** + * @brief SPP start server configuration parameters. + */ +typedef struct { + uint8_t local_scn; /*!< The specific channel you want to get. If channel is 0, means get any channel. */ + bool create_spp_record; /*!< Specifies whether to create the SPP record */ + esp_spp_sec_t sec_mask; /*!< Security Setting Mask. Suggest to use ESP_SPP_SEC_NONE, ESP_SPP_SEC_AUTHORIZE or ESP_SPP_SEC_AUTHENTICATE only */ + esp_spp_role_t role; /*!< Master or slave. */ + const char *name; /*!< Server's name. */ +} esp_spp_start_srv_cfg_t; + /** * @brief SPP callback function events */ @@ -368,6 +379,19 @@ esp_err_t esp_spp_disconnect(uint32_t handle); */ esp_err_t esp_spp_start_srv(esp_spp_sec_t sec_mask, esp_spp_role_t role, uint8_t local_scn, const char *name); +/** + * @brief This function is similar to `esp_spp_start_srv`. + * The only difference is that it adds a parameter to specify whether to create the SPP record. + * @note If the SPP record is not created, it is suggested to use it together with the SDP API. + * + * @param[in] cfg: Configuration parameters for starting the server. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_spp_start_srv_with_cfg(const esp_spp_start_srv_cfg_t *cfg); + /** * @brief This function stops all SPP servers. * The operation will close all active SPP connection first, then the callback function will be called diff --git a/components/bt/host/bluedroid/btc/profile/std/a2dp/btc_av.c b/components/bt/host/bluedroid/btc/profile/std/a2dp/btc_av.c index f13d12556642..1fd74b6adaf0 100644 --- a/components/bt/host/bluedroid/btc/profile/std/a2dp/btc_av.c +++ b/components/bt/host/bluedroid/btc/profile/std/a2dp/btc_av.c @@ -148,6 +148,7 @@ static BOOLEAN btc_av_state_opened_handler(btc_sm_event_t event, void *data); static BOOLEAN btc_av_state_started_handler(btc_sm_event_t event, void *data); static BOOLEAN btc_av_state_closing_handler(btc_sm_event_t event, void *data); static void clean_up(int service_id); +static BOOLEAN btc_a2d_deinit_if_ongoing(void); #if BTC_AV_SRC_INCLUDED static bt_status_t btc_a2d_src_init(void); @@ -332,11 +333,13 @@ static BOOLEAN btc_av_state_idle_handler(btc_sm_event_t event, void *p_data) switch (event) { case BTC_SM_ENTER_EVT: - /* clear the peer_bda */ - memset(&btc_av_cb.peer_bda, 0, sizeof(bt_bdaddr_t)); - btc_av_cb.flags = 0; - btc_av_cb.edr = 0; - btc_a2dp_on_idle(); + if (btc_a2d_deinit_if_ongoing() == FALSE) { + /* clear the peer_bda */ + memset(&btc_av_cb.peer_bda, 0, sizeof(bt_bdaddr_t)); + btc_av_cb.flags = 0; + btc_av_cb.edr = 0; + btc_a2dp_on_idle(); + } break; case BTC_SM_EXIT_EVT: @@ -511,22 +514,18 @@ static BOOLEAN btc_av_state_opening_handler(btc_sm_event_t event, void *p_data) /* change state to open/idle based on the status */ btc_sm_change_state(btc_av_cb.sm_handle, av_state); - if (btc_av_cb.peer_sep == AVDT_TSEP_SNK) { - /* if queued PLAY command, send it now */ - /* necessary to add this? - btc_rc_check_handle_pending_play(p_bta_data->open.bd_addr, - (p_bta_data->open.status == BTA_AV_SUCCESS)); - */ - } else if (btc_av_cb.peer_sep == AVDT_TSEP_SRC && - (p_bta_data->open.status == BTA_AV_SUCCESS)) { - /* Bring up AVRCP connection too if AVRC Initialized */ - if(g_av_with_rc) { - BTA_AvOpenRc(btc_av_cb.bta_handle); - } else { - BTC_TRACE_WARNING("AVRC not Init, not using it."); + if (p_bta_data->open.status == BTA_AV_SUCCESS && !btc_a2d_deinit_if_ongoing()) { + if (btc_av_cb.peer_sep == AVDT_TSEP_SRC) { + /* Bring up AVRCP connection too if AVRC Initialized */ + if(g_av_with_rc) { + BTA_AvOpenRc(btc_av_cb.bta_handle); + } else { + BTC_TRACE_WARNING("AVRC not Init, not using it."); + } } } btc_queue_advance(); + } break; case BTC_AV_CONFIG_EVT: { @@ -805,12 +804,6 @@ static BOOLEAN btc_av_state_opened_handler(btc_sm_event_t event, void *p_data) /* change state to idle, send acknowledgement if start is pending */ btc_sm_change_state(btc_av_cb.sm_handle, BTC_AV_STATE_IDLE); - - if (g_a2dp_source_ongoing_deinit) { - clean_up(BTA_A2DP_SOURCE_SERVICE_ID); - } else if (g_a2dp_sink_ongoing_deinit) { - clean_up(BTA_A2DP_SINK_SERVICE_ID); - } break; } @@ -1014,11 +1007,6 @@ static BOOLEAN btc_av_state_started_handler(btc_sm_event_t event, void *p_data) close->disc_rsn); btc_sm_change_state(btc_av_cb.sm_handle, BTC_AV_STATE_IDLE); - if (g_a2dp_source_ongoing_deinit) { - clean_up(BTA_A2DP_SOURCE_SERVICE_ID); - } else if (g_a2dp_sink_ongoing_deinit) { - clean_up(BTA_A2DP_SINK_SERVICE_ID); - } break; CHECK_RC_EVENT(event, p_data); @@ -1848,13 +1836,17 @@ static void btc_a2d_sink_get_delay_value(void) static void btc_a2d_sink_deinit(void) { + // Cleanup will only occur when the state is IDLE. + // If connected, it will first disconnect and then wait for the state to change to IDLE before performing cleanup. + // If in any other state, it will wait for the process to complete and then call btc_a2d_sink_deinit again. g_a2dp_sink_ongoing_deinit = true; if (btc_av_is_connected()) { BTA_AvClose(btc_av_cb.bta_handle); if (btc_av_cb.peer_sep == AVDT_TSEP_SRC && g_av_with_rc == true) { BTA_AvCloseRc(btc_av_cb.bta_handle); } - } else { + } else if (btc_sm_get_state(btc_av_cb.sm_handle) == BTC_AV_STATE_IDLE) { + /* Only clean up when idle */ clean_up(BTA_A2DP_SINK_SERVICE_ID); } } @@ -1881,13 +1873,16 @@ static bt_status_t btc_a2d_src_init(void) static void btc_a2d_src_deinit(void) { + // Cleanup will only occur when the state is IDLE. + // If connected, it will first disconnect and then wait for the state to change to IDLE before performing cleanup. + // If in any other state, it will wait for the process to complete and then call btc_a2d_src_deinit again. g_a2dp_source_ongoing_deinit = true; if (btc_av_is_connected()) { BTA_AvClose(btc_av_cb.bta_handle); if (btc_av_cb.peer_sep == AVDT_TSEP_SNK && g_av_with_rc == true) { BTA_AvCloseRc(btc_av_cb.bta_handle); } - } else { + } else if (btc_sm_get_state(btc_av_cb.sm_handle) == BTC_AV_STATE_IDLE) { clean_up(BTA_A2DP_SOURCE_SERVICE_ID); } } @@ -1932,6 +1927,23 @@ bt_status_t btc_a2d_src_audio_data_send(esp_a2d_conn_hdl_t conn_hdl, esp_a2d_aud #endif /* BTC_AV_SRC_INCLUDED */ +static BOOLEAN btc_a2d_deinit_if_ongoing(void) +{ +#if BTC_AV_SRC_INCLUDED + if (g_a2dp_source_ongoing_deinit) { + btc_a2d_src_deinit(); + return TRUE; + } +#endif +#if BTC_AV_SINK_INCLUDED + if (g_a2dp_sink_ongoing_deinit) { + btc_a2d_sink_deinit(); + return TRUE; + } +#endif + return FALSE; +} + uint16_t btc_a2d_conn_handle_get(void) { return btc_av_cb.bta_handle; diff --git a/components/bt/host/bluedroid/btc/profile/std/include/btc_spp.h b/components/bt/host/bluedroid/btc/profile/std/include/btc_spp.h index 5082a135e13c..fd349c7de68b 100644 --- a/components/bt/host/bluedroid/btc/profile/std/include/btc_spp.h +++ b/components/bt/host/bluedroid/btc/profile/std/include/btc_spp.h @@ -67,6 +67,7 @@ typedef union { esp_spp_sec_t sec_mask; esp_spp_role_t role; UINT8 local_scn; + bool create_spp_record; UINT8 max_session; char name[ESP_SPP_SERVER_NAME_MAX + 1]; } start_srv; diff --git a/components/bt/host/bluedroid/btc/profile/std/spp/btc_spp.c b/components/bt/host/bluedroid/btc/profile/std/spp/btc_spp.c index bb142531a3e9..8607e4219121 100644 --- a/components/bt/host/bluedroid/btc/profile/std/spp/btc_spp.c +++ b/components/bt/host/bluedroid/btc/profile/std/spp/btc_spp.c @@ -50,6 +50,7 @@ typedef struct { bool connected; bool is_server; bool is_writing; + bool create_spp_record; uint8_t serial; uint8_t scn; uint8_t max_session; @@ -143,6 +144,7 @@ static spp_slot_t *spp_malloc_slot(void) (*slot)->rfc_port_handle = 0; (*slot)->fd = -1; (*slot)->connected = false; + (*slot)->create_spp_record = false; (*slot)->is_server = false; (*slot)->mtu = 0; (*slot)->credit_rx = BTA_JV_MAX_CREDIT_NUM; @@ -378,6 +380,7 @@ static void *btc_spp_rfcomm_inter_cb(tBTA_JV_EVT event, tBTA_JV *p_data, void *u strcpy(slot_new->service_name, slot->service_name); slot_new->sdp_handle = slot->sdp_handle; slot_new->mtu = p_data->rfc_srv_open.peer_mtu; + slot_new->create_spp_record = slot->create_spp_record; slot_new->rfc_handle = p_data->rfc_srv_open.handle; slot_new->rfc_port_handle = BTA_JvRfcommGetPortHdl(slot_new->rfc_handle); BTA_JvSetPmProfile(p_data->rfc_srv_open.handle, BTA_JV_PM_ALL, BTA_JV_CONN_OPEN); @@ -482,7 +485,14 @@ static void btc_spp_dm_inter_cb(tBTA_JV_EVT event, tBTA_JV *p_data, void *user_d } slot->scn = p_data->scn; - BTA_JvCreateRecordByUser(slot->service_name, slot->scn, (void *)slot->id); + if (slot->create_spp_record) { + BTA_JvCreateRecordByUser(slot->service_name, slot->scn, (void *)slot->id); + } else { + slot->sdp_handle = 0xffff; + BTA_JvRfcommStartServer(slot->security, slot->role, slot->scn, + slot->max_session, (tBTA_JV_RFCOMM_CBACK *)btc_spp_rfcomm_inter_cb, (void *)slot->id); + } + osi_mutex_unlock(&spp_local_param.spp_slot_mutex); break; case BTA_JV_CREATE_RECORD_EVT: @@ -748,6 +758,7 @@ static void btc_spp_start_srv(btc_spp_args_t *arg) * make this slot become a listening slot */ slot->is_server = true; + slot->create_spp_record = arg->start_srv.create_spp_record; slot->security = arg->start_srv.sec_mask; slot->role = arg->start_srv.role; slot->scn = arg->start_srv.local_scn; @@ -832,7 +843,7 @@ static void btc_spp_stop_srv(btc_spp_args_t *arg) if (spp_local_param.spp_slots[i] != NULL && spp_local_param.spp_slots[i]->is_server && spp_local_param.spp_slots[i]->sdp_handle > 0 && spp_local_param.spp_slots[i]->scn == srv_scn_arr[j]) { - if (spp_local_param.spp_slots[i]->sdp_handle > 0) { + if (spp_local_param.spp_slots[i]->sdp_handle > 0 && spp_local_param.spp_slots[i]->create_spp_record) { BTA_JvDeleteRecord(spp_local_param.spp_slots[i]->sdp_handle); } diff --git a/components/bt/host/bluedroid/stack/avrc/avrc_sdp.c b/components/bt/host/bluedroid/stack/avrc/avrc_sdp.c index 0b624b3f0c7a..3d84d1f985d5 100644 --- a/components/bt/host/bluedroid/stack/avrc/avrc_sdp.c +++ b/components/bt/host/bluedroid/stack/avrc/avrc_sdp.c @@ -29,8 +29,14 @@ #if (defined(AVRC_INCLUDED) && AVRC_INCLUDED == TRUE) -#ifndef SDP_AVRCP_1_6 +#if AVRC_CA_INCLUDED == TRUE +/* If Cover Art feature is enabled, set AVRCP version to 1.6 */ #define SDP_AVRCP_1_6 TRUE +#define SDP_AVRCP_1_5 FALSE +#else +/* Otherwise, set to 1.5 */ +#define SDP_AVRCP_1_6 FALSE +#define SDP_AVRCP_1_5 TRUE #endif #ifndef SDP_AVCTP_1_4 @@ -52,7 +58,7 @@ const tSDP_PROTOCOL_ELEM avrc_proto_list [] = { #if SDP_AVCTP_1_4 == TRUE {UUID_PROTOCOL_AVCTP, 1, {AVCT_REV_1_4, 0} } #else -#if SDP_AVRCP_1_6 == TRUE +#if (SDP_AVRCP_1_5 == TRUE || SDP_AVRCP_1_6 == TRUE) {UUID_PROTOCOL_AVCTP, 1, {AVCT_REV_1_3, 0} } #else #if AVRC_METADATA_INCLUDED == TRUE @@ -64,7 +70,7 @@ const tSDP_PROTOCOL_ELEM avrc_proto_list [] = { #endif }; -#if SDP_AVRCP_1_6 == TRUE +#if (SDP_AVRCP_1_5 == TRUE || SDP_AVRCP_1_6 == TRUE) const tSDP_PROTO_LIST_ELEM avrc_add_proto_list [] = { { AVRC_NUM_PROTO_ELEMS, @@ -251,7 +257,7 @@ UINT16 AVRC_AddRecord(UINT16 service_uuid, char *p_service_name, char *p_provide /* add service class id list */ class_list[0] = service_uuid; -#if (SDP_AVCTP_1_4 == TRUE || SDP_AVRCP_1_6 == TRUE) +#if (SDP_AVCTP_1_4 == TRUE || SDP_AVRCP_1_5 == TRUE || SDP_AVRCP_1_6 == TRUE) if ( service_uuid == UUID_SERVCLASS_AV_REMOTE_CONTROL ) { class_list[1] = UUID_SERVCLASS_AV_REM_CTRL_CONTROL; count = 2; @@ -263,7 +269,7 @@ UINT16 AVRC_AddRecord(UINT16 service_uuid, char *p_service_name, char *p_provide result &= SDP_AddProtocolList(sdp_handle, AVRC_NUM_PROTO_ELEMS, (tSDP_PROTOCOL_ELEM *)avrc_proto_list); /* add profile descriptor list */ -#if SDP_AVRCP_1_6 == TRUE +#if (SDP_AVRCP_1_5 == TRUE || SDP_AVRCP_1_6 == TRUE) if (browsing_en) { add_additional_protocol_list = TRUE; } else if (service_uuid == UUID_SERVCLASS_AV_REM_CTRL_TARGET && @@ -277,7 +283,11 @@ UINT16 AVRC_AddRecord(UINT16 service_uuid, char *p_service_name, char *p_provide result &= SDP_AddAdditionProtoLists( sdp_handle, 1, (tSDP_PROTO_LIST_ELEM *)avrc_add_proto_list); } +#if SDP_AVRCP_1_5 == TRUE + result &= SDP_AddProfileDescriptorList(sdp_handle, UUID_SERVCLASS_AV_REMOTE_CONTROL, AVRC_REV_1_5); +#elif SDP_AVRCP_1_6 == TRUE result &= SDP_AddProfileDescriptorList(sdp_handle, UUID_SERVCLASS_AV_REMOTE_CONTROL, AVRC_REV_1_6); +#endif #else #if AVRC_METADATA_INCLUDED == TRUE result &= SDP_AddProfileDescriptorList(sdp_handle, UUID_SERVCLASS_AV_REMOTE_CONTROL, AVRC_REV_1_3); diff --git a/components/bt/host/bluedroid/stack/l2cap/l2c_fcr.c b/components/bt/host/bluedroid/stack/l2cap/l2c_fcr.c index 1da2459d6e61..4d3c26201312 100644 --- a/components/bt/host/bluedroid/stack/l2cap/l2c_fcr.c +++ b/components/bt/host/bluedroid/stack/l2cap/l2c_fcr.c @@ -352,6 +352,10 @@ BT_HDR *l2c_fcr_clone_buf (BT_HDR *p_buf, UINT16 new_offset, UINT16 no_of_bytes) buf_size += sizeof(uint32_t); #endif BT_HDR *p_buf2 = (BT_HDR *)osi_malloc(buf_size); + if (!p_buf2) { + L2CAP_TRACE_ERROR ("l2c_fcr_clone_buf() malloc failed"); + return NULL; + } p_buf2->offset = new_offset; p_buf2->len = no_of_bytes; @@ -989,7 +993,7 @@ static void process_s_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf, UINT16 ctrl_word) #endif if (ctrl_word & L2CAP_FCR_P_BIT) { - p_fcrb->rej_sent = FALSE; /* After checkpoint, we can send anoher REJ */ + p_fcrb->rej_sent = FALSE; /* After checkpoint, we can send another REJ */ p_fcrb->send_f_rsp = TRUE; /* Set a flag in case an I-frame is pending */ } @@ -1160,7 +1164,7 @@ static void process_i_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf, UINT16 ctrl_word, B return; } - /* Seq number is the next expected. Clear possible reject exception in case it occured */ + /* Seq number is the next expected. Clear possible reject exception in case it occurred */ p_fcrb->rej_sent = p_fcrb->srej_sent = FALSE; /* Adjust the next_seq, so that if the upper layer sends more data in the callback @@ -1839,7 +1843,7 @@ void l2c_fcr_adj_monitor_retran_timeout (tL2C_CCB *p_ccb) /* adjust our monitor/retran timeout */ if (p_ccb->out_cfg_fcr_present) { /* - ** if we requestd ERTM or accepted ERTM + ** if we requested ERTM or accepted ERTM ** We may accept ERTM even if we didn't request ERTM, in case of requesting STREAM */ if ((p_ccb->our_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) @@ -1860,7 +1864,7 @@ void l2c_fcr_adj_monitor_retran_timeout (tL2C_CCB *p_ccb) ** ** Function l2c_fcr_adj_our_rsp_options ** -** Description Overrides any neccesary FCR options passed in from +** Description Overrides any necessary FCR options passed in from ** L2CA_ConfigRsp based on our FCR options. ** Only makes adjustments if channel is in ERTM mode. ** @@ -1937,7 +1941,7 @@ BOOLEAN l2c_fcr_renegotiate_chan(tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg) /* Try another supported mode if available based on our last attempted channel */ switch (p_ccb->our_cfg.fcr.mode) { - /* Our Streaming mode request was unnacceptable; try ERTM or Basic */ + /* Our Streaming mode request was unacceptable; try ERTM or Basic */ case L2CAP_FCR_STREAM_MODE: /* Peer wants ERTM and we support it */ if ( (peer_mode == L2CAP_FCR_ERTM_MODE) && (p_ccb->ertm_info.allowed_modes & L2CAP_FCR_CHAN_OPT_ERTM) ) { diff --git a/components/bt/host/nimble/Kconfig.in b/components/bt/host/nimble/Kconfig.in index d816de184089..eaa112fce02f 100644 --- a/components/bt/host/nimble/Kconfig.in +++ b/components/bt/host/nimble/Kconfig.in @@ -593,7 +593,7 @@ menu "BLE 5.x Features" config BT_NIMBLE_PERIODIC_ADV_WITH_RESPONSES bool "Enable Periodic Advertisement with Response (EXPERIMENTAL)" - depends on BT_NIMBLE_ENABLE_PERIODIC_ADV + depends on BT_NIMBLE_ENABLE_PERIODIC_ADV && SOC_BLE_PERIODIC_ADV_WITH_RESPONSE default n help This enables controller PAwR (Periodic Advertisement with Response). diff --git a/components/bt/include/esp32c5/include/esp_bt.h b/components/bt/include/esp32c5/include/esp_bt.h index 7132b44c4b9e..b44c861e722b 100644 --- a/components/bt/include/esp32c5/include/esp_bt.h +++ b/components/bt/include/esp32c5/include/esp_bt.h @@ -159,7 +159,7 @@ esp_err_t esp_ble_tx_power_set_enhanced(esp_ble_enhanced_power_type_t power_type */ esp_power_level_t esp_ble_tx_power_get_enhanced(esp_ble_enhanced_power_type_t power_type, uint16_t handle); -#define CONFIG_VERSION 0x20251022 +#define CONFIG_VERSION 0x20251104 #define CONFIG_MAGIC 0x5A5AA5A5 /** @@ -235,6 +235,7 @@ typedef struct { uint8_t adv_rsv_cnt; /*!< BLE adv state machine reserve count number */ uint8_t conn_rsv_cnt; /*!< BLE conn state machine reserve count number */ uint8_t priority_level_cfg; /*!< The option for priority level configuration */ + uint8_t slv_fst_rx_lat_en; /*!< The option for enabling slave fast PDU reception during latency. */ uint32_t config_magic; /*!< Magic number for configuration validation */ } esp_bt_controller_config_t; @@ -297,6 +298,7 @@ typedef struct { .adv_rsv_cnt = BLE_LL_ADV_SM_RESERVE_CNT_N, \ .conn_rsv_cnt = BLE_LL_CONN_SM_RESERVE_CNT_N, \ .priority_level_cfg = BT_LL_CTRL_PRIO_LVL_CFG, \ + .slv_fst_rx_lat_en = DEFAULT_BT_LE_CTRL_SLV_FAST_RX_CONN_DATA_EN, \ .config_magic = CONFIG_MAGIC, \ } diff --git a/components/bt/include/esp32c6/include/esp_bt.h b/components/bt/include/esp32c6/include/esp_bt.h index 7181be6e43e4..3ccdeb7cb730 100644 --- a/components/bt/include/esp32c6/include/esp_bt.h +++ b/components/bt/include/esp32c6/include/esp_bt.h @@ -156,7 +156,7 @@ esp_err_t esp_ble_tx_power_set_enhanced(esp_ble_enhanced_power_type_t power_type */ esp_power_level_t esp_ble_tx_power_get_enhanced(esp_ble_enhanced_power_type_t power_type, uint16_t handle); -#define CONFIG_VERSION 0x20251022 +#define CONFIG_VERSION 0x20251104 #define CONFIG_MAGIC 0x5A5AA5A5 /** @@ -235,6 +235,7 @@ typedef struct { uint8_t adv_rsv_cnt; /*!< BLE adv state machine reserve count number */ uint8_t conn_rsv_cnt; /*!< BLE conn state machine reserve count number */ uint8_t priority_level_cfg; /*!< The option for priority level configuration */ + uint8_t slv_fst_rx_lat_en; /*!< The option for enabling slave fast PDU reception during latency. */ uint32_t config_magic; /*!< Magic number for configuration validation */ } esp_bt_controller_config_t; @@ -300,6 +301,7 @@ typedef struct { .adv_rsv_cnt = BLE_LL_ADV_SM_RESERVE_CNT_N, \ .conn_rsv_cnt = BLE_LL_CONN_SM_RESERVE_CNT_N, \ .priority_level_cfg = BT_LL_CTRL_PRIO_LVL_CFG, \ + .slv_fst_rx_lat_en = DEFAULT_BT_LE_CTRL_SLV_FAST_RX_CONN_DATA_EN, \ .config_magic = CONFIG_MAGIC, \ } #elif CONFIG_IDF_TARGET_ESP32C61 @@ -362,6 +364,7 @@ typedef struct { .adv_rsv_cnt = BLE_LL_ADV_SM_RESERVE_CNT_N, \ .conn_rsv_cnt = BLE_LL_CONN_SM_RESERVE_CNT_N, \ .priority_level_cfg = BT_LL_CTRL_PRIO_LVL_CFG, \ + .slv_fst_rx_lat_en = DEFAULT_BT_LE_CTRL_SLV_FAST_RX_CONN_DATA_EN, \ .config_magic = CONFIG_MAGIC, \ } #endif diff --git a/components/bt/include/esp32h2/include/esp_bt.h b/components/bt/include/esp32h2/include/esp_bt.h index b6bb5bfb23bd..a171b52a6b36 100644 --- a/components/bt/include/esp32h2/include/esp_bt.h +++ b/components/bt/include/esp32h2/include/esp_bt.h @@ -161,7 +161,7 @@ esp_err_t esp_ble_tx_power_set_enhanced(esp_ble_enhanced_power_type_t power_type */ esp_power_level_t esp_ble_tx_power_get_enhanced(esp_ble_enhanced_power_type_t power_type, uint16_t handle); -#define CONFIG_VERSION 0x20251022 +#define CONFIG_VERSION 0x20251104 #define CONFIG_MAGIC 0x5A5AA5A5 /** @@ -237,6 +237,7 @@ typedef struct { uint8_t adv_rsv_cnt; /*!< BLE adv state machine reserve count number */ uint8_t conn_rsv_cnt; /*!< BLE conn state machine reserve count number */ uint8_t priority_level_cfg; /*!< The option for priority level configuration */ + uint8_t slv_fst_rx_lat_en; /*!< The option for enabling slave fast PDU reception during latency. */ uint32_t config_magic; /*!< Configuration magic value */ } esp_bt_controller_config_t; @@ -300,6 +301,7 @@ typedef struct { .adv_rsv_cnt = BLE_LL_ADV_SM_RESERVE_CNT_N, \ .conn_rsv_cnt = BLE_LL_CONN_SM_RESERVE_CNT_N, \ .priority_level_cfg = BT_LL_CTRL_PRIO_LVL_CFG, \ + .slv_fst_rx_lat_en = DEFAULT_BT_LE_CTRL_SLV_FAST_RX_CONN_DATA_EN, \ .config_magic = CONFIG_MAGIC, \ } diff --git a/components/driver/test_apps/.build-test-rules.yml b/components/driver/test_apps/.build-test-rules.yml index c721e4a2b395..0a4a9f9ef56a 100644 --- a/components/driver/test_apps/.build-test-rules.yml +++ b/components/driver/test_apps/.build-test-rules.yml @@ -60,10 +60,6 @@ components/driver/test_apps/legacy_rmt_driver: components/driver/test_apps/legacy_rtc_temp_driver: disable: - if: SOC_TEMP_SENSOR_SUPPORTED != 1 - disable_test: - - if: IDF_TARGET == "esp32p4" - temporary: true - reason: p4 rev3 migration # TODO: IDF-14834 components/driver/test_apps/legacy_sigma_delta_driver: disable: diff --git a/components/driver/test_apps/legacy_rtc_temp_driver/pytest_legacy_temp_sensor_driver.py b/components/driver/test_apps/legacy_rtc_temp_driver/pytest_legacy_temp_sensor_driver.py index 3d27a10b4346..47646ffe489f 100644 --- a/components/driver/test_apps/legacy_rtc_temp_driver/pytest_legacy_temp_sensor_driver.py +++ b/components/driver/test_apps/legacy_rtc_temp_driver/pytest_legacy_temp_sensor_driver.py @@ -18,6 +18,5 @@ ['esp32s2', 'esp32c3', 'esp32s3', 'esp32c2', 'esp32c6', 'esp32h2', 'esp32p4', 'esp32c5', 'esp32c61'], indirect=['target'], ) -@pytest.mark.temp_skip_ci(targets=['esp32p4'], reason='p4 rev3 migration, IDF-14834') def test_legacy_temp_sensor_driver(dut: Dut) -> None: dut.run_all_single_board_cases(timeout=120) diff --git a/components/driver/test_apps/legacy_twai/pytest_twai.py b/components/driver/test_apps/legacy_twai/pytest_twai.py index dd3a5d24e80f..1c6086ceafe5 100644 --- a/components/driver/test_apps/legacy_twai/pytest_twai.py +++ b/components/driver/test_apps/legacy_twai/pytest_twai.py @@ -2,7 +2,7 @@ # SPDX-License-Identifier: CC0-1.0 import logging import subprocess -from time import sleep +import time import pytest from can import Bus @@ -10,6 +10,10 @@ from pytest_embedded import Dut from pytest_embedded_idf.utils import idf_parametrize +# --------------------------------------------------------------------------- +# Loop Back Tests +# --------------------------------------------------------------------------- + @pytest.mark.generic @pytest.mark.parametrize( @@ -24,16 +28,57 @@ def test_legacy_twai_self(dut: Dut) -> None: dut.run_all_single_board_cases(group='twai-loop-back') +# --------------------------------------------------------------------------- +# Helper Functions +# --------------------------------------------------------------------------- + + +def esp_enter_flash_mode(dut: Dut) -> None: + ser = dut.serial.proc + ser.setRTS(True) # EN Low + time.sleep(0.5) + ser.setDTR(True) # GPIO0 Low + ser.setRTS(False) # EN High + dut.expect('waiting for download', timeout=2) + ser.setDTR(False) # Back RTS/DTR to 1/1 to avoid affect to esptool + + +def esp_reset_and_wait_ready(dut: Dut) -> None: + dut.serial.hard_reset() + time.sleep(0.5) + dut.expect_exact('Press ENTER to see the list of tests') + + @pytest.fixture(name='socket_can') def fixture_create_socket_can() -> Bus: # Set up the socket CAN with the bitrate - start_command = 'sudo ip link set can0 up type can bitrate 250000 restart-ms 100' - stop_command = 'sudo ip link set can0 down' - subprocess.run(start_command, shell=True, capture_output=True, text=True) - bus = Bus(interface='socketcan', channel='can0', bitrate=250000) - yield bus # test invoked here - bus.shutdown() - subprocess.run(stop_command, shell=True, capture_output=True, text=True) + start_command = 'sudo -n ip link set can0 up type can bitrate 250000 restart-ms 100' + stop_command = 'sudo -n ip link set can0 down' + status_command = 'sudo -n ip -details link show can0' + + try: + result = subprocess.run(status_command, shell=True, capture_output=True, text=True) + if result.returncode != 0: + raise Exception('CAN interface "can0" not found') + + if 'UP' in result.stdout: # Close the bus anyway if it is already up + subprocess.run(stop_command, shell=True, capture_output=True, text=True) + subprocess.run(start_command, shell=True, capture_output=True, text=True) + + time.sleep(0.5) + bus = Bus(interface='socketcan', channel='can0', bitrate=250000) + yield bus # test invoked here + + bus.shutdown() + except Exception as e: + pytest.skip(f'Open usb-can bus Error: {str(e)}') + finally: + subprocess.run(stop_command, shell=True, capture_output=True, text=True) + + +# --------------------------------------------------------------------------- +# Interactive Tests +# --------------------------------------------------------------------------- @pytest.mark.twai_std @@ -46,14 +91,13 @@ def fixture_create_socket_can() -> Bus: ) @idf_parametrize('target', ['esp32', 'esp32c3', 'esp32c6', 'esp32h2', 'esp32s2', 'esp32s3'], indirect=['target']) def test_legacy_twai_listen_only(dut: Dut, socket_can: Bus) -> None: - dut.serial.hard_reset() - dut.expect_exact('Press ENTER to see the list of tests') + esp_reset_and_wait_ready(dut) # TEST_CASE("twai_listen_only", "[twai]") dut.write('"twai_listen_only"') - # wait the DUT to block at the receive API - sleep(0.03) + # wait the DUT to start listening + time.sleep(0.1) message = Message( arbitration_id=0x123, @@ -62,6 +106,7 @@ def test_legacy_twai_listen_only(dut: Dut, socket_can: Bus) -> None: ) socket_can.send(message, timeout=0.2) dut.expect_unity_test_output() + esp_enter_flash_mode(dut) @pytest.mark.twai_std @@ -74,8 +119,7 @@ def test_legacy_twai_listen_only(dut: Dut, socket_can: Bus) -> None: ) @idf_parametrize('target', ['esp32', 'esp32c3', 'esp32c6', 'esp32h2', 'esp32s2', 'esp32s3'], indirect=['target']) def test_legacy_twai_remote_request(dut: Dut, socket_can: Bus) -> None: - dut.serial.hard_reset() - dut.expect_exact('Press ENTER to see the list of tests') + esp_reset_and_wait_ready(dut) # TEST_CASE("twai_remote_request", "[twai]") dut.write('"twai_remote_request"') @@ -97,3 +141,4 @@ def test_legacy_twai_remote_request(dut: Dut, socket_can: Bus) -> None: print('send', reply) dut.expect_unity_test_output() + esp_enter_flash_mode(dut) diff --git a/components/efuse/esp32p4/esp_efuse_rtc_calib.c b/components/efuse/esp32p4/esp_efuse_rtc_calib.c index 2867168c8ac9..080278cb1bae 100644 --- a/components/efuse/esp32p4/esp_efuse_rtc_calib.c +++ b/components/efuse/esp32p4/esp_efuse_rtc_calib.c @@ -199,7 +199,6 @@ esp_err_t esp_efuse_rtc_calib_get_tsens_val(float* tsens_cal) const esp_efuse_desc_t** cal_temp_efuse; cal_temp_efuse = ESP_EFUSE_TEMPERATURE_SENSOR; int cal_temp_size = esp_efuse_get_field_size(cal_temp_efuse); - assert(cal_temp_size == 9); uint32_t cal_temp = 0; esp_err_t err = esp_efuse_read_field_blob(cal_temp_efuse, &cal_temp, cal_temp_size); diff --git a/components/esp_driver_bitscrambler/src/bitscrambler_loopback.c b/components/esp_driver_bitscrambler/src/bitscrambler_loopback.c index 1fe81e11cb6e..b69b8e840521 100644 --- a/components/esp_driver_bitscrambler/src/bitscrambler_loopback.c +++ b/components/esp_driver_bitscrambler/src/bitscrambler_loopback.c @@ -237,7 +237,7 @@ esp_err_t bitscrambler_loopback_run(bitscrambler_handle_t bs, void *buffer_in, s .length = length_bytes_in, .flags = { .mark_eof = true, - .mark_final = true, + .mark_final = GDMA_FINAL_LINK_TO_NULL, } }; gdma_link_mount_buffers(bsl->tx_link_list, 0, &in_buf_mount_config, 1, NULL); @@ -247,7 +247,7 @@ esp_err_t bitscrambler_loopback_run(bitscrambler_handle_t bs, void *buffer_in, s .length = length_bytes_out, .flags = { .mark_eof = false, - .mark_final = true, + .mark_final = GDMA_FINAL_LINK_TO_NULL, } }; gdma_link_mount_buffers(bsl->rx_link_list, 0, &out_buf_mount_config, 1, NULL); diff --git a/components/esp_driver_cam/dvp/src/esp_cam_ctlr_dvp_gdma.c b/components/esp_driver_cam/dvp/src/esp_cam_ctlr_dvp_gdma.c index 65b2794c336b..8c36e4742f0c 100644 --- a/components/esp_driver_cam/dvp/src/esp_cam_ctlr_dvp_gdma.c +++ b/components/esp_driver_cam/dvp/src/esp_cam_ctlr_dvp_gdma.c @@ -101,7 +101,7 @@ esp_err_t esp_cam_ctlr_dvp_dma_init(esp_cam_ctlr_dvp_dma_t *dma, uint32_t burst_ ESP_GOTO_ON_ERROR(gdma_apply_strategy(dma->dma_chan, &strategy_config), fail1, TAG, "apply strategy failed"); // set DMA transfer ability gdma_transfer_config_t transfer_config = { - .max_data_burst_size = burst_size, + .max_data_burst_size = burst_size ? burst_size : 32, // Enable DMA burst transfer for better performance .access_ext_mem = true, }; ESP_GOTO_ON_ERROR(gdma_config_transfer(dma->dma_chan, &transfer_config), fail1, TAG, "set trans ability failed"); diff --git a/components/esp_driver_i2c/i2c_master.c b/components/esp_driver_i2c/i2c_master.c index 162042c70123..918984d7ac66 100644 --- a/components/esp_driver_i2c/i2c_master.c +++ b/components/esp_driver_i2c/i2c_master.c @@ -5,6 +5,7 @@ */ #include +#include #include #include #include "esp_rom_sys.h" @@ -664,6 +665,16 @@ static void s_i2c_send_command_async(i2c_master_bus_handle_t i2c_master, BaseTyp i2c_hal_master_trans_start(hal); } +static inline bool s_i2c_timeout_range_check(uint32_t *timeout_us, uint32_t sclk_clock_hz) +{ + uint32_t max_timeout_us = (I2C_LL_MAX_TIMEOUT_PERIOD * 1000000ULL) / sclk_clock_hz; + if (*timeout_us > max_timeout_us) { + *timeout_us = max_timeout_us; + return true; + } + return false; +} + static esp_err_t s_i2c_transaction_start(i2c_master_dev_handle_t i2c_dev, int xfer_timeout_ms) { i2c_master_bus_handle_t i2c_master = i2c_dev->master_bus; @@ -692,6 +703,7 @@ static esp_err_t s_i2c_transaction_start(i2c_master_dev_handle_t i2c_dev, int xf } // Set the timeout value + bool timeout_range_exceeded = s_i2c_timeout_range_check(&i2c_dev->scl_wait_us, i2c_master->base->clk_src_freq_hz); i2c_hal_master_set_scl_timeout_val(hal, i2c_dev->scl_wait_us, i2c_master->base->clk_src_freq_hz); i2c_ll_master_set_fractional_divider(hal->dev, 0, 0); @@ -701,6 +713,11 @@ static esp_err_t s_i2c_transaction_start(i2c_master_dev_handle_t i2c_dev, int xf i2c_ll_rxfifo_rst(hal->dev); i2c_ll_enable_intr_mask(hal->dev, I2C_LL_MASTER_EVENT_INTR); portEXIT_CRITICAL(&i2c_master->base->spinlock); + + if (timeout_range_exceeded) { + ESP_LOGW(TAG, "Timeout value exceeds the maximum supported value, rounded down to maximum supported value: %" PRIu32 " us", i2c_dev->scl_wait_us); + } + if (i2c_master->async_trans == true) { s_i2c_send_command_async(i2c_master, NULL); } else { diff --git a/components/esp_driver_i2s/i2s_common.c b/components/esp_driver_i2s/i2s_common.c index 820e3d1065bd..b1da9d1cae95 100644 --- a/components/esp_driver_i2s/i2s_common.c +++ b/components/esp_driver_i2s/i2s_common.c @@ -371,6 +371,40 @@ static esp_err_t i2s_register_channel(i2s_controller_t *i2s_obj, i2s_dir_t dir, return ret; } +#if SOC_I2S_HW_VERSION_1 +esp_err_t i2s_channel_change_port(i2s_chan_handle_t handle, int id) +{ + I2S_NULL_POINTER_CHECK(TAG, handle); + ESP_RETURN_ON_FALSE(id >= 0 && id < SOC_I2S_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid I2S port id"); + if (id == handle->controller->id) { + return ESP_OK; + } + i2s_controller_t *i2s_obj = i2s_acquire_controller_obj(id); + if (!i2s_obj || !i2s_take_available_channel(i2s_obj, handle->dir)) { + return ESP_ERR_NOT_FOUND; + } + i2s_controller_t *old_i2s_obj = handle->controller; + portENTER_CRITICAL(&g_i2s.spinlock); + if (handle->dir == I2S_DIR_TX) { + i2s_obj->tx_chan = handle; + i2s_obj->chan_occupancy |= I2S_DIR_TX; + old_i2s_obj->tx_chan = NULL; + old_i2s_obj->full_duplex = false; + old_i2s_obj->chan_occupancy &= ~I2S_DIR_TX; + } else { + i2s_obj->rx_chan = handle; + i2s_obj->chan_occupancy |= I2S_DIR_RX; + old_i2s_obj->rx_chan = NULL; + old_i2s_obj->full_duplex = false; + old_i2s_obj->chan_occupancy &= ~I2S_DIR_RX; + } + handle->controller = i2s_obj; + portEXIT_CRITICAL(&g_i2s.spinlock); + + return ESP_OK; +} +#endif + #ifndef __cplusplus /* To make sure the i2s_event_callbacks_t is same size as i2s_event_callbacks_internal_t */ _Static_assert(sizeof(i2s_event_callbacks_t) == sizeof(i2s_event_callbacks_internal_t), "Invalid size of i2s_event_callbacks_t structure"); @@ -417,6 +451,9 @@ uint32_t i2s_get_buf_size(i2s_chan_handle_t handle, uint32_t data_bit_width, uin uint32_t bytes_per_sample = (data_bit_width + 7) / 8; #endif // CONFIG_IDF_TARGET_ESP32 uint32_t bytes_per_frame = bytes_per_sample * active_chan; + if (bytes_per_frame == 0) { + return 0; + } uint32_t bufsize = dma_frame_num * bytes_per_frame; #if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE /* bufsize need to align with cache line size */ @@ -998,6 +1035,7 @@ esp_err_t i2s_new_channel(const i2s_chan_config_t *chan_cfg, i2s_chan_handle_t * ESP_GOTO_ON_ERROR(i2s_register_channel(i2s_obj, I2S_DIR_TX, chan_cfg->dma_desc_num), err, TAG, "register I2S tx channel failed"); i2s_obj->tx_chan->role = chan_cfg->role; + i2s_obj->tx_chan->is_port_auto = id == I2S_NUM_AUTO; i2s_obj->tx_chan->intr_prio_flags = chan_cfg->intr_priority ? BIT(chan_cfg->intr_priority) : ESP_INTR_FLAG_LOWMED; i2s_obj->tx_chan->dma.auto_clear_after_cb = chan_cfg->auto_clear_after_cb; i2s_obj->tx_chan->dma.auto_clear_before_cb = chan_cfg->auto_clear_before_cb; @@ -1013,6 +1051,7 @@ esp_err_t i2s_new_channel(const i2s_chan_config_t *chan_cfg, i2s_chan_handle_t * ESP_GOTO_ON_ERROR(i2s_register_channel(i2s_obj, I2S_DIR_RX, chan_cfg->dma_desc_num), err, TAG, "register I2S rx channel failed"); i2s_obj->rx_chan->role = chan_cfg->role; + i2s_obj->rx_chan->is_port_auto = id == I2S_NUM_AUTO; i2s_obj->rx_chan->intr_prio_flags = chan_cfg->intr_priority ? BIT(chan_cfg->intr_priority) : ESP_INTR_FLAG_LOWMED; i2s_obj->rx_chan->dma.desc_num = chan_cfg->dma_desc_num; i2s_obj->rx_chan->dma.frame_num = chan_cfg->dma_frame_num; diff --git a/components/esp_driver_i2s/i2s_pdm.c b/components/esp_driver_i2s/i2s_pdm.c index 760c2838fcf9..280518418c19 100644 --- a/components/esp_driver_i2s/i2s_pdm.c +++ b/components/esp_driver_i2s/i2s_pdm.c @@ -95,11 +95,14 @@ static esp_err_t i2s_pdm_tx_set_clock(i2s_chan_handle_t handle, const i2s_pdm_tx #endif portEXIT_CRITICAL(&g_i2s.spinlock); + uint64_t tmp_div = (uint64_t)ret_mclk_div.integer * ret_mclk_div.denominator + ret_mclk_div.numerator; + ESP_RETURN_ON_FALSE(tmp_div != 0 && ret_mclk_div.denominator != 0, ESP_ERR_INVALID_ARG, TAG, "invalid mclk division result"); + /* Update the mode info: clock configuration */ memcpy(&(pdm_tx_cfg->clk_cfg), clk_cfg, sizeof(i2s_pdm_tx_clk_config_t)); handle->clk_src = clk_cfg->clk_src; handle->sclk_hz = clk_info.sclk; - handle->origin_mclk_hz = ((uint64_t)clk_info.sclk * ret_mclk_div.denominator) / (ret_mclk_div.integer * ret_mclk_div.denominator + ret_mclk_div.numerator); + handle->origin_mclk_hz = ((uint64_t)clk_info.sclk * ret_mclk_div.denominator) / tmp_div; handle->curr_mclk_hz = handle->origin_mclk_hz; ESP_LOGD(TAG, "Clock division info: [sclk] %"PRIu32" Hz [mdiv] %"PRIu32" %"PRIu32"/%"PRIu32" [mclk] %"PRIu32" Hz [bdiv] %d [bclk] %"PRIu32" Hz", @@ -119,6 +122,7 @@ static esp_err_t i2s_pdm_tx_set_slot(i2s_chan_handle_t handle, const i2s_pdm_tx_ handle->active_slot = slot_cfg->slot_mode == I2S_SLOT_MODE_MONO ? 1 : 2; uint32_t buf_size = i2s_get_buf_size(handle, slot_cfg->data_bit_width, handle->dma.frame_num); + ESP_RETURN_ON_FALSE(buf_size != 0, ESP_ERR_INVALID_ARG, TAG, "invalid data_bit_width"); /* The DMA buffer need to re-allocate if the buffer size changed */ if (handle->dma.buf_size != buf_size) { ESP_RETURN_ON_ERROR(i2s_free_dma_desc(handle), TAG, "failed to free the old dma descriptor"); @@ -422,11 +426,14 @@ static esp_err_t i2s_pdm_rx_set_clock(i2s_chan_handle_t handle, const i2s_pdm_rx } portEXIT_CRITICAL(&g_i2s.spinlock); + uint64_t tmp_div = (uint64_t)ret_mclk_div.integer * ret_mclk_div.denominator + ret_mclk_div.numerator; + ESP_RETURN_ON_FALSE(tmp_div != 0 && ret_mclk_div.denominator != 0, ESP_ERR_INVALID_ARG, TAG, "invalid mclk division result"); + /* Update the mode info: clock configuration */ memcpy(&(pdm_rx_cfg->clk_cfg), clk_cfg, sizeof(i2s_pdm_rx_clk_config_t)); handle->clk_src = clk_cfg->clk_src; handle->sclk_hz = clk_info.sclk; - handle->origin_mclk_hz = ((uint64_t)clk_info.sclk * ret_mclk_div.denominator) / (ret_mclk_div.integer * ret_mclk_div.denominator + ret_mclk_div.numerator); + handle->origin_mclk_hz = ((uint64_t)clk_info.sclk * ret_mclk_div.denominator) / tmp_div; handle->curr_mclk_hz = handle->origin_mclk_hz; ESP_LOGD(TAG, "Clock division info: [sclk] %"PRIu32" Hz [mdiv] %"PRIu32" %"PRIu32"/%"PRIu32" [mclk] %"PRIu32" Hz [bdiv] %d [bclk] %"PRIu32" Hz", clk_info.sclk, ret_mclk_div.integer, ret_mclk_div.numerator, ret_mclk_div.denominator, handle->origin_mclk_hz, clk_info.bclk_div, clk_info.bclk); @@ -445,6 +452,7 @@ static esp_err_t i2s_pdm_rx_set_slot(i2s_chan_handle_t handle, const i2s_pdm_rx_ handle->active_slot = slot_cfg->slot_mode == I2S_SLOT_MODE_MONO ? 1 : 2; uint32_t buf_size = i2s_get_buf_size(handle, slot_cfg->data_bit_width, handle->dma.frame_num); + ESP_RETURN_ON_FALSE(buf_size != 0, ESP_ERR_INVALID_ARG, TAG, "invalid data_bit_width"); /* The DMA buffer need to re-allocate if the buffer size changed */ if (handle->dma.buf_size != buf_size) { ESP_RETURN_ON_ERROR(i2s_free_dma_desc(handle), TAG, "failed to free the old dma descriptor"); diff --git a/components/esp_driver_i2s/i2s_private.h b/components/esp_driver_i2s/i2s_private.h index b734f650d8f5..fdd697fcf829 100644 --- a/components/esp_driver_i2s/i2s_private.h +++ b/components/esp_driver_i2s/i2s_private.h @@ -160,6 +160,7 @@ struct i2s_channel_obj_t { int intr_prio_flags;/*!< i2s interrupt priority flags */ void *mode_info; /*!< Slot, clock and gpio information of each mode */ struct { + bool is_port_auto: 1; /*!< Whether the port is auto-assigned */ bool is_etm_start: 1; /*!< Whether start by etm tasks */ bool is_etm_stop: 1; /*!< Whether stop by etm tasks */ bool is_raw_pdm: 1; /*!< Flag of whether send/receive PDM in raw data, i.e., no PCM2PDM/PDM2PCM filter enabled */ @@ -340,6 +341,19 @@ void i2s_output_gpio_reserve(i2s_chan_handle_t handle, int gpio_num); */ void i2s_output_gpio_revoke(i2s_chan_handle_t handle, uint64_t gpio_mask); +#if SOC_I2S_HW_VERSION_1 +/** + * @brief Change the port of the I2S channel + * + * @param handle I2S channel handle + * @param id I2S port id + * @return + * - ESP_OK Change port success + * - ESP_ERR_NOT_FOUND No available I2S port found + */ +esp_err_t i2s_channel_change_port(i2s_chan_handle_t handle, int id); +#endif + #ifdef __cplusplus } #endif diff --git a/components/esp_driver_i2s/i2s_std.c b/components/esp_driver_i2s/i2s_std.c index 701b6df4eec6..9f8ce6abceb5 100644 --- a/components/esp_driver_i2s/i2s_std.c +++ b/components/esp_driver_i2s/i2s_std.c @@ -96,12 +96,14 @@ static esp_err_t i2s_std_set_clock(i2s_chan_handle_t handle, const i2s_std_clk_c } } portEXIT_CRITICAL(&g_i2s.spinlock); + uint64_t tmp_div = (uint64_t)ret_mclk_div.integer * ret_mclk_div.denominator + ret_mclk_div.numerator; + ESP_RETURN_ON_FALSE(tmp_div != 0 && ret_mclk_div.denominator != 0, ESP_ERR_INVALID_ARG, TAG, "invalid mclk division result"); /* Update the mode info: clock configuration */ memcpy(&(std_cfg->clk_cfg), clk_cfg, sizeof(i2s_std_clk_config_t)); handle->clk_src = clk_cfg->clk_src; handle->sclk_hz = clk_info.sclk; - handle->origin_mclk_hz = ((uint64_t)clk_info.sclk * ret_mclk_div.denominator) / (ret_mclk_div.integer * ret_mclk_div.denominator + ret_mclk_div.numerator); + handle->origin_mclk_hz = ((uint64_t)clk_info.sclk * ret_mclk_div.denominator) / tmp_div; handle->curr_mclk_hz = handle->origin_mclk_hz; ESP_LOGD(TAG, "Clock division info: [sclk] %"PRIu32" Hz [mdiv] %"PRIu32" %"PRIu32"/%"PRIu32" [mclk] %"PRIu32" Hz [bdiv] %d [bclk] %"PRIu32" Hz", @@ -117,6 +119,7 @@ static esp_err_t i2s_std_set_slot(i2s_chan_handle_t handle, const i2s_std_slot_c handle->active_slot = slot_cfg->slot_mode == I2S_SLOT_MODE_MONO ? 1 : 2; uint32_t buf_size = i2s_get_buf_size(handle, slot_cfg->data_bit_width, handle->dma.frame_num); + ESP_RETURN_ON_FALSE(buf_size != 0, ESP_ERR_INVALID_ARG, TAG, "invalid data_bit_width"); /* The DMA buffer need to re-allocate if the buffer size changed */ if (handle->dma.buf_size != buf_size) { ESP_RETURN_ON_ERROR(i2s_free_dma_desc(handle), TAG, "failed to free the old dma descriptor"); @@ -248,13 +251,34 @@ static esp_err_t s_i2s_channel_try_to_constitude_std_duplex(i2s_chan_handle_t ha if (memcmp(another_handle->mode_info, &curr_cfg, sizeof(i2s_std_config_t)) == 0) { handle->controller->full_duplex = true; ESP_LOGD(TAG, "Constitude full-duplex on port %d", handle->controller->id); - } + } else { #if SOC_I2S_HW_VERSION_1 - else { - ESP_LOGE(TAG, "Can't set different channel configurations on a same port"); - return ESP_ERR_INVALID_ARG; - } + bool port_changed = false; + if (handle->is_port_auto) { + ESP_LOGD(TAG, "TX & RX on I2S%d are simplex", handle->controller->id); + for (int i = 0; i < SOC_I2S_NUM; i++) { + if (i == handle->controller->id) { + continue; + } + ESP_LOGD(TAG, "Trying to move %s channel from port %d to %d", + handle->dir == I2S_DIR_TX ? "TX" : "RX", handle->controller->id, i); + if (i2s_channel_change_port(handle, i) == ESP_OK) { + ESP_LOGD(TAG, "Move success!"); + port_changed = true; + break; + } else { + ESP_LOGD(TAG, "Move failed..."); + } + } + } + if (!port_changed) { + ESP_LOGE(TAG, "Can't set different channel configurations on a same port"); + return ESP_ERR_INVALID_ARG; + } +#else + ESP_LOGD(TAG, "TX & RX on I2S%d are simplex", handle->controller->id); #endif + } } /* Switch to the slave role if needed */ if (handle->controller->full_duplex && diff --git a/components/esp_driver_i2s/i2s_tdm.c b/components/esp_driver_i2s/i2s_tdm.c index 1b81620494ba..b6ce716a2ea6 100644 --- a/components/esp_driver_i2s/i2s_tdm.c +++ b/components/esp_driver_i2s/i2s_tdm.c @@ -93,12 +93,14 @@ static esp_err_t i2s_tdm_set_clock(i2s_chan_handle_t handle, const i2s_tdm_clk_c } } portEXIT_CRITICAL(&g_i2s.spinlock); + uint64_t tmp_div = (uint64_t)ret_mclk_div.integer * ret_mclk_div.denominator + ret_mclk_div.numerator; + ESP_RETURN_ON_FALSE(tmp_div != 0 && ret_mclk_div.denominator != 0, ESP_ERR_INVALID_ARG, TAG, "invalid mclk division result"); /* Update the mode info: clock configuration */ memcpy(&(tdm_cfg->clk_cfg), clk_cfg, sizeof(i2s_tdm_clk_config_t)); handle->clk_src = clk_cfg->clk_src; handle->sclk_hz = clk_info.sclk; - handle->origin_mclk_hz = ((uint64_t)clk_info.sclk * ret_mclk_div.denominator) / (ret_mclk_div.integer * ret_mclk_div.denominator + ret_mclk_div.numerator); + handle->origin_mclk_hz = ((uint64_t)clk_info.sclk * ret_mclk_div.denominator) / tmp_div; handle->curr_mclk_hz = handle->origin_mclk_hz; ESP_LOGD(TAG, "Clock division info: [sclk] %"PRIu32" Hz [mdiv] %"PRIu32" %"PRIu32"/%"PRIu32" [mclk] %"PRIu32" Hz [bdiv] %d [bclk] %"PRIu32" Hz", @@ -121,6 +123,7 @@ static esp_err_t i2s_tdm_set_slot(i2s_chan_handle_t handle, const i2s_tdm_slot_c "total slots(%"PRIu32") * slot_bit_width(%"PRIu32") exceeds the maximum %d", handle->total_slot, slot_bits, (int)I2S_LL_SLOT_FRAME_BIT_MAX); uint32_t buf_size = i2s_get_buf_size(handle, slot_cfg->data_bit_width, handle->dma.frame_num); + ESP_RETURN_ON_FALSE(buf_size != 0, ESP_ERR_INVALID_ARG, TAG, "invalid data_bit_width"); /* The DMA buffer need to re-allocate if the buffer size changed */ if (handle->dma.buf_size != buf_size) { ESP_RETURN_ON_ERROR(i2s_free_dma_desc(handle), TAG, "failed to free the old dma descriptor"); @@ -252,6 +255,8 @@ static void s_i2s_channel_try_to_constitude_tdm_duplex(i2s_chan_handle_t handle, if (memcmp(another_handle->mode_info, &curr_cfg, sizeof(i2s_tdm_config_t)) == 0) { handle->controller->full_duplex = true; ESP_LOGD(TAG, "Constitude full-duplex on port %d", handle->controller->id); + } else { + ESP_LOGD(TAG, "TX & RX on I2S%d are simplex", handle->controller->id); } } /* Switch to the slave role if needed */ diff --git a/components/esp_driver_i2s/test_apps/i2s/main/test_i2s.c b/components/esp_driver_i2s/test_apps/i2s/main/test_i2s.c index e8ede206bb0c..eb44a62e7fd8 100644 --- a/components/esp_driver_i2s/test_apps/i2s/main/test_i2s.c +++ b/components/esp_driver_i2s/test_apps/i2s/main/test_i2s.c @@ -238,6 +238,8 @@ TEST_CASE("I2S_basic_channel_allocation_reconfig_deleting_test", "[i2s]") } static volatile bool task_run_flag; +static volatile bool read_task_success = true; +static volatile bool write_task_success = true; #define TEST_I2S_DATA 0x78 @@ -270,6 +272,7 @@ static void i2s_read_task(void *args) ret = i2s_channel_read(rx_handle, recv_buf, 2000, &recv_size, 300); if (ret == ESP_ERR_TIMEOUT) { printf("Read timeout count: %"PRIu32"\n", cnt++); + read_task_success = false; } } @@ -291,6 +294,7 @@ static void i2s_write_task(void *args) ret = i2s_channel_write(tx_handle, send_buf, 2000, &send_size, 300); if (ret == ESP_ERR_TIMEOUT) { printf("Write timeout count: %"PRIu32"\n", cnt++); + write_task_success = false; } } @@ -431,6 +435,7 @@ TEST_CASE("I2S_lazy_duplex_test", "[i2s]") }, }, }; + /* Part 1: test common lazy duplex mode */ TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, NULL)); TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_cfg)); TEST_ESP_OK(i2s_channel_enable(tx_handle)); @@ -451,7 +456,70 @@ TEST_CASE("I2S_lazy_duplex_test", "[i2s]") xTaskCreate(i2s_read_check_task, "i2s_read_check_task", 4096, rx_handle, 5, NULL); printf("RX started\n"); - /* Wait 3 seconds to see if any failures occur */ + /* Wait 1 seconds to see if any failures occur */ + vTaskDelay(pdMS_TO_TICKS(1000)); + printf("Finished\n"); + + /* Stop those three tasks */ + task_run_flag = false; + + /* Wait for the three thread deleted */ + vTaskDelay(pdMS_TO_TICKS(1000)); + + /* Disable the channels, they will keep waiting until the current reading / writing finished */ + TEST_ESP_OK(i2s_channel_disable(tx_handle)); + TEST_ESP_OK(i2s_channel_disable(rx_handle)); + /* Delete the channels */ + TEST_ESP_OK(i2s_del_channel(tx_handle)); + TEST_ESP_OK(i2s_del_channel(rx_handle)); + + /* Part 2: Test no lazy duplex mode with port auto assignment */ + chan_cfg.id = I2S_NUM_AUTO; + TEST_ESP_OK(i2s_new_channel(&chan_cfg, NULL, &rx_handle)); + TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, NULL)); + TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_cfg)); + + /* Change the config to not constitute full-duplex */ + std_cfg.gpio_cfg.mclk = I2S_GPIO_UNUSED; + std_cfg.gpio_cfg.bclk = I2S_GPIO_UNUSED; + std_cfg.gpio_cfg.ws = I2S_GPIO_UNUSED; + std_cfg.gpio_cfg.dout = I2S_GPIO_UNUSED; + std_cfg.gpio_cfg.din = I2S_GPIO_UNUSED; +#if CONFIG_IDF_TARGET_ESP32S2 + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, i2s_channel_init_std_mode(tx_handle, &std_cfg)); + /* Delete the channels */ + TEST_ESP_OK(i2s_del_channel(tx_handle)); + TEST_ESP_OK(i2s_del_channel(rx_handle)); + return; +#else + TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_cfg)); +#endif + +#if CONFIG_IDF_TARGET_ESP32 + /* On ESP32, if failed to constitute full-duplex with `I2S_NUM_AUTO`, + the channel will be re-assigned to the next availableport */ + i2s_chan_info_t chan_info; + TEST_ESP_OK(i2s_channel_get_info(rx_handle, &chan_info)); + TEST_ASSERT(chan_info.id == I2S_NUM_0); + TEST_ESP_OK(i2s_channel_get_info(tx_handle, &chan_info)); + TEST_ASSERT(chan_info.id == I2S_NUM_1); +#endif + + TEST_ESP_OK(i2s_channel_enable(tx_handle)); + TEST_ESP_OK(i2s_channel_enable(rx_handle)); + + task_run_flag = true; + read_task_success = true; + write_task_success = true; + /* writing task to keep writing */ + xTaskCreate(i2s_write_task, "i2s_write_task", 4096, tx_handle, 5, NULL); + printf("TX started\n"); + vTaskDelay(pdMS_TO_TICKS(1000)); + /* reading task to keep reading */ + xTaskCreate(i2s_read_task, "i2s_read_task", 4096, rx_handle, 5, NULL); + printf("RX started\n"); + + /* Wait 1 seconds to see if any failures occur */ vTaskDelay(pdMS_TO_TICKS(1000)); printf("Finished\n"); @@ -467,6 +535,9 @@ TEST_CASE("I2S_lazy_duplex_test", "[i2s]") /* Delete the channels */ TEST_ESP_OK(i2s_del_channel(tx_handle)); TEST_ESP_OK(i2s_del_channel(rx_handle)); + /* Check if the reading and writing tasks are successful */ + TEST_ASSERT(read_task_success); + TEST_ASSERT(write_task_success); } static bool whether_contains_exapected_data(uint16_t *src, uint32_t src_len, uint32_t src_step, uint32_t start_val, uint32_t val_step) diff --git a/components/esp_driver_isp/CMakeLists.txt b/components/esp_driver_isp/CMakeLists.txt index f20d5847b73e..476fb925a226 100644 --- a/components/esp_driver_isp/CMakeLists.txt +++ b/components/esp_driver_isp/CMakeLists.txt @@ -46,6 +46,10 @@ if(CONFIG_SOC_ISP_WBG_SUPPORTED) list(APPEND srcs "src/isp_wbg.c") endif() +if(CONFIG_SOC_ISP_CROP_SUPPORTED) + list(APPEND srcs "src/isp_crop.c") +endif() + if(NOT ${target} STREQUAL "linux") list(APPEND requires esp_mm) endif() diff --git a/components/esp_driver_isp/include/driver/isp.h b/components/esp_driver_isp/include/driver/isp.h index 0bdd11d8db06..7fcd99650a88 100644 --- a/components/esp_driver_isp/include/driver/isp.h +++ b/components/esp_driver_isp/include/driver/isp.h @@ -25,3 +25,4 @@ #include "driver/isp_lsc.h" #include "driver/isp_sharpen.h" #include "driver/isp_wbg.h" +#include "driver/isp_crop.h" diff --git a/components/esp_driver_isp/include/driver/isp_crop.h b/components/esp_driver_isp/include/driver/isp_crop.h new file mode 100644 index 000000000000..fa6986d3ebb0 --- /dev/null +++ b/components/esp_driver_isp/include/driver/isp_crop.h @@ -0,0 +1,64 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "esp_err.h" +#include "driver/isp_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief ISP crop configurations + */ +typedef struct { + isp_window_t window; /*!< Crop window coordinates */ +} esp_isp_crop_config_t; + +/** + * @brief ISP Crop configuration + * + * @note After calling this API, crop doesn't take into effect until `esp_isp_crop_enable` is called + * + * @param[in] isp_proc Processor handle + * @param[in] config Crop configurations, set NULL to de-configure the ISP Crop + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG If the combination of arguments is invalid + */ +esp_err_t esp_isp_crop_configure(isp_proc_handle_t isp_proc, const esp_isp_crop_config_t *config); + +/** + * @brief Enable ISP crop function + * + * @param[in] isp_proc Processor handle + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG If the combination of arguments is invalid. + * - ESP_ERR_INVALID_STATE Driver state is invalid. + */ +esp_err_t esp_isp_crop_enable(isp_proc_handle_t isp_proc); + +/** + * @brief Disable ISP crop function + * + * @param[in] isp_proc Processor handle + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG If the combination of arguments is invalid. + * - ESP_ERR_INVALID_STATE Driver state is invalid. + */ +esp_err_t esp_isp_crop_disable(isp_proc_handle_t isp_proc); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_driver_isp/include/esp_private/isp_private.h b/components/esp_driver_isp/include/esp_private/isp_private.h index e94fe16e64dd..7684801b8480 100644 --- a/components/esp_driver_isp/include/esp_private/isp_private.h +++ b/components/esp_driver_isp/include/esp_private/isp_private.h @@ -85,6 +85,7 @@ typedef struct isp_processor_t { ISP_ATOMIC_TYPE(isp_fsm_t) blc_fsm; ISP_ATOMIC_TYPE(isp_fsm_t) ccm_fsm; ISP_ATOMIC_TYPE(isp_fsm_t) color_fsm; + ISP_ATOMIC_TYPE(isp_fsm_t) crop_fsm; ISP_ATOMIC_TYPE(isp_fsm_t) demosaic_fsm; ISP_ATOMIC_TYPE(isp_fsm_t) gamma_fsm; ISP_ATOMIC_TYPE(isp_fsm_t) lsc_fsm; diff --git a/components/esp_driver_isp/src/isp_crop.c b/components/esp_driver_isp/src/isp_crop.c new file mode 100644 index 000000000000..dd879eec724f --- /dev/null +++ b/components/esp_driver_isp/src/isp_crop.c @@ -0,0 +1,80 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include "sdkconfig.h" +#include "esp_log.h" +#include "esp_check.h" +#include "driver/isp_core.h" +#include "driver/isp_crop.h" +#include "esp_private/isp_private.h" +#include "hal/efuse_hal.h" +#include "soc/chip_revision.h" + +/*--------------------------------------------------------------- + Crop +---------------------------------------------------------------*/ + +#define IS_EVEN(x) ((x) % 2 == 0) +#define IS_ODD(x) ((x) % 2 != 0) + +static const char *TAG = "ISP_CROP"; + +esp_err_t esp_isp_crop_configure(isp_proc_handle_t isp_proc, const esp_isp_crop_config_t *config) +{ +#if CONFIG_IDF_TARGET_ESP32P4 + unsigned chip_version = efuse_hal_chip_revision(); + if (!ESP_CHIP_REV_ABOVE(chip_version, 300)) { + ESP_RETURN_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, TAG, "Crop is not supported on ESP32P4 chips prior than v3.0"); + } +#endif + uint32_t x_start = config->window.top_left.x; + uint32_t x_end = config->window.btm_right.x; + uint32_t y_start = config->window.top_left.y; + uint32_t y_end = config->window.btm_right.y; + + ESP_RETURN_ON_FALSE(isp_proc && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(isp_proc->crop_fsm == ISP_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "crop is enabled already"); + ESP_RETURN_ON_FALSE(x_start < x_end, ESP_ERR_INVALID_ARG, TAG, "invalid window x coordinates"); + ESP_RETURN_ON_FALSE(y_start < y_end, ESP_ERR_INVALID_ARG, TAG, "invalid window y coordinates"); + ESP_RETURN_ON_FALSE(x_end <= isp_proc->h_res, ESP_ERR_INVALID_ARG, TAG, "window exceeds horizontal resolution"); + ESP_RETURN_ON_FALSE(y_end <= isp_proc->v_res, ESP_ERR_INVALID_ARG, TAG, "window exceeds vertical resolution"); + + // Check the validity of x/y coordinates + ESP_RETURN_ON_FALSE(IS_EVEN(x_start), ESP_ERR_INVALID_ARG, TAG, "x_start must be even"); + ESP_RETURN_ON_FALSE(IS_ODD(x_end), ESP_ERR_INVALID_ARG, TAG, "x_end must be odd"); + ESP_RETURN_ON_FALSE(IS_EVEN(y_start), ESP_ERR_INVALID_ARG, TAG, "y_start must be even"); + ESP_RETURN_ON_FALSE(IS_ODD(y_end), ESP_ERR_INVALID_ARG, TAG, "y_end must be odd"); + + isp_ll_crop_set_clk_ctrl_mode(isp_proc->hal.hw, ISP_LL_PIPELINE_CLK_CTRL_AUTO); + isp_ll_crop_set_window(isp_proc->hal.hw, x_start, x_end, y_start, y_end); + + return ESP_OK; +} + +esp_err_t esp_isp_crop_enable(isp_proc_handle_t isp_proc) +{ + ESP_RETURN_ON_FALSE(isp_proc, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(isp_proc->crop_fsm == ISP_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "crop is enabled already"); + + isp_ll_crop_enable(isp_proc->hal.hw, true); + isp_proc->crop_fsm = ISP_FSM_ENABLE; + + ESP_LOGD(TAG, "Crop enabled"); + return ESP_OK; +} + +esp_err_t esp_isp_crop_disable(isp_proc_handle_t isp_proc) +{ + ESP_RETURN_ON_FALSE(isp_proc, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(isp_proc->crop_fsm == ISP_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "crop isn't enabled yet"); + + isp_ll_crop_enable(isp_proc->hal.hw, false); + isp_proc->crop_fsm = ISP_FSM_INIT; + + ESP_LOGD(TAG, "Crop disabled"); + return ESP_OK; +} diff --git a/components/esp_driver_jpeg/jpeg_decode.c b/components/esp_driver_jpeg/jpeg_decode.c index c0121b1876c0..290d6a067706 100644 --- a/components/esp_driver_jpeg/jpeg_decode.c +++ b/components/esp_driver_jpeg/jpeg_decode.c @@ -382,6 +382,11 @@ static esp_err_t jpeg_dec_config_dma_descriptor(jpeg_decoder_handle_t decoder_en case JPEG_DECODE_OUT_FORMAT_YUV444: best_hb_idx = JPEG_DEC_YUV444_HB; break; +#if !(CONFIG_ESP_REV_MIN_FULL < 300 && CONFIG_IDF_TARGET_ESP32P4) // Invisible for unsupported chips + case JPEG_DECODE_OUT_FORMAT_YUV420: + best_hb_idx = JPEG_DEC_YUV420_HB; + break; +#endif default: ESP_LOGE(TAG, "wrong, we don't support decode to such format."); return ESP_ERR_NOT_SUPPORTED; @@ -468,7 +473,17 @@ static void jpeg_dec_config_dma_csc(jpeg_decoder_handle_t decoder_engine, dma2d_ } else if (decoder_engine->sample_method == JPEG_DOWN_SAMPLING_YUV420) { rx_csc_option = DMA2D_CSC_RX_YUV420_TO_YUV444; } - } else { + } +#if !(CONFIG_ESP_REV_MIN_FULL < 300 && CONFIG_IDF_TARGET_ESP32P4) // Invisible for unsupported chips + else if (decoder_engine->output_format == JPEG_DECODE_OUT_FORMAT_YUV420) { + if (decoder_engine->sample_method == JPEG_DOWN_SAMPLING_YUV422) { + rx_csc_option = DMA2D_CSC_RX_YUV422_TO_YUV420; + } else if (decoder_engine->sample_method == JPEG_DOWN_SAMPLING_YUV444) { + rx_csc_option = DMA2D_CSC_RX_YUV444_TO_YUV420; + } + } +#endif + else { rx_csc_option = DMA2D_CSC_RX_NONE; } @@ -565,6 +580,7 @@ static bool jpeg_dec_transaction_on_picked(uint32_t channel_num, const dma2d_tra static esp_err_t jpeg_color_space_support_check(jpeg_decoder_handle_t decoder_engine) { +#if (CONFIG_ESP_REV_MIN_FULL < 300 && CONFIG_IDF_TARGET_ESP32P4) // For P4 less than 3.0 if (decoder_engine->sample_method == JPEG_DOWN_SAMPLING_YUV444) { if (decoder_engine->output_format == JPEG_DECODE_OUT_FORMAT_YUV422 || decoder_engine->output_format == JPEG_DECODE_OUT_FORMAT_YUV420) { ESP_LOGE(TAG, "Detected YUV444 but want to convert to YUV422/YUV420, which is not supported"); @@ -581,6 +597,14 @@ static esp_err_t jpeg_color_space_support_check(jpeg_decoder_handle_t decoder_en return ESP_ERR_INVALID_ARG; } } +#else + if (decoder_engine->sample_method == JPEG_DOWN_SAMPLING_YUV420 || decoder_engine->sample_method == JPEG_DOWN_SAMPLING_YUV444) { + if (decoder_engine->output_format == JPEG_DECODE_OUT_FORMAT_YUV422) { + ESP_LOGE(TAG, "Detected YUV444/YUV420 but want to convert to YUV422, which is not supported"); + return ESP_ERR_INVALID_ARG; + } + } +#endif return ESP_OK; } diff --git a/components/esp_driver_jpeg/jpeg_param.c b/components/esp_driver_jpeg/jpeg_param.c index 380c2c614e8f..9d05a3202fab 100644 --- a/components/esp_driver_jpeg/jpeg_param.c +++ b/components/esp_driver_jpeg/jpeg_param.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -40,10 +40,10 @@ const uint8_t zigzag_arr[64] = { * data stream during the decoding process. */ const uint32_t dec_hb_tbl[JPEG_DOWN_SAMPLING_NUM][JPEG_DEC_BEST_HB_MAX] = { - {40, 40, 40, 32, 0}, - {64, 32, 32, 64, 0}, - {48, 32, 32, 48, 0}, - {96, 0, 0, 0, 96}, + {40, 40, 40, 32, 0, 32}, + {64, 32, 32, 64, 0, 32}, + {48, 32, 32, 48, 0, 48}, + {96, 0, 0, 0, 96, 0}, }; /** diff --git a/components/esp_driver_jpeg/jpeg_parse_marker.c b/components/esp_driver_jpeg/jpeg_parse_marker.c index e26bdf218a17..5dd410fe21bd 100644 --- a/components/esp_driver_jpeg/jpeg_parse_marker.c +++ b/components/esp_driver_jpeg/jpeg_parse_marker.c @@ -60,7 +60,7 @@ esp_err_t jpeg_parse_com_marker(jpeg_dec_header_info_t *header_info) uint16_t skip_num = jpeg_get_bytes(header_info, 2); ESP_RETURN_ON_FALSE(skip_num >= 2, ESP_ERR_INVALID_ARG, TAG, "Invalid COM marker length: %"PRIu32, skip_num); uint32_t bytes_to_skip = skip_num - 2; - ESP_RETURN_ON_FALSE(header_info->header_size >= bytes_to_skip, ESP_ERR_INVALID_ARG, TAG, "COM marker data underflow for header_size: %"PRIu32, header_info->header_size); + ESP_RETURN_ON_FALSE(header_info->buffer_left >= bytes_to_skip, ESP_ERR_INVALID_ARG, TAG, "COM marker data underflow for header_size: %"PRIu32, header_info->buffer_left); header_info->buffer_offset += bytes_to_skip; header_info->header_size += bytes_to_skip; header_info->buffer_left -= bytes_to_skip; diff --git a/components/esp_driver_jpeg/jpeg_private.h b/components/esp_driver_jpeg/jpeg_private.h index 56345cfd0545..def879b88749 100644 --- a/components/esp_driver_jpeg/jpeg_private.h +++ b/components/esp_driver_jpeg/jpeg_private.h @@ -61,6 +61,7 @@ typedef enum { JPEG_DEC_RGB888_HB = 2, /*!< output RGB888 format */ JPEG_DEC_RGB565_HB = 3, /*!< output RGB565 format */ JPEG_DEC_GRAY_HB = 4, /*!< output the gray picture */ + JPEG_DEC_YUV420_HB = 5, /*!< output YUV420 format */ JPEG_DEC_BEST_HB_MAX, /*!< Max value of output formats */ } jpeg_dec_format_hb_t; diff --git a/components/esp_driver_ledc/src/ledc.c b/components/esp_driver_ledc/src/ledc.c index 43216629fc01..b10830b91515 100644 --- a/components/esp_driver_ledc/src/ledc.c +++ b/components/esp_driver_ledc/src/ledc.c @@ -1535,8 +1535,25 @@ esp_err_t ledc_fade_stop(ledc_mode_t speed_mode, ledc_channel_t channel) esp_err_t ledc_fade_func_install(int intr_alloc_flags) { LEDC_CHECK(s_ledc_fade_isr_handle == NULL, "fade function already installed", ESP_ERR_INVALID_STATE); + + ledc_mode_t speed_mode = LEDC_SPEED_MODE_MAX; + for (int i = 0; i < LEDC_SPEED_MODE_MAX; i++) { + if (p_ledc_obj[i] != NULL) { + speed_mode = i; + break; + } + } + //OR intr_alloc_flags with ESP_INTR_FLAG_IRAM because the fade isr is in IRAM - return ledc_isr_register(ledc_fade_isr, NULL, intr_alloc_flags | ESP_INTR_FLAG_IRAM, &s_ledc_fade_isr_handle); + return esp_intr_alloc_intrstatus( + ETS_LEDC_INTR_SOURCE, + intr_alloc_flags | ESP_INTR_FLAG_IRAM, + (uint32_t)ledc_hal_get_fade_end_intr_addr(&(p_ledc_obj[speed_mode]->ledc_hal)), + LEDC_LL_FADE_END_INTR_MASK, + ledc_fade_isr, + NULL, + &s_ledc_fade_isr_handle + ); } void ledc_fade_func_uninstall(void) diff --git a/components/esp_driver_ledc/test_apps/ledc/main/test_ledc.c b/components/esp_driver_ledc/test_apps/ledc/main/test_ledc.c index ebbbc982f33b..5c5952be24c6 100644 --- a/components/esp_driver_ledc/test_apps/ledc/main/test_ledc.c +++ b/components/esp_driver_ledc/test_apps/ledc/main/test_ledc.c @@ -258,6 +258,45 @@ TEST_CASE("LEDC fade with step", "[ledc]") ledc_fade_func_uninstall(); } +#if SOC_LEDC_SUPPORT_HS_MODE +TEST_CASE("LEDC fade install with low speed mode only", "[ledc]") +{ + // This test verifies that ledc_fade_func_install works correctly when only + // LEDC_LOW_SPEED_MODE is initialized, without initializing LEDC_HIGH_SPEED_MODE. + // This tests the fix for NULL pointer dereference issue. + + // Only initialize low speed mode, do NOT initialize high speed mode + ledc_channel_config_t ledc_ch_config = initialize_channel_config(); + ledc_ch_config.speed_mode = LEDC_LOW_SPEED_MODE; + ledc_ch_config.duty = 0; + ledc_ch_config.channel = LEDC_CHANNEL_0; + ledc_ch_config.timer_sel = LEDC_TIMER_0; + + ledc_timer_config_t ledc_time_config = create_default_timer_config(); + ledc_time_config.speed_mode = LEDC_LOW_SPEED_MODE; + ledc_time_config.timer_num = LEDC_TIMER_0; + + // Initialize only low speed mode + TEST_ESP_OK(ledc_channel_config(&ledc_ch_config)); + TEST_ESP_OK(ledc_timer_config(&ledc_time_config)); + vTaskDelay(5 / portTICK_PERIOD_MS); + TEST_ESP_OK(ledc_fade_func_install(0)); + + // Verify that fade functionality works correctly with low speed mode + TEST_ESP_OK(ledc_set_fade_with_time(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 4000, 200)); + TEST_ESP_OK(ledc_fade_start(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, LEDC_FADE_WAIT_DONE)); + TEST_ASSERT_EQUAL_INT32(4000, ledc_get_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0)); + + // Test fade down + TEST_ESP_OK(ledc_set_fade_with_time(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 0, 200)); + TEST_ESP_OK(ledc_fade_start(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, LEDC_FADE_WAIT_DONE)); + TEST_ASSERT_EQUAL_INT32(0, ledc_get_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0)); + + // Cleanup + ledc_fade_func_uninstall(); +} +#endif // SOC_LEDC_SUPPORT_HS_MODE + TEST_CASE("LEDC fast switching duty with fade_wait_done", "[ledc]") { const ledc_mode_t test_speed_mode = TEST_SPEED_MODE; diff --git a/components/esp_driver_parlio/include/driver/parlio_rx.h b/components/esp_driver_parlio/include/driver/parlio_rx.h index 9774d871b40c..bb0ac5c0d0b3 100644 --- a/components/esp_driver_parlio/include/driver/parlio_rx.h +++ b/components/esp_driver_parlio/include/driver/parlio_rx.h @@ -22,6 +22,7 @@ extern "C" { typedef struct { size_t trans_queue_depth; /*!< Depth of internal transaction queue */ size_t max_recv_size; /*!< Maximum receive size in one transaction, in bytes. This decides the number of DMA nodes will be used for each transaction */ + size_t dma_burst_size; /*!< DMA burst size, in bytes */ size_t data_width; /*!< Parallel IO data width, can set to 1/2/4/8/..., but can't be greater than PARLIO_RX_UNIT_MAX_DATA_WIDTH */ parlio_clock_source_t clk_src; /*!< Parallel IO clock source */ uint32_t ext_clk_freq_hz; /*!< The external source clock frequency. Only be valid when select PARLIO_CLK_SRC_EXTERNAL as clock source */ diff --git a/components/esp_driver_parlio/include/esp_private/parlio_rx_private.h b/components/esp_driver_parlio/include/esp_private/parlio_rx_private.h new file mode 100644 index 000000000000..9e74c61368c3 --- /dev/null +++ b/components/esp_driver_parlio/include/esp_private/parlio_rx_private.h @@ -0,0 +1,40 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "driver/parlio_rx.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Trigger the fake EOF interrupt + * @note This function is a workaround for the case that level delimiter needs to receive more than 64KB data in one transaction. + * The hardware can't generate the EOF interrupt when the data length is greater than 64KB due to the limitation of the hardware, + * so this function is used to trigger the fake EOF interrupt. + * @note This function will reset the whole parlio module, + * If the pair tx unit is in using, + * the reset operation will affect the TX unit and lead to unknown behavior + * @usage If the application needs to receive more than 64KB data in one transaction, you can follow the steps below: + * 1. Create a level delimiter with a length greater than 64KB + * 2. Register the interrupt of the end edge on the valid GPIO + * 3. Call this function to trigger the fake EOF interrupt in the GPIO interrupt handler + * 4. Receive the transaction that is greater than 64KB + * + * @param rx_unit Parallel IO RX unit that created by `parlio_new_rx_unit` + * @param need_yield Pointer to a status flag to record whether a task switch is needed if this API is being called in an ISR + * @return + * - ESP_OK: Trigger the fake EOF interrupt successfully + * - ESP_ERR_INVALID_ARG: Invalid argument like NULL pointer + * - ESP_ERR_INVALID_STATE: Tx unit is in using, can't be called when pair tx unit is in using + */ +esp_err_t parlio_rx_unit_trigger_fake_eof(parlio_rx_unit_handle_t rx_unit, bool *need_yield); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_driver_parlio/include/esp_private/parlio_private.h b/components/esp_driver_parlio/include/esp_private/parlio_tx_private.h similarity index 100% rename from components/esp_driver_parlio/include/esp_private/parlio_private.h rename to components/esp_driver_parlio/include/esp_private/parlio_tx_private.h diff --git a/components/esp_driver_parlio/linker.lf b/components/esp_driver_parlio/linker.lf index 76b5b7f0cfad..3c61d44c7f3b 100644 --- a/components/esp_driver_parlio/linker.lf +++ b/components/esp_driver_parlio/linker.lf @@ -25,3 +25,15 @@ entries: if SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION = y: gdma_link: gdma_link_get_buffer (noflash) + if PARLIO_RX_ISR_HANDLER_IN_IRAM = y: + gdma_link: gdma_link_mount_buffers (noflash) + gdma_link: gdma_link_get_buffer (noflash) + gdma_link: gdma_link_get_length (noflash) + esp_dma_utils: esp_dma_split_rx_buffer_to_cache_aligned (noflash) + esp_dma_utils: esp_dma_merge_aligned_rx_buffers (noflash) + +[mapping:parlio_driver_soc_periph] +archive: libsoc.a +entries: + if PARLIO_RX_ISR_HANDLER_IN_IRAM = y: + parlio_periph: parlio_periph_signals (noflash) diff --git a/components/esp_driver_parlio/src/parlio_rx.c b/components/esp_driver_parlio/src/parlio_rx.c index b413cc21f452..3dcd2f1554ce 100644 --- a/components/esp_driver_parlio/src/parlio_rx.c +++ b/components/esp_driver_parlio/src/parlio_rx.c @@ -19,12 +19,6 @@ #define PARLIO_MAX_ALIGNED_DMA_BUF_SIZE DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED #endif -#if defined(SOC_GDMA_BUS_AHB) && (SOC_GDMA_TRIG_PERIPH_PARLIO0_BUS == SOC_GDMA_BUS_AHB) -typedef dma_descriptor_align4_t parlio_dma_desc_t; -#elif defined(SOC_GDMA_BUS_AXI) && (SOC_GDMA_TRIG_PERIPH_PARLIO0_BUS == SOC_GDMA_BUS_AXI) -typedef dma_descriptor_align8_t parlio_dma_desc_t; -#endif - #define PARLIO_DMA_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA) /** @@ -32,13 +26,14 @@ typedef dma_descriptor_align8_t parlio_dma_desc_t; */ typedef struct { parlio_rx_delimiter_handle_t delimiter; /*!< Delimiter of this transaction */ - void *payload; /*!< The payload of this transaction, will be mounted to DMA descriptor */ - size_t size; /*!< The payload size in byte */ + dma_buffer_split_array_t aligned_payload; /*!< The aligned payload of this transaction, will be mounted to DMA link list node */ + size_t tot_trans_size; /*!< The total size of the transaction */ size_t recv_bytes; /*!< The received bytes of this transaction will be reset when all data filled in the infinite transaction */ + size_t alignment; /*!< The alignment of the payload buffer */ struct { uint32_t infinite : 1; /*!< Whether this is an infinite transaction */ - uint32_t indirect_mount : 1; /*!< Whether the user payload mount to the descriptor indirectly via an internal DMA buffer */ + uint32_t indirect_mount : 1; /*!< Whether the user payload mount to the link list node indirectly via an internal DMA buffer */ } flags; } parlio_rx_transaction_t; @@ -70,13 +65,18 @@ typedef struct parlio_rx_unit_t { /* DMA Resources */ gdma_channel_handle_t dma_chan; /*!< DMA channel */ size_t max_recv_size; /*!< Maximum receive size for a normal transaction */ - size_t desc_num; /*!< DMA descriptor number */ - size_t desc_size; /*!< DMA descriptors total size */ - parlio_dma_desc_t **dma_descs; /*!< DMA descriptor array pointer */ - parlio_dma_desc_t *curr_desc; /*!< The pointer of the current descriptor */ - void *usr_recv_buf; /*!< The pointe to the user's receiving buffer */ + size_t dma_burst_size; /*!< DMA burst size, in bytes */ + gdma_link_list_handle_t dma_link; /*!< DMA link list handle */ + uint32_t node_num; /*!< The number of nodes in the DMA link list */ + size_t int_mem_align; /*!< Alignment for internal memory */ + size_t ext_mem_align; /*!< Alignment for external memory */ + uint32_t curr_node_id; /*!< The index of the current node in the DMA link list */ + void *usr_recv_buf; /*!< The point to the user's receiving buffer */ /* Infinite transaction specific */ void *dma_buf; /*!< Additional internal DMA buffer only for infinite transactions */ + /* Unaligned DMA buffer management */ + uint8_t *stash_buf[2]; /*!< The ping-pong stash buffer for unaligned DMA buffer */ + uint8_t stash_buf_idx; /*!< The index of the current stash buffer */ /* Callback */ parlio_rx_event_callbacks_t cbs; /*!< The group of callback function pointers */ @@ -116,13 +116,13 @@ typedef struct parlio_rx_delimiter_t { } flags; } parlio_rx_delimiter_t; -#define PRALIO_RX_MOUNT_SIZE_CALC(total_size, div, align) ((((total_size) / (align)) / (div)) * (align)) +#define PARLIO_RX_MOUNT_SIZE_CALC(total_size, div, align) ((((total_size) / (align)) / (div)) * (align)) +#define PARLIO_RX_CHECK_ISR(condition, err) if (!(condition)) { return err; } static portMUX_TYPE s_rx_spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; size_t parlio_rx_mount_transaction_buffer(parlio_rx_unit_handle_t rx_unit, parlio_rx_transaction_t *trans) { - parlio_dma_desc_t **p_desc = rx_unit->dma_descs; /* Update the current transaction to the next one, and declare the delimiter is under using of the rx unit */ memcpy(&rx_unit->curr_trans, trans, sizeof(parlio_rx_transaction_t)); portENTER_CRITICAL_SAFE(&s_rx_spinlock); @@ -131,50 +131,66 @@ size_t parlio_rx_mount_transaction_buffer(parlio_rx_unit_handle_t rx_unit, parli } portEXIT_CRITICAL_SAFE(&s_rx_spinlock); - uint32_t desc_num = trans->size / PARLIO_MAX_ALIGNED_DMA_BUF_SIZE; - uint32_t remain_num = trans->size % PARLIO_MAX_ALIGNED_DMA_BUF_SIZE; - /* If there are still data remained, need one more descriptor */ - desc_num += remain_num ? 1 : 0; - if (trans->flags.infinite && desc_num < 2) { - /* At least 2 descriptors needed */ - desc_num = 2; + /* Calculate the number of nodes needed for the transaction */ + uint32_t body_node_num = trans->aligned_payload.buf.body.length / PARLIO_MAX_ALIGNED_DMA_BUF_SIZE; + uint32_t body_remain_size = trans->aligned_payload.buf.body.length % PARLIO_MAX_ALIGNED_DMA_BUF_SIZE; + /* If there are still data remained, need one more node */ + body_node_num += body_remain_size ? 1 : 0; + + uint32_t head_node_num = trans->aligned_payload.buf.head.length ? 1 : 0; + uint32_t tail_node_num = trans->aligned_payload.buf.tail.length ? 1 : 0; + uint32_t required_node_num = body_node_num + head_node_num + tail_node_num; + + if (trans->flags.infinite && required_node_num < 2) { + /* At least 2 nodes needed */ + required_node_num = 2; + } + rx_unit->node_num = required_node_num; + + gdma_buffer_mount_config_t mount_config[required_node_num] = {}; + /* Mount head buffer */ + if (head_node_num) { + mount_config[0].buffer = trans->aligned_payload.buf.head.aligned_buffer; + mount_config[0].buffer_alignment = trans->alignment; + mount_config[0].length = trans->aligned_payload.buf.head.length; + mount_config[0].flags.bypass_buffer_align_check = false; + mount_config[0].flags.mark_eof = false; + mount_config[0].flags.mark_final = GDMA_FINAL_LINK_TO_DEFAULT; } + /* Mount body buffer */ size_t mount_size = 0; size_t offset = 0; -#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE - uint32_t alignment = rx_unit->base.group->dma_align; -#else - uint32_t alignment = 4; -#endif - /* Loop the descriptors to assign the data */ - for (int i = 0; i < desc_num; i++) { - size_t rest_size = trans->size - offset; - + size_t rest_size = trans->aligned_payload.buf.body.length; + for (int i = head_node_num; i < required_node_num - tail_node_num; i++) { if (rest_size >= 2 * PARLIO_MAX_ALIGNED_DMA_BUF_SIZE) { - mount_size = PRALIO_RX_MOUNT_SIZE_CALC(trans->size, desc_num, alignment); + mount_size = PARLIO_MAX_ALIGNED_DMA_BUF_SIZE; } else if (rest_size <= PARLIO_MAX_ALIGNED_DMA_BUF_SIZE) { - mount_size = (desc_num == 2) && (i == 0) ? PRALIO_RX_MOUNT_SIZE_CALC(rest_size, 2, alignment) : rest_size; + mount_size = ((required_node_num - tail_node_num) == 2) && (i == 0) ? PARLIO_RX_MOUNT_SIZE_CALC(rest_size, 2, trans->alignment) : rest_size; } else { - mount_size = PRALIO_RX_MOUNT_SIZE_CALC(rest_size, 2, alignment); - } - p_desc[i]->buffer = (void *)((uint8_t *)trans->payload + offset); - p_desc[i]->dw0.size = mount_size; - p_desc[i]->dw0.length = mount_size; - p_desc[i]->dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA; - // Link the descriptor - if (i < desc_num - 1) { - p_desc[i]->next = p_desc[i + 1]; - } else { - /* For infinite transaction, link the descriptor as a ring */ - p_desc[i]->next = trans->flags.infinite ? p_desc[0] : NULL; + mount_size = PARLIO_RX_MOUNT_SIZE_CALC(rest_size, 2, trans->alignment); } + mount_config[i].buffer = (void *)((uint8_t *)trans->aligned_payload.buf.body.aligned_buffer + offset); + mount_config[i].buffer_alignment = trans->alignment; + mount_config[i].length = mount_size; + mount_config[i].flags.bypass_buffer_align_check = false; + mount_config[i].flags.mark_eof = false; + mount_config[i].flags.mark_final = GDMA_FINAL_LINK_TO_DEFAULT; offset += mount_size; -#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE - esp_cache_msync(p_desc[i], rx_unit->desc_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); -#endif + rest_size -= mount_size; + } + /* Mount tail buffer */ + if (tail_node_num) { + mount_config[required_node_num - 1].buffer = trans->aligned_payload.buf.tail.aligned_buffer; + mount_config[required_node_num - 1].buffer_alignment = trans->alignment; + mount_config[required_node_num - 1].length = trans->aligned_payload.buf.tail.length; + mount_config[required_node_num - 1].flags.bypass_buffer_align_check = false; } + /* For infinite transaction, link the node as a ring */ + mount_config[required_node_num - 1].flags.mark_final = !trans->flags.infinite ? GDMA_FINAL_LINK_TO_NULL : GDMA_FINAL_LINK_TO_HEAD; + mount_config[required_node_num - 1].flags.mark_eof = true; + gdma_link_mount_buffers(rx_unit->dma_link, 0, mount_config, required_node_num, NULL); /* Reset the current DMA node */ - rx_unit->curr_desc = p_desc[0]; + rx_unit->curr_node_id = 0; return offset; } @@ -324,6 +340,7 @@ static bool parlio_rx_default_eof_callback(gdma_channel_handle_t dma_chan, gdma_ need_yield |= rx_unit->cbs.on_timeout(rx_unit, &evt_data, rx_unit->user_data); } } else { + esp_dma_merge_aligned_rx_buffers(&rx_unit->curr_trans.aligned_payload); /* If received a normal EOF, it's a receive done event on parlio RX */ if (rx_unit->cbs.on_receive_done) { evt_data.data = rx_unit->usr_recv_buf; @@ -350,7 +367,7 @@ static bool parlio_rx_default_eof_callback(gdma_channel_handle_t dma_chan, gdma_ } /* Mount the new transaction buffer and start the new transaction */ parlio_rx_mount_transaction_buffer(rx_unit, &next_trans); - gdma_start(rx_unit->dma_chan, (intptr_t)rx_unit->dma_descs[0]); + gdma_start(rx_unit->dma_chan, gdma_link_get_head_addr(rx_unit->dma_link)); if (rx_unit->cfg.flags.free_clk) { parlio_ll_rx_start(rx_unit->base.group->hal.regs, true); PARLIO_CLOCK_SRC_ATOMIC() { @@ -381,20 +398,28 @@ static bool parlio_rx_default_desc_done_callback(gdma_channel_handle_t dma_chan, return false; } - /* Get the finished descriptor from the current descriptor */ - parlio_dma_desc_t *finished_desc = rx_unit->curr_desc; + /* Get the finished node from the current node */ + void *finished_buffer = gdma_link_get_buffer(rx_unit->dma_link, rx_unit->curr_node_id); + size_t finished_length = gdma_link_get_length(rx_unit->dma_link, rx_unit->curr_node_id); #if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE esp_err_t ret = ESP_OK; - ret |= esp_cache_msync((void *)finished_desc, rx_unit->desc_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); - ret |= esp_cache_msync((void *)(finished_desc->buffer), finished_desc->dw0.size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); + size_t sync_size = finished_length; + /* The sych length should be the cache line size for the un-aligned head and tail part */ + for (int i = 0; i < 2; i++) { + if (finished_buffer == rx_unit->stash_buf[i]) { + sync_size = rx_unit->int_mem_align; + break; + } + } + ret = esp_cache_msync(finished_buffer, sync_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); if (ret != ESP_OK) { ESP_EARLY_LOGW(TAG, "failed to sync dma buffer from memory to cache"); } #endif parlio_rx_event_data_t evt_data = { .delimiter = rx_unit->curr_trans.delimiter, - .data = finished_desc->buffer, - .recv_bytes = finished_desc->dw0.length, + .data = finished_buffer, + .recv_bytes = finished_length, }; if (rx_unit->cbs.on_partial_receive) { need_yield |= rx_unit->cbs.on_partial_receive(rx_unit, &evt_data, rx_unit->user_data); @@ -404,61 +429,43 @@ static bool parlio_rx_default_desc_done_callback(gdma_channel_handle_t dma_chan, memcpy(rx_unit->usr_recv_buf + rx_unit->curr_trans.recv_bytes, evt_data.data, evt_data.recv_bytes); } else { portENTER_CRITICAL_ISR(&s_rx_spinlock); - rx_unit->curr_trans.delimiter->under_using = false; + if (rx_unit->curr_trans.delimiter) { + rx_unit->curr_trans.delimiter->under_using = false; + } portEXIT_CRITICAL_ISR(&s_rx_spinlock); } /* Update received bytes */ - if (rx_unit->curr_trans.recv_bytes >= rx_unit->curr_trans.size) { + if (rx_unit->curr_trans.recv_bytes >= rx_unit->curr_trans.tot_trans_size) { rx_unit->curr_trans.recv_bytes = 0; } rx_unit->curr_trans.recv_bytes += evt_data.recv_bytes; - /* Move to the next DMA descriptor */ - rx_unit->curr_desc = rx_unit->curr_desc->next; + /* Move to the next DMA node */ + rx_unit->curr_node_id++; + rx_unit->curr_node_id %= rx_unit->node_num; return need_yield; } -static esp_err_t parlio_rx_create_dma_descriptors(parlio_rx_unit_handle_t rx_unit, uint32_t max_recv_size) +static esp_err_t parlio_rx_create_dma_link(parlio_rx_unit_handle_t rx_unit, uint32_t max_recv_size) { ESP_RETURN_ON_FALSE(rx_unit, ESP_ERR_INVALID_ARG, TAG, "invalid param"); esp_err_t ret = ESP_OK; - uint32_t desc_num = max_recv_size / DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED + 1; - /* set at least 2 descriptors */ - if (desc_num < 2) { - desc_num = 2; - } - rx_unit->desc_num = desc_num; - /* Allocated and link the descriptor nodes */ - rx_unit->dma_descs = heap_caps_calloc(desc_num, sizeof(parlio_dma_desc_t *), MALLOC_CAP_DMA); - ESP_RETURN_ON_FALSE(rx_unit->dma_descs, ESP_ERR_NO_MEM, TAG, "no memory for DMA descriptor array"); - uint32_t cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA); - size_t alignment = MAX(cache_line_size, PARLIO_DMA_DESC_ALIGNMENT); - rx_unit->desc_size = ALIGN_UP(sizeof(parlio_dma_desc_t), alignment); - for (int i = 0; i < desc_num; i++) { - rx_unit->dma_descs[i] = heap_caps_aligned_calloc(alignment, 1, rx_unit->desc_size, PARLIO_DMA_MEM_ALLOC_CAPS); - ESP_GOTO_ON_FALSE(rx_unit->dma_descs[i], ESP_ERR_NO_MEM, err, TAG, "no memory for DMA descriptors"); -#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE - esp_cache_msync(rx_unit->dma_descs[i], rx_unit->desc_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); -#endif - } + // calculated the total node number, add 2 for the aligned stash buffer + size_t tot_node_num = esp_dma_calculate_node_count(max_recv_size, rx_unit->int_mem_align, PARLIO_DMA_DESCRIPTOR_BUFFER_MAX_SIZE) + 2; + gdma_link_list_config_t dma_link_config = { + .num_items = tot_node_num, + .item_alignment = PARLIO_DMA_DESC_ALIGNMENT, + }; - rx_unit->max_recv_size = max_recv_size; + // create DMA link list, throw the error to the caller if failed + ESP_RETURN_ON_ERROR(gdma_new_link_list(&dma_link_config, &rx_unit->dma_link), TAG, "create DMA link list failed"); - return ret; -err: - for (int i = 0; i < desc_num; i++) { - if (rx_unit->dma_descs[i]) { - free(rx_unit->dma_descs[i]); - rx_unit->dma_descs[i] = NULL; - } - } - free(rx_unit->dma_descs); - rx_unit->dma_descs = NULL; + rx_unit->max_recv_size = max_recv_size; return ret; } -static esp_err_t parlio_rx_unit_init_dma(parlio_rx_unit_handle_t rx_unit) +static esp_err_t parlio_rx_unit_init_dma(parlio_rx_unit_handle_t rx_unit, size_t dma_burst_size) { /* Allocate and connect the GDMA channel */ gdma_channel_alloc_config_t dma_chan_config = { @@ -477,6 +484,21 @@ static esp_err_t parlio_rx_unit_init_dma(parlio_rx_unit_handle_t rx_unit) }; gdma_apply_strategy(rx_unit->dma_chan, &gdma_strategy_conf); + // configure DMA transfer parameters + rx_unit->dma_burst_size = dma_burst_size ? dma_burst_size : 16; + gdma_transfer_config_t trans_cfg = { + .max_data_burst_size = rx_unit->dma_burst_size, // Enable DMA burst transfer for better performance, + .access_ext_mem = true, + }; + ESP_RETURN_ON_ERROR(gdma_config_transfer(rx_unit->dma_chan, &trans_cfg), TAG, "config DMA transfer failed"); + ESP_RETURN_ON_ERROR(gdma_get_alignment_constraints(rx_unit->dma_chan, &rx_unit->int_mem_align, &rx_unit->ext_mem_align), TAG, "get alignment constraints failed"); +#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE + uint32_t cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA); + rx_unit->int_mem_align = rx_unit->int_mem_align > cache_line_size ? rx_unit->int_mem_align : cache_line_size; +#endif + uint32_t ext_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA); + rx_unit->ext_mem_align = rx_unit->ext_mem_align > ext_cache_line_size ? rx_unit->ext_mem_align : ext_cache_line_size; + /* Register callbacks */ gdma_rx_event_callbacks_t cbs = { .on_recv_eof = parlio_rx_default_eof_callback, @@ -575,21 +597,22 @@ static esp_err_t parlio_destroy_rx_unit(parlio_rx_unit_handle_t rx_unit) ESP_RETURN_ON_ERROR(gdma_disconnect(rx_unit->dma_chan), TAG, "disconnect dma channel failed"); ESP_RETURN_ON_ERROR(gdma_del_channel(rx_unit->dma_chan), TAG, "delete dma channel failed"); } - /* Free the DMA descriptors */ - if (rx_unit->dma_descs) { - for (int i = 0; i < rx_unit->desc_num; i++) { - if (rx_unit->dma_descs[i]) { - free(rx_unit->dma_descs[i]); - rx_unit->dma_descs[i] = NULL; - } - } - free(rx_unit->dma_descs); - rx_unit->dma_descs = NULL; + /* Free the DMA link list */ + if (rx_unit->dma_link) { + gdma_del_link_list(rx_unit->dma_link); + rx_unit->dma_link = NULL; } /* Free the internal DMA buffer */ if (rx_unit->dma_buf) { free(rx_unit->dma_buf); } + /* Free the stash buffer */ + for (uint8_t i = 0; i < 2; i++) { + if (rx_unit->stash_buf[i]) { + free(rx_unit->stash_buf[i]); + rx_unit->stash_buf[i] = NULL; + } + } /* Unregister the RX unit from the PARLIO group */ if (rx_unit->base.group) { parlio_unregister_unit_from_group(&rx_unit->base); @@ -638,7 +661,6 @@ esp_err_t parlio_new_rx_unit(const parlio_rx_unit_config_t *config, parlio_rx_un unit->trans_que = xQueueCreateWithCaps(config->trans_queue_depth, sizeof(parlio_rx_transaction_t), PARLIO_MEM_ALLOC_CAPS); ESP_GOTO_ON_FALSE(unit->trans_que, ESP_ERR_NO_MEM, err, TAG, "no memory for transaction queue"); - ESP_GOTO_ON_ERROR(parlio_rx_create_dma_descriptors(unit, config->max_recv_size), err, TAG, "create dma descriptor failed"); /* Register and attach the rx unit to the group */ ESP_GOTO_ON_ERROR(parlio_register_unit_to_group(&unit->base), err, TAG, "failed to register the rx unit to the group"); memcpy(&unit->cfg, config, sizeof(parlio_rx_unit_config_t)); @@ -652,7 +674,15 @@ esp_err_t parlio_new_rx_unit(const parlio_rx_unit_config_t *config, parlio_rx_un /* Initialize GPIO */ ESP_GOTO_ON_ERROR(parlio_rx_unit_set_gpio(unit, config), err, TAG, "failed to set GPIO"); /* Install DMA service */ - ESP_GOTO_ON_ERROR(parlio_rx_unit_init_dma(unit), err, TAG, "install rx DMA failed"); + ESP_GOTO_ON_ERROR(parlio_rx_unit_init_dma(unit, config->dma_burst_size), err, TAG, "install rx DMA failed"); + ESP_GOTO_ON_ERROR(parlio_rx_create_dma_link(unit, config->max_recv_size), err, TAG, "create dma link list failed"); + + for (uint8_t i = 0; i < 2; i++) { + uint32_t max_alignment = unit->int_mem_align > unit->ext_mem_align ? unit->int_mem_align : unit->ext_mem_align; + unit->stash_buf[i] = heap_caps_aligned_calloc(max_alignment, 2, max_alignment, PARLIO_MEM_ALLOC_CAPS | MALLOC_CAP_DMA); + ESP_GOTO_ON_FALSE(unit->stash_buf[i], ESP_ERR_NO_MEM, err, TAG, "no memory for stash buffer"); + } + /* Reset RX module */ PARLIO_RCC_ATOMIC() { parlio_ll_rx_reset_clock(hal->regs); @@ -737,7 +767,16 @@ esp_err_t parlio_rx_unit_enable(parlio_rx_unit_handle_t rx_unit, bool reset_queu xSemaphoreGive(rx_unit->trans_sem); } else if (xQueueReceive(rx_unit->trans_que, &trans, 0) == pdTRUE) { // The semaphore always supposed to be taken successfully - assert(xSemaphoreTake(rx_unit->trans_sem, 0) == pdTRUE); + BaseType_t res = xSemaphoreTake(rx_unit->trans_sem, 0); + (void)res; + assert(res == pdTRUE); + + if (trans.flags.indirect_mount && trans.flags.infinite && rx_unit->dma_buf == NULL) { + rx_unit->dma_buf = heap_caps_aligned_calloc(rx_unit->int_mem_align, 1, trans.aligned_payload.buf.body.length, PARLIO_DMA_MEM_ALLOC_CAPS); + ESP_GOTO_ON_FALSE(rx_unit->dma_buf, ESP_ERR_NO_MEM, err, TAG, "No memory for the internal DMA buffer"); + trans.aligned_payload.buf.body.aligned_buffer = rx_unit->dma_buf; + trans.aligned_payload.buf.body.recovery_address = rx_unit->dma_buf; + } if (rx_unit->cfg.flags.free_clk) { PARLIO_CLOCK_SRC_ATOMIC() { parlio_ll_rx_enable_clock(hal->regs, false); @@ -746,7 +785,7 @@ esp_err_t parlio_rx_unit_enable(parlio_rx_unit_handle_t rx_unit, bool reset_queu assert(trans.delimiter); parlio_rx_set_delimiter_config(rx_unit, trans.delimiter); parlio_rx_mount_transaction_buffer(rx_unit, &trans); - gdma_start(rx_unit->dma_chan, (intptr_t)rx_unit->curr_desc); + gdma_start(rx_unit->dma_chan, gdma_link_get_head_addr(rx_unit->dma_link)); if (rx_unit->cfg.flags.free_clk) { parlio_ll_rx_start(hal->regs, true); PARLIO_CLOCK_SRC_ATOMIC() { @@ -935,7 +974,7 @@ static esp_err_t parlio_rx_unit_do_transaction(parlio_rx_unit_handle_t rx_unit, parlio_rx_mount_transaction_buffer(rx_unit, trans); // Take semaphore without block time here, only indicate there are transactions on receiving xSemaphoreTake(rx_unit->trans_sem, 0); - gdma_start(rx_unit->dma_chan, (intptr_t)rx_unit->curr_desc); + gdma_start(rx_unit->dma_chan, gdma_link_get_head_addr(rx_unit->dma_link)); if (rx_unit->cfg.flags.free_clk) { parlio_ll_rx_start(rx_unit->base.group->hal.regs, true); PARLIO_CLOCK_SRC_ATOMIC() { @@ -958,18 +997,11 @@ esp_err_t parlio_rx_unit_receive(parlio_rx_unit_handle_t rx_unit, ESP_RETURN_ON_FALSE(rx_unit && payload && recv_cfg, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); ESP_RETURN_ON_FALSE(recv_cfg->delimiter, ESP_ERR_INVALID_ARG, TAG, "no delimiter specified"); ESP_RETURN_ON_FALSE(payload_size <= rx_unit->max_recv_size, ESP_ERR_INVALID_ARG, TAG, "trans length too large"); - uint32_t alignment = rx_unit->base.group->dma_align; -#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE - ESP_RETURN_ON_FALSE(payload_size % alignment == 0, ESP_ERR_INVALID_ARG, TAG, "The payload size should align with %"PRIu32, alignment); + size_t alignment = rx_unit->int_mem_align; if (recv_cfg->flags.partial_rx_en) { ESP_RETURN_ON_FALSE(payload_size >= 2 * alignment, ESP_ERR_INVALID_ARG, TAG, "The payload size should greater than %"PRIu32, 2 * alignment); } -#endif -#if CONFIG_PARLIO_RX_ISR_CACHE_SAFE - ESP_RETURN_ON_FALSE(esp_ptr_internal(payload), ESP_ERR_INVALID_ARG, TAG, "payload not in internal RAM"); -#else - ESP_RETURN_ON_FALSE(recv_cfg->flags.indirect_mount || esp_ptr_internal(payload), ESP_ERR_INVALID_ARG, TAG, "payload not in internal RAM"); -#endif + if (recv_cfg->delimiter->eof_data_len) { ESP_RETURN_ON_FALSE(payload_size >= recv_cfg->delimiter->eof_data_len, ESP_ERR_INVALID_ARG, TAG, "payload size should be greater than eof_data_len"); @@ -985,8 +1017,8 @@ esp_err_t parlio_rx_unit_receive(parlio_rx_unit_handle_t rx_unit, rx_units[rx_unit->base.unit_id]. data_sigs[recv_cfg->delimiter->valid_sig_line_id]; } - void *p_buffer = payload; + dma_buffer_split_array_t dma_buf_array = {0}; /* Create the internal DMA buffer for the infinite transaction if indirect_mount is set */ if (recv_cfg->flags.partial_rx_en && recv_cfg->flags.indirect_mount) { ESP_RETURN_ON_FALSE(!rx_unit->dma_buf, ESP_ERR_INVALID_STATE, TAG, "infinite transaction is using the internal DMA buffer"); @@ -994,14 +1026,21 @@ esp_err_t parlio_rx_unit_receive(parlio_rx_unit_handle_t rx_unit, rx_unit->dma_buf = heap_caps_aligned_calloc(alignment, 1, payload_size, PARLIO_DMA_MEM_ALLOC_CAPS); ESP_RETURN_ON_FALSE(rx_unit->dma_buf, ESP_ERR_NO_MEM, TAG, "No memory for the internal DMA buffer"); /* Use the internal DMA buffer so that the user buffer can always be available */ - p_buffer = rx_unit->dma_buf; + dma_buf_array.buf.body.aligned_buffer = rx_unit->dma_buf; + dma_buf_array.buf.body.recovery_address = rx_unit->dma_buf; + dma_buf_array.buf.body.length = payload_size; + } else { + ESP_RETURN_ON_ERROR(esp_dma_split_rx_buffer_to_cache_aligned(payload, payload_size, &dma_buf_array, &rx_unit->stash_buf[rx_unit->stash_buf_idx]), + TAG, "failed to split the unaligned DMA buffer"); + rx_unit->stash_buf_idx = !rx_unit->stash_buf_idx; } /* Create the transaction */ parlio_rx_transaction_t transaction = { .delimiter = recv_cfg->delimiter, - .payload = p_buffer, - .size = payload_size, + .aligned_payload = dma_buf_array, + .tot_trans_size = payload_size, + .alignment = alignment, .recv_bytes = 0, .flags.infinite = recv_cfg->flags.partial_rx_en, .flags.indirect_mount = recv_cfg->flags.indirect_mount, @@ -1053,3 +1092,52 @@ esp_err_t parlio_rx_unit_register_event_callbacks(parlio_rx_unit_handle_t rx_uni xSemaphoreGive(rx_unit->mutex); return ret; } + +esp_err_t parlio_rx_unit_trigger_fake_eof(parlio_rx_unit_handle_t rx_unit, bool *need_yield) +{ + ESP_RETURN_ON_FALSE_ISR(rx_unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + + int uint_id = rx_unit->base.unit_id; + parlio_unit_base_handle_t pair_tx_unit = rx_unit->base.group->tx_units[uint_id]; + /* This function will reset the whole parlio module, + If the pair tx unit is in using, + the reset operation will affect the TX unit and lead to unknown behavior */ + ESP_RETURN_ON_FALSE_ISR(!pair_tx_unit, ESP_ERR_INVALID_STATE, TAG, "can't be called when pair tx unit is in using"); + + /* Stop and reset the DMA channel first */ + ESP_RETURN_ON_ERROR_ISR(gdma_stop(rx_unit->dma_chan), TAG, "stop DMA channel failed"); + ESP_RETURN_ON_ERROR_ISR(gdma_reset(rx_unit->dma_chan), TAG, "reset DMA channel failed"); + + parlio_hal_context_t *hal = &rx_unit->base.group->hal; + portENTER_CRITICAL_SAFE(&s_rx_spinlock); + /* Save the current register values */ + parl_io_dev_t save_curr_regs = *(parl_io_dev_t *)hal->regs; + /* Reset the hardware FSM of the parlio module */ + PARLIO_RCC_ATOMIC() { + parlio_ll_reset_register(rx_unit->base.group->group_id); + } + /* Switch to the default clock source to ensure the register values can be written back successfully */ + PARLIO_CLOCK_SRC_ATOMIC() { + parlio_ll_rx_set_clock_source(hal->regs, PARLIO_CLK_SRC_DEFAULT); + } + portEXIT_CRITICAL_SAFE(&s_rx_spinlock); + /* Restore the register values and clock source*/ + memcpy(hal->regs, &save_curr_regs, sizeof(parl_io_dev_t)); + parlio_ll_rx_update_config(hal->regs); + PARLIO_CLOCK_SRC_ATOMIC() { + parlio_ll_rx_set_clock_source(hal->regs, rx_unit->clk_src); + } + + /* Force to trigger the EOF interrupt */ + gdma_event_data_t event_data = { + .flags.normal_eof = 1 + }; + bool _need_yield = false; + _need_yield |= parlio_rx_default_desc_done_callback(rx_unit->dma_chan, &event_data, rx_unit); + _need_yield |= parlio_rx_default_eof_callback(rx_unit->dma_chan, &event_data, rx_unit); + if (need_yield) { + *need_yield |= _need_yield; + } + + return ESP_OK; +} diff --git a/components/esp_driver_parlio/src/parlio_tx.c b/components/esp_driver_parlio/src/parlio_tx.c index baf1d295c799..706a4e4e64b7 100644 --- a/components/esp_driver_parlio/src/parlio_tx.c +++ b/components/esp_driver_parlio/src/parlio_tx.c @@ -174,7 +174,7 @@ static esp_err_t parlio_tx_unit_init_dma(parlio_tx_unit_t *tx_unit, const parlio // configure DMA transfer parameters gdma_transfer_config_t trans_cfg = { - .max_data_burst_size = config->dma_burst_size ? config->dma_burst_size : 16, // Enable DMA burst transfer for better performance, + .max_data_burst_size = config->dma_burst_size ? config->dma_burst_size : 32, // Enable DMA burst transfer for better performance, .access_ext_mem = true, // support transmit PSRAM buffer }; ESP_RETURN_ON_ERROR(gdma_config_transfer(tx_unit->dma_chan, &trans_cfg), TAG, "config DMA transfer failed"); @@ -481,7 +481,7 @@ static void parlio_mount_buffer(parlio_tx_unit_t *tx_unit, parlio_tx_trans_desc_ .flags = { // if transmission is loop, we don't need to generate the EOF for 1-bit data width, DIG-559 .mark_eof = tx_unit->data_width == 1 ? !t->flags.loop_transmission : true, - .mark_final = !t->flags.loop_transmission, + .mark_final = t->flags.loop_transmission ? GDMA_FINAL_LINK_TO_START : GDMA_FINAL_LINK_TO_NULL, } }; diff --git a/components/esp_driver_parlio/test_apps/parlio/main/test_parlio_rx.c b/components/esp_driver_parlio/test_apps/parlio/main/test_parlio_rx.c index cc454aa7b448..138637e62ff1 100644 --- a/components/esp_driver_parlio/test_apps/parlio/main/test_parlio_rx.c +++ b/components/esp_driver_parlio/test_apps/parlio/main/test_parlio_rx.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -25,6 +25,7 @@ #include "soc/parlio_periph.h" #include "esp_attr.h" #include "test_board.h" +#include "esp_private/parlio_rx_private.h" #define TEST_SPI_HOST SPI2_HOST #define TEST_I2S_PORT I2S_NUM_0 @@ -57,6 +58,10 @@ #define TEST_TASK_DATA_READY_BIT 0x01 #define TEST_TASK_FINISHED_BIT 0x02 +#define TEST_TASK_RECV_READY_BIT 0x04 +#define TEST_TASK_LARGE_TRANS_BIT 0x08 + +#define TEST_TASK_LARGE_TRANS_SIZE 155584 // Use an unaligned size to ensure the reliability typedef struct { uint32_t partial_recv_cnt; @@ -177,8 +182,6 @@ static void pulse_delimiter_sender_task_i2s(void *args) } } -#if CONFIG_IDF_TARGET_ESP32C6 // TODO: IDF-9806 fix the bit shift issue in other target - static void cs_high(spi_transaction_t *trans) { gpio_set_level(TEST_VALID_GPIO, 1); @@ -194,6 +197,7 @@ static void cs_low(spi_transaction_t *trans) static void level_delimiter_sender_task_spi(void *args) { uint32_t *task_flags = (uint32_t *)args; + bool is_large_trans = *task_flags & TEST_TASK_LARGE_TRANS_BIT; spi_device_handle_t dev_handle; spi_bus_config_t bus_cfg = { @@ -210,11 +214,11 @@ static void level_delimiter_sender_task_spi(void *args) .clock_speed_hz = TEST_SPI_CLK_FREQ, .mode = 0, .duty_cycle_pos = 128, - .spics_io_num = TEST_VALID_GPIO, + .spics_io_num = is_large_trans ? -1 : TEST_VALID_GPIO, .queue_size = 5, .flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_POSITIVE_CS, - .pre_cb = cs_high, - .post_cb = cs_low, + .pre_cb = is_large_trans ? NULL : cs_high, + .post_cb = is_large_trans ? NULL : cs_low, }; //Initialize the SPI bus and add device TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST, &bus_cfg, SPI_DMA_CH_AUTO)); @@ -240,8 +244,14 @@ static void level_delimiter_sender_task_spi(void *args) parlio_periph_signals.groups[0].rx_units[0].data_sigs[0]); // Prepare the data the be transmitted - uint8_t *data = (uint8_t *)calloc(1, TEST_EOF_DATA_LEN); - for (int i = 0; i < TEST_EOF_DATA_LEN; i += 4) { + uint8_t *data = NULL; + size_t data_size = TEST_EOF_DATA_LEN; + if (*task_flags & TEST_TASK_LARGE_TRANS_BIT) { + data_size = 1024; + } + data = (uint8_t *)calloc(1, data_size); + TEST_ASSERT_NOT_NULL(data); + for (int i = 0; i < data_size; i += 4) { data[i] = 0x12; data[i + 1] = 0x34; data[i + 2] = 0x56; @@ -249,17 +259,31 @@ static void level_delimiter_sender_task_spi(void *args) } spi_transaction_t t = { .cmd = 0, - .length = TEST_EOF_DATA_LEN * 8, + .length = data_size * 8, .flags = 0, .tx_buffer = data, .user = NULL, }; // Transmit data every 1ms, until the main test thread finished receiving - while (!((*task_flags) & TEST_TASK_FINISHED_BIT)) { - TEST_ESP_OK(spi_device_transmit(dev_handle, &t)); - vTaskDelay(pdMS_TO_TICKS(1)); - *task_flags |= TEST_TASK_DATA_READY_BIT; + if (is_large_trans) { + while (!((*task_flags) & TEST_TASK_FINISHED_BIT)) { + if (!((*task_flags) & TEST_TASK_RECV_READY_BIT)) { + gpio_set_level(TEST_VALID_GPIO, 1); + for (int i = 0; i < 80; i++) { + TEST_ESP_OK(spi_device_transmit(dev_handle, &t)); + } + gpio_set_level(TEST_VALID_GPIO, 0); + *task_flags |= TEST_TASK_DATA_READY_BIT; + } + vTaskDelay(pdMS_TO_TICKS(2)); + } + } else { + while (!((*task_flags) & TEST_TASK_FINISHED_BIT)) { + TEST_ESP_OK(spi_device_transmit(dev_handle, &t)); + vTaskDelay(pdMS_TO_TICKS(2)); + *task_flags |= TEST_TASK_DATA_READY_BIT; + } } // Remove the SPI device and free the bus @@ -275,7 +299,6 @@ static void level_delimiter_sender_task_spi(void *args) vTaskDelay(portMAX_DELAY); } } -#endif static bool test_delimiter(parlio_rx_delimiter_handle_t deli, bool free_running_clk, void (*sender_task_thread)(void *args)) { @@ -292,7 +315,7 @@ static bool test_delimiter(parlio_rx_delimiter_handle_t deli, bool free_running_ static uint32_t task_flags = 0; xTaskCreate(sender_task_thread, "sender task", 4096, &task_flags, 5, &sender_task); // Waiting for the data ready on line - while ((task_flags & TEST_TASK_DATA_READY_BIT)) { + while (!(task_flags & TEST_TASK_DATA_READY_BIT)) { vTaskDelay(1); } @@ -477,7 +500,7 @@ TEST_CASE("parallel_rx_unit_receive_transaction_test", "[parlio_rx]") TEST_ESP_OK(parlio_rx_unit_receive(rx_unit, payload, TEST_PAYLOAD_SIZE, &recv_config)); TEST_ESP_OK(parlio_rx_unit_wait_all_done(rx_unit, 5000)); TEST_ESP_OK(parlio_rx_soft_delimiter_start_stop(rx_unit, deli, false)); - TEST_ASSERT_EQUAL_UINT32(2, test_data.partial_recv_cnt); + TEST_ASSERT_GREATER_OR_EQUAL_UINT32(2, test_data.partial_recv_cnt); TEST_ASSERT_EQUAL_UINT32(1, test_data.recv_done_cnt); memset(&test_data, 0, sizeof(test_data_t)); @@ -489,7 +512,7 @@ TEST_CASE("parallel_rx_unit_receive_transaction_test", "[parlio_rx]") } TEST_ESP_OK(parlio_rx_unit_wait_all_done(rx_unit, 5000)); TEST_ESP_OK(parlio_rx_soft_delimiter_start_stop(rx_unit, deli, false)); - TEST_ASSERT_EQUAL_UINT32(10, test_data.partial_recv_cnt); + TEST_ASSERT_GREATER_OR_EQUAL_UINT32(10, test_data.partial_recv_cnt); TEST_ASSERT_EQUAL_UINT32(5, test_data.recv_done_cnt); memset(&test_data, 0, sizeof(test_data_t)); @@ -525,6 +548,49 @@ TEST_CASE("parallel_rx_unit_receive_transaction_test", "[parlio_rx]") free(payload); }; +#if SOC_PSRAM_DMA_CAPABLE +TEST_CASE("parallel_rx_unit_receive_external_memory_test", "[parlio_rx]") +{ + parlio_rx_unit_handle_t rx_unit = NULL; + parlio_rx_delimiter_handle_t deli = NULL; + size_t payload_size = 1000; + + parlio_rx_unit_config_t config = TEST_DEFAULT_UNIT_CONFIG(PARLIO_CLK_SRC_DEFAULT, 1000000); + config.flags.free_clk = 1; + TEST_ESP_OK(parlio_new_rx_unit(&config, &rx_unit)); + + parlio_rx_soft_delimiter_config_t sft_deli_cfg = { + .sample_edge = PARLIO_SAMPLE_EDGE_POS, + .eof_data_len = payload_size, + .timeout_ticks = 0, + }; + TEST_ESP_OK(parlio_new_rx_soft_delimiter(&sft_deli_cfg, &deli)); + + TEST_ESP_OK(parlio_rx_unit_enable(rx_unit, true)); + + parlio_receive_config_t recv_config = { + .delimiter = deli, + .flags.partial_rx_en = false, + }; + + /* Do not specify alignment, check if the driver can work correctly */ + uint8_t *payload = heap_caps_calloc_prefer(1, payload_size, MALLOC_CAP_DMA | MALLOC_CAP_SPIRAM, TEST_PARLIO_DMA_MEM_ALLOC_CAPS); + printf("payload addr: %p size: %u\n", payload, payload_size); + TEST_ASSERT(payload); + + printf("Testing the external memory receive functionality...\n"); + TEST_ESP_OK(parlio_rx_soft_delimiter_start_stop(rx_unit, deli, true)); + TEST_ESP_OK(parlio_rx_unit_receive(rx_unit, payload, payload_size, &recv_config)); + TEST_ESP_OK(parlio_rx_unit_wait_all_done(rx_unit, 5000)); + TEST_ESP_OK(parlio_rx_soft_delimiter_start_stop(rx_unit, deli, false)); + + TEST_ESP_OK(parlio_rx_unit_disable(rx_unit)); + TEST_ESP_OK(parlio_del_rx_delimiter(deli)); + TEST_ESP_OK(parlio_del_rx_unit(rx_unit)); + free(payload); +} +#endif // SOC_PSRAM_DMA_CAPABLE + TEST_CASE("parallel_rx_unit_receive_timeout_test", "[parlio_rx]") { printf("init a gpio to simulate valid signal\r\n"); @@ -592,3 +658,122 @@ TEST_CASE("parallel_rx_unit_receive_timeout_test", "[parlio_rx]") TEST_ESP_OK(gpio_reset_pin(TEST_VALID_GPIO)); free(payload); } + +typedef struct { + uint32_t partial_recv_cnt; + uint32_t recv_done_cnt; + uint32_t timeout_cnt; + uint32_t isr_send_cnt; + uint32_t isr_send_success_cnt; + parlio_rx_unit_handle_t rx_unit; + parlio_rx_delimiter_handle_t delimiter; + uint8_t *isr_payload; + size_t isr_payload_size; + bool enable_isr_send; +} test_isr_data_t; + +/** + * @brief This ISR is to indicate the SPI transaction finished + */ +static void test_gpio_neg_edge_intr(void *arg) +{ + parlio_rx_unit_handle_t rx_unit = (parlio_rx_unit_handle_t)arg; + bool need_yield = false; + parlio_rx_unit_trigger_fake_eof(rx_unit, &need_yield); + if (need_yield) { + portYIELD_FROM_ISR(); + } +} + +TEST_CASE("parallel_rx_unit_force_trigger_eof_test", "[parlio_rx]") +{ + parlio_rx_unit_handle_t rx_unit = NULL; + + parlio_rx_unit_config_t config = TEST_DEFAULT_UNIT_CONFIG(PARLIO_CLK_SRC_EXTERNAL, 1000000); + config.flags.free_clk = 0; + config.max_recv_size = TEST_TASK_LARGE_TRANS_SIZE; + TEST_ESP_OK(parlio_new_rx_unit(&config, &rx_unit)); + + parlio_rx_level_delimiter_config_t lvl_deli_cfg = { + .valid_sig_line_id = TEST_VALID_SIG, + .sample_edge = PARLIO_SAMPLE_EDGE_POS, + .bit_pack_order = PARLIO_BIT_PACK_ORDER_MSB, + /* Normally the EOF won't be triggered for the level delimiter that eof_data_len larger than 64KB */ + .eof_data_len = TEST_TASK_LARGE_TRANS_SIZE, + .timeout_ticks = 0, + .flags = { + .active_low_en = 0, + }, + }; + parlio_rx_delimiter_handle_t deli = NULL; + TEST_ESP_OK(parlio_new_rx_level_delimiter(&lvl_deli_cfg, &deli)); + + parlio_rx_event_callbacks_t cbs = { + .on_receive_done = test_parlio_rx_done_callback, + }; + test_data_t test_data = { + .partial_recv_cnt = 0, + .recv_done_cnt = 0, + }; + TEST_ESP_OK(parlio_rx_unit_register_event_callbacks(rx_unit, &cbs, &test_data)); + TEST_ESP_OK(parlio_rx_unit_enable(rx_unit, true)); + + TaskHandle_t sender_task; + /* The flag to transport finish information between main test thread and the sender thread + * Set it as static to make sure it'll be valid in another thread */ + static uint32_t task_flags = TEST_TASK_LARGE_TRANS_BIT; + xTaskCreate(level_delimiter_sender_task_spi, "sender task", 4096, &task_flags, 5, &sender_task); + + parlio_receive_config_t recv_config = { + .delimiter = deli, + .flags.partial_rx_en = false, + }; + uint8_t *recv_buff = NULL; + uint32_t alignment = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA); + alignment = alignment < 4 ? 4 : alignment; + size_t buff_size = ALIGN_UP(TEST_TASK_LARGE_TRANS_SIZE, alignment); + recv_buff = heap_caps_aligned_calloc(alignment, 1, buff_size, TEST_PARLIO_DMA_MEM_ALLOC_CAPS); + TEST_ASSERT_NOT_NULL(recv_buff); + + gpio_set_intr_type(TEST_VALID_GPIO, GPIO_INTR_NEGEDGE); + gpio_install_isr_service(0); + gpio_isr_handler_add(TEST_VALID_GPIO, test_gpio_neg_edge_intr, rx_unit); + gpio_intr_enable(TEST_VALID_GPIO); + + uint32_t recv_cnt = 3; + for (int i = 0; i < recv_cnt; i++) { + TEST_ESP_OK(parlio_rx_unit_receive(rx_unit, recv_buff, buff_size, &recv_config)); + printf("[%d] recv ready\n", i); + task_flags |= TEST_TASK_RECV_READY_BIT; + while (!task_flags & TEST_TASK_DATA_READY_BIT) { + vTaskDelay(1); + } + task_flags &= ~TEST_TASK_DATA_READY_BIT; + printf("[%d] send done\n", i); + TEST_ESP_OK(parlio_rx_unit_wait_all_done(rx_unit, 10000)); + task_flags &= ~TEST_TASK_RECV_READY_BIT; + printf("[%d] recv done\n", i); + } + // Indicate the test finished, no need to send data + task_flags |= TEST_TASK_FINISHED_BIT; + + bool is_success = true; + is_success &= test_data.recv_done_cnt == recv_cnt; + + gpio_intr_disable(TEST_VALID_GPIO); + gpio_isr_handler_remove(TEST_VALID_GPIO); + gpio_uninstall_isr_service(); + // Waiting for the sender task quit + while (task_flags) { + vTaskDelay(1); + } + // Delete the sender task + vTaskDelete(sender_task); + free(recv_buff); + + TEST_ESP_OK(parlio_rx_unit_disable(rx_unit)); + TEST_ESP_OK(parlio_del_rx_delimiter(deli)); + TEST_ESP_OK(parlio_del_rx_unit(rx_unit)); + + TEST_ASSERT(is_success); +} diff --git a/components/esp_driver_parlio/test_apps/parlio/main/test_parlio_sleep.c b/components/esp_driver_parlio/test_apps/parlio/main/test_parlio_sleep.c index 8fc99cd40a74..e035b48bea79 100644 --- a/components/esp_driver_parlio/test_apps/parlio/main/test_parlio_sleep.c +++ b/components/esp_driver_parlio/test_apps/parlio/main/test_parlio_sleep.c @@ -94,7 +94,7 @@ static void test_parlio_sleep_retention(bool allow_pd) parlio_rx_level_delimiter_config_t lvl_deli_cfg = { .valid_sig_line_id = PARLIO_RX_UNIT_MAX_DATA_WIDTH - 1, - .sample_edge = PARLIO_SAMPLE_EDGE_POS, + .sample_edge = PARLIO_SAMPLE_EDGE_NEG, // opposite to tx unit in case of timing issue .bit_pack_order = PARLIO_BIT_PACK_ORDER_MSB, .eof_data_len = TEST_PAYLOAD_SIZE, .timeout_ticks = 0, diff --git a/components/esp_driver_ppa/src/ppa_srm.c b/components/esp_driver_ppa/src/ppa_srm.c index fd30be80e73b..c35a736bf327 100644 --- a/components/esp_driver_ppa/src/ppa_srm.c +++ b/components/esp_driver_ppa/src/ppa_srm.c @@ -114,13 +114,6 @@ bool ppa_srm_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel } ppa_srm_color_mode_t ppa_out_color_mode = srm_trans_desc->out.srm_cm; - if (ppa_out_color_mode == PPA_SRM_COLOR_MODE_YUV444) { - ppa_out_color_mode = PPA_SRM_COLOR_MODE_YUV420; - dma2d_csc_config_t dma_rx_csc = { - .rx_csc_option = DMA2D_CSC_RX_YUV420_TO_YUV444, - }; - dma2d_configure_color_space_conversion(dma2d_rx_chan, &dma_rx_csc); - } // Configure the block size to be received by the SRM engine, which is passed from the 2D-DMA TX channel (i.e. 2D-DMA dscr-port mode) uint32_t block_h = 0, block_v = 0; @@ -163,6 +156,28 @@ bool ppa_srm_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channel ppa_ll_srm_enable_mirror_x(platform->hal.dev, srm_trans_desc->mirror_x); ppa_ll_srm_enable_mirror_y(platform->hal.dev, srm_trans_desc->mirror_y); + // Hardware bug workaround (DIG-734) + uint32_t w_out = srm_trans_desc->in.block_w * srm_trans_desc->scale_x_int + srm_trans_desc->in.block_w * srm_trans_desc->scale_x_frag / PPA_LL_SRM_SCALING_FRAG_MAX; + uint32_t w_divisor = (ppa_out_color_mode == PPA_SRM_COLOR_MODE_ARGB8888 || ppa_out_color_mode == PPA_SRM_COLOR_MODE_RGB888) ? 32 : 64; + uint32_t w_left = w_out % w_divisor; + w_left = (w_left == 0) ? w_divisor : w_left; + uint32_t h_mb = (ppa_ll_srm_get_mb_size(platform->hal.dev) == PPA_LL_SRM_MB_SIZE_16_16) ? 16 : 32; + uint32_t h_in_left = srm_trans_desc->in.block_h % h_mb; + h_in_left = (h_in_left == 0) ? h_mb : h_in_left; + uint32_t h_left = h_in_left * srm_trans_desc->scale_y_int + h_in_left * srm_trans_desc->scale_y_frag / PPA_LL_SRM_SCALING_FRAG_MAX; + const uint32_t dma2d_fifo_depth_bits = 12 * 128; + color_space_pixel_format_t out_pixel_format = { + .color_type_id = ppa_out_color_mode, + }; + uint32_t out_pixel_depth = color_hal_pixel_format_get_bit_depth(out_pixel_format); + bool bypass_mb_order = false; + if (((w_out > w_divisor) || (srm_trans_desc->in.block_h > h_mb)) && // will be cut into more than one trans unit + ((w_left * h_left * out_pixel_depth) < dma2d_fifo_depth_bits) + ) { + bypass_mb_order = true; + } + ppa_ll_srm_bypass_mb_order(platform->hal.dev, bypass_mb_order); + ppa_ll_srm_start(platform->hal.dev); // No need to yield @@ -179,7 +194,9 @@ esp_err_t ppa_do_scale_rotate_mirror(ppa_client_handle_t ppa_client, const ppa_s uint32_t buf_alignment_size = (uint32_t)ppa_client->engine->platform->buf_alignment_size; ESP_RETURN_ON_FALSE(((uint32_t)config->out.buffer & (buf_alignment_size - 1)) == 0 && (config->out.buffer_size & (buf_alignment_size - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "out.buffer addr or out.buffer_size not aligned to cache line size"); - ESP_RETURN_ON_FALSE(ppa_ll_srm_is_color_mode_supported(config->in.srm_cm) && ppa_ll_srm_is_color_mode_supported(config->out.srm_cm), ESP_ERR_INVALID_ARG, TAG, "unsupported color mode"); + ESP_RETURN_ON_FALSE(ppa_ll_srm_is_color_mode_supported(config->in.srm_cm) && + (ppa_ll_srm_is_color_mode_supported(config->out.srm_cm) && config->out.srm_cm != PPA_SRM_COLOR_MODE_YUV444), + ESP_ERR_INVALID_ARG, TAG, "unsupported color mode"); // For YUV420 input/output: in desc, ha/hb/va/vb/x/y must be even number // For YUV422 input/output: in desc, ha/hb/x must be even number if (config->in.srm_cm == PPA_SRM_COLOR_MODE_YUV420) { @@ -290,9 +307,6 @@ esp_err_t ppa_do_scale_rotate_mirror(ppa_client_handle_t ppa_client, const ppa_s if (config->in.srm_cm == PPA_SRM_COLOR_MODE_YUV444) { dma_trans_desc->channel_flags |= DMA2D_CHANNEL_FUNCTION_FLAG_TX_CSC; } - if (config->out.srm_cm == PPA_SRM_COLOR_MODE_YUV444) { - dma_trans_desc->channel_flags |= DMA2D_CHANNEL_FUNCTION_FLAG_RX_CSC; - } dma_trans_desc->specified_tx_channel_mask = 0; dma_trans_desc->specified_rx_channel_mask = 0; diff --git a/components/esp_driver_ppa/test_apps/main/test_ppa.c b/components/esp_driver_ppa/test_apps/main/test_ppa.c index 011add8c2e97..8684e6ee1546 100644 --- a/components/esp_driver_ppa/test_apps/main/test_ppa.c +++ b/components/esp_driver_ppa/test_apps/main/test_ppa.c @@ -18,6 +18,7 @@ #include "hal/color_hal.h" #include "esp_cache.h" #include "ppa_performance.h" +#include "esp_random.h" #define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) @@ -837,3 +838,86 @@ TEST_CASE("ppa_fill_performance", "[PPA]") free(out_buf); } + +TEST_CASE("ppa_srm_stress_test", "[PPA]") +{ + // Configurable parameters + const uint32_t w = 200; + const uint32_t h = 200; + const ppa_srm_color_mode_t in_cm = PPA_SRM_COLOR_MODE_RGB565; + const ppa_srm_color_mode_t out_cm = PPA_SRM_COLOR_MODE_RGB565; + const ppa_srm_rotation_angle_t rotation = PPA_SRM_ROTATION_ANGLE_0; + const float scale_x = 1.0; + const float scale_y = 1.0; + + color_space_pixel_format_t in_pixel_format = { + .color_type_id = in_cm, + }; + color_space_pixel_format_t out_pixel_format = { + .color_type_id = out_cm, + }; + + uint32_t in_buf_size = w * h * color_hal_pixel_format_get_bit_depth(in_pixel_format) / 8; + uint32_t out_buf_size = ALIGN_UP(w * h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8, 64); + uint8_t *out_buf = heap_caps_aligned_calloc(4, out_buf_size, sizeof(uint8_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA); + TEST_ASSERT_NOT_NULL(out_buf); + uint8_t *in_buf = heap_caps_aligned_calloc(4, in_buf_size, sizeof(uint8_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA); + TEST_ASSERT_NOT_NULL(in_buf); + + ppa_client_handle_t ppa_client_handle; + ppa_client_config_t ppa_client_config = { + .oper_type = PPA_OPERATION_SRM, + .max_pending_trans_num = 1, + }; + TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_handle)); + + // Test on different sizes of the block + int test_iterations = 50; + while (test_iterations-- > 0) { + uint32_t block_w_initial = esp_random() % (w - 100); + uint32_t block_h_initial = esp_random() % (h - 100); + block_w_initial = (block_w_initial == 0) ? 1 : block_w_initial; + block_h_initial = (block_h_initial == 0) ? 1 : block_h_initial; + uint32_t block_w = 0; + uint32_t block_h = 0; + for (int i = 0; i < 100; i++) { + block_w = block_w_initial + i; + block_h = block_h_initial + i; + // printf("block_w = %ld, block_h = %ld\n", block_w, block_h); + ppa_srm_oper_config_t oper_config = { + .in.buffer = in_buf, + .in.pic_w = w, + .in.pic_h = h, + .in.block_w = block_w, + .in.block_h = block_h, + .in.block_offset_x = 0, + .in.block_offset_y = 0, + .in.srm_cm = in_cm, + + .out.buffer = out_buf, + .out.buffer_size = out_buf_size, + .out.pic_w = block_w, + .out.pic_h = block_h, + .out.block_offset_x = 0, + .out.block_offset_y = 0, + .out.srm_cm = out_cm, + + .rotation_angle = rotation, + .scale_x = scale_x, + .scale_y = scale_y, + + .rgb_swap = 0, + .byte_swap = 0, + + .mode = PPA_TRANS_MODE_BLOCKING, + }; + + TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_handle, &oper_config)); + } + } + + TEST_ESP_OK(ppa_unregister_client(ppa_client_handle)); + + free(in_buf); + free(out_buf); +} diff --git a/components/esp_driver_sdio/src/sdio_slave.c b/components/esp_driver_sdio/src/sdio_slave.c index c1256b069e68..b5cbc626492f 100644 --- a/components/esp_driver_sdio/src/sdio_slave.c +++ b/components/esp_driver_sdio/src/sdio_slave.c @@ -356,15 +356,20 @@ esp_err_t sdio_slave_initialize(sdio_slave_config_t *config) esp_err_t r; intr_handle_t intr_handle = NULL; const int flags = 0; - r = esp_intr_alloc(ETS_SLC0_INTR_SOURCE, flags, sdio_intr, NULL, &intr_handle); - if (r != ESP_OK) { - return r; - } r = init_context(config); - if (r != ESP_OK) { - return r; - } + SDIO_SLAVE_CHECK(r == ESP_OK, "context initialization failed", r); + + r = esp_intr_alloc_intrstatus( + ETS_SLC0_INTR_SOURCE, + flags, + (uint32_t)sdio_slave_hal_get_intr_status_reg(context.hal), + sdio_slave_ll_intr_status_mask, + sdio_intr, + NULL, + &intr_handle + ); + SDIO_SLAVE_CHECK(r == ESP_OK, "interrupt allocation failed", r); context.intr_handle = intr_handle; #if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP diff --git a/components/esp_driver_sdmmc/include/driver/sdmmc_host.h b/components/esp_driver_sdmmc/include/driver/sdmmc_host.h index d6d3f856ac36..1ed17fe499e2 100644 --- a/components/esp_driver_sdmmc/include/driver/sdmmc_host.h +++ b/components/esp_driver_sdmmc/include/driver/sdmmc_host.h @@ -15,7 +15,7 @@ #include "esp_err.h" #include "driver/sdmmc_types.h" #include "driver/sdmmc_default_configs.h" -#include "driver/gpio.h" +#include "soc/gpio_num.h" #ifdef __cplusplus extern "C" { diff --git a/components/esp_driver_spi/src/gpspi/spi_common.c b/components/esp_driver_spi/src/gpspi/spi_common.c index 92973a669494..98da337c7784 100644 --- a/components/esp_driver_spi/src/gpspi/spi_common.c +++ b/components/esp_driver_spi/src/gpspi/spi_common.c @@ -256,7 +256,7 @@ static esp_err_t alloc_dma_chan(spi_host_device_t host_id, spi_dma_chan_t dma_ch #endif // TODO: add support to allow SPI transfer PSRAM buffer gdma_transfer_config_t trans_cfg = { - .max_data_burst_size = 16, + .max_data_burst_size = 32, .access_ext_mem = false, }; ESP_RETURN_ON_ERROR(gdma_config_transfer(dma_ctx->tx_dma_chan, &trans_cfg), SPI_TAG, "config gdma tx transfer failed"); @@ -815,7 +815,7 @@ esp_err_t spi_bus_initialize(spi_host_device_t host_id, const spi_bus_config_t * #elif SOC_GDMA_SUPPORTED SPI_CHECK(dma_chan == SPI_DMA_DISABLED || dma_chan == SPI_DMA_CH_AUTO, "invalid dma channel, chip only support spi dma channel auto-alloc", ESP_ERR_INVALID_ARG); #endif - SPI_CHECK((bus_config->intr_flags & (ESP_INTR_FLAG_HIGH | ESP_INTR_FLAG_EDGE | ESP_INTR_FLAG_INTRDISABLED)) == 0, "intr flag not allowed", ESP_ERR_INVALID_ARG); + SPI_CHECK((bus_config->intr_flags & (ESP_INTR_FLAG_HIGH | ESP_INTR_FLAG_EDGE | ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_INTRDISABLED)) == 0, "intr flag not allowed", ESP_ERR_INVALID_ARG); #ifndef CONFIG_SPI_MASTER_ISR_IN_IRAM SPI_CHECK((bus_config->intr_flags & ESP_INTR_FLAG_IRAM) == 0, "ESP_INTR_FLAG_IRAM should be disabled when CONFIG_SPI_MASTER_ISR_IN_IRAM is not set.", ESP_ERR_INVALID_ARG); #endif diff --git a/components/esp_driver_spi/src/gpspi/spi_slave.c b/components/esp_driver_spi/src/gpspi/spi_slave.c index 8e328fbd2ce9..23efeeba50b4 100644 --- a/components/esp_driver_spi/src/gpspi/spi_slave.c +++ b/components/esp_driver_spi/src/gpspi/spi_slave.c @@ -162,7 +162,7 @@ esp_err_t spi_slave_initialize(spi_host_device_t host, const spi_bus_config_t *b #elif SOC_GDMA_SUPPORTED SPI_CHECK(dma_chan == SPI_DMA_DISABLED || dma_chan == SPI_DMA_CH_AUTO, "invalid dma channel, chip only support spi dma channel auto-alloc", ESP_ERR_INVALID_ARG); #endif - SPI_CHECK((bus_config->intr_flags & (ESP_INTR_FLAG_HIGH | ESP_INTR_FLAG_EDGE | ESP_INTR_FLAG_INTRDISABLED)) == 0, "intr flag not allowed", ESP_ERR_INVALID_ARG); + SPI_CHECK((bus_config->intr_flags & (ESP_INTR_FLAG_HIGH | ESP_INTR_FLAG_EDGE | ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_INTRDISABLED)) == 0, "intr flag not allowed", ESP_ERR_INVALID_ARG); #ifndef CONFIG_SPI_SLAVE_ISR_IN_IRAM SPI_CHECK((bus_config->intr_flags & ESP_INTR_FLAG_IRAM) == 0, "ESP_INTR_FLAG_IRAM should be disabled when CONFIG_SPI_SLAVE_ISR_IN_IRAM is not set.", ESP_ERR_INVALID_ARG); #endif diff --git a/components/esp_driver_touch_sens/common/touch_sens_common.c b/components/esp_driver_touch_sens/common/touch_sens_common.c index 1b0d726f72d4..39c12615c4bc 100644 --- a/components/esp_driver_touch_sens/common/touch_sens_common.c +++ b/components/esp_driver_touch_sens/common/touch_sens_common.c @@ -43,12 +43,22 @@ static void touch_channel_pin_init(int id) { gpio_num_t pin = touch_sensor_channel_io_map[id]; assert(pin >= 0); + /* Touch pad will output the sawtooth wave on the pin, + so it needs to be reserved, in case of conflict with other output signals */ if (esp_gpio_reserve(BIT64(pin)) & BIT64(pin)) { ESP_LOGW(TAG, "The GPIO%d is conflict with other module", (int)pin); } gpio_config_as_analog(pin); } +static void touch_channel_pin_deinit(int id) +{ + gpio_num_t pin = touch_sensor_channel_io_map[id]; + assert(pin >= 0); + /* esp_gpio_revoke is called in gpio_reset_pin */ + gpio_reset_pin(pin); +} + static void s_touch_free_resource(touch_sensor_handle_t sens_handle) { if (!sens_handle) { @@ -211,6 +221,7 @@ esp_err_t touch_sensor_del_channel(touch_channel_handle_t chan_handle) TOUCH_ENTER_CRITICAL(TOUCH_PERIPH_LOCK); sens_handle->chan_mask &= ~(1UL << chan_handle->id); TOUCH_EXIT_CRITICAL(TOUCH_PERIPH_LOCK); + touch_channel_pin_deinit(chan_handle->id); free(g_touch->ch[ch_offset]); g_touch->ch[ch_offset] = NULL; diff --git a/components/esp_driver_tsens/test_apps/.build-test-rules.yml b/components/esp_driver_tsens/test_apps/.build-test-rules.yml index ee0011111241..0117f07d13f6 100644 --- a/components/esp_driver_tsens/test_apps/.build-test-rules.yml +++ b/components/esp_driver_tsens/test_apps/.build-test-rules.yml @@ -6,10 +6,6 @@ components/esp_driver_tsens/test_apps/temperature_sensor: - if: IDF_TARGET == "esp32c5" and CONFIG_NAME == "iram_safe" temporary: false reason: Use test_temperature_sensor_cbs_esp32c5 instead, iram_safe need to use single app large partition table on c5 - disable_test: - - if: IDF_TARGET == "esp32p4" - temporary: true - reason: p4 rev3 migration # TODO: IDF-14834 depends_components: - esp_driver_tsens - esp_phy diff --git a/components/esp_driver_tsens/test_apps/temperature_sensor/pytest_temperature_sensor.py b/components/esp_driver_tsens/test_apps/temperature_sensor/pytest_temperature_sensor.py index ee39b9a29a0d..987e0db89ff5 100644 --- a/components/esp_driver_tsens/test_apps/temperature_sensor/pytest_temperature_sensor.py +++ b/components/esp_driver_tsens/test_apps/temperature_sensor/pytest_temperature_sensor.py @@ -19,7 +19,6 @@ ['esp32s2', 'esp32c3', 'esp32s3', 'esp32c2', 'esp32c6', 'esp32h2', 'esp32p4', 'esp32c5', 'esp32c61'], indirect=['target'], ) -@pytest.mark.temp_skip_ci(targets=['esp32p4'], reason='p4 rev3 migration, IDF-14834') def test_temperature_sensor_driver(dut: Dut) -> None: dut.run_all_single_board_cases() @@ -33,7 +32,6 @@ def test_temperature_sensor_driver(dut: Dut) -> None: indirect=True, ) @idf_parametrize('target', ['esp32c6', 'esp32h2', 'esp32p4', 'esp32c61'], indirect=['target']) -@pytest.mark.temp_skip_ci(targets=['esp32p4'], reason='p4 rev3 migration, IDF-14834') def test_temperature_sensor_cbs(dut: Dut) -> None: dut.run_all_single_board_cases() diff --git a/components/esp_driver_twai/esp_twai_onchip.c b/components/esp_driver_twai/esp_twai_onchip.c index 262f4d183a0b..e6cf64239471 100644 --- a/components/esp_driver_twai/esp_twai_onchip.c +++ b/components/esp_driver_twai/esp_twai_onchip.c @@ -569,6 +569,7 @@ static esp_err_t _node_get_status(twai_node_handle_t node, twai_node_status_t *s status_ret->state = atomic_load(&twai_ctx->state); status_ret->tx_error_count = twai_hal_get_tec(twai_ctx->hal); status_ret->rx_error_count = twai_hal_get_rec(twai_ctx->hal); + status_ret->tx_queue_remaining = uxQueueSpacesAvailable(twai_ctx->tx_mount_queue); } if (record_ret) { *record_ret = twai_ctx->history; @@ -582,7 +583,7 @@ static esp_err_t _node_queue_tx(twai_node_handle_t node, const twai_frame_t *fra { twai_onchip_ctx_t *twai_ctx = __containerof(node, twai_onchip_ctx_t, api_base); if (frame->header.dlc && frame->buffer_len) { - ESP_RETURN_ON_FALSE_ISR(frame->header.dlc == twaifd_len2dlc(frame->buffer_len), ESP_ERR_INVALID_ARG, TAG, "unmatched dlc and buffer_len"); + ESP_RETURN_ON_FALSE_ISR(frame->header.dlc == twaifd_len2dlc(frame->buffer_len), ESP_ERR_INVALID_ARG, TAG, "unmatched dlc(%i) and buffer_len(%i)", frame->header.dlc, twaifd_len2dlc(frame->buffer_len)); } #if !SOC_TWAI_SUPPORT_FD ESP_RETURN_ON_FALSE_ISR(!frame->header.fdf || frame->buffer_len <= TWAI_FRAME_MAX_LEN, ESP_ERR_INVALID_ARG, TAG, "fdf flag or buffer_len not supported"); diff --git a/components/esp_driver_twai/include/esp_twai.h b/components/esp_driver_twai/include/esp_twai.h index 73cba1a6367e..8a99ee75b227 100644 --- a/components/esp_driver_twai/include/esp_twai.h +++ b/components/esp_driver_twai/include/esp_twai.h @@ -18,7 +18,8 @@ extern "C" { * @brief Enable the TWAI node * * @param node Handle to the TWAI node - * @return ESP_OK on success, error code otherwise + * @return - ESP_OK: Success + * - ESP_ERR_INVALID_STATE: Node already in enabled state */ esp_err_t twai_node_enable(twai_node_handle_t node); @@ -26,7 +27,8 @@ esp_err_t twai_node_enable(twai_node_handle_t node); * @brief Disable the TWAI node * * @param node Handle to the TWAI node - * @return ESP_OK on success, error code otherwise + * @return - ESP_OK: Success + * - ESP_ERR_INVALID_STATE: Node not in enabled state */ esp_err_t twai_node_disable(twai_node_handle_t node); @@ -35,7 +37,8 @@ esp_err_t twai_node_disable(twai_node_handle_t node); * @note Follow `on_state_change` callback or `twai_node_get_info` to know recover finish or not * * @param node Handle to the TWAI node - * @return ESP_OK on success, error code otherwise + * @return - ESP_OK: Success + * - ESP_ERR_INVALID_STATE: Node not in bus-off state */ esp_err_t twai_node_recover(twai_node_handle_t node); @@ -43,7 +46,8 @@ esp_err_t twai_node_recover(twai_node_handle_t node); * @brief Delete the TWAI node and release resources * * @param node Handle to the TWAI node - * @return ESP_OK on success, error code otherwise + * @return - ESP_OK: Success + * - ESP_ERR_INVALID_STATE: Node not in disabled state */ esp_err_t twai_node_delete(twai_node_handle_t node); @@ -75,7 +79,9 @@ esp_err_t twai_node_reconfig_timing(twai_node_handle_t node, const twai_timing_a * @param node Handle to the TWAI node * @param filter_id Index of the filter to configure * @param mask_cfg Pointer to the mask filter configuration - * @return ESP_OK on success, error code otherwise + * @return - ESP_OK: Success + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_INVALID_STATE: Node not in disabled state */ esp_err_t twai_node_config_mask_filter(twai_node_handle_t node, uint8_t filter_id, const twai_mask_filter_config_t *mask_cfg); @@ -85,7 +91,9 @@ esp_err_t twai_node_config_mask_filter(twai_node_handle_t node, uint8_t filter_i * @param node Handle to the TWAI node * @param filter_id Index of the filter to configure * @param range_cfg Pointer to the range filter configuration - * @return ESP_OK on success, error code otherwise + * @return - ESP_OK: Success + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_INVALID_STATE: Node not in disabled state */ esp_err_t twai_node_config_range_filter(twai_node_handle_t node, uint8_t filter_id, const twai_range_filter_config_t *range_cfg); @@ -107,7 +115,11 @@ esp_err_t twai_node_get_info(twai_node_handle_t node, twai_node_status_t *status * @param[in] node Handle to the TWAI node * @param[in] frame Pointer to the frame to transmit * @param[in] timeout_ms Maximum wait time if the transmission queue is full (milliseconds), -1 to wait forever - * @return ESP_OK on success, error code otherwise + * @return - ESP_OK: Success + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_INVALID_STATE: Node already in bus-off state + * - ESP_ERR_NOT_SUPPORTED: Node is config as listen only + * - ESP_ERR_TIMEOUT: Timeout to wait for queue space */ esp_err_t twai_node_transmit(twai_node_handle_t node, const twai_frame_t *frame, int timeout_ms); @@ -116,7 +128,9 @@ esp_err_t twai_node_transmit(twai_node_handle_t node, const twai_frame_t *frame, * * @param[in] node Handle to the TWAI node * @param[in] timeout_ms Maximum wait time for all pending transfers to finish (milliseconds), -1 to wait forever - * @return ESP_OK on success, error code otherwise + * @return - ESP_OK: Success + * - ESP_ERR_INVALID_STATE: Node already in bus-off state + * - ESP_ERR_TIMEOUT: Timeout */ esp_err_t twai_node_transmit_wait_all_done(twai_node_handle_t node, int timeout_ms); @@ -129,7 +143,8 @@ esp_err_t twai_node_transmit_wait_all_done(twai_node_handle_t node, int timeout_ * * @param[in] node Handle to the TWAI node * @param[out] rx_frame Pointer to the frame store rx content - * @return ESP_OK on success, error code otherwise + * @return - ESP_OK: Success + * - ESP_ERR_INVALID_STATE: Called from a task or from other callbacks except `rx_done_cb` */ esp_err_t twai_node_receive_from_isr(twai_node_handle_t node, twai_frame_t *rx_frame); diff --git a/components/esp_driver_twai/include/esp_twai_types.h b/components/esp_driver_twai/include/esp_twai_types.h index 35023ae76f73..8690153556b7 100644 --- a/components/esp_driver_twai/include/esp_twai_types.h +++ b/components/esp_driver_twai/include/esp_twai_types.h @@ -42,6 +42,7 @@ typedef struct { twai_error_state_t state; /**< Node's error state */ uint16_t tx_error_count; /**< Node's TX error count */ uint16_t rx_error_count; /**< Node's RX error count */ + uint32_t tx_queue_remaining; /**< Node's TX queue remaining frame space (number of frames) */ } twai_node_status_t; /** diff --git a/components/esp_driver_twai/test_apps/test_twai/main/test_twai_common.c b/components/esp_driver_twai/test_apps/test_twai/main/test_twai_common.c index 18f289dc2c8a..20f5032e359d 100644 --- a/components/esp_driver_twai/test_apps/test_twai/main/test_twai_common.c +++ b/components/esp_driver_twai/test_apps/test_twai/main/test_twai_common.c @@ -230,7 +230,11 @@ TEST_CASE("twai transmit stop resume (loopback)", "[twai]") TEST_ESP_OK(twai_node_enable(node_hdl)); //waiting pkg receive finish - TEST_ESP_OK(twai_node_transmit_wait_all_done(node_hdl, -1)); + twai_node_status_t status = {}; + while (status.tx_queue_remaining < TEST_TWAI_QUEUE_DEPTH) { + TEST_ESP_OK(twai_node_get_info(node_hdl, &status, NULL)); + printf("%ld\n", status.tx_queue_remaining); + } free(tx_msgs); // check if pkg receive correct diff --git a/components/esp_driver_twai/test_apps/test_twai/pytest_driver_twai.py b/components/esp_driver_twai/test_apps/test_twai/pytest_driver_twai.py index 846b2bf8dcec..6c22dad48866 100644 --- a/components/esp_driver_twai/test_apps/test_twai/pytest_driver_twai.py +++ b/components/esp_driver_twai/test_apps/test_twai/pytest_driver_twai.py @@ -2,7 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 import subprocess -from time import sleep +import time import pytest from can import Bus @@ -12,6 +12,9 @@ from pytest_embedded_idf.utils import soc_filtered_targets +# --------------------------------------------------------------------------- +# Loop Back Tests +# --------------------------------------------------------------------------- @pytest.mark.generic @pytest.mark.temp_skip_ci(targets=['esp32p4'], reason='p4 rev3 migration, IDF-14393') @pytest.mark.parametrize('config', ['release', 'cache_safe'], indirect=True) @@ -20,43 +23,77 @@ def test_driver_twai_loopbk(dut: Dut) -> None: dut.run_all_single_board_cases(group='twai', reset=True) -# -------------------------------- test twai interactive ------------------------------ +# --------------------------------------------------------------------------- +# Helper Functions +# --------------------------------------------------------------------------- + + +def esp_enter_flash_mode(dut: Dut) -> None: + ser = dut.serial.proc + ser.setRTS(True) # EN Low + time.sleep(0.5) + ser.setDTR(True) # GPIO0 Low + ser.setRTS(False) # EN High + dut.expect('waiting for download', timeout=2) + ser.setDTR(False) # Back RTS/DTR to 1/1 to avoid affect to esptool + + +def esp_reset_and_wait_ready(dut: Dut) -> None: + dut.serial.hard_reset() + time.sleep(0.5) + dut.expect_exact('Press ENTER to see the list of tests') + + @pytest.fixture(name='socket_can') def fixture_create_socket_can() -> Bus: # Set up the socket CAN with the bitrate - start_command = 'sudo ip link set can0 up type can bitrate 250000' - stop_command = 'sudo ip link set can0 down' + start_command = 'sudo -n ip link set can0 up type can bitrate 250000 restart-ms 100' + stop_command = 'sudo -n ip link set can0 down' + status_command = 'sudo -n ip -details link show can0' + try: + result = subprocess.run(status_command, shell=True, capture_output=True, text=True) + if result.returncode != 0: + raise Exception('CAN interface "can0" not found') + + if 'UP' in result.stdout: # Close the bus anyway if it is already up + subprocess.run(stop_command, shell=True, capture_output=True, text=True) subprocess.run(start_command, shell=True, capture_output=True, text=True) + + time.sleep(0.5) + bus = Bus(interface='socketcan', channel='can0', bitrate=250000) + yield bus # test invoked here + + bus.shutdown() except Exception as e: - print(f'Open bus Error: {e}') - bus = Bus(interface='socketcan', channel='can0', bitrate=250000) - yield bus # test invoked here - bus.shutdown() - subprocess.run(stop_command, shell=True, capture_output=True, text=True) + pytest.skip(f'Open usb-can bus Error: {str(e)}') + finally: + subprocess.run(stop_command, shell=True, capture_output=True, text=True) +# --------------------------------------------------------------------------- +# Interactive Tests +# --------------------------------------------------------------------------- @pytest.mark.twai_std @pytest.mark.temp_skip_ci(targets=['esp32p4'], reason='p4 rev3 migration, IDF-14393') @pytest.mark.parametrize('config', ['release'], indirect=True) @idf_parametrize('target', soc_filtered_targets('SOC_TWAI_SUPPORTED == 1'), indirect=['target']) def test_driver_twai_listen_only(dut: Dut, socket_can: Bus) -> None: - dut.serial.hard_reset() - dut.expect_exact('Press ENTER to see the list of tests') + esp_reset_and_wait_ready(dut) dut.write('"twai_listen_only"') # wait the DUT to finish initialize - sleep(0.1) + time.sleep(0.1) message = Message( arbitration_id=0x6688, is_extended_id=True, data=[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88], ) - print('USB Socket CAN Send:', message) - socket_can.send(message, timeout=0.2) + print('USB Socket CAN Send:', message, 'Return:', socket_can.send(message)) dut.expect_unity_test_output(timeout=10) + esp_enter_flash_mode(dut) @pytest.mark.twai_std @@ -64,8 +101,7 @@ def test_driver_twai_listen_only(dut: Dut, socket_can: Bus) -> None: @pytest.mark.parametrize('config', ['release'], indirect=True) @idf_parametrize('target', soc_filtered_targets('SOC_TWAI_SUPPORTED == 1'), indirect=['target']) def test_driver_twai_remote_request(dut: Dut, socket_can: Bus) -> None: - dut.serial.hard_reset() - dut.expect_exact('Press ENTER to see the list of tests') + esp_reset_and_wait_ready(dut) dut.write('"twai_remote_request"') @@ -83,4 +119,6 @@ def test_driver_twai_remote_request(dut: Dut, socket_can: Bus) -> None: ) socket_can.send(reply, timeout=0.2) print('USB Socket CAN Replied:', reply) + dut.expect_unity_test_output(timeout=10) + esp_enter_flash_mode(dut) diff --git a/components/esp_driver_uart/src/uart.c b/components/esp_driver_uart/src/uart.c index d8eaa6ba0b4d..77715f82fd24 100644 --- a/components/esp_driver_uart/src/uart.c +++ b/components/esp_driver_uart/src/uart.c @@ -1989,9 +1989,15 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b uart_hal_disable_intr_mask(&(uart_context[uart_num].hal), UART_LL_INTR_MASK); uart_hal_clr_intsts_mask(&(uart_context[uart_num].hal), UART_LL_INTR_MASK); - ret = esp_intr_alloc(uart_periph_signal[uart_num].irq, intr_alloc_flags, - uart_rx_intr_handler_default, p_uart_obj[uart_num], - &p_uart_obj[uart_num]->intr_handle); + ret = esp_intr_alloc_intrstatus( + uart_periph_signal[uart_num].irq, + intr_alloc_flags, + (uint32_t)uart_hal_get_intr_status_reg(&(uart_context[uart_num].hal)), + UART_LL_INTR_MASK, + uart_rx_intr_handler_default, + p_uart_obj[uart_num], + &p_uart_obj[uart_num]->intr_handle + ); ESP_GOTO_ON_ERROR(ret, err, UART_TAG, "Could not allocate an interrupt for UART"); // Make sure uart sclk at least exist first (following code touchs hardware, and requires sclk to be enabled) diff --git a/components/esp_driver_uart/src/uhci.c b/components/esp_driver_uart/src/uhci.c index b0a40a6a2354..e390d047c9ed 100644 --- a/components/esp_driver_uart/src/uhci.c +++ b/components/esp_driver_uart/src/uhci.c @@ -269,7 +269,7 @@ static void uhci_do_transmit(uhci_controller_handle_t uhci_ctrl, uhci_transactio .length = trans->buffer_size, .flags = { .mark_eof = true, - .mark_final = true, + .mark_final = GDMA_FINAL_LINK_TO_NULL, } }; @@ -332,7 +332,7 @@ esp_err_t uhci_receive(uhci_controller_handle_t uhci_ctrl, uint8_t *read_buffer, .buffer_alignment = buffer_alignment, .length = uhci_ctrl->rx_dir.buffer_size_per_desc_node[i], .flags = { - .mark_final = false, + .mark_final = GDMA_FINAL_LINK_TO_DEFAULT, } }; ESP_LOGD(TAG, "The DMA node %d has %d byte", i, uhci_ctrl->rx_dir.buffer_size_per_desc_node[i]); diff --git a/components/esp_https_ota/src/esp_https_ota.c b/components/esp_https_ota/src/esp_https_ota.c index 7f0e2e561777..bdfbdeaee4de 100644 --- a/components/esp_https_ota/src/esp_https_ota.c +++ b/components/esp_https_ota/src/esp_https_ota.c @@ -345,8 +345,9 @@ esp_err_t esp_https_ota_begin(const esp_https_ota_config_t *ota_config, esp_http if (ota_config->ota_resumption) { // We allow resumption only if we have minimum buffer size already written to flash if (ota_config->ota_image_bytes_written >= DEFAULT_OTA_BUF_SIZE) { - ESP_LOGI(TAG, "Valid OTA resumption case, offset %d", ota_config->ota_image_bytes_written); - https_ota_handle->binary_file_len = ota_config->ota_image_bytes_written; + // For FE case the flash is written in multiples of 16 bytes. So, we need to align the offset to 16 bytes. + https_ota_handle->binary_file_len = esp_flash_encryption_enabled() ? (ota_config->ota_image_bytes_written & ~0xF) : ota_config->ota_image_bytes_written; + ESP_LOGD(TAG, "Resuming OTA from offset: %d", https_ota_handle->binary_file_len); } } @@ -499,14 +500,6 @@ esp_err_t esp_https_ota_begin(const esp_https_ota_config_t *ota_config, esp_http } const int alloc_size = MAX(ota_config->http_config->buffer_size, DEFAULT_OTA_BUF_SIZE); - if (ota_config->ota_resumption) { - if (esp_flash_encryption_enabled() && (alloc_size & 0xFU) != 0) { - // For FE case the flash is written in multiples of 16 bytes - ESP_LOGE(TAG, "Buffer size must be multiple of 16 bytes for FE and ota resumption case"); - goto http_cleanup; - } - } - if (ota_config->buffer_caps != 0) { https_ota_handle->ota_upgrade_buf = (char *)heap_caps_malloc(alloc_size, ota_config->buffer_caps); } else { diff --git a/components/esp_hw_support/dma/async_memcpy_cp_dma.c b/components/esp_hw_support/dma/async_memcpy_cp_dma.c index add2cec2e510..3e36e6b99798 100644 --- a/components/esp_hw_support/dma/async_memcpy_cp_dma.c +++ b/components/esp_hw_support/dma/async_memcpy_cp_dma.c @@ -232,7 +232,7 @@ static esp_err_t mcp_cpdma_memcpy(async_memcpy_context_t *ctx, void *dst, void * .length = n, .flags = { .mark_eof = true, // mark the last item as EOF, so the RX channel can also received an EOF list item - .mark_final = true, // using singly list, so terminate the link here + .mark_final = GDMA_FINAL_LINK_TO_NULL, // using singly list, so terminate the link here } } }; @@ -256,7 +256,7 @@ static esp_err_t mcp_cpdma_memcpy(async_memcpy_context_t *ctx, void *dst, void * .length = n, .flags = { .mark_eof = false, // EOF is set by TX side - .mark_final = true, // using singly list, so terminate the link here + .mark_final = GDMA_FINAL_LINK_TO_NULL, // using singly list, so terminate the link here } } }; diff --git a/components/esp_hw_support/dma/async_memcpy_gdma.c b/components/esp_hw_support/dma/async_memcpy_gdma.c index a6e1047dd465..f53b0aaf489a 100644 --- a/components/esp_hw_support/dma/async_memcpy_gdma.c +++ b/components/esp_hw_support/dma/async_memcpy_gdma.c @@ -365,7 +365,7 @@ static esp_err_t mcp_gdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *s .length = n, .flags = { .mark_eof = true, // mark the last item as EOF, so the RX channel can also received an EOF list item - .mark_final = true, // using singly list, so terminate the link here + .mark_final = GDMA_FINAL_LINK_TO_NULL, // using singly list, so terminate the link here } } }; diff --git a/components/esp_hw_support/dma/esp_dma_utils.c b/components/esp_hw_support/dma/esp_dma_utils.c index ad97b84e7eaf..a820291bcd3f 100644 --- a/components/esp_hw_support/dma/esp_dma_utils.c +++ b/components/esp_hw_support/dma/esp_dma_utils.c @@ -29,7 +29,7 @@ esp_err_t esp_dma_split_rx_buffer_to_cache_aligned(void *rx_buffer, size_t buffe { esp_err_t ret = ESP_OK; uint8_t* stash_buffer = NULL; - ESP_RETURN_ON_FALSE(rx_buffer && buffer_len && align_buf_array, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE_ISR(rx_buffer && buffer_len && align_buf_array, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); // read the cache line size of internal and external memory, we also use this information to check if a given memory is behind the cache size_t int_mem_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA); @@ -41,80 +41,83 @@ esp_err_t esp_dma_split_rx_buffer_to_cache_aligned(void *rx_buffer, size_t buffe } else if (esp_ptr_internal(rx_buffer)) { split_line_size = int_mem_cache_line_size; } - ESP_LOGV(TAG, "split_line_size:%zu", split_line_size); - - // allocate the stash buffer from internal RAM - // Note, the split_line_size can be 0, in this case, the stash_buffer is also NULL, which is fine - stash_buffer = heap_caps_calloc(2, split_line_size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); - ESP_RETURN_ON_FALSE(!(split_line_size && !stash_buffer), ESP_ERR_NO_MEM, TAG, "no mem for stash buffer"); + bool align_required = split_line_size > 0; + ESP_EARLY_LOGV(TAG, "split_line_size:%zu", split_line_size); + + if (*ret_stash_buffer == NULL) { + // If the stash buffer is not offered by the caller, allocate the stash buffer from internal RAM + // Note, the split_line_size can be 0, in this case, the stash_buffer is also NULL, which is fine + stash_buffer = heap_caps_calloc(2, split_line_size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); + ESP_RETURN_ON_FALSE_ISR(!(split_line_size && !stash_buffer), ESP_ERR_NO_MEM, TAG, "no mem for stash buffer"); + } else { + // If the stash buffer is offered by the caller, check if it is aligned + ESP_RETURN_ON_FALSE_ISR(split_line_size == 0 || (uintptr_t)(*ret_stash_buffer) % split_line_size == 0, + ESP_ERR_INVALID_ARG, TAG, "the offered stash buffer is not aligned"); + // If the stash buffer is offered by the caller, use it + stash_buffer = *ret_stash_buffer; + } // clear align_array to avoid garbage data memset(align_buf_array, 0, sizeof(dma_buffer_split_array_t)); bool need_cache_sync[3] = {false}; - // if split_line_size is non-zero, split the buffer into head, body and tail - if (split_line_size > 0) { + // if align_required, split the buffer into head, body and tail + if (align_required) { // calculate head_overflow_len size_t head_overflow_len = (uintptr_t)rx_buffer % split_line_size; head_overflow_len = head_overflow_len ? split_line_size - head_overflow_len : 0; - ESP_LOGV(TAG, "head_addr:%p head_overflow_len:%zu", rx_buffer, head_overflow_len); + ESP_EARLY_LOGV(TAG, "head_addr:%p head_overflow_len:%zu", rx_buffer, head_overflow_len); // calculate tail_overflow_len size_t tail_overflow_len = ((uintptr_t)rx_buffer + buffer_len) % split_line_size; - ESP_LOGV(TAG, "tail_addr:%p tail_overflow_len:%zu", rx_buffer + buffer_len - tail_overflow_len, tail_overflow_len); + ESP_EARLY_LOGV(TAG, "tail_addr:%p tail_overflow_len:%zu", rx_buffer + buffer_len - tail_overflow_len, tail_overflow_len); + // special handling when input_buffer length is no more than buffer alignment + bool is_small_buf = head_overflow_len >= buffer_len || tail_overflow_len >= buffer_len; uint8_t extra_buf_count = 0; uint8_t* input_buffer = (uint8_t*)rx_buffer; - align_buf_array->buf.head.recovery_address = input_buffer; - align_buf_array->buf.head.aligned_buffer = stash_buffer + split_line_size * extra_buf_count++; - align_buf_array->buf.head.length = head_overflow_len; - need_cache_sync[0] = int_mem_cache_line_size > 0; - align_buf_array->buf.body.recovery_address = input_buffer + head_overflow_len; - align_buf_array->buf.body.aligned_buffer = input_buffer + head_overflow_len; - align_buf_array->buf.body.length = buffer_len - head_overflow_len - tail_overflow_len; - need_cache_sync[1] = true; - align_buf_array->buf.tail.recovery_address = input_buffer + buffer_len - tail_overflow_len; - align_buf_array->buf.tail.aligned_buffer = stash_buffer + split_line_size * extra_buf_count++; - align_buf_array->buf.tail.length = tail_overflow_len; - need_cache_sync[2] = int_mem_cache_line_size > 0; - - // special handling when input_buffer length is no more than buffer alignment - if (head_overflow_len >= buffer_len || tail_overflow_len >= buffer_len) { - align_buf_array->buf.head.length = buffer_len ; - align_buf_array->buf.body.length = 0 ; - align_buf_array->buf.tail.length = 0 ; + if (head_overflow_len || is_small_buf) { + align_buf_array->buf.head.recovery_address = input_buffer; + align_buf_array->buf.head.aligned_buffer = stash_buffer + split_line_size * extra_buf_count++; + align_buf_array->buf.head.length = is_small_buf ? buffer_len : head_overflow_len; + need_cache_sync[0] = int_mem_cache_line_size > 0; + } + int body_len = (int)buffer_len - (int)head_overflow_len - (int)tail_overflow_len; + if (body_len > 0) { + align_buf_array->buf.body.recovery_address = input_buffer + head_overflow_len; + align_buf_array->buf.body.aligned_buffer = input_buffer + head_overflow_len; + align_buf_array->buf.body.length = body_len; + need_cache_sync[1] = true; + } + if (tail_overflow_len && !is_small_buf) { + align_buf_array->buf.tail.recovery_address = input_buffer + buffer_len - tail_overflow_len; + align_buf_array->buf.tail.aligned_buffer = stash_buffer + split_line_size * extra_buf_count++; + align_buf_array->buf.tail.length = tail_overflow_len; + need_cache_sync[2] = int_mem_cache_line_size > 0; } } else { align_buf_array->buf.body.aligned_buffer = rx_buffer; align_buf_array->buf.body.recovery_address = rx_buffer; align_buf_array->buf.body.length = buffer_len; - need_cache_sync[1] = false; - } - - for (int i = 0; i < 3; i++) { - if (align_buf_array->aligned_buffer[i].length == 0) { - align_buf_array->aligned_buffer[i].aligned_buffer = NULL; - align_buf_array->aligned_buffer[i].recovery_address = NULL; - need_cache_sync[i] = false; - } } // invalidate the aligned buffer if necessary for (int i = 0; i < 3; i++) { - if (need_cache_sync[i]) { - size_t sync_size = align_buf_array->aligned_buffer[i].length; + size_t sync_size = align_buf_array->aligned_buffer[i].length; + if (need_cache_sync[i] && sync_size > 0) { if (sync_size < split_line_size) { // If the buffer is smaller than the cache line size, we need to sync the whole buffer sync_size = split_line_size; } esp_err_t res = esp_cache_msync(align_buf_array->aligned_buffer[i].aligned_buffer, sync_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); - ESP_GOTO_ON_ERROR(res, err, TAG, "failed to do cache sync"); + ESP_GOTO_ON_ERROR_ISR(res, err, TAG, "failed to do cache sync"); } } *ret_stash_buffer = stash_buffer; return ESP_OK; err: - if (stash_buffer) { + // Only free the stash buffer if it is not offered by the caller + if (stash_buffer && *ret_stash_buffer == NULL) { free(stash_buffer); } return ret; diff --git a/components/esp_hw_support/dma/gdma_link.c b/components/esp_hw_support/dma/gdma_link.c index 1011abb224a5..8994399d0ef2 100644 --- a/components/esp_hw_support/dma/gdma_link.c +++ b/components/esp_hw_support/dma/gdma_link.c @@ -6,6 +6,8 @@ #include #include +#include +#include #include #include "soc/soc_caps.h" #include "esp_log.h" @@ -74,6 +76,8 @@ esp_err_t gdma_new_link_list(const gdma_link_list_config_t *config, gdma_link_li size_t item_alignment = config->item_alignment ? config->item_alignment : 4; // each list item should align to the specified alignment size_t item_size = ALIGN_UP(sizeof(gdma_link_list_item_t), item_alignment); + // guard against overflow when calculating total bytes for descriptors + ESP_GOTO_ON_FALSE(num_items <= SIZE_MAX / item_size, ESP_ERR_INVALID_SIZE, err, TAG, "list too big"); uint32_t list_items_mem_caps = MALLOC_CAP_8BIT | MALLOC_CAP_DMA; if (config->flags.items_in_ext_mem) { @@ -130,14 +134,13 @@ esp_err_t gdma_del_link_list(gdma_link_list_handle_t list) esp_err_t gdma_link_mount_buffers(gdma_link_list_handle_t list, int start_item_index, const gdma_buffer_mount_config_t *buf_config_array, size_t num_buf, int *end_item_index) { - if(!list || !buf_config_array || !num_buf) { + if (!list || !buf_config_array || !num_buf) { return ESP_ERR_INVALID_ARG; } size_t item_size = list->item_size; uint32_t list_item_capacity = list->num_items; // ensure the start_item_index is between 0 and `list_item_capacity - 1` start_item_index = (start_item_index % list_item_capacity + list_item_capacity) % list_item_capacity; - uint32_t begin_item_idx = start_item_index; gdma_link_list_item_t *lli_nc = NULL; uint32_t num_items_avail = 0; @@ -147,6 +150,9 @@ esp_err_t gdma_link_mount_buffers(gdma_link_list_handle_t list, int start_item_i lli_nc = (gdma_link_list_item_t *)(list->items_nc + (i + start_item_index) % list_item_capacity * item_size); if (lli_nc->dw0.owner == GDMA_LLI_OWNER_CPU) { num_items_avail++; + } else { + // if the DMA descriptor "write back" feature is not enabled, descriptor is always owned by DMA after being used + break; } } } else { @@ -154,31 +160,36 @@ esp_err_t gdma_link_mount_buffers(gdma_link_list_handle_t list, int start_item_i } // check alignment and length for each buffer + uint32_t remaining = num_items_avail; for (size_t bi = 0; bi < num_buf; bi++) { const gdma_buffer_mount_config_t *config = &buf_config_array[bi]; uint8_t *buf = (uint8_t *)config->buffer; size_t len = config->length; + // zero-length/NULL buffers don't consume a slot in pre-check + if (len == 0 || buf == NULL) { + continue; + } size_t buffer_alignment = config->buffer_alignment; if (buffer_alignment == 0) { buffer_alignment = 1; } - // check the buffer alignment - ESP_RETURN_ON_FALSE_ISR((buffer_alignment & (buffer_alignment - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "invalid buffer alignment: %"PRIu32"", buffer_alignment); + // alignment must be a power of 2 + ESP_RETURN_ON_FALSE_ISR((buffer_alignment & (buffer_alignment - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "align err idx=%"PRIu32" align=%"PRIu32, bi, buffer_alignment); size_t max_buffer_mount_length = ALIGN_DOWN(GDMA_MAX_BUFFER_SIZE_PER_LINK_ITEM, buffer_alignment); if (!config->flags.bypass_buffer_align_check) { - ESP_RETURN_ON_FALSE_ISR(((uintptr_t)buf & (buffer_alignment - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "buffer not aligned to %"PRIu32"", buffer_alignment); + ESP_RETURN_ON_FALSE_ISR(((uintptr_t)buf & (buffer_alignment - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "buf misalign idx=%"PRIu32" align=%"PRIu32, bi, buffer_alignment); } - uint32_t num_items_need = (len + max_buffer_mount_length - 1) / max_buffer_mount_length; - // check if there are enough link list items - ESP_RETURN_ON_FALSE_ISR((begin_item_idx + num_items_need) <= (start_item_index + num_items_avail), ESP_ERR_INVALID_ARG, TAG, "no more space for buffer mounting"); - begin_item_idx += num_items_need; + size_t num_items_need = (len + max_buffer_mount_length - 1) / max_buffer_mount_length; + ESP_RETURN_ON_FALSE_ISR(num_items_need <= remaining, ESP_ERR_INVALID_ARG, TAG, + "lli full start=%d need=%"PRIu32" avail=%"PRIu32, start_item_index, num_items_need, remaining); + remaining -= num_items_need; } // link_nodes[start_item_index-1] --> link_nodes[start_item_index] lli_nc = (gdma_link_list_item_t *)(list->items_nc + (start_item_index + list_item_capacity - 1) % list_item_capacity * item_size); lli_nc->next = (gdma_link_list_item_t *)(list->items + start_item_index * item_size); - begin_item_idx = start_item_index; + int begin_item_idx = start_item_index; for (size_t bi = 0; bi < num_buf; bi++) { const gdma_buffer_mount_config_t *config = &buf_config_array[bi]; uint8_t *buf = (uint8_t *)config->buffer; @@ -188,13 +199,16 @@ esp_err_t gdma_link_mount_buffers(gdma_link_list_handle_t list, int start_item_i buffer_alignment = 1; } size_t max_buffer_mount_length = ALIGN_DOWN(GDMA_MAX_BUFFER_SIZE_PER_LINK_ITEM, buffer_alignment); - // skip zero-length buffer + // skip zero-length buffer but scrub any stale descriptor to keep ring clean; no slot consumption if (len == 0 || buf == NULL) { + lli_nc = (gdma_link_list_item_t *)(list->items_nc + begin_item_idx % list_item_capacity * item_size); + // reset the descriptor, especially the owner and next fields + memset(lli_nc, 0, item_size); continue; } - uint32_t num_items_need = (len + max_buffer_mount_length - 1) / max_buffer_mount_length; + size_t num_items_need = (len + max_buffer_mount_length - 1) / max_buffer_mount_length; // mount the buffer to the link list - for (int i = 0; i < num_items_need; i++) { + for (size_t i = 0; i < num_items_need; i++) { lli_nc = (gdma_link_list_item_t *)(list->items_nc + (i + begin_item_idx) % list_item_capacity * item_size); lli_nc->buffer = buf; lli_nc->dw0.length = len > max_buffer_mount_length ? max_buffer_mount_length : len; @@ -203,10 +217,24 @@ esp_err_t gdma_link_mount_buffers(gdma_link_list_handle_t list, int start_item_i lli_nc->dw0.size = lli_nc->dw0.length; // mark the EOF node lli_nc->dw0.suc_eof = (config->flags.mark_eof == 1) && (i == num_items_need - 1); - // mark the final node - if ((config->flags.mark_final == 1) && (i == num_items_need - 1)) { - lli_nc->next = NULL; + if (i == num_items_need - 1) { + // mark the final node + switch (config->flags.mark_final) { + case GDMA_FINAL_LINK_TO_NULL: + lli_nc->next = NULL; + break; + case GDMA_FINAL_LINK_TO_HEAD: + lli_nc->next = (gdma_link_list_item_t *)(list->items); + break; + case GDMA_FINAL_LINK_TO_START: + lli_nc->next = (gdma_link_list_item_t *)(list->items + start_item_index * item_size); + break; + default: + lli_nc->next = (gdma_link_list_item_t *)(list->items + (i + begin_item_idx + 1) % list_item_capacity * item_size); + break; + } } else { + // DMA expects cached addresses in `next` lli_nc->next = (gdma_link_list_item_t *)(list->items + (i + begin_item_idx + 1) % list_item_capacity * item_size); } lli_nc->dw0.owner = GDMA_LLI_OWNER_DMA; @@ -233,7 +261,7 @@ uintptr_t gdma_link_get_head_addr(gdma_link_list_handle_t list) esp_err_t gdma_link_concat(gdma_link_list_handle_t first_link, int first_link_item_index, gdma_link_list_handle_t second_link, int second_link_item_index) { - if(!(first_link && second_link)) { + if (!(first_link && second_link)) { return ESP_ERR_INVALID_ARG; } gdma_link_list_item_t *lli_nc = NULL; @@ -264,7 +292,7 @@ esp_err_t gdma_link_set_owner(gdma_link_list_handle_t list, int item_index, gdma esp_err_t gdma_link_get_owner(gdma_link_list_handle_t list, int item_index, gdma_lli_owner_t *owner) { - if(!list || !owner) { + if (!list || !owner) { return ESP_ERR_INVALID_ARG; } int num_items = list->num_items; diff --git a/components/esp_hw_support/dma/include/esp_private/gdma_link.h b/components/esp_hw_support/dma/include/esp_private/gdma_link.h index 6bccb4c8023f..37d9964a3a16 100644 --- a/components/esp_hw_support/dma/include/esp_private/gdma_link.h +++ b/components/esp_hw_support/dma/include/esp_private/gdma_link.h @@ -56,6 +56,17 @@ esp_err_t gdma_new_link_list(const gdma_link_list_config_t *config, gdma_link_li */ esp_err_t gdma_del_link_list(gdma_link_list_handle_t list); +/** + * @brief Types for the next node of the final item in the DMA link list + * + */ +typedef enum { + GDMA_FINAL_LINK_TO_DEFAULT = 0, /*!< The next node is linked to the default next item in the link list */ + GDMA_FINAL_LINK_TO_NULL = 1, /*!< No next node is linked */ + GDMA_FINAL_LINK_TO_HEAD = 2, /*!< The next node is linked to the head item in the link list */ + GDMA_FINAL_LINK_TO_START = 3, /*!< The next node is linked to the start item in the link list */ +} gdma_final_node_link_type_t; + /** * @brief DMA buffer mount configurations */ @@ -66,12 +77,11 @@ typedef struct { struct gdma_buffer_mount_flags { uint32_t mark_eof: 1; /*!< Whether to mark the list item as the "EOF" item. Note, an "EOF" descriptor can be interrupted differently by peripheral. - But it doesn't mean to terminate a DMA link (use `mark_final` instead). + But it doesn't mean to terminate a DMA link (set `mark_final` to GDMA_FINAL_LINK_TO_NULL instead). EOF link list item can also trigger an interrupt. */ - uint32_t mark_final: 1; /*!< Whether to terminate the DMA link list at this item. - Note, DMA engine will stop at this item and trigger an interrupt. - If `mark_final` is not set, this list item will point to the next item, and - wrap around to the head item if it's the last one in the list. */ + gdma_final_node_link_type_t mark_final: 2; /*!< Specify the next item of the final item of this mount. + For the other items that not the final one, it will be linked to the next item automatically and this field takes no effect. + Note, the final item here does not mean the last item in the link list. It is `start_item_index + num_items - 1` */ uint32_t bypass_buffer_align_check: 1; /*!< Whether to bypass the buffer alignment check. Only enable it when you know what you are doing. */ } flags; //!< Flags for buffer mount configurations diff --git a/components/esp_hw_support/port/esp32c5/Kconfig.hw_support b/components/esp_hw_support/port/esp32c5/Kconfig.hw_support index f5bc18b3b7fd..d90eb136f3e2 100644 --- a/components/esp_hw_support/port/esp32c5/Kconfig.hw_support +++ b/components/esp_hw_support/port/esp32c5/Kconfig.hw_support @@ -11,11 +11,14 @@ choice ESP32C5_REV_MIN config ESP32C5_REV_MIN_100 bool "Rev v1.0 (ECO2)" + config ESP32C5_REV_MIN_102 + bool "Rev v1.2" endchoice config ESP32C5_REV_MIN_FULL int default 100 if ESP32C5_REV_MIN_100 + default 102 if ESP32C5_REV_MIN_102 config ESP_REV_MIN_FULL int diff --git a/components/esp_hw_support/port/esp32c61/Kconfig.hw_support b/components/esp_hw_support/port/esp32c61/Kconfig.hw_support index 71e247da3547..754f14c975ab 100644 --- a/components/esp_hw_support/port/esp32c61/Kconfig.hw_support +++ b/components/esp_hw_support/port/esp32c61/Kconfig.hw_support @@ -11,11 +11,14 @@ choice ESP32C61_REV_MIN config ESP32C61_REV_MIN_100 bool "Rev v1.0" + config ESP32C61_REV_MIN_101 + bool "Rev v1.1" endchoice config ESP32C61_REV_MIN_FULL int default 100 if ESP32C61_REV_MIN_100 + default 101 if ESP32C61_REV_MIN_101 config ESP_REV_MIN_FULL int diff --git a/components/esp_hw_support/test_apps/dma/main/test_gdma.c b/components/esp_hw_support/test_apps/dma/main/test_gdma.c index ce97d0a0b491..bb487b2a2b2b 100644 --- a/components/esp_hw_support/test_apps/dma/main/test_gdma.c +++ b/components/esp_hw_support/test_apps/dma/main/test_gdma.c @@ -278,7 +278,7 @@ static void test_gdma_m2m_transaction(gdma_channel_handle_t tx_chan, gdma_channe #if !SOC_DMA_CAN_ACCESS_FLASH .flags = { .mark_eof = true, - .mark_final = true, // using singly list, so terminate the link here + .mark_final = GDMA_FINAL_LINK_TO_NULL, // using singly list, so terminate the link here } #endif }, @@ -289,7 +289,7 @@ static void test_gdma_m2m_transaction(gdma_channel_handle_t tx_chan, gdma_channe .length = src_string_len, .flags = { .mark_eof = true, - .mark_final = true, // using singly list, so terminate the link here + .mark_final = GDMA_FINAL_LINK_TO_NULL, // using singly list, so terminate the link here } }, #endif @@ -448,7 +448,7 @@ static void test_gdma_m2m_unaligned_buffer_test(uint8_t *dst_data, uint8_t *src_ .length = data_length, .flags = { .mark_eof = true, - .mark_final = true, // using singly list, so terminate the link here + .mark_final = GDMA_FINAL_LINK_TO_NULL, // using singly list, so terminate the link here } } }; @@ -609,7 +609,7 @@ TEST_CASE("GDMA M2M Unaligned RX Buffer Test", "[GDMA][M2M]") .length = COPY_SIZE, .flags = { .mark_eof = true, - .mark_final = true, // using singly list, so terminate the link here + .mark_final = GDMA_FINAL_LINK_TO_NULL, // using singly list, so terminate the link here } }; TEST_ESP_OK(gdma_link_mount_buffers(tx_link_list, 0, &tx_buf_mount_config, 1, NULL)); @@ -619,7 +619,7 @@ TEST_CASE("GDMA M2M Unaligned RX Buffer Test", "[GDMA][M2M]") .buffer_alignment = 32, .length = COPY_SIZE, .flags = { - .mark_final = true, // using singly list, so terminate the link here + .mark_final = GDMA_FINAL_LINK_TO_NULL, // using singly list, so terminate the link here } }; TEST_ESP_OK(gdma_link_mount_buffers(rx_link_list, 0, &rx_buf_mount_config, 1, NULL)); diff --git a/components/esp_lcd/dsi/include/esp_lcd_mipi_dsi.h b/components/esp_lcd/dsi/include/esp_lcd_mipi_dsi.h index 1a78873968df..99917c7742b2 100644 --- a/components/esp_lcd/dsi/include/esp_lcd_mipi_dsi.h +++ b/components/esp_lcd/dsi/include/esp_lcd_mipi_dsi.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ diff --git a/components/esp_lcd/i80/esp_lcd_panel_io_i2s.c b/components/esp_lcd/i80/esp_lcd_panel_io_i2s.c index bfd15c0d2995..83c69180753d 100644 --- a/components/esp_lcd/i80/esp_lcd_panel_io_i2s.c +++ b/components/esp_lcd/i80/esp_lcd_panel_io_i2s.c @@ -523,7 +523,7 @@ static esp_err_t panel_io_i80_tx_param(esp_lcd_panel_io_t *io, int lcd_cmd, cons gdma_buffer_mount_config_t mount_config = { .flags = { .mark_eof = true, - .mark_final = true, // singly link list, mark final descriptor + .mark_final = GDMA_FINAL_LINK_TO_NULL, // singly link list, mark final descriptor } }; @@ -599,7 +599,7 @@ static esp_err_t panel_io_i80_tx_color(esp_lcd_panel_io_t *io, int lcd_cmd, cons gdma_buffer_mount_config_t mount_config = { .flags = { .mark_eof = true, - .mark_final = true, // singly link list, mark final descriptor + .mark_final = GDMA_FINAL_LINK_TO_NULL, // singly link list, mark final descriptor } }; @@ -724,7 +724,7 @@ static void i2s_lcd_trigger_quick_trans_done_event(esp_lcd_i80_bus_handle_t bus) .length = 4, .flags = { .mark_eof = true, // mark the "EOF" flag to trigger I2S EOF interrupt - .mark_final = true, // singly link list, mark final descriptor + .mark_final = GDMA_FINAL_LINK_TO_NULL, // singly link list, mark final descriptor } }; gdma_link_mount_buffers(bus->dma_link, 0, &mount_config, 1, NULL); @@ -811,7 +811,7 @@ static IRAM_ATTR void i2s_lcd_default_isr_handler(void *args) .length = trans_desc->data_length, .flags = { .mark_eof = true, - .mark_final = true, // singly link list, mark final descriptor + .mark_final = GDMA_FINAL_LINK_TO_NULL, // singly link list, mark final descriptor } }; gdma_link_mount_buffers(bus->dma_link, 0, &mount_config, 1, NULL); diff --git a/components/esp_lcd/i80/esp_lcd_panel_io_i80.c b/components/esp_lcd/i80/esp_lcd_panel_io_i80.c index f0ffb1b1a4a7..8a94b8c904c3 100644 --- a/components/esp_lcd/i80/esp_lcd_panel_io_i80.c +++ b/components/esp_lcd/i80/esp_lcd_panel_io_i80.c @@ -500,7 +500,7 @@ static esp_err_t panel_io_i80_tx_param(esp_lcd_panel_io_t *io, int lcd_cmd, cons .length = trans_desc->data_length, .flags = { .mark_eof = true, - .mark_final = true, // singly link list, mark final descriptor + .mark_final = GDMA_FINAL_LINK_TO_NULL, // singly link list, mark final descriptor } }; gdma_link_mount_buffers(bus->dma_link, 0, &mount_config, 1, NULL); @@ -618,7 +618,7 @@ static esp_err_t lcd_i80_init_dma_link(esp_lcd_i80_bus_handle_t bus, const esp_l gdma_apply_strategy(bus->dma_chan, &strategy_config); // config DMA transfer parameters gdma_transfer_config_t trans_cfg = { - .max_data_burst_size = bus_config->dma_burst_size ? bus_config->dma_burst_size : 16, // Enable DMA burst transfer for better performance + .max_data_burst_size = bus_config->dma_burst_size ? bus_config->dma_burst_size : 32, // Enable DMA burst transfer for better performance .access_ext_mem = true, // the LCD can carry pixel buffer from the external memory }; ESP_RETURN_ON_ERROR(gdma_config_transfer(bus->dma_chan, &trans_cfg), TAG, "config DMA transfer failed"); @@ -821,7 +821,7 @@ IRAM_ATTR static void i80_lcd_default_isr_handler(void *args) .length = trans_desc->data_length, .flags = { .mark_eof = true, - .mark_final = true, // singly link list, mark final descriptor + .mark_final = GDMA_FINAL_LINK_TO_NULL, // singly link list, mark final descriptor } }; gdma_link_mount_buffers(bus->dma_link, 0, &mount_config, 1, NULL); diff --git a/components/esp_lcd/parl/esp_lcd_panel_io_parl.c b/components/esp_lcd/parl/esp_lcd_panel_io_parl.c index 1d96b035182f..efdba5926afd 100644 --- a/components/esp_lcd/parl/esp_lcd_panel_io_parl.c +++ b/components/esp_lcd/parl/esp_lcd_panel_io_parl.c @@ -33,7 +33,7 @@ #include "driver/parlio_tx.h" #include "driver/parlio_types.h" #include "esp_private/gpio.h" -#include "esp_private/parlio_private.h" +#include "esp_private/parlio_tx_private.h" #include "esp_lcd_panel_io_interface.h" #include "esp_lcd_panel_io.h" #include "esp_lcd_common.h" diff --git a/components/esp_lcd/rgb/esp_lcd_panel_rgb.c b/components/esp_lcd/rgb/esp_lcd_panel_rgb.c index 9fd7e91e3175..616e68bb6b2c 100644 --- a/components/esp_lcd/rgb/esp_lcd_panel_rgb.c +++ b/components/esp_lcd/rgb/esp_lcd_panel_rgb.c @@ -1052,7 +1052,7 @@ static esp_err_t lcd_rgb_panel_init_trans_link(esp_rgb_panel_t *rgb_panel) gdma_buffer_mount_config_t mount_cfg = { .length = rgb_panel->fb_size, .flags = { - .mark_final = rgb_panel->flags.stream_mode ? false : true, + .mark_final = rgb_panel->flags.stream_mode ? GDMA_FINAL_LINK_TO_DEFAULT : GDMA_FINAL_LINK_TO_NULL, .mark_eof = true, }, }; diff --git a/components/esp_lcd/spi/esp_lcd_panel_io_spi.c b/components/esp_lcd/spi/esp_lcd_panel_io_spi.c index 13a84ca50f01..e276ef9ea347 100644 --- a/components/esp_lcd/spi/esp_lcd_panel_io_spi.c +++ b/components/esp_lcd/spi/esp_lcd_panel_io_spi.c @@ -412,6 +412,9 @@ IRAM_ATTR static void lcd_spi_pre_trans_cb(spi_transaction_t *trans) if (spi_panel_io->dc_gpio_num >= 0) { // set D/C line level if necessary // use ll function to speed up gpio_ll_set_level(&GPIO, spi_panel_io->dc_gpio_num, lcd_trans->flags.dc_gpio_level); + + // ensure the D/C output is enabled + gpio_ll_output_enable(&GPIO, spi_panel_io->dc_gpio_num); } } @@ -419,6 +422,12 @@ static void lcd_spi_post_trans_color_cb(spi_transaction_t *trans) { esp_lcd_panel_io_spi_t *spi_panel_io = trans->user; lcd_spi_trans_descriptor_t *lcd_trans = __containerof(trans, lcd_spi_trans_descriptor_t, base); + + // disable the D/C output as we no longer need it + if (spi_panel_io->dc_gpio_num >= 0) { + gpio_ll_output_disable(&GPIO, spi_panel_io->dc_gpio_num); + } + if (lcd_trans->flags.en_trans_done_cb) { if (spi_panel_io->on_color_trans_done) { spi_panel_io->on_color_trans_done(&spi_panel_io->base, NULL, spi_panel_io->user_ctx); diff --git a/components/esp_mm/esp_cache_msync.c b/components/esp_mm/esp_cache_msync.c index e8af782c2657..d296da1a9b0e 100644 --- a/components/esp_mm/esp_cache_msync.c +++ b/components/esp_mm/esp_cache_msync.c @@ -32,6 +32,16 @@ DEFINE_CRIT_SECTION_LOCK_STATIC(s_spinlock); static _lock_t s_mutex; #endif +void esp_cache_sync_ops_enter_critical_section(void) +{ + esp_os_enter_critical_safe(&s_spinlock); +} + +void esp_cache_sync_ops_exit_critical_section(void) +{ + esp_os_exit_critical_safe(&s_spinlock); +} + #if SOC_CACHE_WRITEBACK_SUPPORTED static void s_c2m_ops(uint32_t vaddr, size_t size) { diff --git a/components/esp_mm/esp_mmu_map.c b/components/esp_mm/esp_mmu_map.c index 6bdd8420efb0..791b55d59d47 100644 --- a/components/esp_mm/esp_mmu_map.c +++ b/components/esp_mm/esp_mmu_map.c @@ -404,7 +404,9 @@ static void IRAM_ATTR NOINLINE_ATTR s_do_cache_invalidate(uint32_t vaddr_start, */ cache_sync(); #else //Other chips + esp_cache_sync_ops_enter_critical_section(); cache_hal_invalidate_addr(vaddr_start, size); + esp_cache_sync_ops_exit_critical_section(); #endif // CONFIG_IDF_TARGET_ESP32 } diff --git a/components/esp_mm/include/esp_private/esp_cache_private.h b/components/esp_mm/include/esp_private/esp_cache_private.h index 9768765b7372..fc09ed44f1b8 100644 --- a/components/esp_mm/include/esp_private/esp_cache_private.h +++ b/components/esp_mm/include/esp_private/esp_cache_private.h @@ -159,6 +159,16 @@ esp_err_t esp_cache_aligned_calloc(size_t n, size_t size, uint32_t heap_caps, vo __attribute__((deprecated("Use 'heap_caps_calloc' with MALLOC_CAP_CACHE_ALIGNED caps instead"))) esp_err_t esp_cache_aligned_calloc_prefer(size_t n, size_t size, void **out_ptr, size_t *actual_size, size_t flag_nums, ...); +/** + * @brief Enter critical section for cache sync operations + */ +void esp_cache_sync_ops_enter_critical_section(void); + +/** + * @brief Exit critical section for cache sync operations + */ +void esp_cache_sync_ops_exit_critical_section(void); + /** * @brief Get Cache alignment requirement for data * diff --git a/components/esp_rom/esp32c2/ld/esp32c2.rom.ble-eco4.ld b/components/esp_rom/esp32c2/ld/esp32c2.rom.ble-eco4.ld index a76958638a7b..12aa1d0ed757 100644 --- a/components/esp_rom/esp32c2/ld/esp32c2.rom.ble-eco4.ld +++ b/components/esp_rom/esp32c2/ld/esp32c2.rom.ble-eco4.ld @@ -811,7 +811,7 @@ r_ble_lll_scan_sched_next_aux = 0x40001700; r_ble_lll_scan_sched_remove = 0x40001704; //r_ble_lll_scan_start = 0x40001708; //r_ble_lll_scan_start_rx = 0x4000170c; -r_ble_lll_scan_stop = 0x40001710; +//r_ble_lll_scan_stop = 0x40001710; r_ble_lll_scan_targeta_is_matched = 0x40001714; r_ble_lll_scan_timer_cb = 0x40001718; r_ble_lll_sched_adv_new = 0x4000171c; diff --git a/components/freertos/test_apps/freertos/kernel/tasks/test_freertos_task_delete.c b/components/freertos/test_apps/freertos/kernel/tasks/test_freertos_task_delete.c index 727951210dab..4f72ba8a967a 100644 --- a/components/freertos/test_apps/freertos/kernel/tasks/test_freertos_task_delete.c +++ b/components/freertos/test_apps/freertos/kernel/tasks/test_freertos_task_delete.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -128,7 +128,8 @@ TEST_CASE("FreeRTOS Delete Blocked Tasks", "[freertos]") (1000 iterations takes about 9 seconds on ESP32 dual core) */ - for (unsigned iter = 0; iter < 1000; iter++) { + unsigned iter = 0; + for (iter = 0; iter < 1000; iter++) { // Create everything SemaphoreHandle_t sem = xSemaphoreCreateMutex(); for (unsigned i = 0; i < CONFIG_FREERTOS_NUMBER_OF_CORES + 1; i++) { @@ -144,14 +145,24 @@ TEST_CASE("FreeRTOS Delete Blocked Tasks", "[freertos]") vTaskDelay(5); // Let the tasks juggle the mutex for a bit for (unsigned i = 0; i < CONFIG_FREERTOS_NUMBER_OF_CORES + 1; i++) { + vTaskSuspend(blocking_tasks[i]); vTaskDelete(blocking_tasks[i]); params[i].deleted = true; } vTaskDelay(4); // Yield to the idle task for cleanup + // As an experiment, until IDFCI-4988 is fixed (CI runner loses connection to the target) + // let's occasionally print a message to the console to ensure the test is running + // and keep the console active. + if (iter && iter % 100 == 0) { + // In each iteration, we create CONFIG_FREERTOS_NUMBER_OF_CORES + 1 tasks + printf("Deleted %u blocked tasks\n", iter * (CONFIG_FREERTOS_NUMBER_OF_CORES + 1)); + } + vSemaphoreDelete(sem); // Check we haven't leaked resources yet TEST_ASSERT_GREATER_OR_EQUAL(before - 256, heap_caps_get_free_size(MALLOC_CAP_8BIT)); } + printf("Deleted %u blocked tasks\n", iter * (CONFIG_FREERTOS_NUMBER_OF_CORES + 1)); } diff --git a/components/freertos/test_apps/freertos/kernel/tasks/test_vTaskSuspendAll_xTaskResumeAll.c b/components/freertos/test_apps/freertos/kernel/tasks/test_vTaskSuspendAll_xTaskResumeAll.c index 746540bc0209..609fe2e55f6a 100644 --- a/components/freertos/test_apps/freertos/kernel/tasks/test_vTaskSuspendAll_xTaskResumeAll.c +++ b/components/freertos/test_apps/freertos/kernel/tasks/test_vTaskSuspendAll_xTaskResumeAll.c @@ -227,6 +227,12 @@ TEST_CASE("Test vTaskSuspendAll() and xTaskResumeAll() multicore", "[freertos]") for (int j = 0; j < 2; j++) { xSemaphoreTake(done_sem, portMAX_DELAY); } + + // Suspend the test tasks in case they haven't suspended themselves yet + vTaskSuspend(taskA_hdl); + vTaskSuspend(taskB_hdl); + vTaskDelay(10); + // Cleanup the tasks vTaskDelete(taskA_hdl); vTaskDelete(taskB_hdl); @@ -339,6 +345,8 @@ static void test_unblk_a1_task(void *arg) // Cleanup A2 and interrupt deregister_intr_cb(); + vTaskSuspend(a2_task_hdl); + vTaskDelay(10); vTaskDelete(a2_task_hdl); // Indicate done and wait to be deleted @@ -391,6 +399,11 @@ TEST_CASE("Test vTaskSuspendAll allows scheduling on other cores", "[freertos]") for (int j = 0; j < 2; j++) { xSemaphoreTake(test_unblk_done_sem, portMAX_DELAY); } + // Suspend the test tasks in case they haven't suspended themselves yet + vTaskSuspend(a1_task_hdl); + vTaskSuspend(b1_task_hdl); + vTaskDelay(10); + // Cleanup tasks vTaskDelete(a1_task_hdl); vTaskDelete(b1_task_hdl); @@ -515,6 +528,10 @@ TEST_CASE("Test vTaskSuspendAll doesn't block unpinned tasks from being schedule for (int i = 0; i < 2; i++) { xSemaphoreTake(test_unpinned_sem, portMAX_DELAY); } + // Suspend the test tasks in case they haven't suspended themselves yet + vTaskSuspend(pinned_task_hdl); + vTaskSuspend(unpinned_task_hdl); + vTaskDelay(10); // Cleanup vTaskDelete(pinned_task_hdl); @@ -643,8 +660,9 @@ TEST_CASE("Test xTaskResumeAll resumes pended tasks", "[freertos]") TEST_ASSERT_EQUAL(pdTRUE, xTaskCreatePinnedToCore(test_pended_running_task, "susp", 2048, (void *)xTaskGetCurrentTaskHandle(), UNITY_FREERTOS_PRIORITY + 1, &susp_tsk_hdl, i)); // Wait for to be notified to test completion ulTaskNotifyTake(pdTRUE, portMAX_DELAY); - // Add a short delay to allow the test_pended_running_task to go to suspend state - vTaskDelay(1); + // Suspend the test task in case it hasn't suspended itself yet + vTaskSuspend(susp_tsk_hdl); + vTaskDelay(10); vTaskDelete(susp_tsk_hdl); } // Add a short delay to allow the idle task to free any remaining task memory @@ -759,8 +777,12 @@ TEST_CASE("Test xTaskSuspendAll on all cores pends all tasks and xTaskResumeAll // Cleanup for (int i = 0; i < TEST_PENDED_NUM_BLOCKED_TASKS; i++) { + vTaskSuspend(blkd_tasks[i]); + vTaskDelay(10); vTaskDelete(blkd_tasks[i]); } + vTaskSuspend(susp_task); + vTaskDelay(10); vTaskDelete(susp_task); vSemaphoreDelete(done_sem); } diff --git a/components/hal/esp32/include/hal/i2c_ll.h b/components/hal/esp32/include/hal/i2c_ll.h index 0e49866be7a0..de9bb8be5c03 100644 --- a/components/hal/esp32/include/hal/i2c_ll.h +++ b/components/hal/esp32/include/hal/i2c_ll.h @@ -890,6 +890,8 @@ static inline i2c_slave_read_write_status_t i2c_ll_slave_get_read_write_status(i #define I2C_LL_SLAVE_RX_INT (I2C_RXFIFO_FULL_INT_ENA_M | I2C_TRANS_COMPLETE_INT_ENA_M) // I2C max timeout value #define I2C_LL_MAX_TIMEOUT I2C_TIME_OUT_REG_V +// I2C max timeout period in clock cycles +#define I2C_LL_MAX_TIMEOUT_PERIOD (I2C_LL_MAX_TIMEOUT) #define I2C_LL_INTR_MASK (0xffff) /*!< I2C all interrupt bitmap */ diff --git a/components/hal/esp32/include/hal/ledc_ll.h b/components/hal/esp32/include/hal/ledc_ll.h index a07812452d2c..af038d29a235 100644 --- a/components/hal/esp32/include/hal/ledc_ll.h +++ b/components/hal/esp32/include/hal/ledc_ll.h @@ -26,6 +26,8 @@ extern "C" { #define LEDC_LL_HPOINT_VAL_MAX (LEDC_HPOINT_LSCH0_V) #define LEDC_LL_FRACTIONAL_BITS (8) #define LEDC_LL_FRACTIONAL_MAX ((1 << LEDC_LL_FRACTIONAL_BITS) - 1) +/// Get the mask of the fade end interrupt status register. +#define LEDC_LL_FADE_END_INTR_MASK (0xffffUL << LEDC_DUTY_CHNG_END_HSCH0_INT_ENA_S) #define LEDC_LL_GLOBAL_CLOCKS { \ LEDC_SLOW_CLK_APB, \ @@ -524,7 +526,6 @@ static inline void ledc_ll_set_fade_end_intr(ledc_dev_t *hw, ledc_mode_t speed_m * * @param hw Beginning address of the peripheral registers * @param speed_mode LEDC speed_mode, high-speed mode or low-speed mode - * @param channel_num LEDC channel index (0-7), select from ledc_channel_t * @param intr_status The fade end interrupt status * * @return None @@ -536,6 +537,18 @@ static inline void ledc_ll_get_fade_end_intr_status(ledc_dev_t *hw, ledc_mode_t *intr_status = (value >> int_en_base) & 0xff; } +/** + * @brief Get the address of the fade end interrupt status register. + * + * @param hw Beginning address of the peripheral registers + * @return Pointer to the fade end interrupt status register. + */ +static inline volatile void* ledc_ll_get_fade_end_intr_addr(ledc_dev_t *hw) +{ + return &hw->int_st.val; +} + + /** * @brief Clear fade end interrupt status * diff --git a/components/hal/esp32/include/hal/sdio_slave_ll.h b/components/hal/esp32/include/hal/sdio_slave_ll.h index 0c06500b640a..ccf9eb23e42b 100644 --- a/components/hal/esp32/include/hal/sdio_slave_ll.h +++ b/components/hal/esp32/include/hal/sdio_slave_ll.h @@ -37,6 +37,8 @@ extern "C" { #define sdio_slave_ll_get_host(ID) (&HOST) /// Get address of the only HINF registers for ESP32 #define sdio_slave_ll_get_hinf(ID) (&HINF) +/// Get the mask of the interrupt status. +#define sdio_slave_ll_intr_status_mask (0xff | SLC_SLC0_RX_DONE_INT_ST | SLC_SLC0_RX_EOF_INT_ST | SLC_SLC0_TX_DONE_INT_ST) /* * SLC2 DMA Desc struct, aka sdio_slave_ll_desc_t @@ -544,6 +546,17 @@ static inline void sdio_slave_ll_slvint_fetch_clear(slc_dev_t *slc, sdio_slave_l slc->slc0_int_clr.val = slv_int; } +/** + * Get the address of the interrupt status register. + * + * @param slc Address of the SLC registers + * @return Address of the interrupt status register + */ +static inline volatile void* sdio_slave_ll_get_intr_status_reg(slc_dev_t *slc) +{ + return &slc->slc0_int_st.val; +} + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32/include/hal/touch_sensor_ll.h b/components/hal/esp32/include/hal/touch_sensor_ll.h index 357399fe1ad8..9f1ef2167bbe 100644 --- a/components/hal/esp32/include/hal/touch_sensor_ll.h +++ b/components/hal/esp32/include/hal/touch_sensor_ll.h @@ -327,7 +327,7 @@ static inline void touch_ll_set_init_charge_voltage(uint32_t touch_num, touch_in */ static inline void touch_ll_enable_channel_mask(uint16_t enable_mask) { - SENS.sar_touch_enable.touch_pad_worken = enable_mask; + SENS.sar_touch_enable.touch_pad_worken = TOUCH_LL_BITS_SWAP(enable_mask); } /** diff --git a/components/hal/esp32/include/hal/uart_ll.h b/components/hal/esp32/include/hal/uart_ll.h index e7592bc5c46a..005e97387cbd 100644 --- a/components/hal/esp32/include/hal/uart_ll.h +++ b/components/hal/esp32/include/hal/uart_ll.h @@ -37,7 +37,6 @@ extern "C" { #define UART_LL_INTR_MASK (0x7ffff) //All interrupt mask - // Define UART interrupts typedef enum { UART_INTR_RXFIFO_FULL = (0x1<<0), @@ -292,6 +291,11 @@ FORCE_INLINE_ATTR uint32_t uart_ll_get_intsts_mask(uart_dev_t *hw) return hw->int_st.val; } +FORCE_INLINE_ATTR volatile void* uart_ll_get_intr_status_reg(uart_dev_t *hw) +{ + return &hw->int_st.val; +} + /** * @brief Clear the UART interrupt status based on the given mask. * diff --git a/components/hal/esp32c2/include/hal/i2c_ll.h b/components/hal/esp32c2/include/hal/i2c_ll.h index b5a02817afdd..28f721c65c3c 100644 --- a/components/hal/esp32c2/include/hal/i2c_ll.h +++ b/components/hal/esp32c2/include/hal/i2c_ll.h @@ -815,6 +815,8 @@ static inline uint32_t i2c_ll_calculate_timeout_us_to_reg_val(uint32_t src_clk_h #define I2C_LL_MASTER_RX_INT (I2C_TIME_OUT_INT_ENA_M|I2C_TRANS_COMPLETE_INT_ENA_M|I2C_ARBITRATION_LOST_INT_ENA_M|I2C_END_DETECT_INT_ENA_M) // I2C max timeout value #define I2C_LL_MAX_TIMEOUT I2C_TIME_OUT_VALUE +// I2C max timeout period in clock cycles +#define I2C_LL_MAX_TIMEOUT_PERIOD (1UL << I2C_LL_MAX_TIMEOUT) #define I2C_LL_INTR_MASK (0x3fff) /*!< I2C all interrupt bitmap */ diff --git a/components/hal/esp32c2/include/hal/ledc_ll.h b/components/hal/esp32c2/include/hal/ledc_ll.h index ba5cbcda86d8..1b496b9b9938 100644 --- a/components/hal/esp32c2/include/hal/ledc_ll.h +++ b/components/hal/esp32c2/include/hal/ledc_ll.h @@ -26,6 +26,8 @@ extern "C" { #define LEDC_LL_HPOINT_VAL_MAX (LEDC_HPOINT_CH0_V) #define LEDC_LL_FRACTIONAL_BITS (8) #define LEDC_LL_FRACTIONAL_MAX ((1 << LEDC_LL_FRACTIONAL_BITS) - 1) +/// Get the mask of the fade end interrupt status register. +#define LEDC_LL_FADE_END_INTR_MASK (0x3fUL << LEDC_DUTY_CHNG_END_CH0_INT_ENA_S) #define LEDC_LL_GLOBAL_CLOCKS { \ LEDC_SLOW_CLK_PLL_DIV, \ @@ -498,7 +500,6 @@ static inline void ledc_ll_set_fade_end_intr(ledc_dev_t *hw, ledc_mode_t speed_m * * @param hw Beginning address of the peripheral registers * @param speed_mode LEDC speed_mode, high-speed mode or low-speed mode - * @param channel_num LEDC channel index (0-7), select from ledc_channel_t * @param intr_status The fade end interrupt status * * @return None @@ -510,6 +511,18 @@ static inline void ledc_ll_get_fade_end_intr_status(ledc_dev_t *hw, ledc_mode_t *intr_status = (value >> int_en_base) & 0xff; } +/** + * @brief Get the address of the fade end interrupt status register. + * + * @param hw Beginning address of the peripheral registers + * @return Pointer to the fade end interrupt status register. + */ +static inline volatile void* ledc_ll_get_fade_end_intr_addr(ledc_dev_t *hw) +{ + return &hw->int_st.val; +} + + /** * @brief Clear fade end interrupt status * diff --git a/components/hal/esp32c2/include/hal/uart_ll.h b/components/hal/esp32c2/include/hal/uart_ll.h index d5c709b5eecb..90b084c393d3 100644 --- a/components/hal/esp32c2/include/hal/uart_ll.h +++ b/components/hal/esp32c2/include/hal/uart_ll.h @@ -302,6 +302,11 @@ FORCE_INLINE_ATTR uint32_t uart_ll_get_intsts_mask(uart_dev_t *hw) return hw->int_st.val; } +FORCE_INLINE_ATTR volatile void* uart_ll_get_intr_status_reg(uart_dev_t *hw) +{ + return &hw->int_st.val; +} + /** * @brief Clear the UART interrupt status based on the given mask. * diff --git a/components/hal/esp32c3/include/hal/i2c_ll.h b/components/hal/esp32c3/include/hal/i2c_ll.h index 687ea77e6b74..f3067b09ba8e 100644 --- a/components/hal/esp32c3/include/hal/i2c_ll.h +++ b/components/hal/esp32c3/include/hal/i2c_ll.h @@ -1000,6 +1000,8 @@ static inline i2c_slave_read_write_status_t i2c_ll_slave_get_read_write_status(i #define I2C_LL_SLAVE_RX_INT (I2C_RXFIFO_WM_INT_ENA_M | I2C_TRANS_COMPLETE_INT_ENA_M) // I2C max timeout value #define I2C_LL_MAX_TIMEOUT I2C_TIME_OUT_REG_V +// I2C max timeout period in clock cycles +#define I2C_LL_MAX_TIMEOUT_PERIOD (1UL << I2C_LL_MAX_TIMEOUT) #define I2C_LL_INTR_MASK (0xffff) /*!< I2C all interrupt bitmap */ diff --git a/components/hal/esp32c3/include/hal/ledc_ll.h b/components/hal/esp32c3/include/hal/ledc_ll.h index ed6544dfdec9..45333a7d5b16 100644 --- a/components/hal/esp32c3/include/hal/ledc_ll.h +++ b/components/hal/esp32c3/include/hal/ledc_ll.h @@ -27,6 +27,8 @@ extern "C" { #define LEDC_LL_HPOINT_VAL_MAX (LEDC_HPOINT_LSCH0_V) #define LEDC_LL_FRACTIONAL_BITS (8) #define LEDC_LL_FRACTIONAL_MAX ((1 << LEDC_LL_FRACTIONAL_BITS) - 1) +/// Get the mask of the fade end interrupt status register. +#define LEDC_LL_FADE_END_INTR_MASK (0x3fUL << LEDC_DUTY_CHNG_END_LSCH0_INT_ENA_S) #define LEDC_LL_GLOBAL_CLOCKS { \ LEDC_SLOW_CLK_APB, \ @@ -499,7 +501,6 @@ static inline void ledc_ll_set_fade_end_intr(ledc_dev_t *hw, ledc_mode_t speed_m * * @param hw Beginning address of the peripheral registers * @param speed_mode LEDC speed_mode, high-speed mode or low-speed mode - * @param channel_num LEDC channel index (0-7), select from ledc_channel_t * @param intr_status The fade end interrupt status * * @return None @@ -511,6 +512,18 @@ static inline void ledc_ll_get_fade_end_intr_status(ledc_dev_t *hw, ledc_mode_t *intr_status = (value >> int_en_base) & 0xff; } +/** + * @brief Get the address of the fade end interrupt status register. + * + * @param hw Beginning address of the peripheral registers + * @return Pointer to the fade end interrupt status register. + */ +static inline volatile void* ledc_ll_get_fade_end_intr_addr(ledc_dev_t *hw) +{ + return &hw->int_st.val; +} + + /** * @brief Clear fade end interrupt status * diff --git a/components/hal/esp32c3/include/hal/uart_ll.h b/components/hal/esp32c3/include/hal/uart_ll.h index 06bad83cdcff..54d24ed6e4b5 100644 --- a/components/hal/esp32c3/include/hal/uart_ll.h +++ b/components/hal/esp32c3/include/hal/uart_ll.h @@ -307,6 +307,11 @@ FORCE_INLINE_ATTR uint32_t uart_ll_get_intsts_mask(uart_dev_t *hw) return hw->int_st.val; } +FORCE_INLINE_ATTR volatile void* uart_ll_get_intr_status_reg(uart_dev_t *hw) +{ + return &hw->int_st.val; +} + /** * @brief Clear the UART interrupt status based on the given mask. * diff --git a/components/hal/esp32c5/include/hal/i2c_ll.h b/components/hal/esp32c5/include/hal/i2c_ll.h index 73d8be5a368c..164e1a4a8f0b 100644 --- a/components/hal/esp32c5/include/hal/i2c_ll.h +++ b/components/hal/esp32c5/include/hal/i2c_ll.h @@ -1039,6 +1039,8 @@ static inline i2c_slave_read_write_status_t i2c_ll_slave_get_read_write_status(i #define I2C_LL_SLAVE_RX_INT (I2C_RXFIFO_WM_INT_ENA_M | I2C_TRANS_COMPLETE_INT_ENA_M) // I2C max timeout value #define I2C_LL_MAX_TIMEOUT I2C_TIME_OUT_VALUE +// I2C max timeout period in clock cycles +#define I2C_LL_MAX_TIMEOUT_PERIOD (1UL << I2C_LL_MAX_TIMEOUT) #define I2C_LL_INTR_MASK (0x3fff) /*!< I2C all interrupt bitmap */ diff --git a/components/hal/esp32c5/include/hal/ledc_ll.h b/components/hal/esp32c5/include/hal/ledc_ll.h index 3e237c680138..349556b84711 100644 --- a/components/hal/esp32c5/include/hal/ledc_ll.h +++ b/components/hal/esp32c5/include/hal/ledc_ll.h @@ -30,6 +30,8 @@ extern "C" { #define LEDC_LL_FRACTIONAL_BITS (8) #define LEDC_LL_FRACTIONAL_MAX ((1 << LEDC_LL_FRACTIONAL_BITS) - 1) #define LEDC_LL_GLOBAL_CLOCKS SOC_LEDC_CLKS +/// Get the mask of the fade end interrupt status register. +#define LEDC_LL_FADE_END_INTR_MASK (0x3fUL << LEDC_DUTY_CHNG_END_CH0_INT_ENA_S) /** * @brief Enable peripheral register clock @@ -504,7 +506,6 @@ static inline void ledc_ll_set_fade_end_intr(ledc_dev_t *hw, ledc_mode_t speed_m * * @param hw Beginning address of the peripheral registers * @param speed_mode LEDC speed_mode, low-speed mode only - * @param channel_num LEDC channel index (0-5), select from ledc_channel_t * @param intr_status The fade end interrupt status * * @return None @@ -516,6 +517,18 @@ static inline void ledc_ll_get_fade_end_intr_status(ledc_dev_t *hw, ledc_mode_t *intr_status = (value >> int_en_base) & 0xff; } +/** + * @brief Get the address of the fade end interrupt status register. + * + * @param hw Beginning address of the peripheral registers + * @return Pointer to the fade end interrupt status register. + */ +static inline volatile void* ledc_ll_get_fade_end_intr_addr(ledc_dev_t *hw) +{ + return &hw->int_st.val; +} + + /** * @brief Clear fade end interrupt status * diff --git a/components/hal/esp32c5/include/hal/sdio_slave_ll.h b/components/hal/esp32c5/include/hal/sdio_slave_ll.h index c02033ecea2d..069bf2ee106e 100644 --- a/components/hal/esp32c5/include/hal/sdio_slave_ll.h +++ b/components/hal/esp32c5/include/hal/sdio_slave_ll.h @@ -37,6 +37,8 @@ extern "C" { #define sdio_slave_ll_get_host(ID) (&HOST) /// Get address of the only HINF registers #define sdio_slave_ll_get_hinf(ID) (&HINF) +/// Get the mask of the interrupt status. +#define sdio_slave_ll_intr_status_mask (0xff | SDIO_SLC0_RX_DONE_INT_ST | SDIO_SLC0_RX_EOF_INT_ST | SDIO_SLC0_TX_DONE_INT_ST) /* * SLC2 DMA Desc struct, aka sdio_slave_ll_desc_t @@ -533,6 +535,17 @@ static inline void sdio_slave_ll_slvint_fetch_clear(slc_dev_t *slc, sdio_slave_l slc->slc_slc0int_clr.val = slv_int; } +/** + * Get the address of the interrupt status register. + * + * @param slc Address of the SLC registers + * @return Address of the interrupt status register + */ +static inline volatile void* sdio_slave_ll_get_intr_status_reg(slc_dev_t *slc) +{ + return &slc->slc_slc0int_st.val; +} + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32c5/include/hal/uart_ll.h b/components/hal/esp32c5/include/hal/uart_ll.h index 22d5b0eb5d2d..77408fc74842 100644 --- a/components/hal/esp32c5/include/hal/uart_ll.h +++ b/components/hal/esp32c5/include/hal/uart_ll.h @@ -526,6 +526,11 @@ FORCE_INLINE_ATTR uint32_t uart_ll_get_intsts_mask(uart_dev_t *hw) return hw->int_st.val; } +FORCE_INLINE_ATTR volatile void* uart_ll_get_intr_status_reg(uart_dev_t *hw) +{ + return &hw->int_st.val; +} + /** * @brief Clear the UART interrupt status based on the given mask. * diff --git a/components/hal/esp32c6/include/hal/i2c_ll.h b/components/hal/esp32c6/include/hal/i2c_ll.h index 49da17be0dc1..056f6372e32f 100644 --- a/components/hal/esp32c6/include/hal/i2c_ll.h +++ b/components/hal/esp32c6/include/hal/i2c_ll.h @@ -1040,6 +1040,8 @@ static inline i2c_slave_read_write_status_t i2c_ll_slave_get_read_write_status(i #define I2C_LL_SLAVE_RX_INT (I2C_RXFIFO_WM_INT_ENA_M | I2C_TRANS_COMPLETE_INT_ENA_M) // I2C max timeout value #define I2C_LL_MAX_TIMEOUT I2C_TIME_OUT_VALUE +// I2C max timeout period in clock cycles +#define I2C_LL_MAX_TIMEOUT_PERIOD (1UL << I2C_LL_MAX_TIMEOUT) #define I2C_LL_INTR_MASK (0x3fff) /*!< I2C all interrupt bitmap */ diff --git a/components/hal/esp32c6/include/hal/ledc_ll.h b/components/hal/esp32c6/include/hal/ledc_ll.h index e6a0c73e3f27..0e36bdc9ae14 100644 --- a/components/hal/esp32c6/include/hal/ledc_ll.h +++ b/components/hal/esp32c6/include/hal/ledc_ll.h @@ -31,6 +31,8 @@ extern "C" { #define LEDC_LL_FRACTIONAL_BITS (8) #define LEDC_LL_FRACTIONAL_MAX ((1 << LEDC_LL_FRACTIONAL_BITS) - 1) #define LEDC_LL_GLOBAL_CLOCKS SOC_LEDC_CLKS +/// Get the mask of the fade end interrupt status register. +#define LEDC_LL_FADE_END_INTR_MASK (0x3fUL << LEDC_DUTY_CHNG_END_CH0_INT_ENA_S) #define LEDC_LL_GLOBAL_CLK_DEFAULT LEDC_SLOW_CLK_RC_FAST @@ -625,7 +627,6 @@ static inline void ledc_ll_set_fade_end_intr(ledc_dev_t *hw, ledc_mode_t speed_m * * @param hw Beginning address of the peripheral registers * @param speed_mode LEDC speed_mode, low-speed mode only - * @param channel_num LEDC channel index (0-5), select from ledc_channel_t * @param intr_status The fade end interrupt status * * @return None @@ -637,6 +638,18 @@ static inline void ledc_ll_get_fade_end_intr_status(ledc_dev_t *hw, ledc_mode_t *intr_status = (value >> int_en_base) & 0xff; } +/** + * @brief Get the address of the fade end interrupt status register. + * + * @param hw Beginning address of the peripheral registers + * @return Pointer to the fade end interrupt status register. + */ +static inline volatile void* ledc_ll_get_fade_end_intr_addr(ledc_dev_t *hw) +{ + return &hw->int_st.val; +} + + /** * @brief Clear fade end interrupt status * diff --git a/components/hal/esp32c6/include/hal/sdio_slave_ll.h b/components/hal/esp32c6/include/hal/sdio_slave_ll.h index 24b592893ab0..1276518a8998 100644 --- a/components/hal/esp32c6/include/hal/sdio_slave_ll.h +++ b/components/hal/esp32c6/include/hal/sdio_slave_ll.h @@ -37,6 +37,8 @@ extern "C" { #define sdio_slave_ll_get_host(ID) (&HOST) /// Get address of the only HINF registers #define sdio_slave_ll_get_hinf(ID) (&HINF) +/// Get the mask of the interrupt status. +#define sdio_slave_ll_intr_status_mask (0xff | SDIO_SLC0_RX_DONE_INT_ST | SDIO_SLC0_RX_EOF_INT_ST | SDIO_SLC0_TX_DONE_INT_ST) /* * SLC2 DMA Desc struct, aka sdio_slave_ll_desc_t @@ -533,6 +535,17 @@ static inline void sdio_slave_ll_slvint_fetch_clear(slc_dev_t *slc, sdio_slave_l slc->slc0int_clr.val = slv_int; } +/** + * Get the address of the interrupt status register. + * + * @param slc Address of the SLC registers + * @return Address of the interrupt status register + */ +static inline volatile void* sdio_slave_ll_get_intr_status_reg(slc_dev_t *slc) +{ + return &slc->slc0int_st.val; +} + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32c6/include/hal/spi_ll.h b/components/hal/esp32c6/include/hal/spi_ll.h index c52ec2f26cdc..c55b9e2283a3 100644 --- a/components/hal/esp32c6/include/hal/spi_ll.h +++ b/components/hal/esp32c6/include/hal/spi_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ diff --git a/components/hal/esp32c6/include/hal/uart_ll.h b/components/hal/esp32c6/include/hal/uart_ll.h index c4c62999e21a..60d353e416cb 100644 --- a/components/hal/esp32c6/include/hal/uart_ll.h +++ b/components/hal/esp32c6/include/hal/uart_ll.h @@ -507,6 +507,11 @@ FORCE_INLINE_ATTR uint32_t uart_ll_get_intsts_mask(uart_dev_t *hw) return hw->int_st.val; } +FORCE_INLINE_ATTR volatile void* uart_ll_get_intr_status_reg(uart_dev_t *hw) +{ + return &hw->int_st.val; +} + /** * @brief Clear the UART interrupt status based on the given mask. * diff --git a/components/hal/esp32c61/include/hal/i2c_ll.h b/components/hal/esp32c61/include/hal/i2c_ll.h index 5e50d96967b3..2445f7d35763 100644 --- a/components/hal/esp32c61/include/hal/i2c_ll.h +++ b/components/hal/esp32c61/include/hal/i2c_ll.h @@ -951,6 +951,8 @@ static inline i2c_slave_read_write_status_t i2c_ll_slave_get_read_write_status(i #define I2C_LL_SLAVE_RX_INT (I2C_RXFIFO_WM_INT_ENA_M | I2C_TRANS_COMPLETE_INT_ENA_M) // I2C max timeout value #define I2C_LL_MAX_TIMEOUT I2C_TIME_OUT_VALUE +// I2C max timeout period in clock cycles +#define I2C_LL_MAX_TIMEOUT_PERIOD (1UL << I2C_LL_MAX_TIMEOUT) #define I2C_LL_INTR_MASK (0x3fff) /*!< I2C all interrupt bitmap */ diff --git a/components/hal/esp32c61/include/hal/ledc_ll.h b/components/hal/esp32c61/include/hal/ledc_ll.h index 4cd0f760aeb4..0d49cda7aefe 100644 --- a/components/hal/esp32c61/include/hal/ledc_ll.h +++ b/components/hal/esp32c61/include/hal/ledc_ll.h @@ -30,6 +30,8 @@ extern "C" { #define LEDC_LL_FRACTIONAL_BITS (8) #define LEDC_LL_FRACTIONAL_MAX ((1 << LEDC_LL_FRACTIONAL_BITS) - 1) #define LEDC_LL_GLOBAL_CLOCKS SOC_LEDC_CLKS +/// Get the mask of the fade end interrupt status register. +#define LEDC_LL_FADE_END_INTR_MASK (0x3fUL << LEDC_DUTY_CHNG_END_CH0_INT_ENA_S) /** * @brief Enable peripheral register clock @@ -504,7 +506,6 @@ static inline void ledc_ll_set_fade_end_intr(ledc_dev_t *hw, ledc_mode_t speed_m * * @param hw Beginning address of the peripheral registers * @param speed_mode LEDC speed_mode, low-speed mode only - * @param channel_num LEDC channel index (0-5), select from ledc_channel_t * @param intr_status The fade end interrupt status * * @return None @@ -516,6 +517,18 @@ static inline void ledc_ll_get_fade_end_intr_status(ledc_dev_t *hw, ledc_mode_t *intr_status = (value >> int_en_base) & 0xff; } +/** + * @brief Get the address of the fade end interrupt status register. + * + * @param hw Beginning address of the peripheral registers + * @return Pointer to the fade end interrupt status register. + */ +static inline volatile void* ledc_ll_get_fade_end_intr_addr(ledc_dev_t *hw) +{ + return &hw->int_st.val; +} + + /** * @brief Clear fade end interrupt status * diff --git a/components/hal/esp32c61/include/hal/sdio_slave_ll.h b/components/hal/esp32c61/include/hal/sdio_slave_ll.h index fb746c8b4f96..f11023260b7b 100644 --- a/components/hal/esp32c61/include/hal/sdio_slave_ll.h +++ b/components/hal/esp32c61/include/hal/sdio_slave_ll.h @@ -37,6 +37,9 @@ extern "C" { #define sdio_slave_ll_get_host(ID) (&HOST) /// Get address of the only HINF registers #define sdio_slave_ll_get_hinf(ID) (&HINF) +/// Get the mask of the interrupt status. +#define sdio_slave_ll_intr_status_mask (0xff | SDIO_SLC0_RX_DONE_INT_ST | SDIO_SLC0_RX_EOF_INT_ST | SDIO_SLC0_TX_DONE_INT_ST) + /* * SLC2 DMA Desc struct, aka sdio_slave_ll_desc_t @@ -533,6 +536,17 @@ static inline void sdio_slave_ll_slvint_fetch_clear(slc_dev_t *slc, sdio_slave_l slc->slc_slc0int_clr.val = slv_int; } +/** + * Get the address of the interrupt status register. + * + * @param slc Address of the SLC registers + * @return Address of the interrupt status register + */ +static inline volatile void* sdio_slave_ll_get_intr_status_reg(slc_dev_t *slc) +{ + return &slc->slc_slc0int_st.val; +} + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32c61/include/hal/uart_ll.h b/components/hal/esp32c61/include/hal/uart_ll.h index 4ca98ecc913b..e8b426abb373 100644 --- a/components/hal/esp32c61/include/hal/uart_ll.h +++ b/components/hal/esp32c61/include/hal/uart_ll.h @@ -354,6 +354,11 @@ FORCE_INLINE_ATTR uint32_t uart_ll_get_intsts_mask(uart_dev_t *hw) return hw->int_st.val; } +FORCE_INLINE_ATTR volatile void* uart_ll_get_intr_status_reg(uart_dev_t *hw) +{ + return &hw->int_st.val; +} + /** * @brief Clear the UART interrupt status based on the given mask. * diff --git a/components/hal/esp32h2/include/hal/i2c_ll.h b/components/hal/esp32h2/include/hal/i2c_ll.h index e0e8334f934d..8d8897695236 100644 --- a/components/hal/esp32h2/include/hal/i2c_ll.h +++ b/components/hal/esp32h2/include/hal/i2c_ll.h @@ -948,6 +948,8 @@ static inline i2c_slave_read_write_status_t i2c_ll_slave_get_read_write_status(i #define I2C_LL_SLAVE_RX_INT (I2C_RXFIFO_WM_INT_ENA_M | I2C_TRANS_COMPLETE_INT_ENA_M) // I2C max timeout value #define I2C_LL_MAX_TIMEOUT I2C_TIME_OUT_VALUE +// I2C max timeout period in clock cycles +#define I2C_LL_MAX_TIMEOUT_PERIOD (1UL << I2C_LL_MAX_TIMEOUT) #define I2C_LL_INTR_MASK (0x3fff) /*!< I2C all interrupt bitmap */ diff --git a/components/hal/esp32h2/include/hal/ledc_ll.h b/components/hal/esp32h2/include/hal/ledc_ll.h index 83510030018a..76c95fe97b57 100644 --- a/components/hal/esp32h2/include/hal/ledc_ll.h +++ b/components/hal/esp32h2/include/hal/ledc_ll.h @@ -31,6 +31,8 @@ extern "C" { #define LEDC_LL_FRACTIONAL_BITS (8) #define LEDC_LL_FRACTIONAL_MAX ((1 << LEDC_LL_FRACTIONAL_BITS) - 1) #define LEDC_LL_GLOBAL_CLOCKS SOC_LEDC_CLKS +/// Get the mask of the fade end interrupt status register. +#define LEDC_LL_FADE_END_INTR_MASK (0x3fUL << LEDC_DUTY_CHNG_END_CH0_INT_ENA_S) /** * @brief Enable peripheral register clock @@ -623,7 +625,6 @@ static inline void ledc_ll_set_fade_end_intr(ledc_dev_t *hw, ledc_mode_t speed_m * * @param hw Beginning address of the peripheral registers * @param speed_mode LEDC speed_mode, low-speed mode only - * @param channel_num LEDC channel index (0-5), select from ledc_channel_t * @param intr_status The fade end interrupt status * * @return None @@ -635,6 +636,18 @@ static inline void ledc_ll_get_fade_end_intr_status(ledc_dev_t *hw, ledc_mode_t *intr_status = (value >> int_en_base) & 0xff; } +/** + * @brief Get the address of the fade end interrupt status register. + * + * @param hw Beginning address of the peripheral registers + * @return Pointer to the fade end interrupt status register. + */ +static inline volatile void* ledc_ll_get_fade_end_intr_addr(ledc_dev_t *hw) +{ + return &hw->int_st.val; +} + + /** * @brief Clear fade end interrupt status * diff --git a/components/hal/esp32h2/include/hal/spi_ll.h b/components/hal/esp32h2/include/hal/spi_ll.h index fade1a565eac..ec5219583e99 100644 --- a/components/hal/esp32h2/include/hal/spi_ll.h +++ b/components/hal/esp32h2/include/hal/spi_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ diff --git a/components/hal/esp32h2/include/hal/uart_ll.h b/components/hal/esp32h2/include/hal/uart_ll.h index 2b9cb2479b3b..f507999fd07e 100644 --- a/components/hal/esp32h2/include/hal/uart_ll.h +++ b/components/hal/esp32h2/include/hal/uart_ll.h @@ -333,6 +333,11 @@ FORCE_INLINE_ATTR uint32_t uart_ll_get_intsts_mask(uart_dev_t *hw) return hw->int_st.val; } +FORCE_INLINE_ATTR volatile void* uart_ll_get_intr_status_reg(uart_dev_t *hw) +{ + return &hw->int_st.val; +} + /** * @brief Clear the UART interrupt status based on the given mask. * diff --git a/components/hal/esp32h21/include/hal/i2c_ll.h b/components/hal/esp32h21/include/hal/i2c_ll.h index 903a77fec6d4..f4302c1329f7 100644 --- a/components/hal/esp32h21/include/hal/i2c_ll.h +++ b/components/hal/esp32h21/include/hal/i2c_ll.h @@ -946,6 +946,8 @@ static inline i2c_slave_read_write_status_t i2c_ll_slave_get_read_write_status(i #define I2C_LL_SLAVE_RX_INT (I2C_RXFIFO_WM_INT_ENA_M | I2C_TRANS_COMPLETE_INT_ENA_M) // I2C max timeout value #define I2C_LL_MAX_TIMEOUT I2C_TIME_OUT_VALUE +// I2C max timeout period in clock cycles +#define I2C_LL_MAX_TIMEOUT_PERIOD (1UL << I2C_LL_MAX_TIMEOUT) #define I2C_LL_INTR_MASK (0x3fff) /*!< I2C all interrupt bitmap */ diff --git a/components/hal/esp32h21/include/hal/uart_ll.h b/components/hal/esp32h21/include/hal/uart_ll.h index 24df5176b709..89cd5293c384 100644 --- a/components/hal/esp32h21/include/hal/uart_ll.h +++ b/components/hal/esp32h21/include/hal/uart_ll.h @@ -332,6 +332,11 @@ FORCE_INLINE_ATTR uint32_t uart_ll_get_intsts_mask(uart_dev_t *hw) return hw->int_st.val; } +FORCE_INLINE_ATTR volatile void* uart_ll_get_intr_status_reg(uart_dev_t *hw) +{ + return &hw->int_st.val; +} + /** * @brief Clear the UART interrupt status based on the given mask. * diff --git a/components/hal/esp32h4/include/hal/uart_ll.h b/components/hal/esp32h4/include/hal/uart_ll.h index e4f68c70bb45..e2ba4e3f679e 100644 --- a/components/hal/esp32h4/include/hal/uart_ll.h +++ b/components/hal/esp32h4/include/hal/uart_ll.h @@ -351,6 +351,11 @@ FORCE_INLINE_ATTR uint32_t uart_ll_get_intsts_mask(uart_dev_t *hw) return hw->int_st.val; } +FORCE_INLINE_ATTR volatile void* uart_ll_get_intr_status_reg(uart_dev_t *hw) +{ + return &hw->int_st.val; +} + /** * @brief Clear the UART interrupt status based on the given mask. * diff --git a/components/hal/esp32p4/include/hal/i2c_ll.h b/components/hal/esp32p4/include/hal/i2c_ll.h index 37fb6e1038fc..71a00cd702f0 100644 --- a/components/hal/esp32p4/include/hal/i2c_ll.h +++ b/components/hal/esp32p4/include/hal/i2c_ll.h @@ -1071,6 +1071,8 @@ static inline i2c_slave_read_write_status_t i2c_ll_slave_get_read_write_status(i #define I2C_LL_SLAVE_RX_INT (I2C_RXFIFO_WM_INT_ENA_M | I2C_TRANS_COMPLETE_INT_ENA_M) // I2C max timeout value #define I2C_LL_MAX_TIMEOUT I2C_TIME_OUT_VALUE +// I2C max timeout period in clock cycles +#define I2C_LL_MAX_TIMEOUT_PERIOD (1UL << I2C_LL_MAX_TIMEOUT) #define I2C_LL_INTR_MASK (0x3fff) /*!< I2C all interrupt bitmap */ diff --git a/components/hal/esp32p4/include/hal/isp_ll.h b/components/hal/esp32p4/include/hal/isp_ll.h index e4fa919634d5..d5abfc435d69 100644 --- a/components/hal/esp32p4/include/hal/isp_ll.h +++ b/components/hal/esp32p4/include/hal/isp_ll.h @@ -70,7 +70,11 @@ extern "C" { #define ISP_LL_EVENT_WBG_FRAME (1<<30) #define ISP_LL_EVENT_CROP_ERR (1<<31) +#if HAL_CONFIG(CHIP_SUPPORT_MIN_REV) >= 300 +#define ISP_LL_EVENT_ALL_MASK (0xFFFFFFFF) +#else #define ISP_LL_EVENT_ALL_MASK (0x1FFFFFFF) +#endif #define ISP_LL_EVENT_AF_MASK (ISP_LL_EVENT_AF_FDONE | ISP_LL_EVENT_AF_ENV) #define ISP_LL_EVENT_AE_MASK (ISP_LL_EVENT_AE_FDONE | ISP_LL_EVENT_AE_ENV) #define ISP_LL_EVENT_AWB_MASK (ISP_LL_EVENT_AWB_FDONE | ISP_LL_EVENT_WBG_FRAME) @@ -199,6 +203,23 @@ typedef enum { ISP_SHADOW_MODE_UPDATE_ONLY_NEXT_VSYNC, } isp_ll_shadow_mode_t; +/*--------------------------------------------------------------- + Crop +---------------------------------------------------------------*/ + +/** + * @brief ISP crop error types + */ +typedef enum { + ISP_LL_CROP_ERR_X_MISMATCH = (1 << 0), /*!< X end coordinate exceeds image size */ + ISP_LL_CROP_ERR_Y_MISMATCH = (1 << 1), /*!< Y end coordinate exceeds image size */ + ISP_LL_CROP_ERR_X_END_EVEN = (1 << 2), /*!< X end coordinate is even (should be odd) */ + ISP_LL_CROP_ERR_Y_END_EVEN = (1 << 3), /*!< Y end coordinate is even (should be odd) */ + ISP_LL_CROP_ERR_X_START_ODD = (1 << 4), /*!< X start coordinate is odd (should be even) */ + ISP_LL_CROP_ERR_Y_START_ODD = (1 << 5), /*!< Y start coordinate is odd (should be even) */ +} isp_ll_crop_error_t; + + /*--------------------------------------------------------------- Clock ---------------------------------------------------------------*/ @@ -2451,6 +2472,128 @@ static inline void isp_ll_hist_set_rgb_coefficient(isp_dev_t *hw, const isp_hist HAL_FORCE_MODIFY_U32_REG_FIELD(hw->hist_coeff, hist_coeff_b, rgb_coeff->coeff_b.decimal); } +/*--------------------------------------------------------------- + CROP +---------------------------------------------------------------*/ + +#if HAL_CONFIG(CHIP_SUPPORT_MIN_REV) >= 300 +/** + * @brief Set crop clock control mode + * + * @param[in] hw Hardware instance address + * @param[in] mode 'isp_ll_pipeline_clk_ctrl_t` + */ +static inline void isp_ll_crop_set_clk_ctrl_mode(isp_dev_t *hw, isp_ll_pipeline_clk_ctrl_t mode) +{ + hw->clk_en.clk_crop_force_on = mode; +} + +/** + * @brief Enable/Disable ISP crop + * + * @param[in] hw Hardware instance address + * @param[in] en enable / disable + */ +static inline void isp_ll_crop_enable(isp_dev_t *hw, bool enable) +{ + hw->cntl.crop_en = enable; +} + +/** + * @brief Set ISP crop window coordinates + * + * @param[in] hw Hardware instance address + * @param[in] x_start Crop start x coordinate (0 to image_width-1) + * @param[in] x_end Crop end x coordinate (x_start+1 to image_width-1) + * @param[in] y_start Crop start y coordinate (0 to image_height-1) + * @param[in] y_end Crop end y coordinate (y_start+1 to image_height-1) + */ +static inline void isp_ll_crop_set_window(isp_dev_t *hw, + uint32_t x_start, uint32_t x_end, + uint32_t y_start, uint32_t y_end) +{ + hw->crop_x_capture.crop_x_start = x_start; + hw->crop_x_capture.crop_x_end = x_end; + hw->crop_y_capture.crop_y_start = y_start; + hw->crop_y_capture.crop_y_end = y_end; +} + +/** + * @brief Get crop window coordinates + * + * @param[in] hw Hardware instance address + * @param[out] x_start Crop start x coordinate (0 to image_width-1) + * @param[out] x_end Crop end x coordinate (x_start+1 to image_width-1) + * @param[out] y_start Crop start y coordinate (0 to image_height-1) + * @param[out] y_end Crop end y coordinate (y_start+1 to image_height-1) + */ +static inline void isp_ll_crop_get_window(isp_dev_t *hw, + uint32_t *x_start, uint32_t *x_end, + uint32_t *y_start, uint32_t *y_end) +{ + *x_start = hw->crop_x_capture.crop_x_start; + *x_end = hw->crop_x_capture.crop_x_end; + *y_start = hw->crop_y_capture.crop_y_start; + *y_end = hw->crop_y_capture.crop_y_end; +} + +/** + * @brief Get crop error status + * + * @param[in] hw Hardware instance address + * @param[out] error_bits Error status bits + */ +static inline void isp_ll_crop_get_error_status(isp_dev_t *hw, uint32_t *error_bits) +{ + *error_bits = hw->crop_err_st.val & 0x3F; // Retrieve lower 6 bits +} + +/** + * @brief Clear crop error + * + * @param[in] hw Hardware instance address + */ +static inline void isp_ll_crop_clear_error(isp_dev_t *hw) +{ + hw->crop_ctrl.crop_sft_rst = 1; +} + +#else +static inline void isp_ll_crop_set_clk_ctrl_mode(isp_dev_t *hw, isp_ll_pipeline_clk_ctrl_t mode) +{ + // for compatibility +} + +static inline void isp_ll_crop_enable(isp_dev_t *hw, bool enable) +{ + // for compatibility +} + +static inline void isp_ll_crop_set_window(isp_dev_t *hw, + uint32_t x_start, uint32_t x_end, + uint32_t y_start, uint32_t y_end) +{ + // for compatibility +} + +static inline void isp_ll_crop_get_window(isp_dev_t *hw, + uint32_t *x_start, uint32_t *x_end, + uint32_t *y_start, uint32_t *y_end) +{ + // for compatibility +} + +static inline void isp_ll_crop_get_error_status(isp_dev_t *hw, uint32_t *error_bits) +{ + // for compatibility +} + +static inline void isp_ll_crop_clear_error(isp_dev_t *hw) +{ + // for compatibility +} +#endif //#if HAL_CONFIG(CHIP_SUPPORT_MIN_REV) >= 300 + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32p4/include/hal/ledc_ll.h b/components/hal/esp32p4/include/hal/ledc_ll.h index 0876a928f046..8c2380af6168 100644 --- a/components/hal/esp32p4/include/hal/ledc_ll.h +++ b/components/hal/esp32p4/include/hal/ledc_ll.h @@ -30,6 +30,8 @@ extern "C" { #define LEDC_LL_FRACTIONAL_BITS (8) #define LEDC_LL_FRACTIONAL_MAX ((1 << LEDC_LL_FRACTIONAL_BITS) - 1) #define LEDC_LL_GLOBAL_CLOCKS SOC_LEDC_CLKS +/// Get the mask of the fade end interrupt status register. +#define LEDC_LL_FADE_END_INTR_MASK (0xffUL << LEDC_DUTY_CHNG_END_CH0_INT_ENA_S) /** * @brief Enable peripheral register clock @@ -517,7 +519,6 @@ static inline void ledc_ll_set_fade_end_intr(ledc_dev_t *hw, ledc_mode_t speed_m * * @param hw Beginning address of the peripheral registers * @param speed_mode LEDC speed_mode, low-speed mode only - * @param channel_num LEDC channel index (0-5), select from ledc_channel_t * @param intr_status The fade end interrupt status * * @return None @@ -529,6 +530,18 @@ static inline void ledc_ll_get_fade_end_intr_status(ledc_dev_t *hw, ledc_mode_t *intr_status = (value >> int_en_base) & 0xff; } +/** + * @brief Get the address of the fade end interrupt status register. + * + * @param hw Beginning address of the peripheral registers + * @return Pointer to the fade end interrupt status register. + */ +static inline volatile void* ledc_ll_get_fade_end_intr_addr(ledc_dev_t *hw) +{ + return &hw->int_st.val; +} + + /** * @brief Clear fade end interrupt status * diff --git a/components/hal/esp32p4/include/hal/ppa_ll.h b/components/hal/esp32p4/include/hal/ppa_ll.h index a0e7323397fc..a2995311513b 100644 --- a/components/hal/esp32p4/include/hal/ppa_ll.h +++ b/components/hal/esp32p4/include/hal/ppa_ll.h @@ -244,7 +244,7 @@ static inline bool ppa_ll_srm_is_color_mode_supported(ppa_srm_color_mode_t color case PPA_SRM_COLOR_MODE_RGB888: case PPA_SRM_COLOR_MODE_RGB565: case PPA_SRM_COLOR_MODE_YUV420: - case PPA_SRM_COLOR_MODE_YUV444: // YUV444 not supported by PPA hardware, but can be converted by 2D-DMA before/after PPA + case PPA_SRM_COLOR_MODE_YUV444: // YUV444 not supported by PPA hardware, but can be converted by 2D-DMA before PPA, and not supported as output color mode #if HAL_CONFIG(CHIP_SUPPORT_MIN_REV) >= 300 case PPA_SRM_COLOR_MODE_YUV422_UYVY: case PPA_SRM_COLOR_MODE_YUV422_VYUY: @@ -550,14 +550,14 @@ static inline void ppa_ll_srm_get_dma_dscr_port_mode_block_size(ppa_dev_t *dev, break; case PPA_SRM_COLOR_MODE_YUV420: *block_h = 20; - *block_v = 18; + *block_v = 20; break; case PPA_SRM_COLOR_MODE_YUV422_UYVY: case PPA_SRM_COLOR_MODE_YUV422_VYUY: case PPA_SRM_COLOR_MODE_YUV422_YUYV: case PPA_SRM_COLOR_MODE_YUV422_YVYU: *block_h = 20; - *block_v = 20; + *block_v = 18; break; default: // Unsupported SRM input color mode @@ -577,14 +577,14 @@ static inline void ppa_ll_srm_get_dma_dscr_port_mode_block_size(ppa_dev_t *dev, break; case PPA_SRM_COLOR_MODE_YUV420: *block_h = 36; - *block_v = 34; + *block_v = 36; break; case PPA_SRM_COLOR_MODE_YUV422_UYVY: case PPA_SRM_COLOR_MODE_YUV422_VYUY: case PPA_SRM_COLOR_MODE_YUV422_YUYV: case PPA_SRM_COLOR_MODE_YUV422_YVYU: *block_h = 36; - *block_v = 36; + *block_v = 34; break; default: // Unsupported SRM input color mode @@ -600,6 +600,17 @@ static inline void ppa_ll_srm_get_dma_dscr_port_mode_block_size(ppa_dev_t *dev, } } +/** + * @brief Whether to bypass the macro block order function in PPA SRM + * + * @param dev Peripheral instance address + * @param enable True to bypass; False to not bypass + */ +static inline void ppa_ll_srm_bypass_mb_order(ppa_dev_t *dev, bool enable) +{ + dev->sr_byte_order.sr_macro_bk_ro_bypass = enable; +} + //////////////////////////////////// Blending //////////////////////////////////////// /* * Alpha Blending Calculation: diff --git a/components/hal/esp32p4/include/hal/uart_ll.h b/components/hal/esp32p4/include/hal/uart_ll.h index ec2a776feeb2..ca6529303c89 100644 --- a/components/hal/esp32p4/include/hal/uart_ll.h +++ b/components/hal/esp32p4/include/hal/uart_ll.h @@ -628,6 +628,11 @@ FORCE_INLINE_ATTR uint32_t uart_ll_get_intsts_mask(uart_dev_t *hw) return hw->int_st.val; } +FORCE_INLINE_ATTR volatile void* uart_ll_get_intr_status_reg(uart_dev_t *hw) +{ + return &hw->int_st.val; +} + /** * @brief Clear the UART interrupt status based on the given mask. * diff --git a/components/hal/esp32s2/include/hal/cache_ll.h b/components/hal/esp32s2/include/hal/cache_ll.h index 0bccb5d3d409..6f648181450a 100644 --- a/components/hal/esp32s2/include/hal/cache_ll.h +++ b/components/hal/esp32s2/include/hal/cache_ll.h @@ -282,7 +282,8 @@ static inline void cache_ll_resume_cache(uint32_t cache_level, cache_type_t type * @return true: enabled; false: disabled */ __attribute__((always_inline)) -static inline bool cache_ll_l1_is_icache_enabled(uint32_t cache_id){ +static inline bool cache_ll_l1_is_icache_enabled(uint32_t cache_id) +{ HAL_ASSERT(cache_id <= CACHE_LL_ID_ALL); bool enabled; diff --git a/components/hal/esp32s2/include/hal/i2c_ll.h b/components/hal/esp32s2/include/hal/i2c_ll.h index 2f9febeb9c07..98b6e0f50929 100644 --- a/components/hal/esp32s2/include/hal/i2c_ll.h +++ b/components/hal/esp32s2/include/hal/i2c_ll.h @@ -956,6 +956,8 @@ static inline i2c_slave_read_write_status_t i2c_ll_slave_get_read_write_status(i #define I2C_LL_SLAVE_RX_INT (I2C_RXFIFO_WM_INT_ENA_M | I2C_TRANS_COMPLETE_INT_ENA_M) // I2C max timeout value #define I2C_LL_MAX_TIMEOUT I2C_TIME_OUT_REG_V +// I2C max timeout period in clock cycles +#define I2C_LL_MAX_TIMEOUT_PERIOD (I2C_LL_MAX_TIMEOUT) #define I2C_LL_INTR_MASK (0x1ffff) /*!< I2C all interrupt bitmap */ diff --git a/components/hal/esp32s2/include/hal/ledc_ll.h b/components/hal/esp32s2/include/hal/ledc_ll.h index 5ddcb5ab87a9..c9d947090a5f 100644 --- a/components/hal/esp32s2/include/hal/ledc_ll.h +++ b/components/hal/esp32s2/include/hal/ledc_ll.h @@ -27,6 +27,8 @@ extern "C" { #define LEDC_LL_HPOINT_VAL_MAX (LEDC_HPOINT_LSCH0_V) #define LEDC_LL_FRACTIONAL_BITS (8) #define LEDC_LL_FRACTIONAL_MAX ((1 << LEDC_LL_FRACTIONAL_BITS) - 1) +/// Get the mask of the fade end interrupt status register. +#define LEDC_LL_FADE_END_INTR_MASK (0xffUL << LEDC_DUTY_CHNG_END_LSCH0_INT_ENA_S) #define LEDC_LL_GLOBAL_CLOCKS { \ LEDC_SLOW_CLK_APB, \ @@ -538,7 +540,6 @@ static inline void ledc_ll_set_fade_end_intr(ledc_dev_t *hw, ledc_mode_t speed_m * * @param hw Beginning address of the peripheral registers * @param speed_mode LEDC speed_mode, high-speed mode or low-speed mode - * @param channel_num LEDC channel index (0-7), select from ledc_channel_t * @param intr_status The fade end interrupt status * * @return None @@ -550,6 +551,18 @@ static inline void ledc_ll_get_fade_end_intr_status(ledc_dev_t *hw, ledc_mode_t *intr_status = (value >> int_en_base) & 0xff; } +/** + * @brief Get the address of the fade end interrupt status register. + * + * @param hw Beginning address of the peripheral registers + * @return Pointer to the fade end interrupt status register. + */ +static inline volatile void* ledc_ll_get_fade_end_intr_addr(ledc_dev_t *hw) +{ + return &hw->int_st.val; +} + + /** * @brief Clear fade end interrupt status * diff --git a/components/hal/esp32s2/include/hal/uart_ll.h b/components/hal/esp32s2/include/hal/uart_ll.h index 963706c2a7c5..3bfc9d6a5892 100644 --- a/components/hal/esp32s2/include/hal/uart_ll.h +++ b/components/hal/esp32s2/include/hal/uart_ll.h @@ -280,6 +280,11 @@ FORCE_INLINE_ATTR uint32_t uart_ll_get_intsts_mask(uart_dev_t *hw) return hw->int_st.val; } +FORCE_INLINE_ATTR volatile void* uart_ll_get_intr_status_reg(uart_dev_t *hw) +{ + return &hw->int_st.val; +} + /** * @brief Clear the UART interrupt status based on the given mask. * diff --git a/components/hal/esp32s3/include/hal/i2c_ll.h b/components/hal/esp32s3/include/hal/i2c_ll.h index 5a96009d148a..8202a9cfc7db 100644 --- a/components/hal/esp32s3/include/hal/i2c_ll.h +++ b/components/hal/esp32s3/include/hal/i2c_ll.h @@ -1004,6 +1004,8 @@ static inline i2c_slave_read_write_status_t i2c_ll_slave_get_read_write_status(i #define I2C_LL_SLAVE_RX_INT (I2C_RXFIFO_WM_INT_ENA_M | I2C_TRANS_COMPLETE_INT_ENA_M) // I2C max timeout value #define I2C_LL_MAX_TIMEOUT I2C_TIME_OUT_VALUE_V +// I2C max timeout period in clock cycles +#define I2C_LL_MAX_TIMEOUT_PERIOD (1UL << I2C_LL_MAX_TIMEOUT) #define I2C_LL_INTR_MASK (0x3fff) /*!< I2C all interrupt bitmap */ diff --git a/components/hal/esp32s3/include/hal/ledc_ll.h b/components/hal/esp32s3/include/hal/ledc_ll.h index 8557ad3645a2..3c7706b46c0e 100644 --- a/components/hal/esp32s3/include/hal/ledc_ll.h +++ b/components/hal/esp32s3/include/hal/ledc_ll.h @@ -27,6 +27,8 @@ extern "C" { #define LEDC_LL_HPOINT_VAL_MAX (LEDC_HPOINT_LSCH0_V) #define LEDC_LL_FRACTIONAL_BITS (8) #define LEDC_LL_FRACTIONAL_MAX ((1 << LEDC_LL_FRACTIONAL_BITS) - 1) +/// Get the mask of the fade end interrupt status register. +#define LEDC_LL_FADE_END_INTR_MASK (0xffUL << LEDC_DUTY_CHNG_END_LSCH0_INT_ENA_S) #define LEDC_LL_GLOBAL_CLOCKS { \ LEDC_SLOW_CLK_APB, \ @@ -499,7 +501,6 @@ static inline void ledc_ll_set_fade_end_intr(ledc_dev_t *hw, ledc_mode_t speed_m * * @param hw Beginning address of the peripheral registers * @param speed_mode LEDC speed_mode, high-speed mode or low-speed mode - * @param channel_num LEDC channel index (0-7), select from ledc_channel_t * @param intr_status The fade end interrupt status * * @return None @@ -511,6 +512,18 @@ static inline void ledc_ll_get_fade_end_intr_status(ledc_dev_t *hw, ledc_mode_t *intr_status = (value >> int_en_base) & 0xff; } +/** + * @brief Get the address of the fade end interrupt status register. + * + * @param hw Beginning address of the peripheral registers + * @return Pointer to the fade end interrupt status register. + */ +static inline volatile void* ledc_ll_get_fade_end_intr_addr(ledc_dev_t *hw) +{ + return &hw->int_st.val; +} + + /** * @brief Clear fade end interrupt status * diff --git a/components/hal/esp32s3/include/hal/uart_ll.h b/components/hal/esp32s3/include/hal/uart_ll.h index 816e8103fe01..0ab5c961f5b7 100644 --- a/components/hal/esp32s3/include/hal/uart_ll.h +++ b/components/hal/esp32s3/include/hal/uart_ll.h @@ -337,6 +337,11 @@ FORCE_INLINE_ATTR uint32_t uart_ll_get_intr_ena_status(uart_dev_t *hw) return hw->int_ena.val; } +FORCE_INLINE_ATTR volatile void* uart_ll_get_intr_status_reg(uart_dev_t *hw) +{ + return &hw->int_st.val; +} + /** * @brief Read the UART rxfifo. * diff --git a/components/hal/include/hal/ledc_hal.h b/components/hal/include/hal/ledc_hal.h index 18ac920a2d6e..c8cafc84493e 100644 --- a/components/hal/include/hal/ledc_hal.h +++ b/components/hal/include/hal/ledc_hal.h @@ -407,6 +407,17 @@ void ledc_hal_clear_fade_end_intr_status(ledc_hal_context_t *hal, ledc_channel_t */ void ledc_hal_get_clk_cfg(ledc_hal_context_t *hal, ledc_timer_t timer_sel, ledc_clk_cfg_t *clk_cfg); +/** + * @brief Get the address of the fade end interrupt status register. + * + * @param hal Context of the HAL layer + * @return Pointer to the fade end interrupt status register. + */ +static inline volatile void* ledc_hal_get_fade_end_intr_addr(ledc_hal_context_t *hal) +{ + return ledc_ll_get_fade_end_intr_addr(hal->dev); +} + #endif //#if SOC_LEDC_SUPPORTED #ifdef __cplusplus diff --git a/components/hal/include/hal/ppa_types.h b/components/hal/include/hal/ppa_types.h index 6f9932225506..0120556ce3fb 100644 --- a/components/hal/include/hal/ppa_types.h +++ b/components/hal/include/hal/ppa_types.h @@ -40,10 +40,9 @@ typedef enum { PPA_SRM_COLOR_MODE_RGB888 = COLOR_TYPE_ID(COLOR_SPACE_RGB, COLOR_PIXEL_RGB888), /*!< PPA SRM color mode: RGB888 */ PPA_SRM_COLOR_MODE_RGB565 = COLOR_TYPE_ID(COLOR_SPACE_RGB, COLOR_PIXEL_RGB565), /*!< PPA SRM color mode: RGB565 */ PPA_SRM_COLOR_MODE_YUV420 = COLOR_TYPE_ID(COLOR_SPACE_YUV, COLOR_PIXEL_YUV420), /*!< PPA SRM color mode: YUV420 */ - PPA_SRM_COLOR_MODE_YUV444 = COLOR_TYPE_ID(COLOR_SPACE_YUV, COLOR_PIXEL_YUV444), /*!< PPA SRM color mode: YUV444 (limited range only)*/ - // YUV444 not supported by PPA hardware, but we can use 2D-DMA to do conversion before sending into and after coming out from the PPA module + PPA_SRM_COLOR_MODE_YUV444 = COLOR_TYPE_ID(COLOR_SPACE_YUV, COLOR_PIXEL_YUV444), /*!< PPA SRM color mode: YUV444 (limited range only and can only be the input color mode)*/ + // YUV444 not supported by PPA hardware, but we can use 2D-DMA to do conversion before sending into the PPA module // If in_pic is YUV444, then TX DMA channel could do DMA2D_CSC_TX_YUV444_TO_RGB888_601/709, so PPA in_color_mode is RGB888 - // If out_pic is YUV444, then RX DMA channel could do DMA2D_CSC_RX_YUV420_TO_YUV444, so PPA out_color_mode is YUV420 PPA_SRM_COLOR_MODE_YUV422_UYVY = COLOR_TYPE_ID(COLOR_SPACE_YUV, COLOR_PIXEL_UYVY422), /*!< PPA SRM color mode: YUV422 */ PPA_SRM_COLOR_MODE_YUV422_VYUY = COLOR_TYPE_ID(COLOR_SPACE_YUV, COLOR_PIXEL_VYUY422), /*!< PPA SRM color mode: YUV422, only available on input */ PPA_SRM_COLOR_MODE_YUV422_YUYV = COLOR_TYPE_ID(COLOR_SPACE_YUV, COLOR_PIXEL_YUYV422), /*!< PPA SRM color mode: YUV422, only available on input */ diff --git a/components/hal/include/hal/sdio_slave_hal.h b/components/hal/include/hal/sdio_slave_hal.h index bfdbe9e9be28..94588c26aff1 100644 --- a/components/hal/include/hal/sdio_slave_hal.h +++ b/components/hal/include/hal/sdio_slave_hal.h @@ -532,6 +532,14 @@ uint8_t sdio_slave_hal_host_get_reg(sdio_slave_context_t *hal, int pos); */ void sdio_slave_hal_host_set_reg(sdio_slave_context_t *hal, int pos, uint8_t reg); +/** + * Get the address of the interrupt status register. + * + * @param hal Context of the HAL layer. + * @return Address of the interrupt status register + */ +volatile void* sdio_slave_hal_get_intr_status_reg(sdio_slave_context_t *hal); + #endif // SOC_SDIO_SLAVE_SUPPORTED #ifdef __cplusplus diff --git a/components/hal/include/hal/spi_hal.h b/components/hal/include/hal/spi_hal.h index 1b7a80dbf5ee..c9d2e136e8cd 100644 --- a/components/hal/include/hal/spi_hal.h +++ b/components/hal/include/hal/spi_hal.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ diff --git a/components/hal/include/hal/spi_slave_hal.h b/components/hal/include/hal/spi_slave_hal.h index dc20208508ec..563e2a704c07 100644 --- a/components/hal/include/hal/spi_slave_hal.h +++ b/components/hal/include/hal/spi_slave_hal.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ diff --git a/components/hal/include/hal/uart_hal.h b/components/hal/include/hal/uart_hal.h index 4c8497cd976a..868b17846b13 100644 --- a/components/hal/include/hal/uart_hal.h +++ b/components/hal/include/hal/uart_hal.h @@ -106,6 +106,16 @@ typedef struct { */ #define uart_hal_get_intr_ena_status(hal) uart_ll_get_intr_ena_status((hal)->dev) +/** + * @brief Get the pointer to the UART interrupt status register + * + * @param hal Context of the HAL layer + * + * @return UART interrupt status register + */ +#define uart_hal_get_intr_status_reg(hal) uart_ll_get_intr_status_reg((hal)->dev) + + /** * @brief Get the UART pattern char configuration * diff --git a/components/hal/include/hal/usb_dwc_hal.h b/components/hal/include/hal/usb_dwc_hal.h index 6178c1d4b885..5948c22c1e6c 100644 --- a/components/hal/include/hal/usb_dwc_hal.h +++ b/components/hal/include/hal/usb_dwc_hal.h @@ -67,6 +67,7 @@ typedef enum { USB_DWC_HAL_PORT_EVENT_DISABLED, /**< The host port has been disabled (no more SOFs). Could be due to disable/reset request, or a port error (e.g. port babble condition. See 11.8.1 of USB2.0 spec) */ USB_DWC_HAL_PORT_EVENT_OVRCUR, /**< The host port has encountered an overcurrent condition */ USB_DWC_HAL_PORT_EVENT_OVRCUR_CLR, /**< The host port has been cleared of the overcurrent condition */ + USB_DWC_HAL_PORT_EVENT_REMOTE_WAKEUP, /**< The host port has detected remote wakeup sequence from a device */ } usb_dwc_hal_port_event_t; /** @@ -305,7 +306,7 @@ static inline void usb_dwc_hal_port_init(usb_dwc_hal_context_t *hal) { //Configure Host related interrupts usb_dwc_ll_haintmsk_dis_chan_intr(hal->dev, 0xFFFFFFFF); //Disable interrupts for all channels - usb_dwc_ll_gintmsk_en_intrs(hal->dev, USB_DWC_LL_INTR_CORE_PRTINT | USB_DWC_LL_INTR_CORE_HCHINT); + usb_dwc_ll_gintmsk_en_intrs(hal->dev, USB_DWC_LL_INTR_CORE_PRTINT | USB_DWC_LL_INTR_CORE_HCHINT | USB_DWC_LL_INTR_CORE_WKUPINT); } /** @@ -318,7 +319,7 @@ static inline void usb_dwc_hal_port_init(usb_dwc_hal_context_t *hal) static inline void usb_dwc_hal_port_deinit(usb_dwc_hal_context_t *hal) { //Disable Host port and channel interrupts - usb_dwc_ll_gintmsk_dis_intrs(hal->dev, USB_DWC_LL_INTR_CORE_PRTINT | USB_DWC_LL_INTR_CORE_HCHINT); + usb_dwc_ll_gintmsk_dis_intrs(hal->dev, USB_DWC_LL_INTR_CORE_PRTINT | USB_DWC_LL_INTR_CORE_HCHINT | USB_DWC_LL_INTR_CORE_WKUPINT); } /** diff --git a/components/hal/ledc_hal_iram.c b/components/hal/ledc_hal_iram.c index 34884c78e74c..804473132c88 100644 --- a/components/hal/ledc_hal_iram.c +++ b/components/hal/ledc_hal_iram.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ diff --git a/components/hal/sdio_slave_hal.c b/components/hal/sdio_slave_hal.c index 1c48fe18d9e4..798efc2c2a3c 100644 --- a/components/hal/sdio_slave_hal.c +++ b/components/hal/sdio_slave_hal.c @@ -721,3 +721,8 @@ void sdio_slave_hal_slvint_fetch_clear(sdio_slave_context_t *hal, sdio_slave_ll_ { sdio_slave_ll_slvint_fetch_clear(hal->slc, out_int_mask); } + +volatile void* sdio_slave_hal_get_intr_status_reg(sdio_slave_context_t *hal) +{ + return sdio_slave_ll_get_intr_status_reg(hal->slc); +} diff --git a/components/hal/spi_hal.c b/components/hal/spi_hal.c index c1865ab3af9f..1e6536636956 100644 --- a/components/hal/spi_hal.c +++ b/components/hal/spi_hal.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ diff --git a/components/hal/usb_dwc_hal.c b/components/hal/usb_dwc_hal.c index ed646682b315..e8ae0afeaecb 100644 --- a/components/hal/usb_dwc_hal.c +++ b/components/hal/usb_dwc_hal.c @@ -44,7 +44,8 @@ //Interrupts that pertain to core events #define CORE_EVENTS_INTRS_MSK (USB_DWC_LL_INTR_CORE_DISCONNINT | \ - USB_DWC_LL_INTR_CORE_HCHINT) + USB_DWC_LL_INTR_CORE_HCHINT | \ + USB_DWC_LL_INTR_CORE_WKUPINT) //Interrupt that pertain to host port events #define PORT_EVENTS_INTRS_MSK (USB_DWC_LL_INTR_HPRT_PRTCONNDET | \ @@ -463,6 +464,8 @@ usb_dwc_hal_port_event_t usb_dwc_hal_decode_intr(usb_dwc_hal_context_t *hal) } else if (intrs_port & USB_DWC_LL_INTR_HPRT_PRTCONNDET && !hal->flags.dbnc_lock_enabled) { event = USB_DWC_HAL_PORT_EVENT_CONN; debounce_lock_enable(hal); + } else if (intrs_core & USB_DWC_LL_INTR_CORE_WKUPINT) { + event = USB_DWC_HAL_PORT_EVENT_REMOTE_WAKEUP; // Remote wakeup was generated from device } } //Port events always take precedence over channel events diff --git a/components/ieee802154/driver/esp_ieee802154_dev.c b/components/ieee802154/driver/esp_ieee802154_dev.c index a6d7bdec93b9..0f167bdaca24 100644 --- a/components/ieee802154/driver/esp_ieee802154_dev.c +++ b/components/ieee802154/driver/esp_ieee802154_dev.c @@ -44,6 +44,7 @@ static bool s_rf_closed = true; #define CCA_DETECTION_TIME 8 extern void ieee802154_txon_delay_set(void); +extern uint32_t bt_bb_get_cur_rx_info(void); IEEE802154_STATIC volatile ieee802154_state_t s_ieee802154_state; static uint8_t *s_tx_frame = NULL; @@ -167,7 +168,7 @@ static IEEE802154_NOINLINE void ieee802154_rx_frame_info_update(void) int8_t ieee802154_get_recent_rssi(void) { - return s_rx_frame_info[s_recent_rx_frame_info_index].rssi; + return (int8_t)(bt_bb_get_cur_rx_info() & 0xff); } uint8_t ieee802154_get_recent_lqi(void) diff --git a/components/mbedtls/port/ecc/ecc_alt.c b/components/mbedtls/port/ecc/ecc_alt.c index 4ffa065c24b3..7b70da59cfdc 100644 --- a/components/mbedtls/port/ecc/ecc_alt.c +++ b/components/mbedtls/port/ecc/ecc_alt.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -7,14 +7,13 @@ #include #include "soc/hwcrypto_periph.h" #include "ecc_impl.h" +#include "hal/ecc_ll.h" #include "mbedtls/ecp.h" #include "mbedtls/platform_util.h" #if defined(MBEDTLS_ECP_MUL_ALT) || defined(MBEDTLS_ECP_MUL_ALT_SOFT_FALLBACK) -#define MAX_SIZE 32 // 256 bits - static int esp_mbedtls_ecp_point_multiply(const mbedtls_ecp_group *grp, mbedtls_ecp_point *R, const mbedtls_mpi *m, const mbedtls_ecp_point *P) { @@ -54,7 +53,11 @@ int ecp_mul_restartable_internal( mbedtls_ecp_group *grp, mbedtls_ecp_point *R, mbedtls_ecp_restart_ctx *rs_ctx ) { int ret = MBEDTLS_ERR_ECP_BAD_INPUT_DATA; - if (grp->id != MBEDTLS_ECP_DP_SECP192R1 && grp->id != MBEDTLS_ECP_DP_SECP256R1) { + if (grp->id != MBEDTLS_ECP_DP_SECP192R1 && grp->id != MBEDTLS_ECP_DP_SECP256R1 +#if SOC_ECC_SUPPORT_CURVE_P384 + && (grp->id != MBEDTLS_ECP_DP_SECP384R1 || !ecc_ll_is_p384_curve_operations_supported()) +#endif + ) { #if defined(MBEDTLS_ECP_MUL_ALT_SOFT_FALLBACK) return ecp_mul_restartable_internal_soft(grp, R, m, P, f_rng, p_rng, rs_ctx); #else @@ -85,7 +88,11 @@ int mbedtls_ecp_check_pubkey( const mbedtls_ecp_group *grp, return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; } - if (grp->id != MBEDTLS_ECP_DP_SECP192R1 && grp->id != MBEDTLS_ECP_DP_SECP256R1) { + if (grp->id != MBEDTLS_ECP_DP_SECP192R1 && grp->id != MBEDTLS_ECP_DP_SECP256R1 +#if SOC_ECC_SUPPORT_CURVE_P384 + && (grp->id != MBEDTLS_ECP_DP_SECP384R1 || !ecc_ll_is_p384_curve_operations_supported()) +#endif + ) { #if defined(MBEDTLS_ECP_VERIFY_ALT_SOFT_FALLBACK) return mbedtls_ecp_check_pubkey_soft(grp, pt); #else diff --git a/components/mbedtls/port/include/ecc_impl.h b/components/mbedtls/port/include/ecc_impl.h index c52c988195ba..d7900ee49fcc 100644 --- a/components/mbedtls/port/include/ecc_impl.h +++ b/components/mbedtls/port/include/ecc_impl.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -14,6 +14,13 @@ extern "C" { #define P256_LEN (256/8) #define P192_LEN (192/8) +#define P384_LEN (384/8) + +#if SOC_ECC_SUPPORT_CURVE_P384 +#define MAX_SIZE P384_LEN +#else +#define MAX_SIZE P256_LEN +#endif /* Note: x & y are stored in little endian order (same as CPU byte order, and order used internally by most libraries). @@ -22,9 +29,9 @@ extern "C" { Note this is opposite to most byte string formats used to represent keys, which are often big endian */ typedef struct { - uint8_t x[P256_LEN]; /* Little endian order */ - uint8_t y[P256_LEN]; /* Little endian order */ - unsigned len; /* P192_LEN or P256_LEN */ + uint8_t x[MAX_SIZE]; /* Little endian order */ + uint8_t y[MAX_SIZE]; /* Little endian order */ + unsigned len; /* P192_LEN, P256_LEN, or P384_LEN */ } ecc_point_t; /** diff --git a/components/mbedtls/test_apps/main/test_ecp.c b/components/mbedtls/test_apps/main/test_ecp.c index db4258703075..d711eef1e347 100644 --- a/components/mbedtls/test_apps/main/test_ecp.c +++ b/components/mbedtls/test_apps/main/test_ecp.c @@ -3,7 +3,7 @@ * Focus on testing functionality where we use ESP32 hardware * accelerated crypto features. * - * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -22,6 +22,9 @@ #include "test_utils.h" #include "ccomp_timer.h" #include "unity.h" +#if CONFIG_MBEDTLS_HARDWARE_ECC +#include "hal/ecc_ll.h" +#endif /* Note: negative value here so that assert message prints a grep-able error hex value (mbedTLS uses -N for error codes) */ @@ -202,6 +205,70 @@ const uint8_t ecc_p256_small_mul_res_y[] = { 0x17, 0xFD, 0xF6, 0x64, 0x41, 0x9E, 0x50, 0x0C }; +#if SOC_ECC_SUPPORT_CURVE_P384 +const uint8_t ecc_p384_point_x[] = { + 0xaa, 0x87, 0xca, 0x22, 0xbe, 0x8b, 0x05, 0x37, + 0x8e, 0xb1, 0xc7, 0x1e, 0xf3, 0x20, 0xad, 0x74, + 0x6e, 0x1d, 0x3b, 0x62, 0x8b, 0xa7, 0x9b, 0x98, + 0x59, 0xf7, 0x41, 0xe0, 0x82, 0x54, 0x2a, 0x38, + 0x55, 0x02, 0xf2, 0x5d, 0xbf, 0x55, 0x29, 0x6c, + 0x3a, 0x54, 0x5e, 0x38, 0x72, 0x76, 0x0a, 0xb7 +}; + +const uint8_t ecc_p384_point_y[] = { + 0x36, 0x17, 0xde, 0x4a, 0x96, 0x26, 0x2c, 0x6f, + 0x5d, 0x9e, 0x98, 0xbf, 0x92, 0x92, 0xdc, 0x29, + 0xf8, 0xf4, 0x1d, 0xbd, 0x28, 0x9a, 0x14, 0x7c, + 0xe9, 0xda, 0x31, 0x13, 0xb5, 0xf0, 0xb8, 0xc0, + 0x0a, 0x60, 0xb1, 0xce, 0x1d, 0x7e, 0x81, 0x9d, + 0x7a, 0x43, 0x1d, 0x7c, 0x90, 0xea, 0x0e, 0x5f +}; + +const uint8_t ecc_p384_scalar[] = { + 0x68, 0xd1, 0x09, 0xa7, 0xc7, 0x7e, 0xeb, 0xbd, + 0x43, 0x18, 0x7e, 0xdd, 0x69, 0x23, 0x7e, 0x0a, + 0xef, 0x07, 0xc2, 0x0e, 0xc5, 0x3d, 0xe7, 0xcb, + 0xd4, 0x36, 0xad, 0x9b, 0xdc, 0xf8, 0x6c, 0x5c, + 0x0c, 0x3d, 0xce, 0x45, 0xcd, 0x6f, 0x7f, 0x18, + 0x40, 0xc5, 0x29, 0xf3, 0xcd, 0x12, 0x1d, 0xc2 +}; + +const uint8_t ecc_p384_mul_res_x[] = { + 0x74, 0x1d, 0xc3, 0xba, 0xac, 0x60, 0x37, 0xfc, + 0x57, 0x85, 0x90, 0x95, 0x64, 0xe6, 0xd1, 0xef, + 0x86, 0xdf, 0x42, 0xe0, 0xaf, 0x11, 0x24, 0x1f, + 0xe9, 0x97, 0x6e, 0x0c, 0xd9, 0xe5, 0xa0, 0x5d, + 0xd9, 0x91, 0x96, 0x71, 0xef, 0x96, 0xe9, 0x7e, + 0x90, 0xba, 0xa8, 0x33, 0xe2, 0x2e, 0xf0, 0x7b +}; + +const uint8_t ecc_p384_mul_res_y[] = { + 0xc3, 0xe0, 0x66, 0x50, 0xd9, 0x1e, 0xa9, 0x42, + 0xcb, 0x0d, 0xec, 0xb6, 0x29, 0xe2, 0xae, 0x75, + 0xc6, 0xa2, 0xb9, 0xa6, 0xcf, 0x2c, 0x97, 0x01, + 0xcc, 0xff, 0x7c, 0x1c, 0xd1, 0x01, 0xde, 0xbc, + 0x40, 0x56, 0x8c, 0x18, 0x21, 0x9d, 0xbd, 0xc0, + 0x2d, 0x41, 0x5b, 0x92, 0x52, 0x5a, 0x40, 0x57 +}; + +const uint8_t ecc_p384_small_mul_res_x[] = { + 0x35, 0x49, 0x60, 0x41, 0xea, 0x25, 0x3b, 0x0d, + 0x15, 0x3c, 0x9b, 0xfb, 0xc1, 0x8a, 0x9e, 0x41, + 0xaf, 0x34, 0x8a, 0xfd, 0x8b, 0x1c, 0x33, 0xa5, + 0xca, 0x5d, 0x7f, 0xbb, 0xfa, 0x2d, 0x5d, 0x9d, + 0x43, 0x6e, 0xd1, 0x01, 0x1b, 0x3d, 0x9d, 0x93, + 0xe4, 0xb4, 0x5d, 0x2a, 0x4b, 0x23, 0x27, 0xf1 +}; + +const uint8_t ecc_p384_small_mul_res_y[] = { + 0x73, 0xce, 0x1e, 0xaa, 0x4f, 0xfd, 0xdc, 0x1d, + 0x69, 0xd9, 0xe0, 0x9d, 0x16, 0x46, 0x19, 0xae, + 0x8d, 0xd2, 0xce, 0x26, 0x6f, 0x9d, 0xb6, 0xc3, + 0x30, 0xa5, 0x05, 0x7c, 0x7d, 0x62, 0xde, 0x8f, + 0x8e, 0xc3, 0xce, 0x9b, 0xa7, 0xc1, 0x71, 0xb9, + 0xb0, 0x2a, 0xda, 0x1c, 0xb3, 0x42, 0x61, 0x58 +}; +#endif /* SOC_ECC_SUPPORT_CURVE_P384 */ static int rng_wrapper(void *ctx, unsigned char *buf, size_t len) { @@ -213,8 +280,8 @@ static void test_ecp_mul(mbedtls_ecp_group_id id, const uint8_t *x_coord, const const uint8_t *result_x_coord, const uint8_t *result_y_coord) { int64_t elapsed_time; - uint8_t x[32]; - uint8_t y[32]; + uint8_t x[48]; + uint8_t y[48]; int size; int ret; @@ -259,6 +326,12 @@ static void test_ecp_mul(mbedtls_ecp_group_id id, const uint8_t *x_coord, const TEST_PERFORMANCE_CCOMP_LESS_THAN(ECP_P192_POINT_MULTIPLY_OP, "%" NEWLIB_NANO_COMPAT_FORMAT" us", NEWLIB_NANO_COMPAT_CAST(elapsed_time)); } else if (id == MBEDTLS_ECP_DP_SECP256R1) { TEST_PERFORMANCE_CCOMP_LESS_THAN(ECP_P256_POINT_MULTIPLY_OP, "%" NEWLIB_NANO_COMPAT_FORMAT" us", NEWLIB_NANO_COMPAT_CAST(elapsed_time)); +#if SOC_ECC_SUPPORT_CURVE_P384 + } else if (id == MBEDTLS_ECP_DP_SECP384R1) { + if (ecc_ll_is_p384_curve_operations_supported()) { + TEST_PERFORMANCE_CCOMP_LESS_THAN(ECP_P384_POINT_MULTIPLY_OP, "%" NEWLIB_NANO_COMPAT_FORMAT" us", NEWLIB_NANO_COMPAT_CAST(elapsed_time)); + } +#endif } mbedtls_ecp_point_free(&R); @@ -285,6 +358,19 @@ TEST_CASE("mbedtls ECP point multiply with SECP256R1", "[mbedtls]") ecc_p256_small_mul_res_x, ecc_p256_small_mul_res_y); } +#if SOC_ECC_SUPPORT_CURVE_P384 +TEST_CASE("mbedtls ECP point multiply with SECP384R1", "[mbedtls]") +{ + if (ecc_ll_is_p384_curve_operations_supported()) { + test_ecp_mul(MBEDTLS_ECP_DP_SECP384R1, ecc_p384_point_x, ecc_p384_point_y, ecc_p384_scalar, + ecc_p384_mul_res_x, ecc_p384_mul_res_y); + + test_ecp_mul(MBEDTLS_ECP_DP_SECP384R1, ecc_p384_point_x, ecc_p384_point_y, NULL, + ecc_p384_small_mul_res_x, ecc_p384_small_mul_res_y); + } +} +#endif /* SOC_ECC_SUPPORT_CURVE_P384 */ + static void test_ecp_verify(mbedtls_ecp_group_id id, const uint8_t *x_coord, const uint8_t *y_coord) { int64_t elapsed_time; @@ -315,6 +401,12 @@ static void test_ecp_verify(mbedtls_ecp_group_id id, const uint8_t *x_coord, con TEST_PERFORMANCE_CCOMP_LESS_THAN(ECP_P192_POINT_VERIFY_OP, "%" NEWLIB_NANO_COMPAT_FORMAT" us", NEWLIB_NANO_COMPAT_CAST(elapsed_time)); } else if (id == MBEDTLS_ECP_DP_SECP256R1) { TEST_PERFORMANCE_CCOMP_LESS_THAN(ECP_P256_POINT_VERIFY_OP, "%" NEWLIB_NANO_COMPAT_FORMAT" us", NEWLIB_NANO_COMPAT_CAST(elapsed_time)); +#if SOC_ECC_SUPPORT_CURVE_P384 + } else if (id == MBEDTLS_ECP_DP_SECP384R1) { + if (ecc_ll_is_p384_curve_operations_supported()) { + TEST_PERFORMANCE_CCOMP_LESS_THAN(ECP_P384_POINT_VERIFY_OP, "%" NEWLIB_NANO_COMPAT_FORMAT" us", NEWLIB_NANO_COMPAT_CAST(elapsed_time)); + } +#endif } mbedtls_ecp_point_free(&P); @@ -330,4 +422,13 @@ TEST_CASE("mbedtls ECP point verify with SECP256R1", "[mbedtls]") { test_ecp_verify(MBEDTLS_ECP_DP_SECP256R1, ecc_p256_mul_res_x, ecc_p256_mul_res_y); } + +#if SOC_ECC_SUPPORT_CURVE_P384 +TEST_CASE("mbedtls ECP point verify with SECP384R1", "[mbedtls]") +{ + if (ecc_ll_is_p384_curve_operations_supported()) { + test_ecp_verify(MBEDTLS_ECP_DP_SECP384R1, ecc_p384_mul_res_x, ecc_p384_mul_res_y); + } +} +#endif /* SOC_ECC_SUPPORT_CURVE_P384 */ #endif /* CONFIG_MBEDTLS_HARDWARE_ECC */ diff --git a/components/openthread/CMakeLists.txt b/components/openthread/CMakeLists.txt index 54e8f99d8a68..34ef874c735e 100644 --- a/components/openthread/CMakeLists.txt +++ b/components/openthread/CMakeLists.txt @@ -102,6 +102,7 @@ if(CONFIG_OPENTHREAD_ENABLED) "openthread/src/core/thread/announce_begin_server.cpp" "openthread/src/core/thread/announce_sender.cpp" "openthread/src/core/thread/address_resolver.cpp" + "openthread/src/core/thread/child.cpp" "openthread/src/core/thread/child_supervision.cpp" "openthread/src/core/thread/csl_tx_scheduler.cpp" "openthread/src/core/thread/discover_scanner.cpp" @@ -115,6 +116,8 @@ if(CONFIG_OPENTHREAD_ENABLED) "openthread/src/core/thread/mesh_forwarder_mtd.cpp" "openthread/src/core/thread/message_framer.cpp" "openthread/src/core/thread/mle.cpp" + "openthread/src/core/thread/mle_ftd.cpp" + "openthread/src/core/thread/mle_p2p.cpp" "openthread/src/core/thread/mle_router.cpp" "openthread/src/core/thread/mle_types.cpp" "openthread/src/core/thread/neighbor.cpp" @@ -125,7 +128,9 @@ if(CONFIG_OPENTHREAD_ENABLED) "openthread/src/core/thread/network_data_types.cpp" "openthread/src/core/thread/network_data_service.cpp" "openthread/src/core/thread/network_diagnostic.cpp" + "openthread/src/core/thread/network_diagnostic_tlvs.cpp" "openthread/src/core/thread/panid_query_server.cpp" + "openthread/src/core/thread/router.cpp" "openthread/src/core/thread/thread_netif.cpp" "openthread/src/core/thread/time_sync_service.cpp" "openthread/src/core/thread/tmf.cpp" @@ -196,6 +201,11 @@ if(CONFIG_OPENTHREAD_ENABLED) "src/port/esp_openthread_messagepool.c") endif() + if(NOT CONFIG_OPENTHREAD_RCP_SPINEL_CONSOLE) + list(APPEND exclude_srcs + "src/ncp/esp_openthread_ncp_console.cpp") + endif() + if(CONFIG_OPENTHREAD_FTD) set(device_type "OPENTHREAD_FTD=1") elseif(CONFIG_OPENTHREAD_MTD) diff --git a/components/openthread/Kconfig b/components/openthread/Kconfig index 2a89bf47fc79..c6bf703d8cdc 100644 --- a/components/openthread/Kconfig +++ b/components/openthread/Kconfig @@ -239,6 +239,12 @@ menu "OpenThread" default y help Select this to enable OpenThread NCP vendor commands. + + config OPENTHREAD_RCP_SPINEL_CONSOLE + bool "Enable RCP console via Spinel" + default n + help + Select this to enable sending console commands to OpenThread RCP via Spinel. endmenu config OPENTHREAD_BORDER_ROUTER diff --git a/components/openthread/include/esp_openthread_spinel.h b/components/openthread/include/esp_openthread_spinel.h index 290009542006..09f7dfe99e84 100644 --- a/components/openthread/include/esp_openthread_spinel.h +++ b/components/openthread/include/esp_openthread_spinel.h @@ -76,6 +76,14 @@ esp_err_t esp_openthread_rcp_init(void); */ esp_err_t esp_openthread_rcp_version_set(const char *version_str); +/** + * @brief Sends a console command to RCP. + * + * @param[in] input The pointer to a command string to be run on RCP. + * + */ +void esp_openthread_rcp_send_command(const char *input); + #ifdef __cplusplus } #endif diff --git a/components/openthread/include/esp_radio_spinel.h b/components/openthread/include/esp_radio_spinel.h index 504eda1f4f82..18638b3275cb 100644 --- a/components/openthread/include/esp_radio_spinel.h +++ b/components/openthread/include/esp_radio_spinel.h @@ -18,10 +18,6 @@ extern "C" { #define ESP_SPINEL_LOG_TAG "ESP_RADIO_SPINEL" -#define SPINEL_PROP_VENDOR_ESP_SET_COORDINATOR (SPINEL_PROP_VENDOR_ESP__BEGIN + 1) /* Vendor command for coordinator.*/ - -#define SPINEL_PROP_VENDOR_ESP_SET_PENDINGMODE (SPINEL_PROP_VENDOR_ESP__BEGIN + 2) /* Vendor command for pending mode.*/ - typedef enum { ESP_RADIO_SPINEL_ZIGBEE = 0x0, /* The index of Zigbee.*/ ESP_RADIO_SPINEL_OPENTHREAD = 0x1, /* The index of OpenThread.*/ diff --git a/components/openthread/lib b/components/openthread/lib index e9980c565588..2e2d91a382ad 160000 --- a/components/openthread/lib +++ b/components/openthread/lib @@ -1 +1 @@ -Subproject commit e9980c56558876ae1102be8ae9dc70821aded72c +Subproject commit 2e2d91a382ad6387ce77551c6adbb1514db12472 diff --git a/components/openthread/openthread b/components/openthread/openthread index 36b14d3ef74f..a12ff0d0f54f 160000 --- a/components/openthread/openthread +++ b/components/openthread/openthread @@ -1 +1 @@ -Subproject commit 36b14d3ef74f5e37e5be8902e1c1955a642fdfbf +Subproject commit a12ff0d0f54fd41954b45047fcdd08f302731c5f diff --git a/components/openthread/private_include/esp_openthread_ncp.h b/components/openthread/private_include/esp_openthread_ncp.h index 65053bb56a8a..2d529dd7704d 100644 --- a/components/openthread/private_include/esp_openthread_ncp.h +++ b/components/openthread/private_include/esp_openthread_ncp.h @@ -4,25 +4,18 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include #include -#if CONFIG_OPENTHREAD_NCP_VENDOR_HOOK - -#define SPINEL_PROP_VENDOR_ESP_SET_COORDINATOR (SPINEL_PROP_VENDOR_ESP__BEGIN + 1) - -#define SPINEL_PROP_VENDOR_ESP_SET_PENDINGMODE (SPINEL_PROP_VENDOR_ESP__BEGIN + 2) - -#define SPINEL_PROP_VENDOR_ESP_COEX_EVENT (SPINEL_PROP_VENDOR_ESP__BEGIN + 3) - -#endif - #ifdef __cplusplus extern "C" { #endif void otAppNcpInit(otInstance *aInstance); +#if CONFIG_OPENTHREAD_RCP_SPINEL_CONSOLE +esp_err_t esp_console_redirect_to_otlog(void); +#endif // CONFIG_OPENTHREAD_RCP_SPINEL_CONSOLE + #ifdef __cplusplus } #endif diff --git a/components/openthread/private_include/esp_spinel_ncp_vendor_macro.h b/components/openthread/private_include/esp_spinel_ncp_vendor_macro.h new file mode 100644 index 000000000000..6d572f45b351 --- /dev/null +++ b/components/openthread/private_include/esp_spinel_ncp_vendor_macro.h @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "spinel.h" + +#define SPINEL_PROP_VENDOR_ESP_SET_COORDINATOR (SPINEL_PROP_VENDOR_ESP__BEGIN + 1) /* Vendor command for coordinator.*/ + +#define SPINEL_PROP_VENDOR_ESP_SET_PENDINGMODE (SPINEL_PROP_VENDOR_ESP__BEGIN + 2) /* Vendor command for pending mode.*/ + +#define SPINEL_PROP_VENDOR_ESP_COEX_EVENT (SPINEL_PROP_VENDOR_ESP__BEGIN + 3) + +#define SPINEL_PROP_VENDOR_ESP_SET_CONSOLE_CMD (SPINEL_PROP_VENDOR_ESP__BEGIN + 4) diff --git a/components/openthread/private_include/openthread-core-esp32x-radio-config.h b/components/openthread/private_include/openthread-core-esp32x-radio-config.h index 8be7e0de72bc..3df6677e2e1c 100644 --- a/components/openthread/private_include/openthread-core-esp32x-radio-config.h +++ b/components/openthread/private_include/openthread-core-esp32x-radio-config.h @@ -192,10 +192,10 @@ /** * @def OPENTHREAD_CONFIG_LOG_OUTPUT * - * The ESP-IDF platform provides an otPlatLog() function. + * Use app log for RCP to transmit logs to host via Spinel. */ #ifndef OPENTHREAD_CONFIG_LOG_OUTPUT -#define OPENTHREAD_CONFIG_LOG_OUTPUT OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED +#define OPENTHREAD_CONFIG_LOG_OUTPUT OPENTHREAD_CONFIG_LOG_OUTPUT_APP #endif /** diff --git a/components/openthread/sbom_openthread.yml b/components/openthread/sbom_openthread.yml index 74398658c691..8e476b6bfde7 100644 --- a/components/openthread/sbom_openthread.yml +++ b/components/openthread/sbom_openthread.yml @@ -5,4 +5,4 @@ supplier: 'Organization: Espressif Systems (Shanghai) CO LTD' originator: 'Organization: Google LLC' description: OpenThread released by Google is an open-source implementation of the Thread networking url: https://github.com/espressif/openthread -hash: 36b14d3ef74f5e37e5be8902e1c1955a642fdfbf +hash: a12ff0d0f54fd41954b45047fcdd08f302731c5f diff --git a/components/openthread/src/ncp/esp_openthread_ncp.cpp b/components/openthread/src/ncp/esp_openthread_ncp.cpp index 94c31113dece..eb0bad9c508a 100644 --- a/components/openthread/src/ncp/esp_openthread_ncp.cpp +++ b/components/openthread/src/ncp/esp_openthread_ncp.cpp @@ -5,8 +5,10 @@ */ #include "sdkconfig.h" +#include "esp_check.h" #include "esp_ieee802154.h" #include "esp_openthread_ncp.h" +#include "esp_spinel_ncp_vendor_macro.h" #include "ncp_base.hpp" #if (CONFIG_ESP_COEX_SW_COEXIST_ENABLE || CONFIG_EXTERNAL_COEX_ENABLE) @@ -17,6 +19,24 @@ #include "utils/uart.h" #endif +#if CONFIG_OPENTHREAD_RCP_SPINEL_CONSOLE +#include "esp_console.h" +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "esp_openthread_common_macro.h" + +static constexpr size_t s_console_command_max_length = 256; +static constexpr size_t s_console_command_queue_length = 8; +static QueueHandle_t s_console_command_queue = nullptr; + +#define NO_LOG_TAG "" // don't use a tag to reduce log lengths from RCP + +struct ConsoleCmdMsg +{ + char cmd[s_console_command_max_length]; +}; +#endif // CONFIG_OPENTHREAD_RCP_SPINEL_CONSOLE + #if (CONFIG_OPENTHREAD_RCP_UART || CONFIG_OPENTHREAD_RCP_USB_SERIAL_JTAG) extern "C" { static int NcpSend(const uint8_t *aBuf, uint16_t aBufLength) @@ -27,15 +47,69 @@ extern "C" { } #endif +#if CONFIG_OPENTHREAD_RCP_SPINEL_CONSOLE +static void console_command_worker_task(void *arg) +{ + ConsoleCmdMsg msg; + + for (;;) { + if (xQueueReceive(s_console_command_queue, &msg, portMAX_DELAY) != pdTRUE) { + continue; + } + + int ret = 0; + esp_err_t err = esp_console_run(msg.cmd, &ret); + + if (err == ESP_ERR_NOT_FOUND){ + ESP_LOGI(NO_LOG_TAG, "Unrecognized command: %s", msg.cmd); + } else if (err == ESP_ERR_INVALID_ARG) { + ESP_LOGI(NO_LOG_TAG, "Command is empty"); + } else if (err == ESP_OK && ret != ESP_OK) { + ESP_LOGI(NO_LOG_TAG, "Command returned non-zero error code: 0x%x (%s)", ret, esp_err_to_name(ret)); + } else if (err != ESP_OK) { + ESP_LOGI(NO_LOG_TAG, "Internal error running '%s': %s", msg.cmd, esp_err_to_name(err)); + } + } +} + +static esp_err_t init_console_command_worker() +{ + if (s_console_command_queue) { + return ESP_OK; + } + + s_console_command_queue = xQueueCreate(s_console_command_queue_length, sizeof(ConsoleCmdMsg)); + ESP_RETURN_ON_FALSE(s_console_command_queue, ESP_ERR_NO_MEM, OT_PLAT_LOG_TAG, "Failed to create s_console_command_queue"); + + BaseType_t ret = xTaskCreatePinnedToCore(console_command_worker_task, "ot_console", 3072, nullptr, 3, nullptr, tskNO_AFFINITY); + + if (ret != pdPASS) { + vQueueDelete(s_console_command_queue); + s_console_command_queue = nullptr; + return ESP_FAIL; + } + + return ESP_OK; +} +#endif // CONFIG_OPENTHREAD_RCP_SPINEL_CONSOLE + extern "C" void otAppNcpInit(otInstance *aInstance) { -#if CONFIG_OPENTHREAD_RCP_SPI - otNcpSpiInit(aInstance); -#else +#if (CONFIG_OPENTHREAD_RCP_UART || CONFIG_OPENTHREAD_RCP_USB_SERIAL_JTAG) IgnoreError(otPlatUartEnable()); - otNcpHdlcInit(aInstance, NcpSend); +#else + otNcpSpiInit(aInstance); #endif + +#if CONFIG_OPENTHREAD_RCP_SPINEL_CONSOLE + esp_err_t err = esp_console_redirect_to_otlog(); + if (err != ESP_OK) { + ESP_LOGE(NO_LOG_TAG, "Failed to redirect console to otPlatLog: %s", esp_err_to_name(err)); + } + + init_console_command_worker(); +#endif // CONFIG_OPENTHREAD_RCP_SPINEL_CONSOLE } namespace ot { @@ -126,6 +200,30 @@ otError NcpBase::VendorSetPropertyHandler(spinel_prop_key_t aPropKey) #endif break; } +#if CONFIG_OPENTHREAD_RCP_SPINEL_CONSOLE + case SPINEL_PROP_VENDOR_ESP_SET_CONSOLE_CMD: { + const uint8_t *data = nullptr; + uint16_t len = 0; + mDecoder.ReadDataWithLen(data, len); + + if (len >= s_console_command_max_length) { + ESP_LOGW(NO_LOG_TAG, "Console command too long (%u bytes, max %zu), dropping", len, s_console_command_max_length - 1); + break; + } + + ConsoleCmdMsg msg{}; + memcpy(msg.cmd, data, len); + msg.cmd[len] = '\0'; + + // Use a separate task to run the command so that RCP does not time out + if (s_console_command_queue == nullptr) { + ESP_LOGW(NO_LOG_TAG, "Console worker not initialized, dropping cmd: %s", msg.cmd); + } else if (xQueueSend(s_console_command_queue, &msg, 0) != pdTRUE) { + ESP_LOGW(NO_LOG_TAG, "Console cmd queue full, dropping cmd: %s", msg.cmd); + } + break; + } +#endif // CONFIG_OPENTHREAD_RCP_SPINEL_CONSOLE default: error = OT_ERROR_NOT_FOUND; diff --git a/components/openthread/src/ncp/esp_openthread_ncp_console.cpp b/components/openthread/src/ncp/esp_openthread_ncp_console.cpp new file mode 100644 index 000000000000..e70c247f5547 --- /dev/null +++ b/components/openthread/src/ncp/esp_openthread_ncp_console.cpp @@ -0,0 +1,170 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_check.h" +#include "esp_err.h" +#include "esp_vfs.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/stream_buffer.h" +#include "openthread/platform/logging.h" +#include "esp_openthread_common_macro.h" +#include "esp_openthread_ncp.h" + +#define OTLOG_LINE_MAX 256 +#define ROM_TAP_SB_SIZE 1024 +#define ROM_TAP_TRIGLVL 1 + +typedef struct { + char line[OTLOG_LINE_MAX]; + size_t len; +} line_buf_t; + +static SemaphoreHandle_t s_mutex; +static line_buf_t s_stdout_buf; +static line_buf_t s_stderr_buf; +static StreamBufferHandle_t s_rom_tap_sb; +static line_buf_t s_rom_line; + +static inline line_buf_t *get_buf_for_fd(int fd) +{ + if (fd == STDERR_FILENO) return &s_stderr_buf; + // default: treat as stdout + return &s_stdout_buf; +} + +// Flush one assembled line to otPlatLog (strip trailing CR/LF) +static void flush_line_to_otlog(line_buf_t *b, otLogLevel level, bool allow_empty) +{ + while (b->len > 0 && (b->line[b->len - 1] == '\n' || b->line[b->len - 1] == '\r')) { + b->len--; + } + b->line[b->len] = '\0'; + + if (b->len == 0 && allow_empty) { + otPlatLog(level, OT_LOG_REGION_NCP, "%s", " "); + return; + } + + otPlatLog(level, OT_LOG_REGION_NCP, "%s", b->line); + b->len = 0; +} + +// Accumulate bytes; emit at newline or buffer full. +static void buffer_and_maybe_flush(int fd, const char *data, size_t size) +{ + line_buf_t *b = get_buf_for_fd(fd); + otLogLevel lvl = (fd == STDERR_FILENO) ? OT_LOG_LEVEL_WARN : OT_LOG_LEVEL_NOTE; + + for (size_t i = 0; i < size; i++) { + char c = data[i]; + if (b->len < OTLOG_LINE_MAX - 1) { + b->line[b->len++] = c; + } + + if (c == '\n') { + flush_line_to_otlog(b, lvl, /*allow_empty=*/true); + } else if (b->len == OTLOG_LINE_MAX - 1) { + flush_line_to_otlog(b, lvl, /*allow_empty=*/false); + } + } +} + +static ssize_t otlog_write(int fd, const void *data, size_t size) +{ + if (!data || size == 0) return 0; + + if (s_mutex) xSemaphoreTake(s_mutex, portMAX_DELAY); + buffer_and_maybe_flush(fd, (const char *)data, size); + if (s_mutex) xSemaphoreGive(s_mutex); + + return (ssize_t)size; +} + +static ssize_t otlog_read(int fd, void *dst, size_t size) +{ + (void)fd; (void)dst; (void)size; + return 0; +} + +static int otlog_open(const char *path, int flags, int mode) +{ + (void)path; (void)flags; (void)mode; + return 0; +} + +static int otlog_close(int fd) +{ + (void)fd; + return 0; +} + +static IRAM_ATTR void rom_putc_tap(char c) +{ + BaseType_t woken = pdFALSE; + + if (!xPortCanYield()) { + xStreamBufferSendFromISR(s_rom_tap_sb, &c, 1, &woken); + if (woken) portYIELD_FROM_ISR(); + } else { + xStreamBufferSend(s_rom_tap_sb, &c, 1, 0); + } +} + +static esp_err_t esp_rom_printf_mirror_install(void) +{ + s_rom_tap_sb = xStreamBufferCreate(ROM_TAP_SB_SIZE, ROM_TAP_TRIGLVL); + ESP_RETURN_ON_FALSE(s_rom_tap_sb, ESP_ERR_NO_MEM, OT_PLAT_LOG_TAG, "Failed to create s_rom_tap_sb buffer"); + + esp_rom_install_channel_putc(1, rom_putc_tap); + return ESP_OK; +} + +static void rom_tap_consumer_task(void *arg) +{ + const otLogLevel lvl = OT_LOG_LEVEL_NOTE; + uint8_t ch; + + for (;;) { + if (xStreamBufferReceive(s_rom_tap_sb, &ch, 1, portMAX_DELAY) != 1) continue; + + if (s_rom_line.len < OTLOG_LINE_MAX - 1) { + s_rom_line.line[s_rom_line.len++] = (char)ch; + } + if (ch == '\n' || s_rom_line.len == OTLOG_LINE_MAX - 1) { + flush_line_to_otlog(&s_rom_line, lvl, /*allow_empty=*/true); + s_rom_line.len = 0; + } + } +} + +esp_err_t esp_console_redirect_to_otlog(void) +{ + if (!s_mutex) { + s_mutex = xSemaphoreCreateMutex(); + ESP_RETURN_ON_FALSE(s_mutex, ESP_ERR_NO_MEM, OT_PLAT_LOG_TAG, "Failed to create s_mutex"); + } + + esp_vfs_t vfs = {}; + vfs.flags = ESP_VFS_FLAG_DEFAULT; + vfs.write = &otlog_write; + vfs.read = &otlog_read; + vfs.open = &otlog_open; + vfs.close = &otlog_close; + + ESP_RETURN_ON_ERROR(esp_vfs_register("/dev/otlog", &vfs, NULL), OT_PLAT_LOG_TAG, "vfs register failed"); + + FILE *fout = freopen("/dev/otlog", "w", stdout); + FILE *ferr = freopen("/dev/otlog", "w", stderr); + ESP_RETURN_ON_FALSE(fout && ferr, ESP_FAIL, OT_PLAT_LOG_TAG, "freopen failed (stdout=%p, stderr=%p)", fout, ferr); + setvbuf(stdout, NULL, _IONBF, 0); + setvbuf(stderr, NULL, _IONBF, 0); + + ESP_RETURN_ON_ERROR(esp_rom_printf_mirror_install(), OT_PLAT_LOG_TAG, "esp_rom_printf mirror install failed"); + xTaskCreatePinnedToCore(rom_tap_consumer_task, "ot_rom_tap", 2048, NULL, 4, NULL, tskNO_AFFINITY); + + return ESP_OK; +} diff --git a/components/openthread/src/port/esp_openthread_messagepool.c b/components/openthread/src/port/esp_openthread_messagepool.c index 0e0794dc8a13..17761b506187 100644 --- a/components/openthread/src/port/esp_openthread_messagepool.c +++ b/components/openthread/src/port/esp_openthread_messagepool.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -14,6 +14,7 @@ int s_buffer_pool_head = -1; otMessageBuffer **s_buffer_pool_pointer = NULL; +otMessageBuffer *s_buffer_pool = NULL; void otPlatMessagePoolInit(otInstance *aInstance, uint16_t aMinNumFreeBuffers, size_t aBufferSize) { @@ -27,6 +28,7 @@ void otPlatMessagePoolInit(otInstance *aInstance, uint16_t aMinNumFreeBuffers, s s_buffer_pool_pointer[i] = buffer_pool + i * aBufferSize / sizeof(otMessageBuffer); } s_buffer_pool_head = aMinNumFreeBuffers - 1; + s_buffer_pool = buffer_pool; ESP_LOGI(OT_PLAT_LOG_TAG, "Create message buffer pool successfully, size %d", aMinNumFreeBuffers*aBufferSize); } @@ -50,3 +52,16 @@ uint16_t otPlatMessagePoolNumFreeBuffers(otInstance *aInstance) { return s_buffer_pool_head + 1; } + +void otPlatMessagePoolDeinit(otInstance *aInstance) +{ + if (s_buffer_pool_pointer != NULL) { + heap_caps_free(s_buffer_pool_pointer); + s_buffer_pool_pointer = NULL; + } + if (s_buffer_pool != NULL) { + heap_caps_free(s_buffer_pool); + s_buffer_pool = NULL; + } + s_buffer_pool_head = -1; +} diff --git a/components/openthread/src/port/esp_openthread_radio.c b/components/openthread/src/port/esp_openthread_radio.c index 3fd86756770c..c5d32fa09f08 100644 --- a/components/openthread/src/port/esp_openthread_radio.c +++ b/components/openthread/src/port/esp_openthread_radio.c @@ -661,8 +661,8 @@ esp_err_t IRAM_ATTR esp_ieee802154_enh_ack_generator(uint8_t *frame, esp_ieee802 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE otMacFrameGetSrcAddr(&ot_frame, &mac_addr); - link_metrics_data_len = otLinkMetricsEnhAckGenData(&mac_addr, esp_ieee802154_get_recent_lqi(), - esp_ieee802154_get_recent_rssi(), link_metrics_data); + link_metrics_data_len = otLinkMetricsEnhAckGenData(&mac_addr, frame_info->lqi, + frame_info->rssi, link_metrics_data); if (link_metrics_data_len > 0) { offset += otMacFrameGenerateEnhAckProbingIe(ack_ie_data, link_metrics_data, link_metrics_data_len); } diff --git a/components/openthread/src/port/esp_openthread_radio_spinel.cpp b/components/openthread/src/port/esp_openthread_radio_spinel.cpp index 846544b98a2b..c5ff44923d7e 100644 --- a/components/openthread/src/port/esp_openthread_radio_spinel.cpp +++ b/components/openthread/src/port/esp_openthread_radio_spinel.cpp @@ -11,10 +11,12 @@ #include "esp_err.h" #include "esp_openthread_border_router.h" #include "esp_openthread_common_macro.h" +#include "esp_openthread_ncp.h" #include "esp_openthread_platform.h" #include "esp_openthread_types.h" #include "esp_system.h" #include "esp_spinel_interface.hpp" +#include "esp_spinel_ncp_vendor_macro.h" #include "esp_spi_spinel_interface.hpp" #include "esp_uart_spinel_interface.hpp" #include "openthread-core-config.h" @@ -210,6 +212,11 @@ void esp_openthread_handle_netif_state_change(bool state) s_radio.SetTimeSyncState(state); } +void esp_openthread_rcp_send_command(const char *input) +{ + s_radio.Set(SPINEL_PROP_VENDOR_ESP_SET_CONSOLE_CMD, SPINEL_DATATYPE_DATA_WLEN_S, input, strlen(input)); +} + void otPlatRadioGetIeeeEui64(otInstance *instance, uint8_t *ieee_eui64) { SuccessOrDie(s_radio.GetIeeeEui64(ieee_eui64)); diff --git a/components/openthread/src/spinel/esp_radio_spinel.cpp b/components/openthread/src/spinel/esp_radio_spinel.cpp index bbcc8d2bea18..8d561d27043b 100644 --- a/components/openthread/src/spinel/esp_radio_spinel.cpp +++ b/components/openthread/src/spinel/esp_radio_spinel.cpp @@ -12,6 +12,7 @@ #include "esp_radio_spinel.h" #include "esp_radio_spinel_platform.h" #include "esp_radio_spinel_adapter.hpp" +#include "esp_spinel_ncp_vendor_macro.h" #include "esp_radio_spinel_uart_interface.hpp" #include "spinel_driver.hpp" #include "openthread/link.h" diff --git a/components/protocomm/src/security/security1.c b/components/protocomm/src/security/security1.c index 08911df4fd83..da707ea17531 100644 --- a/components/protocomm/src/security/security1.c +++ b/components/protocomm/src/security/security1.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2018-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2018-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -101,6 +101,21 @@ static esp_err_t handle_session_command1(session_t *cur_session, return ESP_ERR_INVALID_STATE; } + /* Validate client verifier data before processing */ + if (!in || !in->sc1 || + in->sc1->client_verify_data.data == NULL || + in->sc1->client_verify_data.len != PUBLIC_KEY_LEN) { + ESP_LOGE(TAG, "Invalid client verifier (ptr=%p len=%d)", + (void *) (in && in->sc1 ? in->sc1->client_verify_data.data : NULL), + (int) (in && in->sc1 ? in->sc1->client_verify_data.len : -1)); + if (esp_event_post(PROTOCOMM_SECURITY_SESSION_EVENT, + PROTOCOMM_SECURITY_SESSION_INVALID_SECURITY_PARAMS, + NULL, 0, portMAX_DELAY) != ESP_OK) { + ESP_LOGE(TAG, "Failed to post invalid security params event"); + } + return ESP_ERR_INVALID_ARG; + } + /* Initialize crypto context */ mbedtls_aes_init(&cur_session->ctx_aes); memset(cur_session->stb, 0, sizeof(cur_session->stb)); diff --git a/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in index a04e289fbfca..d14aa56e4a2c 100644 --- a/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in @@ -1883,6 +1883,10 @@ config SOC_BLE_CTE_SUPPORTED bool default y +config SOC_BLE_PERIODIC_ADV_WITH_RESPONSE + bool + default y + config SOC_PHY_CALIBRATION_CLOCK_IS_INDEPENDENT bool default y diff --git a/components/soc/esp32c5/include/soc/soc_caps.h b/components/soc/esp32c5/include/soc/soc_caps.h index 33ab9edf3033..1c8d8373967d 100644 --- a/components/soc/esp32c5/include/soc/soc_caps.h +++ b/components/soc/esp32c5/include/soc/soc_caps.h @@ -718,6 +718,7 @@ #define SOC_BLE_MULTI_CONN_OPTIMIZATION (1) /*!< Support multiple connections optimization */ #define SOC_BLE_PERIODIC_ADV_ENH_SUPPORTED (1) /*!< Support For BLE Periodic Adv Enhancements */ #define SOC_BLE_CTE_SUPPORTED (1) /*!< Support Bluetooth LE Constant Tone Extension (CTE) */ +#define SOC_BLE_PERIODIC_ADV_WITH_RESPONSE (1) /*!< Support Bluetooth LE Periodic Advertising with Response (PAwR) */ /*------------------------------------- PHY CAPS -------------------------------------*/ // #define SOC_PHY_COMBO_MODULE (1) /*!< Support Wi-Fi, BLE and 15.4*/ diff --git a/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in index f365dc2ebdb4..6e0a205e53a9 100644 --- a/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in @@ -1643,6 +1643,10 @@ config SOC_BLE_MULTI_CONN_OPTIMIZATION bool default y +config SOC_BLE_PERIODIC_ADV_WITH_RESPONSE + bool + default y + config SOC_BLE_USE_WIFI_PWR_CLK_WORKAROUND bool default y diff --git a/components/soc/esp32c6/include/soc/soc_caps.h b/components/soc/esp32c6/include/soc/soc_caps.h index e0f696a26d7e..1a3b8cb9cd0c 100644 --- a/components/soc/esp32c6/include/soc/soc_caps.h +++ b/components/soc/esp32c6/include/soc/soc_caps.h @@ -634,6 +634,7 @@ #define SOC_BLE_PERIODIC_ADV_ENH_SUPPORTED (1) /*!< Support For BLE Periodic Adv Enhancements */ #define SOC_BLUFI_SUPPORTED (1) /*!< Support BLUFI */ #define SOC_BLE_MULTI_CONN_OPTIMIZATION (1) /*!< Support multiple connections optimization */ +#define SOC_BLE_PERIODIC_ADV_WITH_RESPONSE (1) /*!< Support Bluetooth LE Periodic Advertising with Response (PAwR) */ #define SOC_BLE_USE_WIFI_PWR_CLK_WORKAROUND (1) diff --git a/components/soc/esp32c61/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c61/include/soc/Kconfig.soc_caps.in index 11bdae667842..a1a51f3dd2a5 100644 --- a/components/soc/esp32c61/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c61/include/soc/Kconfig.soc_caps.in @@ -1355,6 +1355,10 @@ config SOC_BLE_CTE_SUPPORTED bool default y +config SOC_BLE_PERIODIC_ADV_WITH_RESPONSE + bool + default y + config SOC_PHY_COMBO_MODULE bool default y diff --git a/components/soc/esp32c61/include/soc/soc_caps.h b/components/soc/esp32c61/include/soc/soc_caps.h index 24c7fba93508..3ab56bcfc154 100644 --- a/components/soc/esp32c61/include/soc/soc_caps.h +++ b/components/soc/esp32c61/include/soc/soc_caps.h @@ -535,6 +535,7 @@ #define SOC_BLUFI_SUPPORTED (1) /*!< Support BLUFI */ #define SOC_BLE_MULTI_CONN_OPTIMIZATION (1) /*!< Support multiple connections optimization */ #define SOC_BLE_CTE_SUPPORTED (1) /*!< Support Bluetooth LE Constant Tone Extension (CTE) */ +#define SOC_BLE_PERIODIC_ADV_WITH_RESPONSE (1) /*!< Support Bluetooth LE Periodic Advertising with Response (PAwR) */ /*------------------------------------- PHY CAPS -------------------------------------*/ #define SOC_PHY_COMBO_MODULE (1) /*!< Support Wi-Fi, BLE and 15.4*/ diff --git a/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in b/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in index 5ebc633d9830..b3f5913496d0 100644 --- a/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in @@ -1599,6 +1599,10 @@ config SOC_BLE_CTE_SUPPORTED bool default y +config SOC_BLE_PERIODIC_ADV_WITH_RESPONSE + bool + default y + config SOC_DEBUG_HAVE_OCD_STUB_BINS bool default y diff --git a/components/soc/esp32h2/include/soc/soc_caps.h b/components/soc/esp32h2/include/soc/soc_caps.h index c3fb45c68972..453c29dbc83e 100644 --- a/components/soc/esp32h2/include/soc/soc_caps.h +++ b/components/soc/esp32h2/include/soc/soc_caps.h @@ -633,6 +633,7 @@ #define SOC_BLE_MULTI_CONN_OPTIMIZATION (1) /*!< Support multiple connections optimization */ #define SOC_BLE_PERIODIC_ADV_ENH_SUPPORTED (1) /*!< Support For BLE Periodic Adv Enhancements */ #define SOC_BLE_CTE_SUPPORTED (1) /*!< Support Bluetooth LE Constant Tone Extension (CTE) */ +#define SOC_BLE_PERIODIC_ADV_WITH_RESPONSE (1) /*!< Support Bluetooth LE Periodic Advertising with Response (PAwR) */ /*------------------------------------- DEBUG CAPS -------------------------------------*/ #define SOC_DEBUG_HAVE_OCD_STUB_BINS (1) diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index c096e00cb6b8..a4f2fe8530ff 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -947,6 +947,10 @@ config SOC_ISP_COLOR_SUPPORTED bool default y +config SOC_ISP_CROP_SUPPORTED + bool + default y + config SOC_ISP_DEMOSAIC_SUPPORTED bool default y diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index 43311df230cb..e2b4c6c008d0 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -357,6 +357,7 @@ #define SOC_ISP_BLC_SUPPORTED 1 #define SOC_ISP_CCM_SUPPORTED 1 #define SOC_ISP_COLOR_SUPPORTED 1 +#define SOC_ISP_CROP_SUPPORTED 1 #define SOC_ISP_DEMOSAIC_SUPPORTED 1 #define SOC_ISP_DVP_SUPPORTED 1 #define SOC_ISP_LSC_SUPPORTED 1 diff --git a/components/spi_flash/flash_mmap.c b/components/spi_flash/flash_mmap.c index d9476cbe308d..8b5ed948e6f4 100644 --- a/components/spi_flash/flash_mmap.c +++ b/components/spi_flash/flash_mmap.c @@ -22,6 +22,7 @@ #endif #include "esp_private/esp_mmu_map_private.h" +#include "esp_private/esp_cache_private.h" #include "esp_mmu_map.h" #include "esp_rom_spiflash.h" #if CONFIG_SPIRAM @@ -315,7 +316,9 @@ IRAM_ATTR bool spi_flash_check_and_flush_cache(size_t start_addr, size_t length) return true; #else // CONFIG_IDF_TARGET_ESP32 if (vaddr != NULL) { + esp_cache_sync_ops_enter_critical_section(); cache_hal_invalidate_addr((uint32_t)vaddr, SPI_FLASH_MMU_PAGE_SIZE); + esp_cache_sync_ops_exit_critical_section(); ret = true; } #endif // CONFIG_IDF_TARGET_ESP32 diff --git a/docs/doxygen/Doxyfile_esp32p4 b/docs/doxygen/Doxyfile_esp32p4 index bf64bf8cb7c3..37601065176c 100644 --- a/docs/doxygen/Doxyfile_esp32p4 +++ b/docs/doxygen/Doxyfile_esp32p4 @@ -39,6 +39,7 @@ INPUT += \ $(PROJECT_PATH)/components/esp_driver_isp/include/driver/isp_bf.h \ $(PROJECT_PATH)/components/esp_driver_isp/include/driver/isp_blc.h \ $(PROJECT_PATH)/components/esp_driver_isp/include/driver/isp_lsc.h \ + $(PROJECT_PATH)/components/esp_driver_isp/include/driver/isp_crop.h \ $(PROJECT_PATH)/components/esp_driver_isp/include/driver/isp_demosaic.h \ $(PROJECT_PATH)/components/esp_driver_isp/include/driver/isp_sharpen.h \ $(PROJECT_PATH)/components/esp_driver_isp/include/driver/isp_core.h \ diff --git a/docs/en/api-reference/peripherals/isp.rst b/docs/en/api-reference/peripherals/isp.rst index 52a829c66c30..b20fd02bdd2c 100644 --- a/docs/en/api-reference/peripherals/isp.rst +++ b/docs/en/api-reference/peripherals/isp.rst @@ -47,7 +47,7 @@ ISP Pipeline isp_chs [label = "Contrast &\n Hue & Saturation", width = 150, height = 70]; isp_yuv [label = "YUV Limit\n YUB2RGB", width = 120, height = 70]; - isp_header -> BLC -> BF -> LSC -> Demosaic -> CCM -> Gamma -> RGB2YUV -> SHARP -> isp_chs -> isp_yuv -> isp_tail; + isp_header -> BLC -> BF -> LSC -> Demosaic -> CCM -> Gamma -> RGB2YUV -> SHARP -> isp_chs -> isp_yuv -> CROP -> isp_tail; LSC -> HIST Demosaic -> AWB @@ -77,6 +77,7 @@ The ISP driver offers following services: - :ref:`isp-demosaic` - covers how to configure the Demosaic function. - :ref:`isp-gamma-correction` - covers how to enable and configure gamma correction. - :ref:`isp-sharpen` - covers how to configure the sharpening function. +- :ref:`isp-crop` - covers how to enable and configure image cropping function. - :ref:`isp-callback` - covers how to hook user specific code to ISP driver event callback function. - :ref:`isp-thread-safety` - lists which APIs are guaranteed to be thread safe by the driver. - :ref:`isp-kconfig-options` - lists the supported Kconfig options that can bring different effects to the driver. @@ -800,6 +801,51 @@ Calling :cpp:func:`esp_isp_sharpen_disable` does the opposite, that is, put the :cpp:func:`esp_isp_sharpen_configure` is allowed to be called even if the driver is in **init** state, but the sharpen configurations will only be taken into effect when in **enable** state. +.. _isp-crop: + +ISP Image Crop Controller +~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ISP image crop function can extract a specified region from the original image, reducing the amount of data for subsequent processing and improving processing efficiency. The crop function is executed at the end of the ISP pipeline and can output a smaller region than the input image. + +.. note:: + + The ISP image crop function is only available on ESP32-P4 revision 3.0 and above. + +Calling :cpp:func:`esp_isp_crop_configure` to configure the image crop function, you can take the following code as reference: + +.. code-block:: c + + esp_isp_crop_config_t crop_config = { + .window = { + .top_left = { + .x = 100, // Top-left X coordinate of crop region + .y = 100, // Top-left Y coordinate of crop region + }, + .btm_right = { + .x = 699, // Bottom-right X coordinate of crop region + .y = 499, // Bottom-right Y coordinate of crop region + } + } + }; + ESP_ERROR_CHECK(esp_isp_crop_configure(isp_proc, &crop_config)); + ESP_ERROR_CHECK(esp_isp_crop_enable(isp_proc)); + +After calling :cpp:func:`esp_isp_crop_configure`, you need to enable the ISP image crop controller by calling :cpp:func:`esp_isp_crop_enable`. This function: + +* Switches the driver state from **init** to **enable**. + +Calling :cpp:func:`esp_isp_crop_disable` does the opposite, that is, put the driver back to the **init** state. + +:cpp:func:`esp_isp_crop_configure` is allowed to be called even if the driver is in **init** state, but the crop configurations will only be taken into effect when in **enable** state. + +.. note:: + + - The top-left coordinates (top_left) of the crop region must be smaller than the bottom-right coordinates (btm_right) + - The top-left coordinates (top_left) of the crop region must be even, and the bottom-right coordinates (btm_right) must be odd + - The crop region cannot exceed the boundaries of the original image + - Adjust the display medium (such as LCD) size according to the cropped resolution to ensure complete display and avoid black borders or stretching. + .. _isp-callback: @@ -927,6 +973,7 @@ API Reference .. include-build-file:: inc/isp_gamma.inc .. include-build-file:: inc/isp_hist.inc .. include-build-file:: inc/isp_color.inc +.. include-build-file:: inc/isp_crop.inc .. include-build-file:: inc/isp_core.inc .. include-build-file:: inc/components/esp_driver_isp/include/driver/isp_types.inc .. include-build-file:: inc/components/hal/include/hal/isp_types.inc diff --git a/docs/en/api-reference/peripherals/ppa.rst b/docs/en/api-reference/peripherals/ppa.rst index 74998a03b2ee..3a6f64fe55e4 100644 --- a/docs/en/api-reference/peripherals/ppa.rst +++ b/docs/en/api-reference/peripherals/ppa.rst @@ -93,6 +93,10 @@ Some notes to avoid confusion in configuring :cpp:type:`ppa_srm_oper_config_t`: - Output block's width/height is totally determined by the input block's width/height, scaling factor, and rotation angle, so output block's width/height does not need to be configured. However, please make sure the output block can fit at the offset location in the output picture. - If the color mode of the input or output picture is ``PPA_SRM_COLOR_MODE_YUV420``, then its ``pic_w``, ``pic_h``, ``block_w``, ``block_h``, ``block_offset_x``, ``block_offset_y`` fields must be even. +.. note:: + + The PPA SRM internally uses bilinear scaling algorithm to process. Therefore, it may cause chromatic aberration and loss of contrast at the edges in a scaled picture. + Blend ~~~~~ diff --git a/docs/en/api-reference/peripherals/twai.rst b/docs/en/api-reference/peripherals/twai.rst index 59e03f4f5f3d..9badaa7aba72 100644 --- a/docs/en/api-reference/peripherals/twai.rst +++ b/docs/en/api-reference/peripherals/twai.rst @@ -103,7 +103,12 @@ TWAI messages come in various types, which are specified by their headers. A typ .. image:: ../../../_static/diagrams/twai/frame_struct.svg :align: center -To reduce performance overhead caused by memory copying, the TWAI driver uses pointers to pass messages. The following code demonstrates how to transmit a typical data frame: +To reduce performance overhead caused by memory copying, the TWAI driver uses pointers to pass messages. The driver is designed to operate in asynchronous mode, so the :cpp:type:`twai_frame_t` structure and the memory pointed to by :cpp:member:`twai_frame_t::buffer` must remain valid until the transmission is actually complete. You can determine when transmission is complete in the following ways: + +- Call the :cpp:func:`twai_node_transmit_wait_all_done` function to wait for all transmissions to complete. +- Register the :cpp:member:`twai_event_callbacks_t::on_tx_done` event callback function to receive a notification when transmission is complete. + +The following code demonstrates how to transmit a typical data frame: .. code:: c @@ -200,7 +205,7 @@ The TWAI driver supports transmitting messages from an Interrupt Service Routine } .. note:: - When calling :cpp:func:`twai_node_transmit` from an ISR, the ``timeout`` parameter is ignored, and the function will not block. If the transmit queue is full, the function will return immediately with an error. It is the application's responsibility to handle cases where the queue is full. + When calling :cpp:func:`twai_node_transmit` from an ISR, the ``timeout`` parameter is ignored, and the function will not block. If the transmit queue is full, the function will return immediately with an error. It is the application's responsibility to handle cases where the queue is full. Similarly, the ``twai_frame_t`` structure and the memory pointed to by ``buffer`` must remain valid until the transmission is complete. You can get the completed frame by the :cpp:member:`twai_tx_done_event_data_t::done_tx_frame` pointer. Bit Timing Customization ------------------------ @@ -311,7 +316,7 @@ The TWAI controller can detect errors caused by bus interference or corrupted fr - **Error Passive**: When either TEC or REC is greater than or equal to 128, the node enters the passive error state. It can still communicate on the bus but sends only one **passive error flag** when detecting errors. - **Bus Off**: When **TEC** is greater than or equal to 256, the node enters the bus off (offline) state. The node is effectively disconnected and does not affect the bus. It remains offline until recovery is triggered by software. -Software can retrieve the node status anytime via the function :cpp:func:`twai_node_get_info`. When the controller detects errors, it triggers the :cpp:member:`twai_event_callbacks_t::on_error` callback, where the error data provides detailed information. +Software can retrieve the node status from tasks via the function :cpp:func:`twai_node_get_info`. When the controller detects errors, it triggers the :cpp:member:`twai_event_callbacks_t::on_error` callback, where the error data provides detailed information. When the node’s error state changes, the :cpp:member:`twai_event_callbacks_t::on_state_change` callback is triggered, allowing the application to respond to the state transition. If the node is offline and needs recovery, call :cpp:func:`twai_node_recover` from a task context. **Note that recovery is not immediate; the controller will automatically reconnect to the bus only after detecting 129 consecutive recessive bits (11 bits each).** diff --git a/docs/en/api-reference/system/power_management.rst b/docs/en/api-reference/system/power_management.rst index e953ee028025..bddee9ad42a8 100644 --- a/docs/en/api-reference/system/power_management.rst +++ b/docs/en/api-reference/system/power_management.rst @@ -102,10 +102,13 @@ Peripheral clock sources such as ``REF_TICK``, ``XTAL``, ``RC_FAST`` (i.e., ``RT Currently, the following peripheral drivers are aware of DFS and use the ``ESP_PM_APB_FREQ_MAX`` lock for the duration of the transaction: -- SPI master -- I2C -- I2S (If the APLL clock is used, then it will use the ``ESP_PM_NO_LIGHT_SLEEP`` lock) -- SDMMC +.. list:: + + - SPI master + - I2C + :SOC_I2S_HW_VERSION_1 or not SOC_I2S_SUPPORTS_APLL: - I2S + :not SOC_I2S_HW_VERSION_1 and SOC_I2S_SUPPORTS_APLL: - I2S (if the APLL clock is used, then it will use the ``ESP_PM_NO_LIGHT_SLEEP`` lock) + - SDMMC The following drivers hold the ``ESP_PM_APB_FREQ_MAX`` lock while the driver is enabled: diff --git a/docs/en/security/flash-encryption.rst b/docs/en/security/flash-encryption.rst index efe90e745c73..e7cd221d06f2 100644 --- a/docs/en/security/flash-encryption.rst +++ b/docs/en/security/flash-encryption.rst @@ -510,6 +510,9 @@ If all partitions needs to be updated in encrypted format, run: idf.py encrypted-flash monitor +.. note:: + + The above operations are only applicable when the ``DIS_DOWNLOAD_MANUAL_ENCRYPT`` eFuse bit has not been programmed. If this eFuse bit has been programmed, you must flash the pre-encrypted ciphertext image instead. .. _flash-enc-release-mode: diff --git a/docs/en/security/security-features-enablement-workflows.rst b/docs/en/security/security-features-enablement-workflows.rst index 51d587cfb165..92c605a47a35 100644 --- a/docs/en/security/security-features-enablement-workflows.rst +++ b/docs/en/security/security-features-enablement-workflows.rst @@ -318,6 +318,10 @@ In this case all the eFuses related to Flash Encryption are written with help of espsecure.py encrypt_flash_data {IDF_TARGET_FLASH_ENC_ARGS} --keyfile my_flash_encryption_key.bin --address 0x10000 --output my-app-enc.bin build/my-app.bin + .. note:: + + If secure boot is enabled, perform secure boot signing of the firmware before carrying out the above encryption operation. + In the above command, the offsets are used for a sample firmware, and the actual offset for your firmware can be obtained by checking the partition table entry or by running `idf.py partition-table`. Please note that not all the binaries need to be encrypted, the encryption applies only to those generated from the partitions which are marked as ``encrypted`` in the partition table definition file. Other binaries are flashed unencrypted, i.e., as a plain output of the build process. The above files can then be flashed to their respective offset using ``esptool.py``. To see all of the command line options recommended for ``esptool.py``, see the output printed when ``idf.py build`` succeeds. @@ -685,7 +689,7 @@ The details about NVS encryption and related schemes can be found at :doc:`NVS E * CSV file name - In this case, ``sample_singlepage_blob.csv`` is the CSV file which contains the NVS data. Please replace this with the file you wish to choose. - * NVS partition offset - This is the offset at which that NVS partition shall be stored in the flash of {IDF_TARGET_NAME}. The offset of your NVS partition can be found by executing ``idf.py partition-table`` in the projtect directory. Please update the sample value of ``0x3000`` in the above-provided command to the correct offset. + * NVS partition size - This is the size of the NVS partition in bytes. Please update the sample value of ``0x3000`` in the above-provided command to the correct size of your NVS partition. 4. Configure the project @@ -734,7 +738,7 @@ In this case we generate NVS Encryption keys on a host. This key is then flashed * CSV file name - In this case `sample_singlepage_blob.csv` is the CSV file which contains the NVS data. Please replace it with the file you wish to choose. - * NVS partition offset - This is the offset at which the NVS partition shall be stored in the flash of {IDF_TARGET_NAME}. The offset of your NVS partition can be found by executing ``idf.py partition-table`` in the projtect directory. Please update the sample value of ``0x3000`` in the above-provided command to the correct offset. + * NVS partition size - This is the size of the NVS partition in bytes. Please update the sample value of ``0x3000`` in the above-provided command to the correct size of your NVS partition. 3. Configure the project @@ -745,4 +749,4 @@ In this case we generate NVS Encryption keys on a host. This key is then flashed The NVS partition (``nvs_encr_partition.bin``) and NVS encryption key (``nvs_encr_key.bin``) can then be flashed to their respective offset using ``esptool.py``. To see all of the command line options recommended for ``esptool.py``, check the output print when ``idf.py build`` succeeds. - If Flash Encryption is enabled for the chip, then please encrypt the partition first before flashing. You may refer the flashing related steps of `Flash Encryption workflow `_. + If Flash Encryption is enabled for the chip, then please encrypt the NVS key partition first before flashing. You may refer the flashing related steps of `Flash Encryption workflow `_. diff --git a/docs/en/security/security.rst b/docs/en/security/security.rst index cb3c1a8667f3..0de2c2cace43 100644 --- a/docs/en/security/security.rst +++ b/docs/en/security/security.rst @@ -75,7 +75,7 @@ Please refer to :doc:`flash-encryption` for detailed information about this feat Flash Encryption Best Practices ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -* It is recommended to use flash Encryption release mode for the production use-cases. +* It is recommended to use :ref:`flash-enc-release-mode` for the production use-cases. * It is recommended to have a unique flash encryption key per device. * Enable :ref:`secure_boot-guide` as an extra layer of protection, and to prevent an attacker from selectively corrupting any part of the flash before boot. diff --git a/docs/zh_CN/api-reference/peripherals/isp.rst b/docs/zh_CN/api-reference/peripherals/isp.rst index 4b7e441f8a21..009eed08b0d8 100644 --- a/docs/zh_CN/api-reference/peripherals/isp.rst +++ b/docs/zh_CN/api-reference/peripherals/isp.rst @@ -47,7 +47,7 @@ ISP 流水线 isp_chs [label = "对比度 &\n 色调 & 饱和度", width = 150, height = 70]; isp_yuv [label = "YUV 限制\n YUB2RGB", width = 120, height = 70]; - isp_header -> BLC -> BF -> LSC -> 去马赛克 -> CCM -> gamma 校正 -> RGB 转 YUV -> 锐化 -> isp_chs -> isp_yuv -> isp_tail; + isp_header -> BLC -> BF -> LSC -> 去马赛克 -> CCM -> gamma 校正 -> RGB 转 YUV -> 锐化 -> isp_chs -> isp_yuv -> 裁剪 -> isp_tail; LSC -> HIST 去马赛克 -> AWB @@ -77,6 +77,7 @@ ISP 驱动程序提供以下服务: - :ref:`isp-demosaic` - 涵盖如何配置去马赛克功能。 - :ref:`isp-gamma-correction` - 涵盖如何启用和配置 gamma 校正。 - :ref:`isp-sharpen` - 涵盖如何配置锐化功能。 +- :ref:`isp-crop` - 涵盖如何启用和配置图像裁剪功能。 - :ref:`isp-callback` - 涵盖如何将用户特定代码挂接到 ISP 驱动事件回调。 - :ref:`isp-thread-safety` - 列出了驱动程序中线程安全的 API。 - :ref:`isp-kconfig-options` - 列出了支持的 Kconfig 选项,这些选项可以对驱动程序产生不同影响。 @@ -262,7 +263,7 @@ ISP AF 控制器 使用连续统计时,AF 环境检测器将失效。 -.. code-block:: c +.. code:: c esp_isp_af_config_t af_config = { .edge_thresh = 128, @@ -399,7 +400,7 @@ ISP AE 控制器 .. code-block:: c - esp_isp_ae_config_t ae_config = { + esp_isp_ae_config_t ae_config = { .sample_point = ISP_AE_SAMPLE_POINT_AFTER_DEMOSAIC, }; isp_ae_ctlr_t ae_ctlr = NULL; @@ -518,7 +519,6 @@ ISP BF 控制器 调用 :cpp:func:`esp_isp_bf_disable` 函数会执行相反的操作,即将驱动程序恢复到 **init** 状态。 - .. _isp-blc: ISP BLC 控制器 @@ -801,6 +801,50 @@ ISP 锐化控制器 即使驱动程序处于 **init** 状态,也可以调用 :cpp:func:`esp_isp_sharpen_configure`,但锐化配置只有在 **enable** 状态下才会生效。 +.. _isp-crop: + +ISP 图像裁剪控制器 +~~~~~~~~~~~~~~~~~~ + +ISP 图像裁剪功能可以从原始图像中提取指定区域,减少后续处理的数据量,提高处理效率。裁剪功能在 ISP 流水线的末端执行,可以输出比输入图像更小的区域。 + +.. note:: + + ISP 图像裁剪功能仅在 ESP32-P4 revision 3.0 及以上版本中可用。 + +可调用 :cpp:func:`esp_isp_crop_configure` 函数配置图像裁剪功能,请参考以下代码: + +.. code-block:: c + + esp_isp_crop_config_t crop_config = { + .window = { + .top_left = { + .x = 100, // 裁剪区域左上角 X 坐标 + .y = 100, // 裁剪区域左上角 Y 坐标 + }, + .btm_right = { + .x = 699, // 裁剪区域右下角 X 坐标 + .y = 499, // 裁剪区域右下角 Y 坐标 + } + } + }; + ESP_ERROR_CHECK(esp_isp_crop_configure(isp_proc, &crop_config)); + ESP_ERROR_CHECK(esp_isp_crop_enable(isp_proc)); + +调用 :cpp:func:`esp_isp_crop_configure` 后,需要通过调用 :cpp:func:`esp_isp_crop_enable` 来启用 ISP 图像裁剪控制器。此函数: + +* 将驱动程序状态从 **init** 切换到 **enable**。 + +调用 :cpp:func:`esp_isp_crop_disable` 函数会执行相反的操作,即将驱动程序恢复到 **init** 状态。 + +即使驱动程序处于 **init** 状态,也可以调用 :cpp:func:`esp_isp_crop_configure`,但裁剪配置只有在 **enable** 状态下才会生效。 + +.. note:: + + - 裁剪区域的左上角坐标 (top_left) 必须小于右下角坐标 (btm_right) + - 裁剪区域的左上角坐标必须为偶数,右下角坐标必须为奇数 + - 裁剪区域不能超出原始图像的边界 + - 需根据裁剪后分辨率调整显示介质(如LCD)的尺寸,确保显示完整,避免出现黑边或拉伸现象。 .. _isp-callback: @@ -850,7 +894,7 @@ ISP AE 环境检测器启动后,将动态生成特定事件。若想在事件 注册 ISP HIST 统计完成事件回调函数 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -ISP HIST 控制器完成亮度统计后,将动态生成特定事件。若想在统计完成时收到通知,请通过调用 :cpp:func:`esp_isp_hist_register_event_callbacks` 将目标函数挂挂接到中断服务程序。所有支持的事件回调函数可参见 :cpp:type:`esp_isp_hist_cbs_t`: +ISP HIST 控制器完成亮度统计后,将动态生成特定事件。若想在统计完成时收到通知,请通过调用 :cpp:func:`esp_isp_hist_register_event_callbacks` 将目标函数挂接到中断服务程序。所有支持的事件回调函数可参见 :cpp:type:`esp_isp_hist_cbs_t`: - :cpp:member:`esp_isp_hist_cbs_t::on_statistics_done` 在完成亮度统计时设置回调函数。该函数原型在 :cpp:type:`esp_isp_hist_callback_t` 中声明。 @@ -928,6 +972,7 @@ API 参考 .. include-build-file:: inc/isp_gamma.inc .. include-build-file:: inc/isp_hist.inc .. include-build-file:: inc/isp_color.inc +.. include-build-file:: inc/isp_crop.inc .. include-build-file:: inc/isp_core.inc .. include-build-file:: inc/components/esp_driver_isp/include/driver/isp_types.inc .. include-build-file:: inc/components/hal/include/hal/isp_types.inc diff --git a/docs/zh_CN/api-reference/peripherals/ppa.rst b/docs/zh_CN/api-reference/peripherals/ppa.rst index 053908b71ae5..c9b4841413bc 100644 --- a/docs/zh_CN/api-reference/peripherals/ppa.rst +++ b/docs/zh_CN/api-reference/peripherals/ppa.rst @@ -93,6 +93,10 @@ PPA 操作包括: - 输出块的宽度/高度完全由输入块的宽度/高度、缩放因子和旋转角度决定,因此无需配置输出块的宽度/高度。但请确保输出块可以适应输出图片中的偏移位置。 - 如果输入或输出图片的色彩模式为 ``PPA_SRM_COLOR_MODE_YUV420``,那么其 ``pic_w``、``pic_h``、``block_w``、``block_h``、``block_offset_x`` 以及 ``block_offset_y`` 字段必须为偶数。 +.. note:: + + PPA SRM 内部使用双线性缩放算法进行处理。因此,缩放后的图片边缘可能会出现色差和对比度损失。 + 叠加 ~~~~ diff --git a/docs/zh_CN/api-reference/peripherals/twai.rst b/docs/zh_CN/api-reference/peripherals/twai.rst index e01c2300639e..f7585ab98c11 100644 --- a/docs/zh_CN/api-reference/peripherals/twai.rst +++ b/docs/zh_CN/api-reference/peripherals/twai.rst @@ -103,7 +103,12 @@ TWAI 报文有多种类型,由报头指定。一个典型的数据帧报文主 .. image:: ../../../_static/diagrams/twai/frame_struct.svg :align: center -为减少拷贝带来的性能损失,TWAI 驱动使用指针进行传递。以下代码展示了如何发送一条典型的数据帧报文: +为减少拷贝带来的性能损失,TWAI 驱动使用指针进行传递。且驱动设计为异步模式,因此,在传输真正完成之前, :cpp:type:`twai_frame_t` 实体及其 :cpp:member:`twai_frame_t::buffer` 指向的内存必须保持有效。可通过以下方式得知传输完成: + +- 调用 :cpp:func:`twai_node_transmit_wait_all_done` 函数等待所有传输完成。 +- 注册 :cpp:member:`twai_event_callbacks_t::on_tx_done` 事件回调函数,在传输完成时接收通知。 + +以下代码展示了如何发送一条典型的数据帧报文: .. code:: c @@ -200,7 +205,7 @@ TWAI 驱动支持在中断服务程序 (ISR) 中发送报文。这对于需要 } .. note:: - 在 ISR 中调用 :cpp:func:`twai_node_transmit` 时,``timeout`` 参数将被忽略,函数不会阻塞。如果发送队列已满,函数将立即返回错误。应用程序需要自行处理队列已满的情况。 + 在 ISR 中调用 :cpp:func:`twai_node_transmit` 时,``timeout`` 参数将被忽略,函数不会阻塞。如果发送队列已满,函数将立即返回错误。应用程序需要自行处理队列已满的情况。同样,``twai_frame_t`` 及其 ``buffer`` 指向的内存必须在 **该传输** 完成之前保持有效。通过 :cpp:member:`twai_tx_done_event_data_t::done_tx_frame` 指针可得知该次完成的报文。 位时序自定义 ------------- @@ -311,7 +316,7 @@ TWAI控制器能够检测由于总线干扰产生的/损坏的不符合帧格式 - **被动错误:** 当 TEC 或 REC 中的一个大于或等于 128 时,节点处于被动错误状态。仍可以参与总线通信,但在检测到错误时,只能发送一次 **被动错误标志**。 - **离线:** 当 **TEC** 大于或等于 256 时,节点进入离线状态。离线的节点相当于断开连接,不会对总线产生任何影响。节点将保持离线状态,直到软件触发恢复操作。 -软件可随时使用函数 :cpp:func:`twai_node_get_info` 获取节点状态。或当控制器检测到错误时,会产生 :cpp:member:`twai_event_callbacks_t::on_error` 回调,可通过传参中的错误数据查看错误原因。 +软件可随时在 task 中使用函数 :cpp:func:`twai_node_get_info` 获取节点状态。或当控制器检测到错误时,会产生 :cpp:member:`twai_event_callbacks_t::on_error` 回调,可通过传参中的错误数据查看错误原因。 当错误导致节点状态变化时,会进入 :cpp:member:`twai_event_callbacks_t::on_state_change` 回调,可在回调中查看节点的状态变化。若节点已经离线且需要恢复,需要在task中调用 :cpp:func:`twai_node_recover`。 **但注意,控制器不会立即恢复** ,需要在检测到 129 次连续 11 个隐性位后才会自动重新连接到总线。 diff --git a/docs/zh_CN/api-reference/system/power_management.rst b/docs/zh_CN/api-reference/system/power_management.rst index 11fdde8cbbba..dd568616a9a2 100644 --- a/docs/zh_CN/api-reference/system/power_management.rst +++ b/docs/zh_CN/api-reference/system/power_management.rst @@ -102,10 +102,13 @@ ESP-IDF 中集成的电源管理算法可以根据应用程序组件的需求, 目前以下外设驱动程序可感知动态调频,并在调频期间使用 ``ESP_PM_APB_FREQ_MAX`` 锁: -- SPI master -- I2C -- I2S(如果 APLL 锁在使用中,I2S 则会启用 ``ESP_PM_NO_LIGHT_SLEEP`` 锁) -- SDMMC +.. list:: + + - SPI master + - I2C + :SOC_I2S_HW_VERSION_1 or not SOC_I2S_SUPPORTS_APLL: - I2S + :not SOC_I2S_HW_VERSION_1 and SOC_I2S_SUPPORTS_APLL: - I2S(如果 APLL 锁在使用中,I2S 则会启用 ``ESP_PM_NO_LIGHT_SLEEP`` 锁) + - SDMMC 启用以下驱动程序时,将占用 ``ESP_PM_APB_FREQ_MAX`` 锁: diff --git a/docs/zh_CN/security/flash-encryption.rst b/docs/zh_CN/security/flash-encryption.rst index fb57142778b3..57f68eaba5bf 100644 --- a/docs/zh_CN/security/flash-encryption.rst +++ b/docs/zh_CN/security/flash-encryption.rst @@ -510,6 +510,9 @@ flash 加密设置 idf.py encrypted-flash monitor +.. note:: + + 上述操作仅适用于 ``DIS_DOWNLOAD_MANUAL_ENCRYPT`` eFuse 位未被烧录的情况。如果该 eFuse 位已被烧录,则需要烧录加密后的密文镜像。 .. _flash-enc-release-mode: diff --git a/docs/zh_CN/security/secure-boot-v2.rst b/docs/zh_CN/security/secure-boot-v2.rst index bf21e1897cea..bf5fb86412a6 100644 --- a/docs/zh_CN/security/secure-boot-v2.rst +++ b/docs/zh_CN/security/secure-boot-v2.rst @@ -137,7 +137,7 @@ 5. 引导加载程序验证应用程序镜像的签名块,请参阅 :ref:`verify_signature-block`。如果验证失败,启动过程将中止。 -6. 引导加载程序使用原始镜像数据、相应的签名块以及 eFuse 验证引导加载程序镜像,请参阅 :ref:`verify_image`。如果验证失败,启动过程将中止。如果验证失败,但发现了其他应用程序镜像,引导加载程序将使用步骤 5 到 7 验证另一个镜像。该过程将重复,直至找到有效镜像,或所有镜像验证完毕。 +6. 引导加载程序使用原始镜像数据、相应的签名块以及 eFuse 验证应用程序镜像,请参阅 :ref:`verify_image`。如果验证失败,启动过程将中止。如果验证失败,但发现了其他应用程序镜像,引导加载程序将使用步骤 5 到 7 验证另一个镜像。该过程将重复,直至找到有效镜像,或所有镜像验证完毕。 7. 引导加载程序执行经验证的应用程序镜像。 diff --git a/docs/zh_CN/security/security-features-enablement-workflows.rst b/docs/zh_CN/security/security-features-enablement-workflows.rst index d8e5e7dc0140..835017819177 100644 --- a/docs/zh_CN/security/security-features-enablement-workflows.rst +++ b/docs/zh_CN/security/security-features-enablement-workflows.rst @@ -318,6 +318,10 @@ espsecure.py encrypt_flash_data {IDF_TARGET_FLASH_ENC_ARGS} --keyfile my_flash_encryption_key.bin --address 0x10000 --output my-app-enc.bin build/my-app.bin + .. note:: + + 如果同时启用了安全启动功能,请先对固件进行安全启动签名,再执行上述加密操作。 + 上述命令中的偏移量仅适用于示例固件,请通过检查分区表条目或运行 `idf.py partition-table` 来获取你固件的实际偏移量。请注意,不需要加密所有二进制文件,只需加密在分区表定义文件中带有 ``encrypted`` 标记的文件,其他二进制文件只作为构建过程的普通输出进行烧录。 使用 ``esptool.py`` 可以将上述文件烧写到各自的偏移地址。要查看所有推荐的 ``esptool.py`` 命令行选项,请查阅 ``idf.py build`` 构建成功后打印的输出。 @@ -685,7 +689,7 @@ Secure Boot v2 指南 * CSV 文件名 - 此命令中,``sample_singlepage_blob.csv`` 是指包含 NVS 数据的 CSV 文件,请将其替换为所选择的文件。 - * NVS 分区偏移量 - 这是 {IDF_TARGET_NAME} flash 中存储 NVS 分区的偏移地址。通过在项目目录下执行 ``idf.py partition-table`` 命令,可以找到 NVS 分区偏移地址。请将上述命令中的示例值 ``0x3000`` 调整为正确的偏移量。 + * NVS 分区大小 - 这是 NVS 分区的大小(以字节为单位)。请将上述命令中的示例值 ``0x3000`` 更新为你实际 NVS 分区的正确大小。 4. 配置项目 @@ -734,7 +738,7 @@ Secure Boot v2 指南 * CSV 文件名 - 上述命名中的 `sample_singlepage_blob.csv` 是指包含 NVS 数据的 CSV 文件,请将其替换为所选文件。 - * NVS 分区偏移量 - 这是 NVS 分区在 {IDF_TARGET_NAME} 的 flash 中存储时的偏移地址。在项目目录中执行 ``idf.py partition-table`` 命令,可以找到 NVS 分区的偏移量。请将上述命令中的示例值 ``0x3000`` 替换为正确的偏移量。 + * NVS 分区大小 - 这是 NVS 分区的大小(以字节为单位)。请将上述命令中的示例值 ``0x3000`` 更新为你实际 NVS 分区的正确大小。 3. 配置项目 @@ -745,4 +749,4 @@ Secure Boot v2 指南 使用 ``esptool.py`` 命令,将 NVS 分区 (``nvs_encr_partition.bin``) 和 NVS 加密密钥 (``nvs_encr_key.bin``) 烧录到各自的偏移地址。通过 ``idf.py build`` 成功后打印的输出,可查看所有推荐的 ``esptool.py`` 命令行选项。 - 若芯片启用了 flash 加密,请在烧录之前先加密分区。详情请参阅 `flash 加密工作流程 `_ 中与烧录相关的步骤。 + 若芯片启用了 flash 加密,请在烧录前先对 NVS 加密密钥分区进行加密。详情请参阅 `flash 加密工作流程 `_ 中与烧录相关的步骤。 diff --git a/docs/zh_CN/security/security.rst b/docs/zh_CN/security/security.rst index 9f1f171dbedd..19b3d504d621 100644 --- a/docs/zh_CN/security/security.rst +++ b/docs/zh_CN/security/security.rst @@ -75,7 +75,7 @@ flash 加密功能可以加密外部 flash 中的内容,从而保护存储在 flash 加密最佳实践 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -* 建议在生产环境中使用 flash 加密的量产模式。 +* 建议在生产环境中使用 flash 加密的 :ref:`flash-enc-release-mode`。 * 建议为每个设备生成唯一的 flash 加密密钥。 * 启用 :ref:`secure_boot-guide` 作为额外保护层,防止 flash 在启动前遭受恶意攻击。 diff --git a/examples/bluetooth/bluedroid/classic_bt/pytest_classic_bt_test.py b/examples/bluetooth/bluedroid/classic_bt/pytest_classic_bt_test.py index 4154dce4148a..74a9e40c5a87 100644 --- a/examples/bluetooth/bluedroid/classic_bt/pytest_classic_bt_test.py +++ b/examples/bluetooth/bluedroid/classic_bt/pytest_classic_bt_test.py @@ -6,6 +6,7 @@ import pexpect import pytest from pytest_embedded_idf.dut import IdfDut + # Case 1: SPP @@ -15,7 +16,8 @@ [ ( 2, - f'{os.path.join(os.path.dirname(__file__), "bt_spp_acceptor")}|{os.path.join(os.path.dirname(__file__), "bt_spp_initiator")}', + f'{os.path.join(os.path.dirname(__file__), "bt_spp_acceptor")}|' + f'{os.path.join(os.path.dirname(__file__), "bt_spp_initiator")}', 'esp32|esp32', 'y', 'test', @@ -49,7 +51,8 @@ def test_bt_spp_only(app_path: str, dut: Tuple[IdfDut, IdfDut]) -> None: [ ( 2, - f'{os.path.join(os.path.dirname(__file__), "bt_spp_vfs_acceptor")}|{os.path.join(os.path.dirname(__file__), "bt_spp_vfs_initiator")}', + f'{os.path.join(os.path.dirname(__file__), "bt_spp_vfs_acceptor")}|' + f'{os.path.join(os.path.dirname(__file__), "bt_spp_vfs_initiator")}', 'esp32|esp32', 'test', ), @@ -77,7 +80,8 @@ def test_bt_spp_vfs(app_path: str, dut: Tuple[IdfDut, IdfDut]) -> None: [ ( 2, - f'{os.path.join(os.path.dirname(__file__), "a2dp_sink")}|{os.path.join(os.path.dirname(__file__), "a2dp_source")}', + f'{os.path.join(os.path.dirname(__file__), "a2dp_sink")}|' + f'{os.path.join(os.path.dirname(__file__), "a2dp_source")}', 'esp32|esp32', 'test', ), @@ -157,7 +161,8 @@ def test_bt_hid(app_path: str, dut: Tuple[IdfDut, IdfDut]) -> None: [ ( 2, - f'{os.path.join(os.path.dirname(__file__), "bt_l2cap_server")}|{os.path.join(os.path.dirname(__file__), "bt_l2cap_client")}', + f'{os.path.join(os.path.dirname(__file__), "bt_l2cap_server")}|' + f'{os.path.join(os.path.dirname(__file__), "bt_l2cap_client")}', 'esp32|esp32', 'test', ), @@ -169,8 +174,11 @@ def test_bt_l2cap(app_path: str, dut: Tuple[IdfDut, IdfDut]) -> None: client = dut[1] server.expect_exact('ESP_BT_L2CAP_INIT_EVT: status:0', timeout=30) - server.expect_exact('ESP_BT_L2CAP_START_EVT: status:0', timeout=30) - server.expect_exact('ESP_SDP_CREATE_RECORD_COMP_EVT: status:0', timeout=30) + server.expect( + r'(?s)(ESP_BT_L2CAP_START_EVT: status:0.*ESP_SDP_CREATE_RECORD_COMP_EVT: status:0|' + r'ESP_SDP_CREATE_RECORD_COMP_EVT: status:0.*ESP_BT_L2CAP_START_EVT: status:0)', + timeout=30, + ) client.expect_exact('ESP_BT_L2CAP_INIT_EVT: status:0', timeout=30) client.expect_exact('ESP_SDP_SEARCH_COMP_EVT: status:0', timeout=30) client.expect_exact('ESP_BT_L2CAP_OPEN_EVT: status:0', timeout=30) diff --git a/examples/openthread/ot_rcp/main/CMakeLists.txt b/examples/openthread/ot_rcp/main/CMakeLists.txt index e881658a3165..83b084e90987 100644 --- a/examples/openthread/ot_rcp/main/CMakeLists.txt +++ b/examples/openthread/ot_rcp/main/CMakeLists.txt @@ -1,3 +1,2 @@ idf_component_register(SRCS "esp_ot_rcp.c" - PRIV_REQUIRES esp_coex esp_event nvs_flash openthread INCLUDE_DIRS ".") diff --git a/examples/openthread/ot_rcp/main/esp_ot_rcp.c b/examples/openthread/ot_rcp/main/esp_ot_rcp.c index fddd77668469..b3c4e6c26f55 100644 --- a/examples/openthread/ot_rcp/main/esp_ot_rcp.c +++ b/examples/openthread/ot_rcp/main/esp_ot_rcp.c @@ -29,6 +29,10 @@ #error "RCP is only supported for the SoCs which have IEEE 802.15.4 module" #endif +#if CONFIG_OPENTHREAD_RCP_SPINEL_CONSOLE +#include "esp_console.h" +#endif + #define TAG "ot_esp_rcp" extern void otAppNcpInit(otInstance *instance); @@ -59,5 +63,11 @@ void app_main(void) }, }; +#if CONFIG_OPENTHREAD_RCP_SPINEL_CONSOLE + esp_console_config_t console_config = ESP_CONSOLE_CONFIG_DEFAULT(); + esp_console_init(&console_config); + esp_console_register_help_command(); +#endif + ESP_ERROR_CHECK(esp_openthread_start(&config)); } diff --git a/examples/openthread/ot_rcp/sdkconfig.defaults b/examples/openthread/ot_rcp/sdkconfig.defaults index 0419e6a33c7a..d35408beb288 100644 --- a/examples/openthread/ot_rcp/sdkconfig.defaults +++ b/examples/openthread/ot_rcp/sdkconfig.defaults @@ -52,3 +52,9 @@ CONFIG_OPENTHREAD_LOG_LEVEL_DYNAMIC=n CONFIG_OPENTHREAD_LOG_LEVEL_NONE=y CONFIG_OPENTHREAD_TIMING_OPTIMIZATION=y CONFIG_FREERTOS_HZ=1000 + +# +# Turn on RCP console by default, overriding default log level from above +# +CONFIG_OPENTHREAD_RCP_SPINEL_CONSOLE=y +CONFIG_LOG_DEFAULT_LEVEL_INFO=y diff --git a/examples/peripherals/.build-test-rules.yml b/examples/peripherals/.build-test-rules.yml index 20dfad0ee99b..f44e6ad3fc4a 100644 --- a/examples/peripherals/.build-test-rules.yml +++ b/examples/peripherals/.build-test-rules.yml @@ -442,20 +442,12 @@ examples/peripherals/spi_slave_hd/segment_mode/seg_slave: examples/peripherals/temperature_sensor/temp_sensor: disable: - if: SOC_TEMP_SENSOR_SUPPORTED != 1 - disable_test: - - if: IDF_TARGET == "esp32p4" - temporary: true - reason: p4 rev3 migration # TODO: IDF-14834 depends_components: - esp_driver_tsens examples/peripherals/temperature_sensor/temp_sensor_monitor: disable: - if: SOC_TEMPERATURE_SENSOR_INTR_SUPPORT != 1 - disable_test: - - if: IDF_TARGET == "esp32p4" - temporary: true - reason: p4 rev3 migration # TODO: IDF-14834 depends_components: - esp_driver_tsens diff --git a/examples/peripherals/camera/common_components/dsi_init/example_dsi_init.c b/examples/peripherals/camera/common_components/dsi_init/example_dsi_init.c index 3b2413b9888f..f9bc0c8cdecf 100644 --- a/examples/peripherals/camera/common_components/dsi_init/example_dsi_init.c +++ b/examples/peripherals/camera/common_components/dsi_init/example_dsi_init.c @@ -15,7 +15,14 @@ #include "example_dsi_init_config.h" #include "sdkconfig.h" -void example_dsi_resource_alloc(esp_lcd_dsi_bus_handle_t *mipi_dsi_bus, esp_lcd_panel_io_handle_t *mipi_dbi_io, esp_lcd_panel_handle_t *mipi_dpi_panel, void **frame_buffer) +static const char *TAG = "example_dsi_init"; + +void example_dsi_resource_alloc(const example_dsi_alloc_config_t *config, + esp_lcd_dsi_bus_handle_t *mipi_dsi_bus, + esp_lcd_panel_io_handle_t *mipi_dbi_io, + esp_lcd_panel_handle_t *mipi_dpi_panel, + void** fb0, + void** fb1) { //---------------DSI resource allocation------------------// esp_lcd_dsi_bus_config_t bus_config = { @@ -32,7 +39,22 @@ void example_dsi_resource_alloc(esp_lcd_dsi_bus_handle_t *mipi_dsi_bus, esp_lcd_ }; ESP_ERROR_CHECK(esp_lcd_new_panel_io_dbi(*mipi_dsi_bus, &dbi_config, mipi_dbi_io)); + // Use default config if not provided + example_dsi_alloc_config_t default_config = EXAMPLE_DSI_ALLOC_CONFIG_DEFAULT(); + if (config == NULL) { + config = &default_config; + } + + if (config->num_fbs < 1 || config->num_fbs > 2) { + ESP_LOGE(TAG, "Invalid num_fbs: %d, must be 1 or 2", config->num_fbs); + return; + } + + uint8_t num_fbs = config->num_fbs; + ESP_LOGI(TAG, "Allocating DSI resources with %d frame buffer(s)", num_fbs); + esp_lcd_dpi_panel_config_t dpi_config = { + .num_fbs = num_fbs, .dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT, .dpi_clock_freq_mhz = EXAMPLE_MIPI_DSI_DPI_CLK_MHZ, .virtual_channel = 0, @@ -83,7 +105,23 @@ void example_dsi_resource_alloc(esp_lcd_dsi_bus_handle_t *mipi_dsi_bus, esp_lcd_ ESP_ERROR_CHECK(esp_lcd_new_panel_ek79007(*mipi_dbi_io, &lcd_dev_config, mipi_dpi_panel)); #endif - ESP_ERROR_CHECK(esp_lcd_dpi_panel_get_frame_buffer(*mipi_dpi_panel, 1, frame_buffer)); + // Get frame buffer addresses + if (fb0 != NULL) { + if (num_fbs == 2) { + if (fb1 != NULL) { + ESP_ERROR_CHECK(esp_lcd_dpi_panel_get_frame_buffer(*mipi_dpi_panel, 2, fb0, fb1)); + ESP_LOGD(TAG, "Frame buffer[0] allocated at: %p", *fb0); + ESP_LOGD(TAG, "Frame buffer[1] allocated at: %p", *fb1); + } else { + ESP_LOGW(TAG, "num_fbs is 2 but fb1 is NULL, only getting fb0"); + ESP_ERROR_CHECK(esp_lcd_dpi_panel_get_frame_buffer(*mipi_dpi_panel, 1, fb0)); + ESP_LOGD(TAG, "Frame buffer[0] allocated at: %p", *fb0); + } + } else { + ESP_ERROR_CHECK(esp_lcd_dpi_panel_get_frame_buffer(*mipi_dpi_panel, 1, fb0)); + ESP_LOGD(TAG, "Frame buffer[0] allocated at: %p", *fb0); + } + } } void example_dpi_panel_reset(esp_lcd_panel_handle_t mipi_dpi_panel) diff --git a/examples/peripherals/camera/common_components/dsi_init/include/example_dsi_init.h b/examples/peripherals/camera/common_components/dsi_init/include/example_dsi_init.h index 433c3d088187..bcf1f1073c8d 100644 --- a/examples/peripherals/camera/common_components/dsi_init/include/example_dsi_init.h +++ b/examples/peripherals/camera/common_components/dsi_init/include/example_dsi_init.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -14,14 +14,37 @@ extern "C" { #endif /** - * @brief DSI init function + * @brief DSI allocation configuration + */ +typedef struct { + uint8_t num_fbs; /*!< Number of frame buffers (1-2). */ +} example_dsi_alloc_config_t; + +/** + * @brief Default DSI allocation configuration + */ +#define EXAMPLE_DSI_ALLOC_CONFIG_DEFAULT() { \ + .num_fbs = 1, \ +} + +/** + * @brief DSI init function with configurable frame buffers + * + * @param[in] config DSI allocation configuration. If NULL, uses default (single buffer) + * @param[out] mipi_dsi_bus MIPI DSI bus handle + * @param[out] mipi_dbi_io MIPI DBI io handle + * @param[out] mipi_dpi_panel MIPI DPI panel handle + * @param[out] fb0 Pointer to receive the address of frame buffer 0 (first frame buffer) + * @param[out] fb1 Pointer to receive the address of frame buffer 1 (second frame buffer). If NULL, single buffer mode is used. * - * @param[out] mipi_dsi_bus MIPI DSI bus handle - * @param[out] mipi_dbi_io MIPI DBI io handle - * @param[out] mipi_dpi_panel MIPI DPI panel handle - * @param[out] frame_buffer frame buffer + * @note The number of frame buffers allocated is determined by config->num_fbs. If config is NULL, single buffer mode is used. */ -void example_dsi_resource_alloc(esp_lcd_dsi_bus_handle_t *mipi_dsi_bus, esp_lcd_panel_io_handle_t *mipi_dbi_io, esp_lcd_panel_handle_t *mipi_dpi_panel, void **frame_buffer); +void example_dsi_resource_alloc(const example_dsi_alloc_config_t *config, + esp_lcd_dsi_bus_handle_t *mipi_dsi_bus, + esp_lcd_panel_io_handle_t *mipi_dbi_io, + esp_lcd_panel_handle_t *mipi_dpi_panel, + void **fb0, + void **fb1); /** * @brief DPI panel reset function diff --git a/examples/peripherals/camera/dvp_dsi/main/dvp_dsi_main.c b/examples/peripherals/camera/dvp_dsi/main/dvp_dsi_main.c index 8b27b2241a4d..0bf5d203cce2 100644 --- a/examples/peripherals/camera/dvp_dsi/main/dvp_dsi_main.c +++ b/examples/peripherals/camera/dvp_dsi/main/dvp_dsi_main.c @@ -57,10 +57,10 @@ void app_main(void) ESP_ERROR_CHECK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy)); //---------------DSI Init------------------// - example_dsi_resource_alloc(&mipi_dsi_bus, &mipi_dbi_io, &mipi_dpi_panel, &frame_buffer); + example_dsi_resource_alloc(NULL, &mipi_dsi_bus, &mipi_dbi_io, &mipi_dpi_panel, &frame_buffer, NULL); //---------------Necessary variable config------------------// - frame_buffer_size = CONFIG_EXAMPLE_MIPI_DSI_DISP_HRES * CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES * EXAMPLE_RGB565_BITS_PER_PIXEL / 8; + frame_buffer_size = CONFIG_EXAMPLE_MIPI_DSI_DISP_HRES * CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES * EXAMPLE_RGB565_BYTES_PER_PIXEL; ESP_LOGD(TAG, "CONFIG_EXAMPLE_MIPI_DSI_DISP_HRES: %d, CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES: %d, bits per pixel: %d", CONFIG_EXAMPLE_MIPI_DSI_DISP_HRES, CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES, EXAMPLE_RGB565_BITS_PER_PIXEL); ESP_LOGD(TAG, "frame_buffer_size: %zu", frame_buffer_size); @@ -108,7 +108,7 @@ void app_main(void) } //--------Allocate Camera Buffer----------// - size_t cam_buffer_size = CONFIG_EXAMPLE_CAM_HRES * CONFIG_EXAMPLE_CAM_VRES * EXAMPLE_RGB565_BITS_PER_PIXEL / 8; + size_t cam_buffer_size = CONFIG_EXAMPLE_CAM_HRES * CONFIG_EXAMPLE_CAM_VRES * EXAMPLE_RGB565_BYTES_PER_PIXEL; void *cam_buffer = esp_cam_ctlr_alloc_buffer(cam_handle, cam_buffer_size, MALLOC_CAP_DMA | MALLOC_CAP_SPIRAM); if (!cam_buffer) { ESP_LOGE(TAG, "no mem for cam_buffer"); diff --git a/examples/peripherals/camera/dvp_dsi/main/example_config.h b/examples/peripherals/camera/dvp_dsi/main/example_config.h index 6ff0ec4137bf..eb76ca211f48 100644 --- a/examples/peripherals/camera/dvp_dsi/main/example_config.h +++ b/examples/peripherals/camera/dvp_dsi/main/example_config.h @@ -13,6 +13,8 @@ extern "C" { #endif #define EXAMPLE_RGB565_BITS_PER_PIXEL (16) +#define EXAMPLE_RGB565_BYTES_PER_PIXEL (EXAMPLE_RGB565_BITS_PER_PIXEL / 8) + #define EXAMPLE_DVP_CAM_XCLK_FREQ_HZ (20000000) #define EXAMPLE_DVP_CAM_DATA_WIDTH (8) @@ -36,6 +38,7 @@ extern "C" { #define EXAMPLE_DVP_CAM_HSYNC_IO (-1) #else + #define EXAMPLE_DVP_CAM_SCCB_SCL_IO (33) #define EXAMPLE_DVP_CAM_SCCB_SDA_IO (32) diff --git a/examples/peripherals/camera/dvp_isp_dsi/main/dvp_isp_dsi_main.c b/examples/peripherals/camera/dvp_isp_dsi/main/dvp_isp_dsi_main.c index 9b6c049dfba3..6bd754312e82 100644 --- a/examples/peripherals/camera/dvp_isp_dsi/main/dvp_isp_dsi_main.c +++ b/examples/peripherals/camera/dvp_isp_dsi/main/dvp_isp_dsi_main.c @@ -89,10 +89,10 @@ void app_main(void) * ISP convert to RGB565 */ //---------------DSI Init------------------// - example_dsi_resource_alloc(&mipi_dsi_bus, &mipi_dbi_io, &mipi_dpi_panel, &frame_buffer); + example_dsi_resource_alloc(NULL, &mipi_dsi_bus, &mipi_dbi_io, &mipi_dpi_panel, &frame_buffer, NULL); //---------------Necessary variable config------------------// - frame_buffer_size = CONFIG_EXAMPLE_MIPI_DSI_DISP_HRES * CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES * EXAMPLE_RGB565_BITS_PER_PIXEL / 8; + frame_buffer_size = CONFIG_EXAMPLE_MIPI_DSI_DISP_HRES * CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES * EXAMPLE_RGB565_BYTES_PER_PIXEL; ESP_LOGD(TAG, "CONFIG_EXAMPLE_MIPI_DSI_DISP_HRES: %d, CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES: %d, bits per pixel: %d", CONFIG_EXAMPLE_MIPI_DSI_DISP_HRES, CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES, EXAMPLE_RGB565_BITS_PER_PIXEL); ESP_LOGD(TAG, "frame_buffer_size: %zu", frame_buffer_size); @@ -157,7 +157,7 @@ void app_main(void) } //--------Allocate Camera Buffer----------// - size_t cam_buffer_size = CONFIG_EXAMPLE_CAM_HRES * CONFIG_EXAMPLE_CAM_VRES * EXAMPLE_RGB565_BITS_PER_PIXEL / 8; + size_t cam_buffer_size = CONFIG_EXAMPLE_CAM_HRES * CONFIG_EXAMPLE_CAM_VRES * EXAMPLE_RGB565_BYTES_PER_PIXEL; void *cam_buffer = esp_cam_ctlr_alloc_buffer(cam_handle, cam_buffer_size, MALLOC_CAP_DMA | MALLOC_CAP_SPIRAM); if (!cam_buffer) { ESP_LOGE(TAG, "no mem for cam_buffer"); diff --git a/examples/peripherals/camera/dvp_isp_dsi/main/example_config.h b/examples/peripherals/camera/dvp_isp_dsi/main/example_config.h index 81b3188c62b9..c2eb00ffcee9 100644 --- a/examples/peripherals/camera/dvp_isp_dsi/main/example_config.h +++ b/examples/peripherals/camera/dvp_isp_dsi/main/example_config.h @@ -13,6 +13,7 @@ extern "C" { #endif #define EXAMPLE_RGB565_BITS_PER_PIXEL 16 +#define EXAMPLE_RGB565_BYTES_PER_PIXEL (EXAMPLE_RGB565_BITS_PER_PIXEL / 8) #define EXAMPLE_ISP_DVP_CAM_SCCB_SCL_IO (33) #define EXAMPLE_ISP_DVP_CAM_SCCB_SDA_IO (32) diff --git a/examples/peripherals/camera/dvp_spi_lcd/main/dvp_spi_lcd_main.c b/examples/peripherals/camera/dvp_spi_lcd/main/dvp_spi_lcd_main.c index eacaf2dff745..d9032a57c5c4 100644 --- a/examples/peripherals/camera/dvp_spi_lcd/main/dvp_spi_lcd_main.c +++ b/examples/peripherals/camera/dvp_spi_lcd/main/dvp_spi_lcd_main.c @@ -24,7 +24,7 @@ static const char *TAG = "dvp_spi_lcd"; -#define BUFFER_SIZE (CONFIG_EXAMPLE_CAM_HRES * CONFIG_EXAMPLE_CAM_VRES * EXAMPLE_RGB565_BITS_PER_PIXEL / 8) +#define BUFFER_SIZE (CONFIG_EXAMPLE_CAM_HRES * CONFIG_EXAMPLE_CAM_VRES * EXAMPLE_RGB565_BYTES_PER_PIXEL) typedef struct { esp_lcd_panel_handle_t panel_hdl; @@ -164,7 +164,7 @@ void app_main(void) } //--------Allocate Camera Buffer----------// - size_t cam_buffer_size = CONFIG_EXAMPLE_CAM_HRES * CONFIG_EXAMPLE_CAM_VRES * EXAMPLE_RGB565_BITS_PER_PIXEL / 8; + size_t cam_buffer_size = CONFIG_EXAMPLE_CAM_HRES * CONFIG_EXAMPLE_CAM_VRES * EXAMPLE_RGB565_BYTES_PER_PIXEL; void *cam_buffer = NULL; cam_buffer = esp_cam_ctlr_alloc_buffer(cam_handle, cam_buffer_size, EXAMPLE_DVP_CAM_BUF_ALLOC_CAPS); diff --git a/examples/peripherals/camera/dvp_spi_lcd/main/example_config.h b/examples/peripherals/camera/dvp_spi_lcd/main/example_config.h index 5609ef560cb6..95d178d46894 100644 --- a/examples/peripherals/camera/dvp_spi_lcd/main/example_config.h +++ b/examples/peripherals/camera/dvp_spi_lcd/main/example_config.h @@ -14,6 +14,7 @@ extern "C" { //----------CAM Config------------// #define EXAMPLE_RGB565_BITS_PER_PIXEL 16 +#define EXAMPLE_RGB565_BYTES_PER_PIXEL (EXAMPLE_RGB565_BITS_PER_PIXEL / 8) #define EXAMPLE_DVP_CAM_SCCB_SCL_IO (5) #define EXAMPLE_DVP_CAM_SCCB_SDA_IO (4) diff --git a/examples/peripherals/camera/mipi_isp_dsi/main/example_config.h b/examples/peripherals/camera/mipi_isp_dsi/main/example_config.h index 570ebdc86692..dc2725abad2e 100644 --- a/examples/peripherals/camera/mipi_isp_dsi/main/example_config.h +++ b/examples/peripherals/camera/mipi_isp_dsi/main/example_config.h @@ -13,6 +13,7 @@ extern "C" { #endif #define EXAMPLE_RGB565_BITS_PER_PIXEL 16 +#define EXAMPLE_RGB565_BYTES_PER_PIXEL (EXAMPLE_RGB565_BITS_PER_PIXEL / 8) #define EXAMPLE_MIPI_IDI_CLOCK_RATE (50000000) #define EXAMPLE_MIPI_CSI_LANE_BITRATE_MBPS 200 //line_rate = pclk * 4 diff --git a/examples/peripherals/camera/mipi_isp_dsi/main/mipi_isp_dsi_main.c b/examples/peripherals/camera/mipi_isp_dsi/main/mipi_isp_dsi_main.c index d7f3645da610..da5329845c70 100644 --- a/examples/peripherals/camera/mipi_isp_dsi/main/mipi_isp_dsi_main.c +++ b/examples/peripherals/camera/mipi_isp_dsi/main/mipi_isp_dsi_main.c @@ -51,10 +51,10 @@ void app_main(void) * ISP convert to RGB565 */ //---------------DSI Init------------------// - example_dsi_resource_alloc(&mipi_dsi_bus, &mipi_dbi_io, &mipi_dpi_panel, &frame_buffer); + example_dsi_resource_alloc(NULL, &mipi_dsi_bus, &mipi_dbi_io, &mipi_dpi_panel, &frame_buffer, NULL); //---------------Necessary variable config------------------// - frame_buffer_size = CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES * CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES * EXAMPLE_RGB565_BITS_PER_PIXEL / 8; + frame_buffer_size = CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES * CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES * EXAMPLE_RGB565_BYTES_PER_PIXEL; ESP_LOGD(TAG, "CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES: %d, CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES: %d, bits per pixel: %d", CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES, CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES, EXAMPLE_RGB565_BITS_PER_PIXEL); ESP_LOGD(TAG, "frame_buffer_size: %zu", frame_buffer_size); diff --git a/examples/peripherals/isp/multi_pipelines/README.md b/examples/peripherals/isp/multi_pipelines/README.md index b526b160d84e..1fc9625535d7 100644 --- a/examples/peripherals/isp/multi_pipelines/README.md +++ b/examples/peripherals/isp/multi_pipelines/README.md @@ -16,6 +16,9 @@ This example demonstrates how to use the ISP (image signal processor) to work wi - ISP GAMMA feature - ISP Color feature - ISP LSC feature +- ISP Crop feature (need to enable in `idf.py menuconfig`) + +Additionally, this example also implements **Dual Frame Buffer (Ping-Pong Buffering)**, which eliminates screen tearing for smooth video display ## Usage @@ -121,6 +124,21 @@ Remember to select the LCD screen model and set corresponding correct horizontal Available options for the camera sensor output horizontal/vertical resolution can be seen in ``menuconfig`` > ``Example Configuration``. Note that the horizontal resolution for the camera should be the same as the LCD screen horizontal resolution. +#### Optional: Image Cropping Configuration + +This example supports optional image cropping, which allows you to capture and display a specific region of the camera output. To enable this feature: + +1. Navigate to `menuconfig` > `Example Configuration` +2. Enable `Enable ISP Image Cropping` +3. Configure the crop region: + - `ISP Crop Top-Left Horizontal`: X coordinate of top-left corner + - `ISP Crop Top-Left Vertical`: Y coordinate of top-left corner + - `ISP Crop Bottom-Right Horizontal`: X coordinate of bottom-right corner + - `ISP Crop Bottom-Right Vertical`: Y coordinate of bottom-right corner + +**Note**: When cropping is enabled: +- The cropped image maintains its relative position on the screen +- A dedicated frame processing task handles the image transformation ### Build and Flash @@ -142,15 +160,23 @@ To exit the serial monitor, use `Ctrl` + `]`. If you see the following console output, your example should be running correctly: ``` -I (1085) main_task: Calling app_main() -I (1095) ili9881c: ID1: 0x98, ID2: 0x81, ID3: 0x5c -I (1125) gpio: GPIO[31]| InputEn: 1| OutputEn: 1| OpenDrain: 1| Pullup: 1| Pulldown: 0| Intr:0 -I (1125) gpio: GPIO[34]| InputEn: 1| OutputEn: 1| OpenDrain: 1| Pullup: 1| Pulldown: 0| Intr:0 -I (1295) ov5647: Detected Camera sensor PID=0x5647 with index 0 -I (1305) cam_dsi: fmt[0].name:MIPI_2lane_24Minput_RAW8_800x1280_50fps -I (1305) cam_dsi: fmt[1].name:MIPI_2lane_24Minput_RAW8_800x640_50fps -I (1315) cam_dsi: fmt[2].name:MIPI_2lane_24Minput_RAW8_800x800_50fps -I (1355) cam_dsi: Format in use:MIPI_2lane_24Minput_RAW8_800x640_50fps +I (1425) main_task: Calling app_main() +I (1425) example_dsi_init: Allocating DSI resources with 2 frame buffer(s) +I (1485) example_dsi_init: Frame buffer[0] allocated at: 0x48000a40 +I (1485) example_dsi_init: Frame buffer[1] allocated at: 0x481f4a80 +I (1485) isp_dsi: Original CSI resolution: 800x640 +I (1485) isp_dsi: Display resolution: 800x640, bits per pixel: 16 +I (1495) isp_dsi: frame_buffer_size: 2048000 +I (1495) isp_dsi: Frame buffers: fb0=0x48000a40, fb1=0x481f4a80 +I (1515) ov5647: Detected Camera sensor PID=0x5647 +I (1535) sensor_init: fmt[0].name:MIPI_2lane_24Minput_RAW8_800x1280_50fps +I (1535) sensor_init: fmt[1].name:MIPI_2lane_24Minput_RAW8_800x640_50fps +I (1535) sensor_init: fmt[2].name:MIPI_2lane_24Minput_RAW8_800x800_50fps +I (1535) sensor_init: fmt[3].name:MIPI_2lane_24Minput_RAW10_1920x1080_30fps +I (1545) sensor_init: fmt[4].name:MIPI_2lane_24Minput_RAW10_1280x960_binning_45fps +I (2025) sensor_init: Format in use:MIPI_2lane_24Minput_RAW8_800x640_50fps +I (2045) isp_dsi: ISP Crop not configured +I (2105) ili9881c: ID1: 0x98, ID2: 0x81, ID3: 0x5c ``` Below picture is from the video stream of OV5647 and ILI9881C. The camera module is auto-focused and calibrated by ESP on-chip ISP hardware. The edge is over-sharpened as example code configured. diff --git a/examples/peripherals/isp/multi_pipelines/main/Kconfig.projbuild b/examples/peripherals/isp/multi_pipelines/main/Kconfig.projbuild index 98a42e128a77..5e4244b886d5 100644 --- a/examples/peripherals/isp/multi_pipelines/main/Kconfig.projbuild +++ b/examples/peripherals/isp/multi_pipelines/main/Kconfig.projbuild @@ -44,4 +44,57 @@ menu "Example Configuration" default 600 if EXAMPLE_MIPI_CSI_VRES_600 default 640 if EXAMPLE_MIPI_CSI_VRES_640 default 1280 if EXAMPLE_MIPI_CSI_VRES_1280 + + config EXAMPLE_ISP_CROP_ENABLE + depends on (ESP32P4_REV_MIN_FULL >= 300) + bool "Enable ISP crop functionality" + default n + help + Enable ISP crop functionality. When enabled, you can configure + the crop area for the ISP pipeline. + + menu "ISP Crop Configuration" + visible if EXAMPLE_ISP_CROP_ENABLE + + config EXAMPLE_ISP_CROP_TOP_LEFT_H + int "ISP crop top-left horizontal coordinate" + default 0 + range 0 799 if EXAMPLE_MIPI_CSI_HRES_800 + range 0 1023 if EXAMPLE_MIPI_CSI_HRES_1024 + help + Horizontal coordinate of the top-left corner for ISP crop. + Must be an even number and less than bottom-right horizontal coordinate. + + config EXAMPLE_ISP_CROP_TOP_LEFT_V + int "ISP crop top-left vertical coordinate" + default 0 + range 0 599 if EXAMPLE_MIPI_CSI_VRES_600 + range 0 639 if EXAMPLE_MIPI_CSI_VRES_640 + range 0 1279 if EXAMPLE_MIPI_CSI_VRES_1280 + help + Vertical coordinate of the top-left corner for ISP crop. + Must be an even number and less than bottom-right vertical coordinate. + + config EXAMPLE_ISP_CROP_BOTTOM_RIGHT_H + int "ISP crop bottom-right horizontal coordinate" + default 799 if EXAMPLE_MIPI_CSI_HRES_800 + default 1023 if EXAMPLE_MIPI_CSI_HRES_1024 + range 1 799 if EXAMPLE_MIPI_CSI_HRES_800 + range 1 1023 if EXAMPLE_MIPI_CSI_HRES_1024 + help + Horizontal coordinate of the bottom-right corner for ISP crop. + Must be an odd number, greater than top-left horizontal coordinate, and not exceed display width. + + config EXAMPLE_ISP_CROP_BOTTOM_RIGHT_V + int "ISP crop bottom-right vertical coordinate" + default 599 if EXAMPLE_MIPI_CSI_VRES_600 + default 639 if EXAMPLE_MIPI_CSI_VRES_640 + default 1279 if EXAMPLE_MIPI_CSI_VRES_1280 + range 1 599 if EXAMPLE_MIPI_CSI_VRES_600 + range 1 639 if EXAMPLE_MIPI_CSI_VRES_640 + range 1 1279 if EXAMPLE_MIPI_CSI_VRES_1280 + help + Vertical coordinate of the bottom-right corner for ISP crop. + Must be an odd number, greater than top-left vertical coordinate, and not exceed display height. + endmenu endmenu diff --git a/examples/peripherals/isp/multi_pipelines/main/example_config.h b/examples/peripherals/isp/multi_pipelines/main/example_config.h index 9c5482fe3926..f4ca7636f017 100644 --- a/examples/peripherals/isp/multi_pipelines/main/example_config.h +++ b/examples/peripherals/isp/multi_pipelines/main/example_config.h @@ -11,6 +11,7 @@ extern "C" { #endif #define EXAMPLE_RGB565_BITS_PER_PIXEL 16 +#define EXAMPLE_RGB565_BYTES_PER_PIXEL (EXAMPLE_RGB565_BITS_PER_PIXEL / 8) #define EXAMPLE_MIPI_SCCB_FREQ (100000) #define EXAMPLE_MIPI_CSI_LANE_BITRATE_MBPS 200 //line_rate = pclk * 4 diff --git a/examples/peripherals/isp/multi_pipelines/main/isp_dsi_main.c b/examples/peripherals/isp/multi_pipelines/main/isp_dsi_main.c index 79ac25707403..b84532617d9c 100644 --- a/examples/peripherals/isp/multi_pipelines/main/isp_dsi_main.c +++ b/examples/peripherals/isp/multi_pipelines/main/isp_dsi_main.c @@ -33,6 +33,103 @@ static const char *TAG = "isp_dsi"; static bool s_camera_get_new_vb(esp_cam_ctlr_handle_t handle, esp_cam_ctlr_trans_t *trans, void *user_data); static bool s_camera_get_finished_trans(esp_cam_ctlr_handle_t handle, esp_cam_ctlr_trans_t *trans, void *user_data); +/*--------------------------------------------------------------- + Ping-Pong Buffer Management +---------------------------------------------------------------*/ +typedef struct { + void *fb0; // Frame buffer 0 + void *fb1; // Frame buffer 1 + void *csi_buffer; // Current buffer for CSI to write + void *dsi_buffer; // Current buffer for DSI to display + esp_lcd_panel_handle_t panel;// DPI panel handle + int h_res; // Horizontal resolution + int v_res; // Vertical resolution (full screen) +#ifdef CONFIG_EXAMPLE_ISP_CROP_ENABLE + int crop_h_res; // Cropped horizontal resolution + int crop_v_res; // Cropped vertical resolution + void *pending_buffer; // Buffer pending to be displayed + SemaphoreHandle_t frame_ready_sem; // Semaphore to signal frame ready +#endif +} pingpong_buffer_ctx_t; + +#ifdef CONFIG_EXAMPLE_ISP_CROP_ENABLE +/** + * @brief Process frame: Add blank areas to fill full screen resolution + * + * Algorithm: Fill from bottom to top to avoid overwriting crop data + * - Cropped image is placed at original position (relative to full frame) + * - Other areas are filled with white (0xFFFF for RGB565) + * + * Example: Original 100x100, crop (50,50) to (100,100) → shows at bottom-right + * + * @param buffer Frame buffer to process (contains cropped image at start) + * @param ctx Ping-pong buffer context + */ +static void process_frame_with_blanks(void *buffer, pingpong_buffer_ctx_t *ctx) +{ + if (ctx->crop_v_res == ctx->v_res) { + // No cropping, no need to process + return; + } + + uint16_t *fb = (uint16_t *)buffer; + + const int crop_left = CONFIG_EXAMPLE_ISP_CROP_TOP_LEFT_H; + const int crop_top = CONFIG_EXAMPLE_ISP_CROP_TOP_LEFT_V; + const int crop_right = CONFIG_EXAMPLE_ISP_CROP_BOTTOM_RIGHT_H; + const int crop_bottom = CONFIG_EXAMPLE_ISP_CROP_BOTTOM_RIGHT_V; + const int crop_width = crop_right - crop_left + 1; + + const int full_width = CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES; + const int full_height = CONFIG_EXAMPLE_MIPI_CSI_DISP_VRES; + + // Helper macros for pixel indexing +#define SRC_PIXEL(x, y) fb[(y) * crop_width + (x)] +#define DST_PIXEL(x, y) fb[(y) * full_width + (x)] + + // ========== Step 1: Fill bottom blank region [crop_bottom+1, full_height) ========== + if (crop_bottom + 1 < full_height) { + memset(&DST_PIXEL(0, crop_bottom + 1), + 0xFF, + full_width * (full_height - crop_bottom - 1) * EXAMPLE_RGB565_BYTES_PER_PIXEL); + } + + // ========== Step 2: Process crop region [crop_top, crop_bottom] ========== + for (int y = crop_bottom; y >= crop_top; y--) { + int src_y = y - crop_top; // Corresponding row in cropped data (0-based) + + // Fill right blank region first (crop_right+1, full_width) + if (crop_right + 1 < full_width) { + memset(&DST_PIXEL(crop_right + 1, y), + 0xFF, + (full_width - crop_right - 1) * EXAMPLE_RGB565_BYTES_PER_PIXEL); + } + + // Copy crop data from source to destination + memcpy(&DST_PIXEL(crop_left, y), + &SRC_PIXEL(0, src_y), + crop_width * EXAMPLE_RGB565_BYTES_PER_PIXEL); + + // Fill left blank region [0, crop_left) + if (crop_left > 0) { + memset(&DST_PIXEL(0, y), + 0xFF, + crop_left * EXAMPLE_RGB565_BYTES_PER_PIXEL); + } + } + + // ========== Step 3: Fill top blank region [0, crop_top) ========== + if (crop_top > 0) { + memset(&DST_PIXEL(0, 0), + 0xFF, + full_width * crop_top * EXAMPLE_RGB565_BYTES_PER_PIXEL); + } + +#undef SRC_PIXEL +#undef DST_PIXEL +} +#endif + /*--------------------------------------------------------------- AF ---------------------------------------------------------------*/ @@ -185,13 +282,47 @@ static uint32_t s_gamma_correction_curve(uint32_t x) return pow((double)x / 256, 0.7) * 256; } +#ifdef CONFIG_EXAMPLE_ISP_CROP_ENABLE +/*--------------------------------------------------------------- + Frame Processing Task +---------------------------------------------------------------*/ +static void frame_processing_task(void *arg) +{ + pingpong_buffer_ctx_t *ctx = (pingpong_buffer_ctx_t *)arg; + + while (1) { + // Wait for frame ready signal from ISR + if (xSemaphoreTake(ctx->frame_ready_sem, portMAX_DELAY) == pdTRUE) { + // Process the frame: add blank areas if needed + process_frame_with_blanks(ctx->pending_buffer, ctx); + + // Ping-Pong switch: swap CSI write buffer and DSI display buffer + void *temp = ctx->csi_buffer; + ctx->csi_buffer = ctx->dsi_buffer; + ctx->dsi_buffer = temp; + + // Trigger buffer switch by calling draw_bitmap + // DPI driver will detect which buffer we're using and switch to it + ESP_ERROR_CHECK(esp_lcd_panel_draw_bitmap(ctx->panel, + 0, 0, + ctx->h_res, + ctx->crop_v_res, + ctx->pending_buffer)); + + ESP_LOGD(TAG, "Frame displayed: %p", ctx->pending_buffer); + } + } +} +#endif // CONFIG_EXAMPLE_ISP_CROP_ENABLE + void app_main(void) { esp_err_t ret = ESP_FAIL; esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL; esp_lcd_panel_io_handle_t mipi_dbi_io = NULL; esp_lcd_panel_handle_t mipi_dpi_panel = NULL; - void *frame_buffer = NULL; + void *fb0 = NULL; + void *fb1 = NULL; size_t frame_buffer_size = 0; //mipi ldo @@ -207,18 +338,54 @@ void app_main(void) * Sensor use RAW8 * ISP convert to RGB565 */ - //---------------DSI Init------------------// - example_dsi_resource_alloc(&mipi_dsi_bus, &mipi_dbi_io, &mipi_dpi_panel, &frame_buffer); + //---------------DSI Init with Dual Frame Buffers------------------// + example_dsi_alloc_config_t dsi_alloc_config = { + .num_fbs = 2, // Enable dual frame buffers + }; + example_dsi_resource_alloc(&dsi_alloc_config, &mipi_dsi_bus, &mipi_dbi_io, &mipi_dpi_panel, &fb0, &fb1); //---------------Necessary variable config------------------// - frame_buffer_size = CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES * CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES * EXAMPLE_RGB565_BITS_PER_PIXEL / 8; + int display_h_res = CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES; + int display_v_res = CONFIG_EXAMPLE_MIPI_CSI_DISP_VRES; + +#ifdef CONFIG_EXAMPLE_ISP_CROP_ENABLE + // Use cropped resolution for frame buffer + display_h_res = CONFIG_EXAMPLE_ISP_CROP_BOTTOM_RIGHT_H - CONFIG_EXAMPLE_ISP_CROP_TOP_LEFT_H + 1; + display_v_res = CONFIG_EXAMPLE_ISP_CROP_BOTTOM_RIGHT_V - CONFIG_EXAMPLE_ISP_CROP_TOP_LEFT_V + 1; +#endif + + frame_buffer_size = CONFIG_EXAMPLE_MIPI_DSI_DISP_HRES * CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES * EXAMPLE_RGB565_BYTES_PER_PIXEL; + + ESP_LOGI(TAG, "Original CSI resolution: %dx%d", CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES, CONFIG_EXAMPLE_MIPI_CSI_DISP_VRES); + ESP_LOGI(TAG, "Display resolution: %dx%d, bits per pixel: %d", display_h_res, display_v_res, EXAMPLE_RGB565_BITS_PER_PIXEL); + ESP_LOGI(TAG, "Frame buffers: fb0=%p, fb1=%p", fb0, fb1); + + //---------------Ping-Pong Buffer Context------------------// + pingpong_buffer_ctx_t pp_ctx = { + .fb0 = fb0, + .fb1 = fb1, + .csi_buffer = fb0, // CSI starts writing to fb0 + .dsi_buffer = fb1, // DSI starts displaying fb1 + .panel = mipi_dpi_panel, + .h_res = CONFIG_EXAMPLE_MIPI_DSI_DISP_HRES, + .v_res = CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES, +#ifdef CONFIG_EXAMPLE_ISP_CROP_ENABLE + .crop_h_res = display_h_res, + .crop_v_res = display_v_res, + .pending_buffer = NULL, + .frame_ready_sem = xSemaphoreCreateBinary(), +#endif + }; - ESP_LOGD(TAG, "CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES: %d, CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES: %d, bits per pixel: %d", CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES, CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES, 8); - ESP_LOGD(TAG, "frame_buffer_size: %zu", frame_buffer_size); - ESP_LOGD(TAG, "frame_buffer: %p", frame_buffer); +#ifdef CONFIG_EXAMPLE_ISP_CROP_ENABLE + if (pp_ctx.frame_ready_sem == NULL) { + ESP_LOGE(TAG, "Failed to create frame ready semaphore"); + return; + } +#endif esp_cam_ctlr_trans_t new_trans = { - .buffer = frame_buffer, + .buffer = pp_ctx.csi_buffer, .buflen = frame_buffer_size, }; @@ -248,8 +415,8 @@ void app_main(void) //---------------CSI Init------------------// esp_cam_ctlr_csi_config_t csi_config = { .ctlr_id = 0, - .h_res = CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES, - .v_res = CONFIG_EXAMPLE_MIPI_CSI_DISP_VRES, + .h_res = display_h_res, + .v_res = display_v_res, .lane_bit_rate_mbps = EXAMPLE_MIPI_CSI_LANE_BITRATE_MBPS, .input_data_color_type = CAM_CTLR_COLOR_RAW8, .output_data_color_type = CAM_CTLR_COLOR_RGB565, @@ -268,7 +435,7 @@ void app_main(void) .on_get_new_trans = s_camera_get_new_vb, .on_trans_finished = s_camera_get_finished_trans, }; - if (esp_cam_ctlr_register_event_callbacks(handle, &cbs, &new_trans) != ESP_OK) { + if (esp_cam_ctlr_register_event_callbacks(handle, &cbs, &pp_ctx) != ESP_OK) { ESP_LOGE(TAG, "ops register fail"); return; } @@ -422,6 +589,29 @@ void app_main(void) ESP_ERROR_CHECK(esp_isp_lsc_enable(isp_proc)); #endif +#ifdef CONFIG_EXAMPLE_ISP_CROP_ENABLE + /*--------------------------------------------------------------- + CROP + ---------------------------------------------------------------*/ + esp_isp_crop_config_t crop_config = { + .window = { + .top_left = { + .x = CONFIG_EXAMPLE_ISP_CROP_TOP_LEFT_H, + .y = CONFIG_EXAMPLE_ISP_CROP_TOP_LEFT_V + }, + .btm_right = { + .x = CONFIG_EXAMPLE_ISP_CROP_BOTTOM_RIGHT_H, + .y = CONFIG_EXAMPLE_ISP_CROP_BOTTOM_RIGHT_V + } + } + }; + ESP_ERROR_CHECK(esp_isp_crop_configure(isp_proc, &crop_config)); + ESP_ERROR_CHECK(esp_isp_crop_enable(isp_proc)); + ESP_LOGI(TAG, "ISP Crop enabled: (%d,%d) to (%d,%d)", + CONFIG_EXAMPLE_ISP_CROP_TOP_LEFT_H, CONFIG_EXAMPLE_ISP_CROP_TOP_LEFT_V, + CONFIG_EXAMPLE_ISP_CROP_BOTTOM_RIGHT_H, CONFIG_EXAMPLE_ISP_CROP_BOTTOM_RIGHT_V); +#endif + typedef struct af_task_param_t { isp_proc_handle_t isp_proc; esp_sccb_io_handle_t dw9714_io_handle; @@ -433,12 +623,20 @@ void app_main(void) }; xTaskCreatePinnedToCore(af_task, "af_task", 8192, &af_task_param, 5, NULL, 0); +#ifdef CONFIG_EXAMPLE_ISP_CROP_ENABLE + //---------------Frame Processing Task------------------// + xTaskCreatePinnedToCore(frame_processing_task, "frame_proc", 4096, &pp_ctx, 6, NULL, 0); + ESP_LOGI(TAG, "Frame processing task created"); +#endif + //---------------DPI Reset------------------// example_dpi_panel_reset(mipi_dpi_panel); - //init to all white - memset(frame_buffer, 0xFF, frame_buffer_size); - esp_cache_msync((void *)frame_buffer, frame_buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); + //init both frame buffers to white + memset(fb0, 0xFF, frame_buffer_size); + memset(fb1, 0xFF, frame_buffer_size); + esp_cache_msync((void *)fb0, frame_buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); + esp_cache_msync((void *)fb1, frame_buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); if (esp_cam_ctlr_start(handle) != ESP_OK) { ESP_LOGE(TAG, "Driver start fail"); @@ -454,14 +652,30 @@ void app_main(void) static bool s_camera_get_new_vb(esp_cam_ctlr_handle_t handle, esp_cam_ctlr_trans_t *trans, void *user_data) { - esp_cam_ctlr_trans_t new_trans = *(esp_cam_ctlr_trans_t *)user_data; - trans->buffer = new_trans.buffer; - trans->buflen = new_trans.buflen; + pingpong_buffer_ctx_t *ctx = (pingpong_buffer_ctx_t *)user_data; + + // Provide the current CSI buffer for the next frame + trans->buffer = ctx->csi_buffer; + trans->buflen = CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES * CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES * EXAMPLE_RGB565_BYTES_PER_PIXEL; return false; } bool s_camera_get_finished_trans(esp_cam_ctlr_handle_t handle, esp_cam_ctlr_trans_t *trans, void *user_data) { + pingpong_buffer_ctx_t *ctx = (pingpong_buffer_ctx_t *)user_data; + +#ifdef CONFIG_EXAMPLE_ISP_CROP_ENABLE + BaseType_t high_task_wakeup = pdFALSE; + ctx->pending_buffer = trans->buffer; + xSemaphoreGiveFromISR(ctx->frame_ready_sem, &high_task_wakeup); + return (high_task_wakeup == pdTRUE); +#else + void *temp = ctx->csi_buffer; + ctx->csi_buffer = ctx->dsi_buffer; + ctx->dsi_buffer = temp; + + ESP_ERROR_CHECK(esp_lcd_panel_draw_bitmap(ctx->panel, 0, 0, ctx->h_res, ctx->v_res, trans->buffer)); return false; +#endif } diff --git a/examples/peripherals/ppa/ppa_dsi/main/ppa_dsi_main.c b/examples/peripherals/ppa/ppa_dsi/main/ppa_dsi_main.c index 02d46d7fb782..04839b51a3d5 100644 --- a/examples/peripherals/ppa/ppa_dsi/main/ppa_dsi_main.c +++ b/examples/peripherals/ppa/ppa_dsi/main/ppa_dsi_main.c @@ -329,7 +329,7 @@ void app_main(void) ESP_ERROR_CHECK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy)); //---------------DSI Init------------------// - example_dsi_resource_alloc(&mipi_dsi_bus, &mipi_dbi_io, &mipi_dpi_panel, NULL); + example_dsi_resource_alloc(NULL, &mipi_dsi_bus, &mipi_dbi_io, &mipi_dpi_panel, NULL, NULL); example_dpi_panel_reset(mipi_dpi_panel); example_dpi_panel_init(mipi_dpi_panel); diff --git a/examples/peripherals/temperature_sensor/temp_sensor/pytest_temp_sensor_example.py b/examples/peripherals/temperature_sensor/temp_sensor/pytest_temp_sensor_example.py index 97dcf3aba1ac..df1727e3f46c 100644 --- a/examples/peripherals/temperature_sensor/temp_sensor/pytest_temp_sensor_example.py +++ b/examples/peripherals/temperature_sensor/temp_sensor/pytest_temp_sensor_example.py @@ -11,7 +11,6 @@ ['esp32s2', 'esp32c3', 'esp32s3', 'esp32c2', 'esp32c6', 'esp32h2', 'esp32p4', 'esp32c5', 'esp32c61'], indirect=['target'], ) -@pytest.mark.temp_skip_ci(targets=['esp32p4'], reason='p4 rev3 migration, IDF-14834') def test_temp_sensor_example(dut: Dut) -> None: dut.expect_exact('Install temperature sensor') dut.expect_exact('Enable temperature sensor') diff --git a/examples/peripherals/temperature_sensor/temp_sensor_monitor/pytest_temp_sensor_monitor_example.py b/examples/peripherals/temperature_sensor/temp_sensor_monitor/pytest_temp_sensor_monitor_example.py index a1fc48b1a0a7..f5cccf04ac3e 100644 --- a/examples/peripherals/temperature_sensor/temp_sensor_monitor/pytest_temp_sensor_monitor_example.py +++ b/examples/peripherals/temperature_sensor/temp_sensor_monitor/pytest_temp_sensor_monitor_example.py @@ -7,7 +7,6 @@ @pytest.mark.generic @idf_parametrize('target', ['esp32c6', 'esp32h2', 'esp32p4', 'esp32c5', 'esp32c61'], indirect=['target']) -@pytest.mark.temp_skip_ci(targets=['esp32p4'], reason='p4 rev3 migration, IDF-14834') def test_temp_sensor_monitor_example(dut: Dut) -> None: dut.expect_exact('Install temperature sensor') dut.expect_exact('Enable temperature sensor') diff --git a/examples/peripherals/usb/host/cdc/cdc_acm_host/main/usb_cdc_example_main.c b/examples/peripherals/usb/host/cdc/cdc_acm_host/main/usb_cdc_example_main.c index f8c548e01a84..4f8b4d02c985 100644 --- a/examples/peripherals/usb/host/cdc/cdc_acm_host/main/usb_cdc_example_main.c +++ b/examples/peripherals/usb/host/cdc/cdc_acm_host/main/usb_cdc_example_main.c @@ -67,9 +67,8 @@ static void handle_event(const cdc_acm_host_dev_event_data_t *event, void *user_ case CDC_ACM_HOST_SERIAL_STATE: ESP_LOGI(TAG, "Serial state notif 0x%04X", event->data.serial_state.val); break; - case CDC_ACM_HOST_NETWORK_CONNECTION: default: - ESP_LOGW(TAG, "Unsupported CDC event: %i", event->type); + ESP_LOGW(TAG, "Unsupported CDC event: %d (possibly suspend/resume)", event->type); break; } } diff --git a/examples/peripherals/usb/host/hid/main/hid_host_example.c b/examples/peripherals/usb/host/hid/main/hid_host_example.c index d7e25f101551..16756b1f1b77 100644 --- a/examples/peripherals/usb/host/hid/main/hid_host_example.c +++ b/examples/peripherals/usb/host/hid/main/hid_host_example.c @@ -502,8 +502,9 @@ void hid_host_interface_callback(hid_host_device_handle_t hid_device_handle, hid_proto_name_str[dev_params.proto]); break; default: - ESP_LOGE(TAG, "HID Device, protocol '%s' Unhandled event", - hid_proto_name_str[dev_params.proto]); + ESP_LOGW(TAG, "HID Device, protocol '%s' Unhandled event: %d (possibly suspend/resume)", + hid_proto_name_str[dev_params.proto], + event); break; } } diff --git a/examples/peripherals/usb/host/msc/main/msc_example_main.c b/examples/peripherals/usb/host/msc/main/msc_example_main.c index 690fb82dd3f4..8508d0a96a77 100644 --- a/examples/peripherals/usb/host/msc/main/msc_example_main.c +++ b/examples/peripherals/usb/host/msc/main/msc_example_main.c @@ -264,6 +264,8 @@ static void msc_event_cb(const msc_host_event_t *event, void *arg) .data.device_handle = event->device.handle, }; xQueueSend(app_queue, &message, portMAX_DELAY); + } else { + ESP_LOGW(TAG, "Unsupported MSC event: %d (possibly suspend/resume)", event->event); } } diff --git a/examples/peripherals/usb/host/usb_host_lib/main/class_driver.c b/examples/peripherals/usb/host/usb_host_lib/main/class_driver.c index 5a89e40dea3a..a0b656b9772e 100644 --- a/examples/peripherals/usb/host/usb_host_lib/main/class_driver.c +++ b/examples/peripherals/usb/host/usb_host_lib/main/class_driver.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -80,8 +80,8 @@ static void client_event_cb(const usb_host_client_event_msg_t *event_msg, void * xSemaphoreGive(driver_obj->constant.mux_lock); break; default: - // Should never occur - abort(); + ESP_LOGW(TAG, "Unsupported client event: %d (possibly suspend/resume)", event_msg->event); + break; } } diff --git a/examples/peripherals/usb/host/uvc/main/main.c b/examples/peripherals/usb/host/uvc/main/main.c index 60edd7326be2..91d65486090a 100644 --- a/examples/peripherals/usb/host/uvc/main/main.c +++ b/examples/peripherals/usb/host/uvc/main/main.c @@ -114,7 +114,7 @@ static void stream_callback(const uvc_host_stream_event_data_t *event, void *use ESP_LOGW(TAG, "Frame buffer underflow"); break; default: - abort(); + ESP_LOGW(TAG, "Unsupported UVC event: %d (possibly suspend/resume)", event->type); break; } } diff --git a/examples/system/.build-test-rules.yml b/examples/system/.build-test-rules.yml index 26eb93764bde..ff5c6ab3b9b3 100644 --- a/examples/system/.build-test-rules.yml +++ b/examples/system/.build-test-rules.yml @@ -157,7 +157,7 @@ examples/system/nmi_isr: examples/system/ota/advanced_https_ota: disable: - - if: IDF_TARGET in ["esp32h2", "esp32h21", "esp32h4"] + - if: IDF_TARGET in ["esp32h21", "esp32h4"] temporary: true reason: not supported yet disable_test: @@ -174,10 +174,11 @@ examples/system/ota/advanced_https_ota: - esp_phy - bt - protocol_examples_common + - openthread examples/system/ota/native_ota_example: disable: - - if: IDF_TARGET in ["esp32h2", "esp32h21", "esp32h4"] + - if: IDF_TARGET in ["esp32h21", "esp32h4"] temporary: true reason: not supported yet disable_test: @@ -191,6 +192,7 @@ examples/system/ota/native_ota_example: - esp_driver_gpio - bootloader_support - protocol_examples_common + - openthread examples/system/ota/otatool: enable: @@ -198,7 +200,7 @@ examples/system/ota/otatool: examples/system/ota/partitions_ota: disable: - - if: IDF_TARGET in ["esp32h2", "esp32h21", "esp32h4"] + - if: IDF_TARGET in ["esp32h21", "esp32h4"] temporary: true reason: not supported yet - if: CONFIG_NAME == "recovery_bootloader" and SOC_RECOVERY_BOOTLOADER_SUPPORTED != 1 @@ -220,10 +222,11 @@ examples/system/ota/partitions_ota: - esp_wifi - esp_phy - mbedtls + - openthread examples/system/ota/simple_ota_example: disable: - - if: IDF_TARGET in ["esp32h2", "esp32h21", "esp32h4"] + - if: IDF_TARGET in ["esp32h21", "esp32h4"] temporary: true reason: not supported yet - if: CONFIG_NAME == "spiram" and SOC_SPIRAM_SUPPORTED != 1 @@ -240,6 +243,7 @@ examples/system/ota/simple_ota_example: - esp_wifi - esp_phy - mbedtls + - openthread examples/system/perfmon: enable: diff --git a/examples/system/ota/README.md b/examples/system/ota/README.md index ed153c17377f..4a714722fa0c 100644 --- a/examples/system/ota/README.md +++ b/examples/system/ota/README.md @@ -158,6 +158,46 @@ The ``native_ota_example`` contains code to demonstrate how to check the version In ``native_ota_example``, ``$PROJECT_PATH/version.txt`` is used to define the app version. Change the version in the file to compile the new firmware. +## OTA over Thread + +These examples also support OTA (Over-the-Air) updates over a Thread network and supports binding to a Thread interface on targets with native IEEE 802.15.4 support, such as the ESP32-H2. + +### Thread Connectivity Prerequisites + +To enable OTA over Thread, you need a Thread End Device (which is the "ESP-Dev-Board", for example, ESP32-H2) and a [Thread Border Router](https://github.com/espressif/esp-thread-br). IPv6 connectivity must be established between the Thread End Device, the Thread Border Router, and the HTTP server. You can follow the guide in [Bi-directional IPv6 Connectivity](https://docs.espressif.com/projects/esp-thread-br/en/latest/codelab/connectivity.html) for setup instructions. + +Once IPv6 connectivity is established, the HTTP server will receive a valid IPv6 address starting with the On-link Prefix assigned by the Thread Border Router. This address will be used by the Thread End Device to retrieve the OTA binary. + +### HTTP(S) Server Requirements + +When running the HTTP(S) server, ensure that it supports IPv6. This can be achieved using a OpenSSL based server as detailed above: + +``` +openssl s_server -WWW -key ca_key.pem -cert ca_cert.pem -port 8070 +``` + +* **Note:** Use the IPv6 address from the Bi-directional IPv6 Connectivity guide to generate the certificate (CN prompt). + +* **Note:** The example script for the Python based server does not support IPv6 by default. + +An alternative means of debugging with IPv6 support is to use Python's built-in HTTP server with the `--bind ::` option: + +``` +python -m http.server 8070 --bind :: +``` + +OTA over Thread only supports IPv6 addresses. If the HTTP(S) server can only be accessed via an IPv4 address, the End Device will need to synthesize an IPv6 address using the [NAT64](https://docs.espressif.com/projects/esp-thread-br/en/latest/codelab/nat64.html) feature. + +### Thread Configuration Options (End Device) + +- `idf.py menuconfig → Component config → OpenThread → OpenThread` = `y` *(enabled by default for ESP32-H2)* +- `idf.py menuconfig → Component config → OpenThread → Thread Core Features → Thread Operational Dataset` — must match the Border Router configuration +- `idf.py menuconfig → Component config → Example Configuration → firmware upgrade URL endpoint` = `"https://[fda3:e29:9416:af10:db46:b477:354e:416e]:8070/blink.bin"` + *(Refer to [Thread Connectivity Prerequisites](#thread-connectivity-prerequisites) for details on obtaining this IPv6 address, which belongs to the HTTP(S) server host PC and is accessible by the end device.)* + +> **Note:** +> Thread network throughput is typically lower than Wi-Fi or Ethernet. It is recommended to start with a small OTA binary such as `blink.bin` from `examples/get-started/blink` to validate connectivity. + ## Troubleshooting * Check that your PC can ping the "ESP-Dev-Board" using its IP, and that the IP, AP and other configuration settings are correctly configured in menuconfig diff --git a/examples/system/ota/advanced_https_ota/README.md b/examples/system/ota/advanced_https_ota/README.md index 3ab4875b55ae..89b9ec3ddfa0 100644 --- a/examples/system/ota/advanced_https_ota/README.md +++ b/examples/system/ota/advanced_https_ota/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-P4 | ESP32-S2 | ESP32-S3 | -| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | -------- | # Advanced HTTPS OTA example diff --git a/examples/system/ota/advanced_https_ota/main/Kconfig.projbuild b/examples/system/ota/advanced_https_ota/main/Kconfig.projbuild index 7bc48c99fcbb..1b5813e272f7 100644 --- a/examples/system/ota/advanced_https_ota/main/Kconfig.projbuild +++ b/examples/system/ota/advanced_https_ota/main/Kconfig.projbuild @@ -53,4 +53,11 @@ menu "Example Configuration" This option allows one to configure the OTA process to resume downloading the OTA image from where it left off in case of an error or reboot. + config EXAMPLE_OTA_BUF_SIZE + int "OTA buffer size" + default 1024 + range 1024 65536 + help + Buffer size for OTA data transfers in bytes. + endmenu diff --git a/examples/system/ota/advanced_https_ota/main/advanced_https_ota_example.c b/examples/system/ota/advanced_https_ota/main/advanced_https_ota_example.c index 9664133d4fa0..dbae02c6f814 100644 --- a/examples/system/ota/advanced_https_ota/main/advanced_https_ota_example.c +++ b/examples/system/ota/advanced_https_ota/main/advanced_https_ota_example.c @@ -227,6 +227,7 @@ void advanced_ota_example_task(void *pvParameter) #ifdef CONFIG_EXAMPLE_ENABLE_PARTIAL_HTTP_DOWNLOAD .save_client_session = true, #endif + .buffer_size = CONFIG_EXAMPLE_OTA_BUF_SIZE, }; #ifdef CONFIG_EXAMPLE_FIRMWARE_UPGRADE_URL_FROM_STDIN diff --git a/examples/system/ota/advanced_https_ota/pytest_advanced_ota.py b/examples/system/ota/advanced_https_ota/pytest_advanced_ota.py index 04acfeb49715..11183d98917c 100644 --- a/examples/system/ota/advanced_https_ota/pytest_advanced_ota.py +++ b/examples/system/ota/advanced_https_ota/pytest_advanced_ota.py @@ -281,6 +281,75 @@ def test_examples_protocol_advanced_https_ota_example_ota_resumption(dut: Dut) - thread1.terminate() +@pytest.mark.flash_encryption_ota +@pytest.mark.parametrize('config', ['ota_resumption_flash_enc'], indirect=True) +@pytest.mark.parametrize('skip_autoflash', ['y'], indirect=True) +@idf_parametrize('target', ['esp32'], indirect=['target']) +def test_examples_protocol_advanced_https_ota_example_ota_resumption_flash_enc(dut: Dut) -> None: + """ + This is a positive test case, which stops the download midway and resumes downloading again with + flash encryption enabled. + steps: | + 1. join AP/Ethernet + 2. Fetch OTA image over HTTPS + 3. Reboot with the new OTA image + """ + # Number of iterations to validate OTA + server_port = 8001 + bin_name = 'advanced_https_ota.bin' + + # For flash encryption, we need to manually erase and flash + dut.serial.erase_flash() + dut.serial.flash() + + # Erase NVS partition + dut.serial.erase_partition(NVS_PARTITION) + + # Start server + thread1 = multiprocessing.Process(target=start_https_server, args=(dut.app.binary_path, '0.0.0.0', server_port)) + thread1.daemon = True + thread1.start() + try: + # start test + dut.expect('Loaded app from partition at offset', timeout=30) + + try: + ip_address = dut.expect(r'IPv4 address: (\d+\.\d+\.\d+\.\d+)[^\d]', timeout=60)[1].decode() + print(f'Connected to AP/Ethernet with IP: {ip_address}') + except pexpect.exceptions.TIMEOUT: + raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP/Ethernet') + + dut.expect('Starting Advanced OTA example', timeout=30) + host_ip = get_host_ip4_by_dest_ip(ip_address) + + print('writing to device: {}'.format('https://' + host_ip + ':' + str(server_port) + '/' + bin_name)) + dut.write('https://' + host_ip + ':' + str(server_port) + '/' + bin_name) + dut.expect('Starting OTA...', timeout=60) + + restart_device_with_random_delay(dut, 5, 15) + + # Validate that the device restarts correctly + dut.expect('Loaded app from partition at offset', timeout=180) + + try: + ip_address = dut.expect(r'IPv4 address: (\d+\.\d+\.\d+\.\d+)[^\d]', timeout=60)[1].decode() + print(f'Connected to AP/Ethernet with IP: {ip_address}') + except pexpect.exceptions.TIMEOUT: + raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP/Ethernet') + + dut.expect('Starting Advanced OTA example', timeout=30) + host_ip = get_host_ip4_by_dest_ip(ip_address) + + print('writing to device: {}'.format('https://' + host_ip + ':' + str(server_port) + '/' + bin_name)) + dut.write('https://' + host_ip + ':' + str(server_port) + '/' + bin_name) + dut.expect('Starting OTA...', timeout=60) + + dut.expect('upgrade successful. Rebooting ...', timeout=150) + + finally: + thread1.terminate() + + @pytest.mark.ethernet_ota @idf_parametrize('target', ['esp32'], indirect=['target']) def test_examples_protocol_advanced_https_ota_example_truncated_bin(dut: Dut) -> None: diff --git a/examples/system/ota/advanced_https_ota/sdkconfig.ci.ota_resumption_flash_enc b/examples/system/ota/advanced_https_ota/sdkconfig.ci.ota_resumption_flash_enc new file mode 100644 index 000000000000..7cf6ae8feffc --- /dev/null +++ b/examples/system/ota/advanced_https_ota/sdkconfig.ci.ota_resumption_flash_enc @@ -0,0 +1,37 @@ +CONFIG_EXAMPLE_FIRMWARE_UPGRADE_URL="FROM_STDIN" +CONFIG_EXAMPLE_SKIP_COMMON_NAME_CHECK=y +CONFIG_EXAMPLE_SKIP_VERSION_CHECK=y +CONFIG_EXAMPLE_OTA_RECV_TIMEOUT=15000 +CONFIG_EXAMPLE_ENABLE_OTA_RESUMPTION=y + +CONFIG_EXAMPLE_CONNECT_ETHERNET=y +CONFIG_EXAMPLE_CONNECT_WIFI=n +CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y +CONFIG_EXAMPLE_ETH_PHY_IP101=y +CONFIG_EXAMPLE_ETH_MDC_GPIO=23 +CONFIG_EXAMPLE_ETH_MDIO_GPIO=18 +CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5 +CONFIG_EXAMPLE_ETH_PHY_ADDR=1 +CONFIG_MBEDTLS_TLS_CLIENT_ONLY=y +CONFIG_COMPILER_OPTIMIZATION_SIZE=y +CONFIG_EXAMPLE_CONNECT_IPV6=y +CONFIG_EXAMPLE_ETHERNET_EMAC_TASK_STACK_SIZE=3072 +CONFIG_EXAMPLE_CONNECT_IPV6=n +CONFIG_PARTITION_TABLE_OFFSET=0xd000 + +# Default settings for testing this example in CI. +# This configuration is not secure, don't use it in production! +# See Flash Encryption API Guide for more details. + +CONFIG_SECURE_FLASH_ENC_ENABLED=y +CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT=y +CONFIG_SECURE_BOOT_ALLOW_ROM_BASIC=y +CONFIG_SECURE_BOOT_ALLOW_JTAG=y +CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_ENC=y +CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_DEC=y +CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_CACHE=y +CONFIG_SECURE_FLASH_REQUIRE_ALREADY_ENABLED=y +CONFIG_NVS_ENCRYPTION=n # this test combination is only for flash encryption and ota resumption use-case and hence disabling it. + +# Custom OTA buffer size configuration +CONFIG_EXAMPLE_OTA_BUF_SIZE=2047 diff --git a/examples/system/ota/advanced_https_ota/sdkconfig.defaults.esp32h2 b/examples/system/ota/advanced_https_ota/sdkconfig.defaults.esp32h2 new file mode 100644 index 000000000000..909d9d7a8a7d --- /dev/null +++ b/examples/system/ota/advanced_https_ota/sdkconfig.defaults.esp32h2 @@ -0,0 +1,4 @@ +CONFIG_OPENTHREAD_ENABLED=y + +# CMAC is required for openthread +CONFIG_MBEDTLS_CMAC_C=y diff --git a/examples/system/ota/native_ota_example/README.md b/examples/system/ota/native_ota_example/README.md index 85500a1e2bd9..0b261f4351fe 100644 --- a/examples/system/ota/native_ota_example/README.md +++ b/examples/system/ota/native_ota_example/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-P4 | ESP32-S2 | ESP32-S3 | -| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | -------- | # Native OTA example diff --git a/examples/system/ota/native_ota_example/sdkconfig.defaults.esp32h2 b/examples/system/ota/native_ota_example/sdkconfig.defaults.esp32h2 new file mode 100644 index 000000000000..909d9d7a8a7d --- /dev/null +++ b/examples/system/ota/native_ota_example/sdkconfig.defaults.esp32h2 @@ -0,0 +1,4 @@ +CONFIG_OPENTHREAD_ENABLED=y + +# CMAC is required for openthread +CONFIG_MBEDTLS_CMAC_C=y diff --git a/examples/system/ota/partitions_ota/README.md b/examples/system/ota/partitions_ota/README.md index 5e13b4407306..0d01cf427332 100644 --- a/examples/system/ota/partitions_ota/README.md +++ b/examples/system/ota/partitions_ota/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-P4 | ESP32-S2 | ESP32-S3 | -| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | -------- | # Partition OTA Example diff --git a/examples/system/ota/partitions_ota/main/Kconfig.projbuild b/examples/system/ota/partitions_ota/main/Kconfig.projbuild index 77681a9a492d..5c69912b9628 100644 --- a/examples/system/ota/partitions_ota/main/Kconfig.projbuild +++ b/examples/system/ota/partitions_ota/main/Kconfig.projbuild @@ -50,5 +50,12 @@ menu "Example Configuration" depends on EXAMPLE_CONNECT_ETHERNET help Select ethernet interface to pass the OTA data. + + config EXAMPLE_FIRMWARE_UPGRADE_BIND_IF_THREAD + bool "Bind thread interface" + depends on EXAMPLE_CONNECT_THREAD + help + Select Thread interface to pass the OTA data. + endchoice endmenu diff --git a/examples/system/ota/partitions_ota/main/app_main.c b/examples/system/ota/partitions_ota/main/app_main.c index 02aa42536589..925d7ed71b0b 100644 --- a/examples/system/ota/partitions_ota/main/app_main.c +++ b/examples/system/ota/partitions_ota/main/app_main.c @@ -48,6 +48,8 @@ static const char *bind_interface_name = EXAMPLE_NETIF_DESC_ETH; #elif CONFIG_EXAMPLE_FIRMWARE_UPGRADE_BIND_IF_STA static const char *bind_interface_name = EXAMPLE_NETIF_DESC_STA; +#elif CONFIG_EXAMPLE_FIRMWARE_UPGRADE_BIND_IF_THREAD +static const char *bind_interface_name = EXAMPLE_NETIF_DESC_THREAD; #endif #endif diff --git a/examples/system/ota/partitions_ota/sdkconfig.defaults.esp32h2 b/examples/system/ota/partitions_ota/sdkconfig.defaults.esp32h2 new file mode 100644 index 000000000000..909d9d7a8a7d --- /dev/null +++ b/examples/system/ota/partitions_ota/sdkconfig.defaults.esp32h2 @@ -0,0 +1,4 @@ +CONFIG_OPENTHREAD_ENABLED=y + +# CMAC is required for openthread +CONFIG_MBEDTLS_CMAC_C=y diff --git a/examples/system/ota/simple_ota_example/README.md b/examples/system/ota/simple_ota_example/README.md index 03415d7ba1e7..b63b17d5225e 100644 --- a/examples/system/ota/simple_ota_example/README.md +++ b/examples/system/ota/simple_ota_example/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-P4 | ESP32-S2 | ESP32-S3 | -| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | -------- | # Simple OTA example diff --git a/examples/system/ota/simple_ota_example/main/Kconfig.projbuild b/examples/system/ota/simple_ota_example/main/Kconfig.projbuild index 4404a99f44ed..ec64ddedf471 100644 --- a/examples/system/ota/simple_ota_example/main/Kconfig.projbuild +++ b/examples/system/ota/simple_ota_example/main/Kconfig.projbuild @@ -50,6 +50,12 @@ menu "Example Configuration" depends on EXAMPLE_CONNECT_ETHERNET help Select ethernet interface to pass the OTA data. + + config EXAMPLE_FIRMWARE_UPGRADE_BIND_IF_THREAD + bool "Bind thread interface" + depends on EXAMPLE_CONNECT_THREAD + help + Select Thread interface to pass the OTA data. endchoice config EXAMPLE_TLS_DYN_BUF_RX_STATIC diff --git a/examples/system/ota/simple_ota_example/main/simple_ota_example.c b/examples/system/ota/simple_ota_example/main/simple_ota_example.c index 52e1bac6bbe9..aeb87816d053 100644 --- a/examples/system/ota/simple_ota_example/main/simple_ota_example.c +++ b/examples/system/ota/simple_ota_example/main/simple_ota_example.c @@ -36,6 +36,8 @@ static const char *bind_interface_name = EXAMPLE_NETIF_DESC_ETH; #elif CONFIG_EXAMPLE_FIRMWARE_UPGRADE_BIND_IF_STA static const char *bind_interface_name = EXAMPLE_NETIF_DESC_STA; +#elif CONFIG_EXAMPLE_FIRMWARE_UPGRADE_BIND_IF_THREAD +static const char *bind_interface_name = EXAMPLE_NETIF_DESC_THREAD; #endif #endif diff --git a/examples/system/ota/simple_ota_example/sdkconfig.defaults.esp32h2 b/examples/system/ota/simple_ota_example/sdkconfig.defaults.esp32h2 new file mode 100644 index 000000000000..909d9d7a8a7d --- /dev/null +++ b/examples/system/ota/simple_ota_example/sdkconfig.defaults.esp32h2 @@ -0,0 +1,4 @@ +CONFIG_OPENTHREAD_ENABLED=y + +# CMAC is required for openthread +CONFIG_MBEDTLS_CMAC_C=y diff --git a/tools/test_idf_tools/test_idf_tools.py b/tools/test_idf_tools/test_idf_tools.py index 2df939cf6a6b..6fd848a9ce5b 100755 --- a/tools/test_idf_tools/test_idf_tools.py +++ b/tools/test_idf_tools/test_idf_tools.py @@ -116,7 +116,7 @@ def get_version_dict(): class TestUsageBase(unittest.TestCase): @classmethod def setUpClass(cls): - with open(os.path.join(os.getenv('IDF_PATH'), 'tools/tools.json'), 'r') as json_file: + with open(os.path.join(os.getenv('IDF_PATH'), 'tools/tools.json')) as json_file: tools_dict = json.load(json_file) cls.tools_dict = tools_dict @@ -142,7 +142,7 @@ def setUpClass(cls): cls.temp_tools_dir = tempfile.mkdtemp(prefix='idf_tools_tmp') - print('Using IDF_TOOLS_PATH={}'.format(cls.temp_tools_dir)) + print(f'Using IDF_TOOLS_PATH={cls.temp_tools_dir}') os.environ['IDF_TOOLS_PATH'] = cls.temp_tools_dir cls.idf_env_json = os.path.join(cls.temp_tools_dir, 'idf-env.json') @@ -163,13 +163,13 @@ def tearDown(self): def assert_tool_installed(self, output, tool, tool_version, tool_archive_name=None): if tool_archive_name is None: tool_archive_name = tool - self.assertIn('Installing %s@' % tool + tool_version, output) + self.assertIn(f'Installing {tool}@{tool_version}', output) self.assertRegex(output, re.compile(rf'Downloading \S+{tool_archive_name}')) def assert_tool_not_installed(self, output, tool, tool_version, tool_archive_name=None): if tool_archive_name is None: tool_archive_name = tool - self.assertNotIn('Installing %s@' % tool + tool_version, output) + self.assertNotIn(f'Installing {tool}@{tool_version}', output) self.assertNotRegex(output, re.compile(rf'Downloading \S+{tool_archive_name}')) def run_idf_tools_with_action(self, action): @@ -278,8 +278,8 @@ def test_deactivate(self): 'export IDF_DEACTIVATE_FILE_PATH=', output, 'No IDF_DEACTIVATE_FILE_PATH exported into environment' ) deactivate_file = re.findall(r'(?:IDF_DEACTIVATE_FILE_PATH=")(.*)(?:")', output)[0] - self.assertTrue(os.path.isfile(deactivate_file), 'File {} was not found. '.format(deactivate_file)) - self.assertNotEqual(os.stat(self.idf_env_json).st_size, 0, 'File {} is empty. '.format(deactivate_file)) + self.assertTrue(os.path.isfile(deactivate_file), f'File {deactivate_file} was not found. ') + self.assertNotEqual(os.stat(self.idf_env_json).st_size, 0, f'File {deactivate_file} is empty. ') def test_export_recommended_version(self): always_install_and_recommended_tools = [] @@ -369,14 +369,14 @@ def test_export_with_required_tools_check_skipped(self): class TestUsageUnix(TestUsage): def test_usage_basic(self): output = self.run_idf_tools_with_action(['list']) - self.assertIn('* %s:' % ESP32ULP, output) - self.assertIn('- %s (recommended)' % ESP32ULP_VERSION, output) - self.assertIn('* %s:' % OPENOCD, output) - self.assertIn('- %s (recommended)' % OPENOCD_VERSION, output) - self.assertIn('* %s:' % RISCV_ELF, output) - self.assertIn('- %s (recommended)' % RISCV_ELF_VERSION, output) - self.assertIn('* %s:' % XTENSA_ELF, output) - self.assertIn('- %s (recommended)' % XTENSA_ELF_VERSION, output) + self.assertIn(f'* {ESP32ULP}:', output) + self.assertIn(f'- {ESP32ULP_VERSION} (recommended)', output) + self.assertIn(f'* {OPENOCD}:', output) + self.assertIn(f'- {OPENOCD_VERSION} (recommended)', output) + self.assertIn(f'* {RISCV_ELF}:', output) + self.assertIn(f'- {RISCV_ELF_VERSION} (recommended)', output) + self.assertIn(f'* {XTENSA_ELF}:', output) + self.assertIn(f'- {XTENSA_ELF_VERSION} (recommended)', output) required_tools_installed = 7 output = self.run_idf_tools_with_action(['install']) @@ -403,23 +403,19 @@ def test_usage_basic(self): self.assertIn('version installed in tools directory: ' + tool_version, output) output = self.run_idf_tools_with_action(['export']) - self.assertIn('%s/tools/esp32ulp-elf/%s/esp32ulp-elf/bin' % (self.temp_tools_dir, ESP32ULP_VERSION), output) + self.assertIn(f'{self.temp_tools_dir}/tools/esp32ulp-elf/{ESP32ULP_VERSION}/esp32ulp-elf/bin', output) + self.assertIn(f'{self.temp_tools_dir}/tools/xtensa-esp-elf/{XTENSA_ELF_VERSION}/xtensa-esp-elf/bin', output) + self.assertIn(f'{self.temp_tools_dir}/tools/openocd-esp32/{OPENOCD_VERSION}/openocd-esp32/bin', output) + self.assertIn(f'{self.temp_tools_dir}/tools/riscv32-esp-elf/{RISCV_ELF_VERSION}/riscv32-esp-elf/bin', output) self.assertIn( - '%s/tools/xtensa-esp-elf/%s/xtensa-esp-elf/bin' % (self.temp_tools_dir, XTENSA_ELF_VERSION), output - ) - self.assertIn('%s/tools/openocd-esp32/%s/openocd-esp32/bin' % (self.temp_tools_dir, OPENOCD_VERSION), output) - self.assertIn( - '%s/tools/riscv32-esp-elf/%s/riscv32-esp-elf/bin' % (self.temp_tools_dir, RISCV_ELF_VERSION), output - ) - self.assertIn( - '%s/tools/xtensa-esp-elf-gdb/%s/xtensa-esp-elf-gdb/bin' % (self.temp_tools_dir, XTENSA_ESP_GDB_VERSION), + f'{self.temp_tools_dir}/tools/xtensa-esp-elf-gdb/{XTENSA_ESP_GDB_VERSION}/xtensa-esp-elf-gdb/bin', output, ) self.assertIn( - '%s/tools/riscv32-esp-elf-gdb/%s/riscv32-esp-elf-gdb/bin' % (self.temp_tools_dir, RISCV_ESP_GDB_VERSION), + f'{self.temp_tools_dir}/tools/riscv32-esp-elf-gdb/{RISCV_ESP_GDB_VERSION}/riscv32-esp-elf-gdb/bin', output, ) - self.assertIn('%s/tools/esp-rom-elfs/%s/' % (self.temp_tools_dir, ESP_ROM_ELFS_VERSION), output) + self.assertIn(f'{self.temp_tools_dir}/tools/esp-rom-elfs/{ESP_ROM_ELFS_VERSION}/', output) output = self.run_idf_tools_with_action(['list', '--outdated']) self.assertEqual('', output) @@ -470,23 +466,19 @@ def test_tools_for_esp32(self): self.assertIn('version installed in tools directory: ' + tool_version, output) output = self.run_idf_tools_with_action(['export']) - self.assertIn('%s/tools/esp32ulp-elf/%s/esp32ulp-elf/bin' % (self.temp_tools_dir, ESP32ULP_VERSION), output) - self.assertIn( - '%s/tools/xtensa-esp-elf/%s/xtensa-esp-elf/bin' % (self.temp_tools_dir, XTENSA_ELF_VERSION), output - ) - self.assertIn('%s/tools/openocd-esp32/%s/openocd-esp32/bin' % (self.temp_tools_dir, OPENOCD_VERSION), output) + self.assertIn(f'{self.temp_tools_dir}/tools/esp32ulp-elf/{ESP32ULP_VERSION}/esp32ulp-elf/bin', output) + self.assertIn(f'{self.temp_tools_dir}/tools/xtensa-esp-elf/{XTENSA_ELF_VERSION}/xtensa-esp-elf/bin', output) + self.assertIn(f'{self.temp_tools_dir}/tools/openocd-esp32/{OPENOCD_VERSION}/openocd-esp32/bin', output) self.assertIn( - '%s/tools/xtensa-esp-elf-gdb/%s/xtensa-esp-elf-gdb/bin' % (self.temp_tools_dir, XTENSA_ESP_GDB_VERSION), + f'{self.temp_tools_dir}/tools/xtensa-esp-elf-gdb/{XTENSA_ESP_GDB_VERSION}/xtensa-esp-elf-gdb/bin', output, ) + self.assertNotIn(f'{self.temp_tools_dir}/tools/riscv32-esp-elf/{RISCV_ELF_VERSION}/riscv32-esp-elf/bin', output) self.assertNotIn( - '%s/tools/riscv32-esp-elf/%s/riscv32-esp-elf/bin' % (self.temp_tools_dir, RISCV_ELF_VERSION), output - ) - self.assertNotIn( - '%s/tools/riscv32-esp-elf-gdb/%s/riscv32-esp-elf-gdb/bin' % (self.temp_tools_dir, RISCV_ESP_GDB_VERSION), + f'{self.temp_tools_dir}/tools/riscv32-esp-elf-gdb/{RISCV_ESP_GDB_VERSION}/riscv32-esp-elf-gdb/bin', output, ) - self.assertIn('%s/tools/esp-rom-elfs/%s/' % (self.temp_tools_dir, ESP_ROM_ELFS_VERSION), output) + self.assertIn(f'{self.temp_tools_dir}/tools/esp-rom-elfs/{ESP_ROM_ELFS_VERSION}/', output) def test_tools_for_esp32c3(self): required_tools_installed = 4 @@ -506,19 +498,15 @@ def test_tools_for_esp32c3(self): self.assertIn('version installed in tools directory: ' + tool_version, output) output = self.run_idf_tools_with_action(['export']) - self.assertIn('%s/tools/openocd-esp32/%s/openocd-esp32/bin' % (self.temp_tools_dir, OPENOCD_VERSION), output) - self.assertIn( - '%s/tools/riscv32-esp-elf/%s/riscv32-esp-elf/bin' % (self.temp_tools_dir, RISCV_ELF_VERSION), output - ) - self.assertNotIn('%s/tools/esp32ulp-elf/%s/esp32ulp-elf/bin' % (self.temp_tools_dir, ESP32ULP_VERSION), output) + self.assertIn(f'{self.temp_tools_dir}/tools/openocd-esp32/{OPENOCD_VERSION}/openocd-esp32/bin', output) + self.assertIn(f'{self.temp_tools_dir}/tools/riscv32-esp-elf/{RISCV_ELF_VERSION}/riscv32-esp-elf/bin', output) + self.assertNotIn(f'{self.temp_tools_dir}/tools/esp32ulp-elf/{ESP32ULP_VERSION}/esp32ulp-elf/bin', output) + self.assertNotIn(f'{self.temp_tools_dir}/tools/xtensa-esp-elf/{XTENSA_ELF_VERSION}/xtensa-esp-elf/bin', output) self.assertNotIn( - '%s/tools/xtensa-esp-elf/%s/xtensa-esp-elf/bin' % (self.temp_tools_dir, XTENSA_ELF_VERSION), output - ) - self.assertNotIn( - '%s/tools/xtensa-esp-elf-gdb/%s/xtensa-esp-elf-gdb/bin' % (self.temp_tools_dir, XTENSA_ESP_GDB_VERSION), + f'{self.temp_tools_dir}/tools/xtensa-esp-elf-gdb/{XTENSA_ESP_GDB_VERSION}/xtensa-esp-elf-gdb/bin', output, ) - self.assertIn('%s/tools/esp-rom-elfs/%s/' % (self.temp_tools_dir, ESP_ROM_ELFS_VERSION), output) + self.assertIn(f'{self.temp_tools_dir}/tools/esp-rom-elfs/{ESP_ROM_ELFS_VERSION}/', output) def test_tools_for_esp32s2(self): required_tools_installed = 6 @@ -538,23 +526,19 @@ def test_tools_for_esp32s2(self): self.assertIn('version installed in tools directory: ' + tool_version, output) output = self.run_idf_tools_with_action(['export']) + self.assertIn(f'{self.temp_tools_dir}/tools/xtensa-esp-elf/{XTENSA_ELF_VERSION}/xtensa-esp-elf/bin', output) + self.assertIn(f'{self.temp_tools_dir}/tools/openocd-esp32/{OPENOCD_VERSION}/openocd-esp32/bin', output) + self.assertIn(f'{self.temp_tools_dir}/tools/esp32ulp-elf/{ESP32ULP_VERSION}/esp32ulp-elf/bin', output) + self.assertIn(f'{self.temp_tools_dir}/tools/riscv32-esp-elf/{RISCV_ELF_VERSION}/riscv32-esp-elf/bin', output) self.assertIn( - '%s/tools/xtensa-esp-elf/%s/xtensa-esp-elf/bin' % (self.temp_tools_dir, XTENSA_ELF_VERSION), output - ) - self.assertIn('%s/tools/openocd-esp32/%s/openocd-esp32/bin' % (self.temp_tools_dir, OPENOCD_VERSION), output) - self.assertIn('%s/tools/esp32ulp-elf/%s/esp32ulp-elf/bin' % (self.temp_tools_dir, ESP32ULP_VERSION), output) - self.assertIn( - '%s/tools/riscv32-esp-elf/%s/riscv32-esp-elf/bin' % (self.temp_tools_dir, RISCV_ELF_VERSION), output - ) - self.assertIn( - '%s/tools/xtensa-esp-elf-gdb/%s/xtensa-esp-elf-gdb/bin' % (self.temp_tools_dir, XTENSA_ESP_GDB_VERSION), + f'{self.temp_tools_dir}/tools/xtensa-esp-elf-gdb/{XTENSA_ESP_GDB_VERSION}/xtensa-esp-elf-gdb/bin', output, ) self.assertNotIn( - '%s/tools/riscv32-esp-elf-gdb/%s/riscv32-esp-elf-gdb/bin' % (self.temp_tools_dir, RISCV_ESP_GDB_VERSION), + f'{self.temp_tools_dir}/tools/riscv32-esp-elf-gdb/{RISCV_ESP_GDB_VERSION}/riscv32-esp-elf-gdb/bin', output, ) - self.assertIn('%s/tools/esp-rom-elfs/%s/' % (self.temp_tools_dir, ESP_ROM_ELFS_VERSION), output) + self.assertIn(f'{self.temp_tools_dir}/tools/esp-rom-elfs/{ESP_ROM_ELFS_VERSION}/', output) def test_tools_for_esp32s3(self): required_tools_installed = 6 @@ -580,23 +564,19 @@ def test_tools_for_esp32s3(self): self.assertIn('version installed in tools directory: ' + tool_version, output) output = self.run_idf_tools_with_action(['export']) - self.assertIn('%s/tools/openocd-esp32/%s/openocd-esp32/bin' % (self.temp_tools_dir, OPENOCD_VERSION), output) - self.assertIn( - '%s/tools/xtensa-esp-elf/%s/xtensa-esp-elf/bin' % (self.temp_tools_dir, XTENSA_ELF_VERSION), output - ) - self.assertIn('%s/tools/esp32ulp-elf/%s/esp32ulp-elf/bin' % (self.temp_tools_dir, ESP32ULP_VERSION), output) - self.assertIn( - '%s/tools/riscv32-esp-elf/%s/riscv32-esp-elf/bin' % (self.temp_tools_dir, RISCV_ELF_VERSION), output - ) + self.assertIn(f'{self.temp_tools_dir}/tools/openocd-esp32/{OPENOCD_VERSION}/openocd-esp32/bin', output) + self.assertIn(f'{self.temp_tools_dir}/tools/xtensa-esp-elf/{XTENSA_ELF_VERSION}/xtensa-esp-elf/bin', output) + self.assertIn(f'{self.temp_tools_dir}/tools/esp32ulp-elf/{ESP32ULP_VERSION}/esp32ulp-elf/bin', output) + self.assertIn(f'{self.temp_tools_dir}/tools/riscv32-esp-elf/{RISCV_ELF_VERSION}/riscv32-esp-elf/bin', output) self.assertIn( - '%s/tools/xtensa-esp-elf-gdb/%s/xtensa-esp-elf-gdb/bin' % (self.temp_tools_dir, XTENSA_ESP_GDB_VERSION), + f'{self.temp_tools_dir}/tools/xtensa-esp-elf-gdb/{XTENSA_ESP_GDB_VERSION}/xtensa-esp-elf-gdb/bin', output, ) self.assertNotIn( - '%s/tools/riscv32-esp-elf-gdb/%s/riscv32-esp-elf-gdb/bin' % (self.temp_tools_dir, RISCV_ESP_GDB_VERSION), + f'{self.temp_tools_dir}/tools/riscv32-esp-elf-gdb/{RISCV_ESP_GDB_VERSION}/riscv32-esp-elf-gdb/bin', output, ) - self.assertIn('%s/tools/esp-rom-elfs/%s/' % (self.temp_tools_dir, ESP_ROM_ELFS_VERSION), output) + self.assertIn(f'{self.temp_tools_dir}/tools/esp-rom-elfs/{ESP_ROM_ELFS_VERSION}/', output) def test_tools_for_esp32p4(self): required_tools_installed = 4 @@ -664,14 +644,14 @@ def test_export_supported_version_cmake(self): class TestUsageWin(TestUsage): def test_usage_basic_win(self): output = self.run_idf_tools_with_action(['list']) - self.assertIn('* %s:' % ESP32ULP, output) - self.assertIn('- %s (recommended)' % ESP32ULP_VERSION, output) - self.assertIn('* %s:' % OPENOCD, output) - self.assertIn('- %s (recommended)' % OPENOCD_VERSION, output) - self.assertIn('* %s:' % RISCV_ELF, output) - self.assertIn('- %s (recommended)' % RISCV_ELF_VERSION, output) - self.assertIn('* %s:' % XTENSA_ELF, output) - self.assertIn('- %s (recommended)' % XTENSA_ELF_VERSION, output) + self.assertIn(f'* {ESP32ULP}:', output) + self.assertIn(f'- {ESP32ULP_VERSION} (recommended)', output) + self.assertIn(f'* {OPENOCD}:', output) + self.assertIn(f'- {OPENOCD_VERSION} (recommended)', output) + self.assertIn(f'* {RISCV_ELF}:', output) + self.assertIn(f'- {RISCV_ELF_VERSION} (recommended)', output) + self.assertIn(f'* {XTENSA_ELF}:', output) + self.assertIn(f'- {XTENSA_ELF_VERSION} (recommended)', output) required_tools_installed = 12 output = self.run_idf_tools_with_action(['install']) @@ -739,7 +719,7 @@ def test_usage_basic_win(self): self.assertIn(os.path.join(self.temp_tools_dir, 'tools', NINJA, NINJA_VERSION), output) self.assertIn(os.path.join(self.temp_tools_dir, 'tools', IDF_EXE, IDF_EXE_VERSION), output) self.assertIn( - os.path.join(self.temp_tools_dir, 'tools', CCACHE, CCACHE_VERSION, 'ccache-4.11.2-windows-x86_64'), output + os.path.join(self.temp_tools_dir, 'tools', CCACHE, CCACHE_VERSION, 'ccache-4.12.1-windows-x86_64'), output ) self.assertIn( os.path.join(self.temp_tools_dir, 'tools', DFU_UTIL, DFU_UTIL_VERSION, 'dfu-util-0.11-win64'), output @@ -843,7 +823,7 @@ def test_tools_for_esp32_win(self): self.assertIn(os.path.join(self.temp_tools_dir, 'tools', NINJA, NINJA_VERSION), output) self.assertIn(os.path.join(self.temp_tools_dir, 'tools', IDF_EXE, IDF_EXE_VERSION), output) self.assertIn( - os.path.join(self.temp_tools_dir, 'tools', CCACHE, CCACHE_VERSION, 'ccache-4.11.2-windows-x86_64'), output + os.path.join(self.temp_tools_dir, 'tools', CCACHE, CCACHE_VERSION, 'ccache-4.12.1-windows-x86_64'), output ) self.assertNotIn( os.path.join(self.temp_tools_dir, 'tools', DFU_UTIL, DFU_UTIL_VERSION, 'dfu-util-0.11-win64'), output @@ -915,7 +895,7 @@ def test_tools_for_esp32c3_win(self): self.assertIn(os.path.join(self.temp_tools_dir, 'tools', NINJA, NINJA_VERSION), output) self.assertIn(os.path.join(self.temp_tools_dir, 'tools', IDF_EXE, IDF_EXE_VERSION), output) self.assertIn( - os.path.join(self.temp_tools_dir, 'tools', CCACHE, CCACHE_VERSION, 'ccache-4.11.2-windows-x86_64'), output + os.path.join(self.temp_tools_dir, 'tools', CCACHE, CCACHE_VERSION, 'ccache-4.12.1-windows-x86_64'), output ) self.assertNotIn( os.path.join(self.temp_tools_dir, 'tools', DFU_UTIL, DFU_UTIL_VERSION, 'dfu-util-0.11-win64'), output @@ -993,7 +973,7 @@ def test_tools_for_esp32s2_win(self): self.assertIn(os.path.join(self.temp_tools_dir, 'tools', NINJA, NINJA_VERSION), output) self.assertIn(os.path.join(self.temp_tools_dir, 'tools', IDF_EXE, IDF_EXE_VERSION), output) self.assertIn( - os.path.join(self.temp_tools_dir, 'tools', CCACHE, CCACHE_VERSION, 'ccache-4.11.2-windows-x86_64'), output + os.path.join(self.temp_tools_dir, 'tools', CCACHE, CCACHE_VERSION, 'ccache-4.12.1-windows-x86_64'), output ) self.assertIn( os.path.join(self.temp_tools_dir, 'tools', DFU_UTIL, DFU_UTIL_VERSION, 'dfu-util-0.11-win64'), output @@ -1073,7 +1053,7 @@ def test_tools_for_esp32s3_win(self): self.assertIn(os.path.join(self.temp_tools_dir, 'tools', NINJA, NINJA_VERSION), output) self.assertIn(os.path.join(self.temp_tools_dir, 'tools', IDF_EXE, IDF_EXE_VERSION), output) self.assertIn( - os.path.join(self.temp_tools_dir, 'tools', CCACHE, CCACHE_VERSION, 'ccache-4.11.2-windows-x86_64'), output + os.path.join(self.temp_tools_dir, 'tools', CCACHE, CCACHE_VERSION, 'ccache-4.12.1-windows-x86_64'), output ) self.assertIn( os.path.join(self.temp_tools_dir, 'tools', DFU_UTIL, DFU_UTIL_VERSION, 'dfu-util-0.11-win64'), output @@ -1123,7 +1103,7 @@ def test_tools_for_esp32p4_win(self): self.assertIn(os.path.join(self.temp_tools_dir, 'tools', NINJA, NINJA_VERSION), output) self.assertIn(os.path.join(self.temp_tools_dir, 'tools', IDF_EXE, IDF_EXE_VERSION), output) self.assertIn( - os.path.join(self.temp_tools_dir, 'tools', CCACHE, CCACHE_VERSION, 'ccache-4.11.2-windows-x86_64'), output + os.path.join(self.temp_tools_dir, 'tools', CCACHE, CCACHE_VERSION, 'ccache-4.12.1-windows-x86_64'), output ) self.assertNotIn( os.path.join(self.temp_tools_dir, 'tools', XTENSA_ELF, XTENSA_ELF_VERSION, XTENSA_ELF, 'bin'), output @@ -1175,16 +1155,16 @@ def test_validation(self): def test_json_rewrite(self): idf_tools.main(['rewrite']) - with open(self.tools_old, 'r') as f: + with open(self.tools_old) as f: json_old = f.read() - with open(self.tools_new, 'r') as f: + with open(self.tools_new) as f: json_new = f.read() self.assertEqual(json_old, json_new, "Please check 'tools/tools.new.json' to find a cause!") def add_version_get_expected_json(self, addition_file, replace=False): - with open(self.tools_old, 'r') as f: + with open(self.tools_old) as f: expected_json = json.load(f) - with open(addition_file, 'r') as f: + with open(addition_file) as f: addition_json = json.load(f) for tool in expected_json['tools']: if tool['name'] == self.test_tool_name: @@ -1197,7 +1177,7 @@ def add_version_get_expected_json(self, addition_file, replace=False): def test_add_version_artifact_addition(self): filenames = [] - with open('add_version/artifact_input.json', 'r') as f: + with open('add_version/artifact_input.json') as f: add_tools_info = json.load(f) for tool in add_tools_info: filenames.append(tool['filename']) @@ -1218,7 +1198,7 @@ def test_add_version_artifact_addition(self): + filenames ) expected_json = self.add_version_get_expected_json('add_version/artifact_expected_addition.json') - with open(self.tools_new, 'r') as f1: + with open(self.tools_new) as f1: self.assertEqual(json.load(f1), expected_json, "Please check 'tools/tools.new.json' to find a cause!") def test_add_version_checksum_addition(self): @@ -1236,7 +1216,7 @@ def test_add_version_checksum_addition(self): ] ) expected_json = self.add_version_get_expected_json('add_version/checksum_expected_addition.json') - with open(self.tools_new, 'r') as f1: + with open(self.tools_new) as f1: self.assertEqual(json.load(f1), expected_json, "Please check 'tools/tools.new.json' to find a cause!") def test_add_version_checksum_with_override(self): @@ -1255,7 +1235,7 @@ def test_add_version_checksum_with_override(self): ] ) expected_json = self.add_version_get_expected_json('add_version/checksum_expected_override.json', True) - with open(self.tools_new, 'r') as f1: + with open(self.tools_new) as f1: self.assertEqual(json.load(f1), expected_json, "Please check 'tools/tools.new.json' to find a cause!") @@ -1299,7 +1279,7 @@ def tearDownClass(cls): '\nINFO: System dependencies have been modified for TestSystemDependencies. ' 'Other tests may not work correctly' ) - super(TestSystemDependencies, cls).tearDownClass() + super().tearDownClass() def test_commands_when_nodeps(self): # Prerequisites diff --git a/tools/tools.json b/tools/tools.json index cf94ce86e9a9..5f82060b2447 100644 --- a/tools/tools.json +++ b/tools/tools.json @@ -826,7 +826,7 @@ "description": "Ccache (compiler cache)", "export_paths": [ [ - "ccache-4.11.2-windows-x86_64" + "ccache-4.12.1-windows-x86_64" ] ], "export_vars": { @@ -854,12 +854,12 @@ "version_regex": "ccache version ([0-9.]+)", "versions": [ { - "name": "4.11.2", + "name": "4.12.1", "status": "recommended", "win64": { - "sha256": "1f39f3ad5aae3fe915e99ad1302633bc8f6718e58fa7c0de2b0ba7e080f0f08c", - "size": 1642225, - "url": "https://github.com/ccache/ccache/releases/download/v4.11.2/ccache-4.11.2-windows-x86_64.zip" + "sha256": "98aea520d66905b8ba7a8e648a4cc0ca941d5e119d441f1e879a4a9045bf18f6", + "size": 1710234, + "url": "https://github.com/ccache/ccache/releases/download/v4.12.1/ccache-4.12.1-windows-x86_64.zip" } } ]