From 7280739e3b1b5618ffe2e84aeb89d5e70c91752b Mon Sep 17 00:00:00 2001 From: Song Ruo Jing Date: Tue, 18 Mar 2025 12:11:45 +0800 Subject: [PATCH] fix(ledc): fix race condition in ledc_fade_stop causing assert failure Closes https://github.com/espressif/esp-idf/issues/15580 --- components/driver/ledc/ledc.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/components/driver/ledc/ledc.c b/components/driver/ledc/ledc.c index b5e4e21932..2d2925caed 100644 --- a/components/driver/ledc/ledc.c +++ b/components/driver/ledc/ledc.c @@ -1234,15 +1234,17 @@ esp_err_t ledc_fade_stop(ledc_mode_t speed_mode, ledc_channel_t channel) LEDC_CHECK(p_ledc_obj[speed_mode] != NULL, LEDC_NOT_INIT, ESP_ERR_INVALID_STATE); LEDC_CHECK(ledc_fade_channel_init_check(speed_mode, channel) == ESP_OK , LEDC_FADE_INIT_ERROR_STR, ESP_FAIL); ledc_fade_t *fade = s_ledc_fade_rec[speed_mode][channel]; - ledc_fade_fsm_t state = fade->fsm; + bool skip = false; bool wait_for_idle = false; + portENTER_CRITICAL(&ledc_spinlock); + ledc_fade_fsm_t state = fade->fsm; assert(state != LEDC_FSM_KILLED_PENDING); if (state == LEDC_FSM_IDLE) { // if there is no fade going on, do nothing - return ESP_OK; + skip = true; + goto exit; } // Fade state is either HW_FADE or ISR_CAL (there is a fade in process) - portENTER_CRITICAL(&ledc_spinlock); // Disable ledc channel interrupt first ledc_enable_intr_type(speed_mode, channel, LEDC_INTR_DISABLE); // Config duty to the duty cycle at this moment @@ -1257,21 +1259,22 @@ esp_err_t ledc_fade_stop(ledc_mode_t speed_mode, ledc_channel_t channel) 0 //uint32_t duty_scale ); _ledc_update_duty(speed_mode, channel); - state = fade->fsm; - assert(state != LEDC_FSM_IDLE && state != LEDC_FSM_KILLED_PENDING); if (state == LEDC_FSM_HW_FADE) { fade->fsm = LEDC_FSM_IDLE; } else if (state == LEDC_FSM_ISR_CAL) { fade->fsm = LEDC_FSM_KILLED_PENDING; wait_for_idle = true; } +exit: portEXIT_CRITICAL(&ledc_spinlock); if (wait_for_idle) { // Wait for ISR return, which gives the semaphore and switchs state to IDLE _ledc_fade_hw_acquire(speed_mode, channel); assert(fade->fsm == LEDC_FSM_IDLE); } - _ledc_fade_hw_release(speed_mode, channel); + if (!skip) { + _ledc_fade_hw_release(speed_mode, channel); + } return ESP_OK; } #endif